fixed-merkle-tree/lib/FixedMerkleTree.js

105 lines
3.8 KiB
JavaScript
Raw Normal View History

2022-03-14 10:42:36 +01:00
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const simpleHash_1 = __importDefault(require("./simpleHash"));
const BaseTree_1 = require("./BaseTree");
class MerkleTree extends BaseTree_1.BaseTree {
constructor(levels, elements = [], { hashFunction = simpleHash_1.default, zeroElement = 0, } = {}) {
super();
this.levels = levels;
if (elements.length > this.capacity) {
throw new Error('Tree is full');
}
this._hashFn = hashFunction;
this.zeroElement = zeroElement;
this._layers = [];
const leaves = elements.slice();
this._layers = [leaves];
this._buildZeros();
this._buildHashes();
}
_buildHashes() {
for (let layerIndex = 1; layerIndex <= this.levels; layerIndex++) {
const nodes = this._layers[layerIndex - 1];
this._layers[layerIndex] = this._processNodes(nodes, layerIndex);
}
}
/**
* Insert multiple elements into the tree.
* @param {Array} elements Elements to insert
*/
bulkInsert(elements) {
if (!elements.length) {
return;
}
if (this._layers[0].length + elements.length > this.capacity) {
throw new Error('Tree is full');
}
// First we insert all elements except the last one
// updating only full subtree hashes (all layers where inserted element has odd index)
// the last element will update the full path to the root making the tree consistent again
for (let i = 0; i < elements.length - 1; i++) {
this._layers[0].push(elements[i]);
let level = 0;
let index = this._layers[0].length - 1;
while (index % 2 === 1) {
level++;
index >>= 1;
this._layers[level][index] = this._hashFn(this._layers[level - 1][index * 2], this._layers[level - 1][index * 2 + 1]);
}
}
this.insert(elements[elements.length - 1]);
}
getTreeEdge(edgeIndex) {
const edgeElement = this._layers[0][edgeIndex];
if (edgeElement === undefined) {
throw new Error('Element not found');
}
const edgePath = this.path(edgeIndex);
return { edgePath, edgeElement, edgeIndex, edgeElementsCount: this._layers[0].length };
}
/**
* 🪓
* @param count
*/
getTreeSlices(count = 4) {
const length = this._layers[0].length;
let size = Math.ceil(length / count);
if (size % 2)
size++;
const slices = [];
for (let i = 0; i < length; i += size) {
const edgeLeft = i;
const edgeRight = i + size;
slices.push({ edge: this.getTreeEdge(edgeLeft), elements: this.elements.slice(edgeLeft, edgeRight) });
}
return slices;
}
/**
* Serialize entire tree state including intermediate layers into a plain object
* Deserializing it back will not require to recompute any hashes
* Elements are not converted to a plain type, this is responsibility of the caller
*/
serialize() {
return {
levels: this.levels,
_zeros: this._zeros,
_layers: this._layers,
};
}
/**
* Deserialize data into a MerkleTree instance
* Make sure to provide the same hashFunction as was used in the source tree,
* otherwise the tree state will be invalid
*/
static deserialize(data, hashFunction) {
return new MerkleTree(data.levels, data._layers[0], { hashFunction, zeroElement: data._zeros[0] });
}
toString() {
return JSON.stringify(this.serialize());
}
}
exports.default = MerkleTree;