merkletreejs
Version:
Construct Merkle Trees and verify proofs
153 lines (152 loc) • 5.59 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MerkleRadixTree = void 0;
const Base_1 = __importDefault(require("./Base"));
class MerkleRadixNode {
constructor(key = '', value = null, hashFn) {
this.key = key;
this.value = value;
this.children = new Map();
this.hashFn = hashFn;
this.hash = this.computeHash();
}
computeHash() {
let hash = this.hashFn('');
hash = Buffer.concat([hash, Base_1.default.bufferify(this.key), this.value != null ? Base_1.default.bufferify(this.value) : Buffer.alloc(0)]);
for (const child of this.children.values()) {
hash = Buffer.concat([hash, child.hash]);
}
const result = this.hashFn(hash);
return result;
}
updateHash() {
this.hash = this.computeHash();
}
}
class MerkleRadixTree extends Base_1.default {
constructor(hashFn) {
super();
this.hashFn = this.bufferifyFn(hashFn);
this.root = new MerkleRadixNode('', null, this.hashFn);
}
insert(key, value) {
let node = this.root;
let commonPrefixLength = 0;
while (key.length > 0) {
const child = [...node.children.values()].find(child => key.startsWith(child.key));
if (!child) {
node.children.set(key, new MerkleRadixNode(key, value, this.hashFn));
node.updateHash(); // Update the hash of the current node
return;
}
commonPrefixLength = this.commonPrefixLength(key, child.key);
if (commonPrefixLength === child.key.length) {
node = child;
key = key.slice(commonPrefixLength);
}
else {
const commonPrefix = key.slice(0, commonPrefixLength);
const childSuffix = child.key.slice(commonPrefixLength);
const newNode = new MerkleRadixNode(commonPrefix, null, this.hashFn);
node.children.delete(child.key);
node.children.set(commonPrefix, newNode);
newNode.children.set(childSuffix, child);
child.key = childSuffix;
if (commonPrefixLength < key.length) {
const suffix = key.slice(commonPrefixLength);
newNode.children.set(suffix, new MerkleRadixNode(suffix, value, this.hashFn));
}
else {
newNode.value = value;
}
node.updateHash();
newNode.updateHash(); // Update the hash of the new node
return;
}
}
node.value = value;
node.updateHash(); // Update the hash of the node where the value was inserted
}
lookup(key) {
let node = this.root;
while (key.length > 0) {
const child = [...node.children.values()].find(child => key.startsWith(child.key));
if (!child) {
return null;
}
const commonPrefixLength = this.commonPrefixLength(key, child.key);
if (commonPrefixLength === child.key.length) {
node = child;
key = key.slice(commonPrefixLength);
}
else {
return null;
}
}
return node.value;
}
commonPrefixLength(str1, str2) {
let length = 0;
while (length < str1.length && length < str2.length && str1[length] === str2[length]) {
length++;
}
return length;
}
generateProof(key) {
let node = this.root;
const proof = [];
while (key.length > 0) {
const siblings = [];
for (const child of node.children.values()) {
if (child.key !== key) {
siblings.push({
key: child.key,
hash: child.hash
});
}
}
proof.push({
key: node.key,
hash: node.hash,
siblings
});
const child = [...node.children.values()].find(child => key.startsWith(child.key));
if (!child) {
return null;
}
const commonPrefixLength = this.commonPrefixLength(key, child.key);
if (commonPrefixLength === child.key.length) {
node = child;
key = key.slice(commonPrefixLength);
}
else {
return null;
}
}
proof.push({
key: node.key,
hash: node.hash,
siblings: []
});
return proof;
}
verifyProof(proof, rootHash) {
if (!proof || proof.length === 0) {
return false;
}
let currentHash = proof[proof.length - 1].hash;
for (let i = proof.length - 2; i >= 0; i--) {
const item = proof[i];
let concatenatedHash = Buffer.concat([this.hashFn(''), this.bufferify(item.key), currentHash]);
for (const sibling of item.siblings) {
concatenatedHash = Buffer.concat([concatenatedHash, sibling.hash]);
}
currentHash = this.hashFn(concatenatedHash);
}
return currentHash.equals(rootHash);
}
}
exports.MerkleRadixTree = MerkleRadixTree;