56 lines
1.8 KiB
JavaScript
56 lines
1.8 KiB
JavaScript
|
// For ESM compatibility
|
||
|
import pkg from 'js-sha3';
|
||
|
const {
|
||
|
keccak256
|
||
|
} = pkg;
|
||
|
import { BeeArgumentError } from "../utils/error.js";
|
||
|
import { keccak256Hash } from "../utils/hash.js";
|
||
|
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
|
||
|
*/
|
||
|
|
||
|
export 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 = keccak256Hash(chunkHashInput);
|
||
|
return chunkHash;
|
||
|
}
|
||
|
|
||
|
function bmtRootHash(payload) {
|
||
|
if (payload.length > MAX_CHUNK_PAYLOAD_SIZE) {
|
||
|
throw new 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;
|
||
|
}
|