56 lines
2.1 KiB
JavaScript
56 lines
2.1 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.bmtHash = void 0;
|
|
// For ESM compatibility
|
|
const js_sha3_1 = __importDefault(require("js-sha3"));
|
|
const { keccak256 } = js_sha3_1.default;
|
|
const error_1 = require("../utils/error");
|
|
const hash_1 = require("../utils/hash");
|
|
const MAX_CHUNK_PAYLOAD_SIZE = 4096;
|
|
const SEGMENT_SIZE = 32;
|
|
const SEGMENT_PAIR_SIZE = 2 * SEGMENT_SIZE;
|
|
const HASH_SIZE = 32;
|
|
/**
|
|
* Calculate a Binary Merkle Tree hash for a chunk
|
|
*
|
|
* The BMT chunk address is the hash of the 8 byte span and the root
|
|
* hash of a binary Merkle tree (BMT) built on the 32-byte segments
|
|
* of the underlying data.
|
|
*
|
|
* If the chunk content is less than 4k, the hash is calculated as
|
|
* if the chunk was padded with all zeros up to 4096 bytes.
|
|
*
|
|
* @param chunkContent Chunk data including span and payload as well
|
|
*
|
|
* @returns the keccak256 hash in a byte array
|
|
*/
|
|
function bmtHash(chunkContent) {
|
|
const span = chunkContent.slice(0, 8);
|
|
const payload = chunkContent.slice(8);
|
|
const rootHash = bmtRootHash(payload);
|
|
const chunkHashInput = new Uint8Array([...span, ...rootHash]);
|
|
const chunkHash = (0, hash_1.keccak256Hash)(chunkHashInput);
|
|
return chunkHash;
|
|
}
|
|
exports.bmtHash = bmtHash;
|
|
function bmtRootHash(payload) {
|
|
if (payload.length > MAX_CHUNK_PAYLOAD_SIZE) {
|
|
throw new error_1.BeeArgumentError('invalid data length', payload);
|
|
}
|
|
// create an input buffer padded with zeros
|
|
let input = new Uint8Array([...payload, ...new Uint8Array(MAX_CHUNK_PAYLOAD_SIZE - payload.length)]);
|
|
while (input.length !== HASH_SIZE) {
|
|
const output = new Uint8Array(input.length / 2);
|
|
// in each round we hash the segment pairs together
|
|
for (let offset = 0; offset < input.length; offset += SEGMENT_PAIR_SIZE) {
|
|
const hashNumbers = keccak256.array(input.slice(offset, offset + SEGMENT_PAIR_SIZE));
|
|
output.set(hashNumbers, offset / 2);
|
|
}
|
|
input = output;
|
|
}
|
|
return input;
|
|
}
|