fixed-merkle-tree 0.7.3-p0
Signed-off-by: T-Hax <>
This commit is contained in:
parent
19124126f4
commit
e676b48b59
40 changed files with 2976 additions and 6343 deletions
154
@tornado/fixed-merkle-tree/lib/BaseTree.js
Normal file
154
@tornado/fixed-merkle-tree/lib/BaseTree.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BaseTree = void 0;
|
||||
class BaseTree {
|
||||
get capacity() {
|
||||
return 2 ** this.levels;
|
||||
}
|
||||
get layers() {
|
||||
return this._layers.slice();
|
||||
}
|
||||
get zeros() {
|
||||
return this._zeros.slice();
|
||||
}
|
||||
get elements() {
|
||||
return this._layers[0].slice();
|
||||
}
|
||||
get root() {
|
||||
var _a;
|
||||
return (_a = this._layers[this.levels][0]) !== null && _a !== void 0 ? _a : this._zeros[this.levels];
|
||||
}
|
||||
/**
|
||||
* Find an element in the tree
|
||||
* @param elements elements of tree
|
||||
* @param element An element to find
|
||||
* @param comparator A function that checks leaf value equality
|
||||
* @param fromIndex The index to start the search at. If the index is greater than or equal to the array's length, -1 is returned
|
||||
* @returns {number} Index if element is found, otherwise -1
|
||||
*/
|
||||
static indexOf(elements, element, fromIndex, comparator) {
|
||||
if (comparator) {
|
||||
return elements.findIndex((el) => comparator(element, el));
|
||||
}
|
||||
else {
|
||||
return elements.indexOf(element, fromIndex);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Insert new element into the tree
|
||||
* @param element Element to insert
|
||||
*/
|
||||
insert(element) {
|
||||
if (this._layers[0].length >= this.capacity) {
|
||||
throw new Error('Tree is full');
|
||||
}
|
||||
this.update(this._layers[0].length, element);
|
||||
}
|
||||
/*
|
||||
* 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;
|
||||
const left = this._layers[level - 1][index * 2];
|
||||
const right = this._layers[level - 1][index * 2 + 1];
|
||||
this._layers[level][index] = this._hashFn(left, right);
|
||||
}
|
||||
}
|
||||
this.insert(elements[elements.length - 1]);
|
||||
}
|
||||
/**
|
||||
* Change an element in the tree
|
||||
* @param {number} index Index of element to change
|
||||
* @param element Updated element value
|
||||
*/
|
||||
update(index, element) {
|
||||
if (isNaN(Number(index)) || index < 0 || index > this._layers[0].length || index >= this.capacity) {
|
||||
throw new Error('Insert index out of bounds: ' + index);
|
||||
}
|
||||
this._layers[0][index] = element;
|
||||
this._processUpdate(index);
|
||||
}
|
||||
/**
|
||||
* Get merkle path to a leaf
|
||||
* @param {number} index Leaf index to generate path for
|
||||
* @returns {{pathElements: Object[], pathIndex: number[]}} An object containing adjacent elements and left-right index
|
||||
*/
|
||||
path(index) {
|
||||
if (isNaN(Number(index)) || index < 0 || index >= this._layers[0].length) {
|
||||
throw new Error('Index out of bounds: ' + index);
|
||||
}
|
||||
let elIndex = +index;
|
||||
const pathElements = [];
|
||||
const pathIndices = [];
|
||||
const pathPositions = [];
|
||||
for (let level = 0; level < this.levels; level++) {
|
||||
pathIndices[level] = elIndex % 2;
|
||||
const leafIndex = elIndex ^ 1;
|
||||
if (leafIndex < this._layers[level].length) {
|
||||
pathElements[level] = this._layers[level][leafIndex];
|
||||
pathPositions[level] = leafIndex;
|
||||
}
|
||||
else {
|
||||
pathElements[level] = this._zeros[level];
|
||||
pathPositions[level] = 0;
|
||||
}
|
||||
elIndex >>= 1;
|
||||
}
|
||||
return {
|
||||
pathElements,
|
||||
pathIndices,
|
||||
pathPositions,
|
||||
pathRoot: this.root,
|
||||
};
|
||||
}
|
||||
_buildZeros() {
|
||||
this._zeros = [this.zeroElement];
|
||||
for (let i = 1; i <= this.levels; i++) {
|
||||
this._zeros[i] = this._hashFn(this._zeros[i - 1], this._zeros[i - 1]);
|
||||
}
|
||||
}
|
||||
_processNodes(nodes, layerIndex) {
|
||||
const length = nodes.length;
|
||||
let currentLength = Math.ceil(length / 2);
|
||||
const currentLayer = new Array(currentLength);
|
||||
currentLength--;
|
||||
const starFrom = length - ((length % 2) ^ 1);
|
||||
let j = 0;
|
||||
for (let i = starFrom; i >= 0; i -= 2) {
|
||||
if (nodes[i - 1] === undefined)
|
||||
break;
|
||||
const left = nodes[i - 1];
|
||||
const right = (i === starFrom && length % 2 === 1) ? this._zeros[layerIndex - 1] : nodes[i];
|
||||
currentLayer[currentLength - j] = this._hashFn(left, right);
|
||||
j++;
|
||||
}
|
||||
return currentLayer;
|
||||
}
|
||||
_processUpdate(index) {
|
||||
for (let level = 1; level <= this.levels; level++) {
|
||||
index >>= 1;
|
||||
const left = this._layers[level - 1][index * 2];
|
||||
const right = index * 2 + 1 < this._layers[level - 1].length
|
||||
? this._layers[level - 1][index * 2 + 1]
|
||||
: this._zeros[level - 1];
|
||||
this._layers[level][index] = this._hashFn(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.BaseTree = BaseTree;
|
Loading…
Add table
Add a link
Reference in a new issue