@aztec/merkle-tree 0.1.0-alpha17 → 0.1.0-alpha21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tsbuildinfo +1 -1
- package/dest/interfaces/indexed_tree.d.ts +1 -8
- package/dest/interfaces/indexed_tree.d.ts.map +1 -1
- package/dest/new_tree.js +1 -1
- package/dest/standard_indexed_tree/standard_indexed_tree.d.ts +37 -22
- package/dest/standard_indexed_tree/standard_indexed_tree.d.ts.map +1 -1
- package/dest/standard_indexed_tree/standard_indexed_tree.js +78 -87
- package/dest/standard_indexed_tree/test/standard_indexed_tree.test.d.ts.map +1 -0
- package/dest/standard_indexed_tree/test/standard_indexed_tree.test.js +337 -0
- package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.d.ts +23 -0
- package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.d.ts.map +1 -0
- package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.js +59 -0
- package/dest/standard_tree/standard_tree.d.ts +0 -10
- package/dest/standard_tree/standard_tree.d.ts.map +1 -1
- package/dest/standard_tree/standard_tree.js +3 -38
- package/dest/tree_base.d.ts +22 -9
- package/dest/tree_base.d.ts.map +1 -1
- package/dest/tree_base.js +48 -5
- package/package.json +3 -3
- package/src/interfaces/indexed_tree.ts +0 -10
- package/src/new_tree.ts +1 -1
- package/src/standard_indexed_tree/standard_indexed_tree.ts +86 -96
- package/src/standard_indexed_tree/{standard_indexed_tree.test.ts → test/standard_indexed_tree.test.ts} +35 -9
- package/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +63 -0
- package/src/standard_tree/standard_tree.ts +2 -40
- package/src/tree_base.ts +55 -8
- package/dest/standard_indexed_tree/standard_indexed_tree.test.d.ts.map +0 -1
- package/dest/standard_indexed_tree/standard_indexed_tree.test.js +0 -316
- /package/dest/standard_indexed_tree/{standard_indexed_tree.test.d.ts → test/standard_indexed_tree.test.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AppendOnlyTree } from '../interfaces/append_only_tree.js';
|
|
2
|
-
import { TreeBase
|
|
2
|
+
import { TreeBase } from '../tree_base.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A Merkle tree implementation that uses a LevelDB database to store the tree.
|
|
@@ -9,46 +9,8 @@ export class StandardTree extends TreeBase implements AppendOnlyTree {
|
|
|
9
9
|
* Appends the given leaves to the tree.
|
|
10
10
|
* @param leaves - The leaves to append.
|
|
11
11
|
* @returns Empty promise.
|
|
12
|
-
*
|
|
13
|
-
* @remarks The batch insertion algorithm works as follows:
|
|
14
|
-
* 1. Insert all the leaves,
|
|
15
|
-
* 2. start iterating over levels from the bottom up,
|
|
16
|
-
* 3. on each level iterate over all the affected nodes (i.e. nodes whose preimages have changed),
|
|
17
|
-
* 4. fetch the preimage, hash it and insert the updated value.
|
|
18
|
-
* @remarks This algorithm is optimal when it comes to the number of hashing operations. It might not be optimal when
|
|
19
|
-
* it comes to the number of database reads, but that should be irrelevant given that most of the time
|
|
20
|
-
* `getLatestValueAtIndex` will return a value from cache (because at least one of the 2 children was
|
|
21
|
-
* touched in previous iteration).
|
|
22
12
|
*/
|
|
23
13
|
public async appendLeaves(leaves: Buffer[]): Promise<void> {
|
|
24
|
-
|
|
25
|
-
if (numLeaves + BigInt(leaves.length) - 1n > this.maxIndex) {
|
|
26
|
-
throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 1. Insert all the leaves
|
|
30
|
-
let firstIndex = numLeaves;
|
|
31
|
-
let level = this.depth;
|
|
32
|
-
for (let i = 0; i < leaves.length; i++) {
|
|
33
|
-
const cacheKey = indexToKeyHash(this.name, level, firstIndex + BigInt(i));
|
|
34
|
-
this.cache[cacheKey] = leaves[i];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let lastIndex = firstIndex + BigInt(leaves.length);
|
|
38
|
-
// 2. Iterate over all the levels from the bottom up
|
|
39
|
-
while (level > 0) {
|
|
40
|
-
firstIndex >>= 1n;
|
|
41
|
-
lastIndex >>= 1n;
|
|
42
|
-
// 3.Iterate over all the affected nodes at this level and update them
|
|
43
|
-
for (let index = firstIndex; index <= lastIndex; index++) {
|
|
44
|
-
const lhs = await this.getLatestValueAtIndex(level, index * 2n, true);
|
|
45
|
-
const rhs = await this.getLatestValueAtIndex(level, index * 2n + 1n, true);
|
|
46
|
-
const cacheKey = indexToKeyHash(this.name, level - 1, index);
|
|
47
|
-
this.cache[cacheKey] = this.hasher.compress(lhs, rhs);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
level -= 1;
|
|
51
|
-
}
|
|
52
|
-
this.cachedSize = numLeaves + BigInt(leaves.length);
|
|
14
|
+
await super.appendLeaves(leaves);
|
|
53
15
|
}
|
|
54
16
|
}
|
package/src/tree_base.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { toBigIntLE, toBufferLE } from '@aztec/foundation/bigint-buffer';
|
|
|
6
6
|
|
|
7
7
|
const MAX_DEPTH = 254;
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const indexToKeyHash = (name: string, level: number, index: bigint) => `${name}:${level}:${index}`;
|
|
10
10
|
const encodeMeta = (root: Buffer, depth: number, size: bigint) => {
|
|
11
11
|
const data = Buffer.alloc(36);
|
|
12
12
|
root.copy(data);
|
|
@@ -34,13 +34,13 @@ export abstract class TreeBase implements MerkleTree {
|
|
|
34
34
|
protected cachedSize?: bigint;
|
|
35
35
|
private root!: Buffer;
|
|
36
36
|
private zeroHashes: Buffer[] = [];
|
|
37
|
-
|
|
37
|
+
private cache: { [key: string]: Buffer } = {};
|
|
38
38
|
|
|
39
39
|
public constructor(
|
|
40
40
|
protected db: LevelUp,
|
|
41
41
|
protected hasher: Hasher,
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
private name: string,
|
|
43
|
+
private depth: number,
|
|
44
44
|
protected size: bigint = 0n,
|
|
45
45
|
root?: Buffer,
|
|
46
46
|
) {
|
|
@@ -61,7 +61,7 @@ export abstract class TreeBase implements MerkleTree {
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Returns the root of the tree.
|
|
64
|
-
* @param includeUncommitted - If true, root incorporating
|
|
64
|
+
* @param includeUncommitted - If true, root incorporating uncommitted changes is returned.
|
|
65
65
|
* @returns The root of the tree.
|
|
66
66
|
*/
|
|
67
67
|
public getRoot(includeUncommitted: boolean): Buffer {
|
|
@@ -70,7 +70,7 @@ export abstract class TreeBase implements MerkleTree {
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Returns the number of leaves in the tree.
|
|
73
|
-
* @param includeUncommitted - If true, the returned number of leaves includes
|
|
73
|
+
* @param includeUncommitted - If true, the returned number of leaves includes uncommitted changes.
|
|
74
74
|
* @returns The number of leaves in the tree.
|
|
75
75
|
*/
|
|
76
76
|
public getNumLeaves(includeUncommitted: boolean) {
|
|
@@ -188,7 +188,7 @@ export abstract class TreeBase implements MerkleTree {
|
|
|
188
188
|
* @returns The latest value at the given index.
|
|
189
189
|
* Note: If the value is not in the cache, it will be fetched from the database.
|
|
190
190
|
*/
|
|
191
|
-
|
|
191
|
+
private async getLatestValueAtIndex(level: number, index: bigint, includeUncommitted: boolean): Promise<Buffer> {
|
|
192
192
|
const key = indexToKeyHash(this.name, level, index);
|
|
193
193
|
if (includeUncommitted && this.cache[key] !== undefined) {
|
|
194
194
|
return this.cache[key];
|
|
@@ -224,7 +224,7 @@ export abstract class TreeBase implements MerkleTree {
|
|
|
224
224
|
* Initializes the tree from the database.
|
|
225
225
|
*/
|
|
226
226
|
public async initFromDb(): Promise<void> {
|
|
227
|
-
// Implemented only by
|
|
227
|
+
// Implemented only by Indexed Tree to populate the leaf cache.
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
/**
|
|
@@ -239,4 +239,51 @@ export abstract class TreeBase implements MerkleTree {
|
|
|
239
239
|
await this.db.put(this.name, data);
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Appends the given leaves to the tree.
|
|
245
|
+
* @param leaves - The leaves to append.
|
|
246
|
+
* @returns Empty promise.
|
|
247
|
+
*
|
|
248
|
+
* @remarks The batch insertion algorithm works as follows:
|
|
249
|
+
* 1. Insert all the leaves,
|
|
250
|
+
* 2. start iterating over levels from the bottom up,
|
|
251
|
+
* 3. on each level iterate over all the affected nodes (i.e. nodes whose preimages have changed),
|
|
252
|
+
* 4. fetch the preimage, hash it and insert the updated value.
|
|
253
|
+
* @remarks This algorithm is optimal when it comes to the number of hashing operations. It might not be optimal when
|
|
254
|
+
* it comes to the number of database reads, but that should be irrelevant given that most of the time
|
|
255
|
+
* `getLatestValueAtIndex` will return a value from cache (because at least one of the 2 children was
|
|
256
|
+
* touched in previous iteration).
|
|
257
|
+
*/
|
|
258
|
+
protected async appendLeaves(leaves: Buffer[]): Promise<void> {
|
|
259
|
+
const numLeaves = this.getNumLeaves(true);
|
|
260
|
+
if (numLeaves + BigInt(leaves.length) - 1n > this.maxIndex) {
|
|
261
|
+
throw Error(`Can't append beyond max index. Max index: ${this.maxIndex}`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 1. Insert all the leaves
|
|
265
|
+
let firstIndex = numLeaves;
|
|
266
|
+
let level = this.depth;
|
|
267
|
+
for (let i = 0; i < leaves.length; i++) {
|
|
268
|
+
const cacheKey = indexToKeyHash(this.name, level, firstIndex + BigInt(i));
|
|
269
|
+
this.cache[cacheKey] = leaves[i];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
let lastIndex = firstIndex + BigInt(leaves.length);
|
|
273
|
+
// 2. Iterate over all the levels from the bottom up
|
|
274
|
+
while (level > 0) {
|
|
275
|
+
firstIndex >>= 1n;
|
|
276
|
+
lastIndex >>= 1n;
|
|
277
|
+
// 3.Iterate over all the affected nodes at this level and update them
|
|
278
|
+
for (let index = firstIndex; index <= lastIndex; index++) {
|
|
279
|
+
const lhs = await this.getLatestValueAtIndex(level, index * 2n, true);
|
|
280
|
+
const rhs = await this.getLatestValueAtIndex(level, index * 2n + 1n, true);
|
|
281
|
+
const cacheKey = indexToKeyHash(this.name, level - 1, index);
|
|
282
|
+
this.cache[cacheKey] = this.hasher.compress(lhs, rhs);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
level -= 1;
|
|
286
|
+
}
|
|
287
|
+
this.cachedSize = numLeaves + BigInt(leaves.length);
|
|
288
|
+
}
|
|
242
289
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"standard_indexed_tree.test.d.ts","sourceRoot":"","sources":["../../src/standard_indexed_tree/standard_indexed_tree.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import { default as levelup } from 'levelup';
|
|
2
|
-
import { INITIAL_LEAF, Pedersen, SiblingPath } from '../index.js';
|
|
3
|
-
import { StandardIndexedTree } from './standard_indexed_tree.js';
|
|
4
|
-
import { treeTestSuite } from '../test/test_suite.js';
|
|
5
|
-
import { createMemDown } from '../test/utils/create_mem_down.js';
|
|
6
|
-
import { newTree } from '../new_tree.js';
|
|
7
|
-
import { loadTree } from '../load_tree.js';
|
|
8
|
-
import { toBufferBE } from '@aztec/foundation/bigint-buffer';
|
|
9
|
-
import { CircuitsWasm } from '@aztec/circuits.js';
|
|
10
|
-
const createDb = async (levelUp, hasher, name, depth) => {
|
|
11
|
-
return await newTree(StandardIndexedTree, levelUp, hasher, name, depth);
|
|
12
|
-
};
|
|
13
|
-
const createFromName = async (levelUp, hasher, name) => {
|
|
14
|
-
return await loadTree(StandardIndexedTree, levelUp, hasher, name);
|
|
15
|
-
};
|
|
16
|
-
const createIndexedTreeLeaf = (value, nextIndex, nextValue) => {
|
|
17
|
-
return [toBufferBE(BigInt(value), 32), toBufferBE(BigInt(nextIndex), 32), toBufferBE(BigInt(nextValue), 32)];
|
|
18
|
-
};
|
|
19
|
-
const verifyCommittedState = async (tree, root, siblingPathIndex, emptySiblingPath) => {
|
|
20
|
-
expect(tree.getRoot(false)).toEqual(root);
|
|
21
|
-
expect(tree.getNumLeaves(false)).toEqual(1n);
|
|
22
|
-
expect(await tree.getSiblingPath(siblingPathIndex, false)).toEqual(emptySiblingPath);
|
|
23
|
-
};
|
|
24
|
-
const TEST_TREE_DEPTH = 3;
|
|
25
|
-
treeTestSuite('StandardIndexedTree', createDb, createFromName);
|
|
26
|
-
describe('StandardIndexedTreeSpecific', () => {
|
|
27
|
-
let wasm;
|
|
28
|
-
let pedersen;
|
|
29
|
-
beforeEach(async () => {
|
|
30
|
-
wasm = await CircuitsWasm.get();
|
|
31
|
-
pedersen = new Pedersen(wasm);
|
|
32
|
-
});
|
|
33
|
-
it('produces the correct roots and sibling paths', async () => {
|
|
34
|
-
// Create a depth-3 indexed merkle tree
|
|
35
|
-
const db = levelup(createMemDown());
|
|
36
|
-
const tree = await createDb(db, pedersen, 'test', 3);
|
|
37
|
-
/**
|
|
38
|
-
* Initial state:
|
|
39
|
-
*
|
|
40
|
-
* index 0 1 2 3 4 5 6 7
|
|
41
|
-
* ---------------------------------------------------------------------
|
|
42
|
-
* val 0 0 0 0 0 0 0 0
|
|
43
|
-
* nextIdx 0 0 0 0 0 0 0 0
|
|
44
|
-
* nextVal 0 0 0 0 0 0 0 0.
|
|
45
|
-
*/
|
|
46
|
-
const initialLeafHash = pedersen.compressInputs(createIndexedTreeLeaf(0, 0, 0));
|
|
47
|
-
const level1ZeroHash = pedersen.compress(INITIAL_LEAF, INITIAL_LEAF);
|
|
48
|
-
const level2ZeroHash = pedersen.compress(level1ZeroHash, level1ZeroHash);
|
|
49
|
-
let index0Hash = initialLeafHash;
|
|
50
|
-
// Each element is named by the level followed by the index on that level. E.g. e10 -> level 1, index 0, e21 -> level 2, index 1
|
|
51
|
-
let e10 = pedersen.compress(index0Hash, INITIAL_LEAF);
|
|
52
|
-
let e20 = pedersen.compress(e10, level1ZeroHash);
|
|
53
|
-
const initialE20 = e20; // Kept for calculating committed state later
|
|
54
|
-
const initialE10 = e10;
|
|
55
|
-
let root = pedersen.compress(e20, level2ZeroHash);
|
|
56
|
-
const initialRoot = root;
|
|
57
|
-
const emptySiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]);
|
|
58
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
59
|
-
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
60
|
-
expect(await tree.getSiblingPath(0n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]));
|
|
61
|
-
await verifyCommittedState(tree, initialRoot, 0n, emptySiblingPath);
|
|
62
|
-
/**
|
|
63
|
-
* Add new value 30:
|
|
64
|
-
*
|
|
65
|
-
* index 0 1 2 3 4 5 6 7
|
|
66
|
-
* ---------------------------------------------------------------------
|
|
67
|
-
* val 0 30 0 0 0 0 0 0
|
|
68
|
-
* nextIdx 1 0 0 0 0 0 0 0
|
|
69
|
-
* nextVal 30 0 0 0 0 0 0 0.
|
|
70
|
-
*/
|
|
71
|
-
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 1, 30));
|
|
72
|
-
let index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 0, 0));
|
|
73
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
74
|
-
e20 = pedersen.compress(e10, level1ZeroHash);
|
|
75
|
-
root = pedersen.compress(e20, level2ZeroHash);
|
|
76
|
-
await tree.appendLeaves([toBufferBE(30n, 32)]);
|
|
77
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
78
|
-
expect(tree.getNumLeaves(true)).toEqual(2n);
|
|
79
|
-
expect(await tree.getSiblingPath(1n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index0Hash, level1ZeroHash, level2ZeroHash]));
|
|
80
|
-
// ensure the committed state is correct
|
|
81
|
-
const initialSiblingPath = new SiblingPath(TEST_TREE_DEPTH, [initialLeafHash, level1ZeroHash, level2ZeroHash]);
|
|
82
|
-
await verifyCommittedState(tree, initialRoot, 1n, initialSiblingPath);
|
|
83
|
-
/**
|
|
84
|
-
* Add new value 10:
|
|
85
|
-
*
|
|
86
|
-
* index 0 1 2 3 4 5 6 7
|
|
87
|
-
* ---------------------------------------------------------------------
|
|
88
|
-
* val 0 30 10 0 0 0 0 0
|
|
89
|
-
* nextIdx 2 0 1 0 0 0 0 0
|
|
90
|
-
* nextVal 10 0 30 0 0 0 0 0.
|
|
91
|
-
*/
|
|
92
|
-
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 2, 10));
|
|
93
|
-
let index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 1, 30));
|
|
94
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
95
|
-
let e11 = pedersen.compress(index2Hash, INITIAL_LEAF);
|
|
96
|
-
e20 = pedersen.compress(e10, e11);
|
|
97
|
-
root = pedersen.compress(e20, level2ZeroHash);
|
|
98
|
-
await tree.appendLeaves([toBufferBE(10n, 32)]);
|
|
99
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
100
|
-
expect(tree.getNumLeaves(true)).toEqual(3n);
|
|
101
|
-
expect(await tree.getSiblingPath(2n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e10, level2ZeroHash]));
|
|
102
|
-
// ensure the committed state is correct
|
|
103
|
-
await verifyCommittedState(tree, initialRoot, 2n, new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]));
|
|
104
|
-
/**
|
|
105
|
-
* Add new value 20:
|
|
106
|
-
*
|
|
107
|
-
* index 0 1 2 3 4 5 6 7
|
|
108
|
-
* ---------------------------------------------------------------------
|
|
109
|
-
* val 0 30 10 20 0 0 0 0
|
|
110
|
-
* nextIdx 2 0 3 1 0 0 0 0
|
|
111
|
-
* nextVal 10 0 20 30 0 0 0 0.
|
|
112
|
-
*/
|
|
113
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
114
|
-
index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 3, 20));
|
|
115
|
-
const index3Hash = pedersen.compressInputs(createIndexedTreeLeaf(20, 1, 30));
|
|
116
|
-
e11 = pedersen.compress(index2Hash, index3Hash);
|
|
117
|
-
e20 = pedersen.compress(e10, e11);
|
|
118
|
-
root = pedersen.compress(e20, level2ZeroHash);
|
|
119
|
-
await tree.appendLeaves([toBufferBE(20n, 32)]);
|
|
120
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
121
|
-
expect(tree.getNumLeaves(true)).toEqual(4n);
|
|
122
|
-
expect(await tree.getSiblingPath(3n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, level2ZeroHash]));
|
|
123
|
-
// ensure the committed state is correct
|
|
124
|
-
await verifyCommittedState(tree, initialRoot, 3n, new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]));
|
|
125
|
-
/**
|
|
126
|
-
* Add new value 50:
|
|
127
|
-
*
|
|
128
|
-
* index 0 1 2 3 4 5 6 7
|
|
129
|
-
* ---------------------------------------------------------------------
|
|
130
|
-
* val 0 30 10 20 50 0 0 0
|
|
131
|
-
* nextIdx 2 4 3 1 0 0 0 0
|
|
132
|
-
* nextVal 10 50 20 30 0 0 0 0.
|
|
133
|
-
*/
|
|
134
|
-
index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 4, 50));
|
|
135
|
-
const index4Hash = pedersen.compressInputs(createIndexedTreeLeaf(50, 0, 0));
|
|
136
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
137
|
-
e20 = pedersen.compress(e10, e11);
|
|
138
|
-
const e12 = pedersen.compress(index4Hash, INITIAL_LEAF);
|
|
139
|
-
const e21 = pedersen.compress(e12, level1ZeroHash);
|
|
140
|
-
root = pedersen.compress(e20, e21);
|
|
141
|
-
await tree.appendLeaves([toBufferBE(50n, 32)]);
|
|
142
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
143
|
-
expect(tree.getNumLeaves(true)).toEqual(5n);
|
|
144
|
-
// ensure the committed state is correct
|
|
145
|
-
await verifyCommittedState(tree, initialRoot, 4n, new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, initialE20]));
|
|
146
|
-
// check all uncommitted hash paths
|
|
147
|
-
expect(await tree.getSiblingPath(0n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index1Hash, e11, e21]));
|
|
148
|
-
expect(await tree.getSiblingPath(1n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index0Hash, e11, e21]));
|
|
149
|
-
expect(await tree.getSiblingPath(2n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index3Hash, e10, e21]));
|
|
150
|
-
expect(await tree.getSiblingPath(3n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, e21]));
|
|
151
|
-
expect(await tree.getSiblingPath(4n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, e20]));
|
|
152
|
-
expect(await tree.getSiblingPath(5n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index4Hash, level1ZeroHash, e20]));
|
|
153
|
-
expect(await tree.getSiblingPath(6n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e12, e20]));
|
|
154
|
-
expect(await tree.getSiblingPath(7n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e12, e20]));
|
|
155
|
-
// check all committed hash paths
|
|
156
|
-
expect(await tree.getSiblingPath(0n, false)).toEqual(emptySiblingPath);
|
|
157
|
-
expect(await tree.getSiblingPath(1n, false)).toEqual(initialSiblingPath);
|
|
158
|
-
expect(await tree.getSiblingPath(2n, false)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]));
|
|
159
|
-
expect(await tree.getSiblingPath(3n, false)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]));
|
|
160
|
-
const e2SiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, initialE20]);
|
|
161
|
-
expect(await tree.getSiblingPath(4n, false)).toEqual(e2SiblingPath);
|
|
162
|
-
expect(await tree.getSiblingPath(5n, false)).toEqual(e2SiblingPath);
|
|
163
|
-
expect(await tree.getSiblingPath(6n, false)).toEqual(e2SiblingPath);
|
|
164
|
-
expect(await tree.getSiblingPath(7n, false)).toEqual(e2SiblingPath);
|
|
165
|
-
await tree.commit();
|
|
166
|
-
// check all committed hash paths equal uncommitted hash paths
|
|
167
|
-
for (let i = 0; i < 8; i++) {
|
|
168
|
-
expect(await tree.getSiblingPath(BigInt(i), false)).toEqual(await tree.getSiblingPath(BigInt(i), true));
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
it('Can append empty leaves and handle insertions', async () => {
|
|
172
|
-
// Create a depth-3 indexed merkle tree
|
|
173
|
-
const db = levelup(createMemDown());
|
|
174
|
-
const tree = await createDb(db, pedersen, 'test', 3);
|
|
175
|
-
/**
|
|
176
|
-
* Initial state:
|
|
177
|
-
*
|
|
178
|
-
* index 0 1 2 3 4 5 6 7
|
|
179
|
-
* ---------------------------------------------------------------------
|
|
180
|
-
* val 0 0 0 0 0 0 0 0
|
|
181
|
-
* nextIdx 0 0 0 0 0 0 0 0
|
|
182
|
-
* nextVal 0 0 0 0 0 0 0 0.
|
|
183
|
-
*/
|
|
184
|
-
const INITIAL_LEAF = toBufferBE(0n, 32);
|
|
185
|
-
const initialLeafHash = pedersen.compressInputs(createIndexedTreeLeaf(0, 0, 0));
|
|
186
|
-
const level1ZeroHash = pedersen.compress(INITIAL_LEAF, INITIAL_LEAF);
|
|
187
|
-
const level2ZeroHash = pedersen.compress(level1ZeroHash, level1ZeroHash);
|
|
188
|
-
let index0Hash = initialLeafHash;
|
|
189
|
-
let e10 = pedersen.compress(index0Hash, INITIAL_LEAF);
|
|
190
|
-
let e20 = pedersen.compress(e10, level1ZeroHash);
|
|
191
|
-
const inite10 = e10;
|
|
192
|
-
const inite20 = e20;
|
|
193
|
-
let root = pedersen.compress(e20, level2ZeroHash);
|
|
194
|
-
const initialRoot = root;
|
|
195
|
-
const emptySiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]);
|
|
196
|
-
const initialSiblingPath = new SiblingPath(TEST_TREE_DEPTH, [initialLeafHash, level1ZeroHash, level2ZeroHash]);
|
|
197
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
198
|
-
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
199
|
-
expect(await tree.getSiblingPath(0n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]));
|
|
200
|
-
await verifyCommittedState(tree, initialRoot, 0n, emptySiblingPath);
|
|
201
|
-
/**
|
|
202
|
-
* Add new value 30:
|
|
203
|
-
*
|
|
204
|
-
* index 0 1 2 3 4 5 6 7
|
|
205
|
-
* ---------------------------------------------------------------------
|
|
206
|
-
* val 0 30 0 0 0 0 0 0
|
|
207
|
-
* nextIdx 1 0 0 0 0 0 0 0
|
|
208
|
-
* nextVal 30 0 0 0 0 0 0 0.
|
|
209
|
-
*/
|
|
210
|
-
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 1, 30));
|
|
211
|
-
let index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 0, 0));
|
|
212
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
213
|
-
e20 = pedersen.compress(e10, level1ZeroHash);
|
|
214
|
-
root = pedersen.compress(e20, level2ZeroHash);
|
|
215
|
-
await tree.appendLeaves([toBufferBE(30n, 32)]);
|
|
216
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
217
|
-
expect(tree.getNumLeaves(true)).toEqual(2n);
|
|
218
|
-
expect(await tree.getSiblingPath(1n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index0Hash, level1ZeroHash, level2ZeroHash]));
|
|
219
|
-
// ensure the committed state is correct
|
|
220
|
-
await verifyCommittedState(tree, initialRoot, 1n, initialSiblingPath);
|
|
221
|
-
/**
|
|
222
|
-
* Add new value 10:
|
|
223
|
-
*
|
|
224
|
-
* index 0 1 2 3 4 5 6 7
|
|
225
|
-
* ---------------------------------------------------------------------
|
|
226
|
-
* val 0 30 10 0 0 0 0 0
|
|
227
|
-
* nextIdx 2 0 1 0 0 0 0 0
|
|
228
|
-
* nextVal 10 0 30 0 0 0 0 0.
|
|
229
|
-
*/
|
|
230
|
-
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 2, 10));
|
|
231
|
-
let index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 1, 30));
|
|
232
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
233
|
-
let e11 = pedersen.compress(index2Hash, INITIAL_LEAF);
|
|
234
|
-
e20 = pedersen.compress(e10, e11);
|
|
235
|
-
root = pedersen.compress(e20, level2ZeroHash);
|
|
236
|
-
await tree.appendLeaves([toBufferBE(10n, 32)]);
|
|
237
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
238
|
-
expect(tree.getNumLeaves(true)).toEqual(3n);
|
|
239
|
-
expect(await tree.getSiblingPath(2n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e10, level2ZeroHash]));
|
|
240
|
-
// ensure the committed state is correct
|
|
241
|
-
await verifyCommittedState(tree, initialRoot, 2n, new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]));
|
|
242
|
-
/**
|
|
243
|
-
* Add new value 20:
|
|
244
|
-
*
|
|
245
|
-
* index 0 1 2 3 4 5 6 7
|
|
246
|
-
* ---------------------------------------------------------------------
|
|
247
|
-
* val 0 30 10 20 0 0 0 0
|
|
248
|
-
* nextIdx 2 0 3 1 0 0 0 0
|
|
249
|
-
* nextVal 10 0 20 30 0 0 0 0.
|
|
250
|
-
*/
|
|
251
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
252
|
-
index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 3, 20));
|
|
253
|
-
const index3Hash = pedersen.compressInputs(createIndexedTreeLeaf(20, 1, 30));
|
|
254
|
-
e11 = pedersen.compress(index2Hash, index3Hash);
|
|
255
|
-
e20 = pedersen.compress(e10, e11);
|
|
256
|
-
root = pedersen.compress(e20, level2ZeroHash);
|
|
257
|
-
await tree.appendLeaves([toBufferBE(20n, 32)]);
|
|
258
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
259
|
-
expect(tree.getNumLeaves(true)).toEqual(4n);
|
|
260
|
-
expect(await tree.getSiblingPath(3n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, level2ZeroHash]));
|
|
261
|
-
// ensure the committed state is correct
|
|
262
|
-
await verifyCommittedState(tree, initialRoot, 3n, new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]));
|
|
263
|
-
// Add 2 empty values
|
|
264
|
-
const emptyLeaves = [toBufferBE(0n, 32), toBufferBE(0n, 32)];
|
|
265
|
-
await tree.appendLeaves(emptyLeaves);
|
|
266
|
-
// The root should be the same but the size should have increased
|
|
267
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
268
|
-
expect(tree.getNumLeaves(true)).toEqual(6n);
|
|
269
|
-
/**
|
|
270
|
-
* Add new value 50:
|
|
271
|
-
*
|
|
272
|
-
* index 0 1 2 3 4 5 6 7
|
|
273
|
-
* --------------------------------------------------------------------
|
|
274
|
-
* val 0 30 10 20 0 0 50 0
|
|
275
|
-
* nextIdx 2 6 3 1 0 0 0 0
|
|
276
|
-
* nextVal 10 50 20 30 0 0 0 0.
|
|
277
|
-
*/
|
|
278
|
-
index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 6, 50));
|
|
279
|
-
const index6Hash = pedersen.compressInputs(createIndexedTreeLeaf(50, 0, 0));
|
|
280
|
-
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
281
|
-
e20 = pedersen.compress(e10, e11);
|
|
282
|
-
const e13 = pedersen.compress(index6Hash, INITIAL_LEAF);
|
|
283
|
-
const e21 = pedersen.compress(level1ZeroHash, e13);
|
|
284
|
-
root = pedersen.compress(e20, e21);
|
|
285
|
-
await tree.appendLeaves([toBufferBE(50n, 32)]);
|
|
286
|
-
expect(tree.getRoot(true)).toEqual(root);
|
|
287
|
-
expect(tree.getNumLeaves(true)).toEqual(7n);
|
|
288
|
-
// ensure the committed state is correct
|
|
289
|
-
await verifyCommittedState(tree, initialRoot, 6n, new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, inite20]));
|
|
290
|
-
// // check all uncommitted hash paths
|
|
291
|
-
expect(await tree.getSiblingPath(0n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index1Hash, e11, e21]));
|
|
292
|
-
expect(await tree.getSiblingPath(1n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index0Hash, e11, e21]));
|
|
293
|
-
expect(await tree.getSiblingPath(2n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index3Hash, e10, e21]));
|
|
294
|
-
expect(await tree.getSiblingPath(3n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, e21]));
|
|
295
|
-
expect(await tree.getSiblingPath(4n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e13, e20]));
|
|
296
|
-
expect(await tree.getSiblingPath(5n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e13, e20]));
|
|
297
|
-
expect(await tree.getSiblingPath(6n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, e20]));
|
|
298
|
-
expect(await tree.getSiblingPath(7n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index6Hash, level1ZeroHash, e20]));
|
|
299
|
-
// check all committed hash paths
|
|
300
|
-
expect(await tree.getSiblingPath(0n, false)).toEqual(emptySiblingPath);
|
|
301
|
-
expect(await tree.getSiblingPath(1n, false)).toEqual(initialSiblingPath);
|
|
302
|
-
expect(await tree.getSiblingPath(2n, false)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]));
|
|
303
|
-
expect(await tree.getSiblingPath(3n, false)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]));
|
|
304
|
-
const e2SiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, inite20]);
|
|
305
|
-
expect(await tree.getSiblingPath(4n, false)).toEqual(e2SiblingPath);
|
|
306
|
-
expect(await tree.getSiblingPath(5n, false)).toEqual(e2SiblingPath);
|
|
307
|
-
expect(await tree.getSiblingPath(6n, false)).toEqual(e2SiblingPath);
|
|
308
|
-
expect(await tree.getSiblingPath(7n, false)).toEqual(e2SiblingPath);
|
|
309
|
-
await tree.commit();
|
|
310
|
-
// check all committed hash paths equal uncommitted hash paths
|
|
311
|
-
for (let i = 0; i < 8; i++) {
|
|
312
|
-
expect(await tree.getSiblingPath(BigInt(i), false)).toEqual(await tree.getSiblingPath(BigInt(i), true));
|
|
313
|
-
}
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhbmRhcmRfaW5kZXhlZF90cmVlLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RhbmRhcmRfaW5kZXhlZF90cmVlL3N0YW5kYXJkX2luZGV4ZWRfdHJlZS50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxPQUFPLElBQUksT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzdDLE9BQU8sRUFBVSxZQUFZLEVBQWMsUUFBUSxFQUFFLFdBQVcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN0RixPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFdEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRTdELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUVsRCxNQUFNLFFBQVEsR0FBRyxLQUFLLEVBQUUsT0FBd0IsRUFBRSxNQUFjLEVBQUUsSUFBWSxFQUFFLEtBQWEsRUFBRSxFQUFFO0lBQy9GLE9BQU8sTUFBTSxPQUFPLENBQUMsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLENBQUM7QUFDMUUsQ0FBQyxDQUFDO0FBRUYsTUFBTSxjQUFjLEdBQUcsS0FBSyxFQUFFLE9BQXdCLEVBQUUsTUFBYyxFQUFFLElBQVksRUFBRSxFQUFFO0lBQ3RGLE9BQU8sTUFBTSxRQUFRLENBQUMsbUJBQW1CLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztBQUNw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
|
|
File without changes
|