UNPKG

@ledgerhq/hw-app-btc

Version:
117 lines 3.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.hashLeaf = exports.Merkle = void 0; const bitcoinjs_lib_1 = require("bitcoinjs-lib"); /** * This class implements the merkle tree used by Ledger Bitcoin app v2+, * which is documented at * https://github.com/LedgerHQ/app-bitcoin-new/blob/master/doc/merkle.md */ class Merkle { leaves; rootNode; leafNodes; h; constructor(leaves, hasher = bitcoinjs_lib_1.crypto.sha256) { this.leaves = leaves; this.h = hasher; const nodes = this.calculateRoot(leaves); this.rootNode = nodes.root; this.leafNodes = nodes.leaves; } getRoot() { return this.rootNode.hash; } size() { return this.leaves.length; } getLeaves() { return this.leaves; } getLeafHash(index) { return this.leafNodes[index].hash; } getProof(index) { if (index >= this.leaves.length) throw Error("Index out of bounds"); return proveNode(this.leafNodes[index]); } calculateRoot(leaves) { const n = leaves.length; if (n == 0) { return { root: new Node(undefined, undefined, Buffer.alloc(32, 0)), leaves: [], }; } if (n == 1) { const newNode = new Node(undefined, undefined, leaves[0]); return { root: newNode, leaves: [newNode] }; } const leftCount = highestPowerOf2LessThan(n); const leftBranch = this.calculateRoot(leaves.slice(0, leftCount)); const rightBranch = this.calculateRoot(leaves.slice(leftCount)); const leftChild = leftBranch.root; const rightChild = rightBranch.root; const hash = this.hashNode(leftChild.hash, rightChild.hash); const node = new Node(leftChild, rightChild, hash); leftChild.parent = node; rightChild.parent = node; return { root: node, leaves: leftBranch.leaves.concat(rightBranch.leaves) }; } hashNode(left, right) { return this.h(Buffer.concat([Buffer.from([1]), left, right])); } } exports.Merkle = Merkle; function hashLeaf(buf, hashFunction = bitcoinjs_lib_1.crypto.sha256) { return hashConcat(Buffer.from([0]), buf, hashFunction); } exports.hashLeaf = hashLeaf; function hashConcat(bufA, bufB, hashFunction) { return hashFunction(Buffer.concat([bufA, bufB])); } class Node { leftChild; rightChild; parent; hash; constructor(left, right, hash) { this.leftChild = left; this.rightChild = right; this.hash = hash; } isLeaf() { return this.leftChild == undefined; } } function proveNode(node) { if (!node.parent) { return []; } if (node.parent.leftChild == node) { if (!node.parent.rightChild) { throw new Error("Expected right child to exist"); } return [node.parent.rightChild.hash, ...proveNode(node.parent)]; } else { if (!node.parent.leftChild) { throw new Error("Expected left child to exist"); } return [node.parent.leftChild.hash, ...proveNode(node.parent)]; } } function highestPowerOf2LessThan(n) { if (n < 2) { throw Error("Expected n >= 2"); } if (isPowerOf2(n)) { return n / 2; } return 1 << Math.floor(Math.log2(n)); } function isPowerOf2(n) { return (n & (n - 1)) == 0; } //# sourceMappingURL=merkle.js.map