diff --git a/src/merkleTree.js b/src/merkleTree.js index af7d72a..3e6bb3b 100644 --- a/src/merkleTree.js +++ b/src/merkleTree.js @@ -74,15 +74,30 @@ class MerkleTree { } /** - * Insert multiple elements into the tree. Tree will be fully rebuilt during this operation. + * Insert multiple elements into the tree. * @param {Array} elements Elements to insert */ bulkInsert(elements) { if (this._layers[0].length + elements.length > this.capacity) { throw new Error('Tree is full') } - this._layers[0].push(...elements) - this._rebuild() + // 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._hash( + this._layers[level - 1][index * 2], + this._layers[level - 1][index * 2 + 1], + ) + } + } + this.insert(elements[elements.length - 1]) } /** diff --git a/test/merkleTree.test.js b/test/merkleTree.test.js index b941abc..30d6ebb 100644 --- a/test/merkleTree.test.js +++ b/test/merkleTree.test.js @@ -71,6 +71,32 @@ describe('MerkleTree', () => { tree.root().should.equal('10132905325673518287563057607527946096399700874345297651940963130460267058606') }) + it('should give the same result as sequental inserts', () => { + const initialArray = [ + [1], + [1, 2], + [1, 2, 3], + [1, 2, 3, 4], + ] + const insertedArray = [ + [11], + [11, 12], + [11, 12, 13], + [11, 12, 13, 14], + ] + for (const initial of initialArray) { + for (const inserted of insertedArray) { + const tree1 = new MerkleTree(10, initial) + const tree2 = new MerkleTree(10, initial) + tree1.bulkInsert(inserted) + for (const item of inserted) { + tree2.insert(item) + } + tree1.root().should.equal(tree2.root()) + } + } + }).timeout(10000) + it('should work with max elements', () => { const tree = new MerkleTree(2, [1, 2]) tree.bulkInsert([3, 4])