@chainsafe/ssz
Version:
Simple Serialize
75 lines • 2.79 kB
JavaScript
import { hasher } from "@chainsafe/persistent-merkle-tree";
import { zeroHash } from "@chainsafe/persistent-merkle-tree";
/** Dedicated property to cache hashTreeRoot of immutable CompositeType values */
export const symbolCachedPermanentRoot = Symbol("ssz_cached_permanent_root");
/**
* Cache a root for a ValueWithCachedPermanentRoot instance
* - if safeCache is true and output is 32 bytes and offset is 0, use output directly
* - if safeCache, use output subarray
* - otherwise, need to clone the root at output offset
*/
export function cacheRoot(value, output, offset, safeCache) {
const cachedRoot = safeCache && output.length === 32 && offset === 0
? output
: safeCache
? output.subarray(offset, offset + 32)
: // Buffer.prototype.slice does not copy memory, Enforce Uint8Array usage https://github.com/nodejs/node/issues/28087
Uint8Array.prototype.slice.call(output, offset, offset + 32);
value[symbolCachedPermanentRoot] = cachedRoot;
}
export function hash64(bytes32A, bytes32B) {
return hasher.digest64(bytes32A, bytes32B);
}
export function merkleize(chunks, padFor) {
const layerCount = bitLength(nextPowerOf2(padFor) - 1);
if (chunks.length === 0) {
return zeroHash(layerCount);
}
let chunkCount = chunks.length;
// Instead of pushing on all padding zero chunks at the leaf level
// we push on zero hash chunks at the highest possible level to avoid over-hashing
for (let l = 0; l < layerCount; l++) {
const padCount = chunkCount % 2;
const paddedChunkCount = chunkCount + padCount;
// if the chunks.length is odd
// we need to push on the zero-hash of that level to merkleize that level
for (let i = 0; i < padCount; i++) {
chunks[chunkCount + i] = zeroHash(l);
}
for (let i = 0; i < paddedChunkCount; i += 2) {
chunks[i / 2] = hash64(chunks[i], chunks[i + 1]);
}
chunkCount = paddedChunkCount / 2;
}
return chunks[0];
}
/** @ignore */
export function mixInLength(root, length) {
const lengthBuf = Buffer.alloc(32);
lengthBuf.writeUIntLE(length, 0, 6);
return hash64(root, lengthBuf);
}
// x2 faster than bitLengthStr() which uses Number.toString(2)
export function bitLength(i) {
if (i === 0) {
return 0;
}
return Math.floor(Math.log2(i)) + 1;
}
/**
* Given maxChunkCount return the chunkDepth
* ```
* n: [0,1,2,3,4,5,6,7,8,9]
* d: [0,0,1,2,2,3,3,3,3,4]
* ```
*/
export function maxChunksToDepth(n) {
if (n === 0)
return 0;
return Math.ceil(Math.log2(n));
}
/** @ignore */
export function nextPowerOf2(n) {
return n <= 0 ? 1 : Math.pow(2, bitLength(n - 1));
}
//# sourceMappingURL=merkleize.js.map