postchain-client
Version:
Client library for accessing a Postchain node through REST.
110 lines (109 loc) • 4.29 kB
JavaScript
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