@ledgerhq/hw-app-btc
Version:
Ledger Hardware Wallet Bitcoin Application API
117 lines • 3.5 kB
JavaScript
"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