UNPKG

postchain-client

Version:

Client library for accessing a Postchain node through REST.

110 lines (109 loc) 4.29 kB
import { hashConcat } from "../encryption/encryption"; import { Buffer } from "buffer"; var internalNodePrefix = Buffer.alloc(1, 0); var leafPrefix = Buffer.alloc(1, 1); var nonExistingNodeHash = Buffer.alloc(32); export function calculateRoot(hashes, depth, leafDepth) { var numTransactions = hashes.length; if (numTransactions === 0) { return Buffer.alloc(32); } if (depth === undefined) { depth = 0; } if (!leafDepth) { leafDepth = Math.ceil(Math.log2(numTransactions)); } if (depth === leafDepth) { return hashes[0]; } var maxLeavesPerChild = Math.pow(2, leafDepth - depth - 1); var prefix = depth === leafDepth - 1 ? leafPrefix : internalNodePrefix; if (numTransactions <= maxLeavesPerChild) { var left = calculateRoot(hashes, depth + 1, leafDepth); return hashConcat([prefix, left, nonExistingNodeHash]); } var left = calculateRoot(hashes.slice(0, maxLeavesPerChild), depth + 1, leafDepth); var right = calculateRoot(hashes.slice(maxLeavesPerChild), depth + 1, leafDepth); return hashConcat([prefix, left, prefix, right]); } function internalMerklePath(hashes, targetIndex, depth, leafDepth) { var numTransactions = hashes.length; if (depth === leafDepth) { return []; } var maxLeavesPerChild = Math.pow(2, leafDepth - depth - 1); var prefix = depth == leafDepth - 1 ? leafPrefix : internalNodePrefix; if (numTransactions <= maxLeavesPerChild) { var path = internalMerklePath(hashes, targetIndex, depth + 1, leafDepth); path.push({ side: 1, hash: nonExistingNodeHash }); return path; } if (targetIndex < maxLeavesPerChild) { var path = internalMerklePath(hashes.slice(0, maxLeavesPerChild), targetIndex, depth + 1, leafDepth); var right = calculateRoot(hashes.slice(maxLeavesPerChild), depth + 1, leafDepth); path.push({ side: 1, hash: right }); } else { var left = calculateRoot(hashes.slice(0, maxLeavesPerChild), depth + 1, leafDepth); var path = internalMerklePath(hashes.slice(maxLeavesPerChild), targetIndex - maxLeavesPerChild, depth + 1, leafDepth); path.push({ side: 0, hash: left }); } return path; } /* * a path looks like this: * {merklePath: [{side: <0|1>, hash: <hash buffer depth n-1>}, * {side: <0|1>, hash: <hash buffer depth n-2>}, * ... * {side: <0|1>, hash: <hash buffer depth 1>}]} */ export function merklePath(hashes, target) { if (!hashes || hashes.length == 0) { throw new Error("Cannot make merkle path from empty transaction set"); } var index = -1; for (var i = 0; i < hashes.length; i++) { if (hashes[i].equals(target)) { index = i; break; } } if (index === -1) { throw new Error("Target is not in list of hashes"); } var leafDepth = Math.ceil(Math.log2(hashes.length)); var path = internalMerklePath(hashes, index, 0, leafDepth); return path; } /** * * @param path The merkle path to validate. * Format [{side: <0|1>, hash: <hash buffer depth n-1>}, * {side: <0|1>, hash: <hash buffer depth n-2>}, * ..., * {side: <0|1>, hash: <hash buffer depth 1>}] * @param target the leaf hash that the path proves belongs in the merkleRoot * @param merkleRoot The merkle root that supposedly contains the target via the supplied path. * The merkle root is typically taken from a block header. */ export function validateMerklePath(path, target, merkleRoot) { let currentHash = target; for (let i = 0; i < path.length; i++) { const item = path[i]; const prefix = (i === 0) ? Buffer.from([1]) : Buffer.from([0]); if (item.side === 0) { currentHash = hashConcat([prefix, item.hash, prefix, currentHash]); } else { if (item.hash.equals(nonExistingNodeHash)) { currentHash = hashConcat([prefix, currentHash, nonExistingNodeHash]); } else { currentHash = hashConcat([prefix, currentHash, prefix, item.hash]); } } } return merkleRoot.equals(currentHash); } //# sourceMappingURL=merkleHelper.js.map