@aztec/merkle-tree 0.1.0-alpha10
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/.eslintrc.cjs +1 -0
- package/.tsbuildinfo +1 -0
- package/README.md +41 -0
- package/dest/hasher.d.ts +11 -0
- package/dest/hasher.d.ts.map +1 -0
- package/dest/hasher.js +2 -0
- package/dest/index.d.ts +14 -0
- package/dest/index.d.ts.map +1 -0
- package/dest/index.js +14 -0
- package/dest/interfaces/append_only_tree.d.ts +13 -0
- package/dest/interfaces/append_only_tree.d.ts.map +1 -0
- package/dest/interfaces/append_only_tree.js +2 -0
- package/dest/interfaces/indexed_tree.d.ts +63 -0
- package/dest/interfaces/indexed_tree.d.ts.map +1 -0
- package/dest/interfaces/indexed_tree.js +2 -0
- package/dest/interfaces/merkle_tree.d.ts +47 -0
- package/dest/interfaces/merkle_tree.d.ts.map +1 -0
- package/dest/interfaces/merkle_tree.js +2 -0
- package/dest/interfaces/update_only_tree.d.ts +15 -0
- package/dest/interfaces/update_only_tree.d.ts.map +1 -0
- package/dest/interfaces/update_only_tree.js +2 -0
- package/dest/load_tree.d.ts +13 -0
- package/dest/load_tree.d.ts.map +1 -0
- package/dest/load_tree.js +17 -0
- package/dest/new_tree.d.ts +15 -0
- package/dest/new_tree.d.ts.map +1 -0
- package/dest/new_tree.js +16 -0
- package/dest/pedersen.d.ts +42 -0
- package/dest/pedersen.d.ts.map +1 -0
- package/dest/pedersen.js +49 -0
- package/dest/sibling_path/sibling_path.d.ts +92 -0
- package/dest/sibling_path/sibling_path.d.ts.map +1 -0
- package/dest/sibling_path/sibling_path.js +120 -0
- package/dest/sparse_tree/sparse_tree.d.ts +15 -0
- package/dest/sparse_tree/sparse_tree.d.ts.map +1 -0
- package/dest/sparse_tree/sparse_tree.js +31 -0
- package/dest/sparse_tree/sparse_tree.test.d.ts +2 -0
- package/dest/sparse_tree/sparse_tree.test.d.ts.map +1 -0
- package/dest/sparse_tree/sparse_tree.test.js +132 -0
- package/dest/standard_indexed_tree/standard_indexed_tree.d.ts +230 -0
- package/dest/standard_indexed_tree/standard_indexed_tree.d.ts.map +1 -0
- package/dest/standard_indexed_tree/standard_indexed_tree.js +497 -0
- package/dest/standard_indexed_tree/standard_indexed_tree.test.d.ts +2 -0
- package/dest/standard_indexed_tree/standard_indexed_tree.test.d.ts.map +1 -0
- package/dest/standard_indexed_tree/standard_indexed_tree.test.js +316 -0
- package/dest/standard_tree/standard_tree.d.ts +25 -0
- package/dest/standard_tree/standard_tree.d.ts.map +1 -0
- package/dest/standard_tree/standard_tree.js +50 -0
- package/dest/standard_tree/standard_tree.test.d.ts +2 -0
- package/dest/standard_tree/standard_tree.test.d.ts.map +1 -0
- package/dest/standard_tree/standard_tree.test.js +58 -0
- package/dest/test/standard_based_test_suite.d.ts +6 -0
- package/dest/test/standard_based_test_suite.d.ts.map +1 -0
- package/dest/test/standard_based_test_suite.js +86 -0
- package/dest/test/test_suite.d.ts +6 -0
- package/dest/test/test_suite.d.ts.map +1 -0
- package/dest/test/test_suite.js +118 -0
- package/dest/test/utils/append_leaves.d.ts +5 -0
- package/dest/test/utils/append_leaves.d.ts.map +1 -0
- package/dest/test/utils/append_leaves.js +14 -0
- package/dest/test/utils/create_mem_down.d.ts +3 -0
- package/dest/test/utils/create_mem_down.d.ts.map +1 -0
- package/dest/test/utils/create_mem_down.js +3 -0
- package/dest/test/utils/pedersen_with_counter.d.ts +24 -0
- package/dest/test/utils/pedersen_with_counter.d.ts.map +1 -0
- package/dest/test/utils/pedersen_with_counter.js +31 -0
- package/dest/tree_base.d.ts +118 -0
- package/dest/tree_base.d.ts.map +1 -0
- package/dest/tree_base.js +214 -0
- package/package.json +14 -0
- package/package.local.json +3 -0
- package/src/hasher.ts +9 -0
- package/src/index.ts +13 -0
- package/src/interfaces/append_only_tree.ts +12 -0
- package/src/interfaces/indexed_tree.ts +78 -0
- package/src/interfaces/merkle_tree.ts +52 -0
- package/src/interfaces/update_only_tree.ts +15 -0
- package/src/load_tree.ts +24 -0
- package/src/new_tree.ts +26 -0
- package/src/pedersen.ts +58 -0
- package/src/sibling_path/sibling_path.ts +139 -0
- package/src/sparse_tree/sparse_tree.test.ts +177 -0
- package/src/sparse_tree/sparse_tree.ts +32 -0
- package/src/standard_indexed_tree/standard_indexed_tree.test.ts +450 -0
- package/src/standard_indexed_tree/standard_indexed_tree.ts +591 -0
- package/src/standard_tree/standard_tree.test.ts +74 -0
- package/src/standard_tree/standard_tree.ts +54 -0
- package/src/test/standard_based_test_suite.ts +139 -0
- package/src/test/test_suite.ts +162 -0
- package/src/test/utils/append_leaves.ts +15 -0
- package/src/test/utils/create_mem_down.ts +3 -0
- package/src/test/utils/pedersen_with_counter.ts +30 -0
- package/src/tree_base.ts +242 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { default as levelup } from 'levelup';
|
|
2
|
+
import { Hasher } from '../hasher.js';
|
|
3
|
+
import { treeTestSuite } from '../test/test_suite.js';
|
|
4
|
+
import { SparseTree } from './sparse_tree.js';
|
|
5
|
+
import { standardBasedTreeTestSuite } from '../test/standard_based_test_suite.js';
|
|
6
|
+
import { createMemDown } from '../test/utils/create_mem_down.js';
|
|
7
|
+
import { Pedersen } from '../pedersen.js';
|
|
8
|
+
import { randomBytes } from 'crypto';
|
|
9
|
+
import { INITIAL_LEAF, SiblingPath } from '../index.js';
|
|
10
|
+
import { UpdateOnlyTree } from '../interfaces/update_only_tree.js';
|
|
11
|
+
import { newTree } from '../new_tree.js';
|
|
12
|
+
import { loadTree } from '../load_tree.js';
|
|
13
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
14
|
+
import { IWasmModule } from '@aztec/foundation/wasm';
|
|
15
|
+
import { CircuitsWasm } from '@aztec/circuits.js';
|
|
16
|
+
|
|
17
|
+
const log = createLogger('aztec:sparse_tree_test');
|
|
18
|
+
|
|
19
|
+
const createDb = async (
|
|
20
|
+
levelUp: levelup.LevelUp,
|
|
21
|
+
hasher: Hasher,
|
|
22
|
+
name: string,
|
|
23
|
+
depth: number,
|
|
24
|
+
): Promise<UpdateOnlyTree> => {
|
|
25
|
+
return await newTree(SparseTree, levelUp, hasher, name, depth);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string): Promise<UpdateOnlyTree> => {
|
|
29
|
+
return await loadTree(SparseTree, levelUp, hasher, name);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const TEST_TREE_DEPTH = 3;
|
|
33
|
+
|
|
34
|
+
treeTestSuite('SparseTree', createDb, createFromName);
|
|
35
|
+
standardBasedTreeTestSuite('SparseTree', createDb);
|
|
36
|
+
|
|
37
|
+
describe('SparseTreeSpecific', () => {
|
|
38
|
+
let wasm: IWasmModule;
|
|
39
|
+
let pedersen: Pedersen;
|
|
40
|
+
|
|
41
|
+
beforeEach(async () => {
|
|
42
|
+
wasm = await CircuitsWasm.get();
|
|
43
|
+
pedersen = new Pedersen(wasm);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('throws when index is bigger than (2^DEPTH - 1) ', async () => {
|
|
47
|
+
const db = levelup(createMemDown());
|
|
48
|
+
const depth = 32;
|
|
49
|
+
const tree = await createDb(db, pedersen, 'test', depth);
|
|
50
|
+
|
|
51
|
+
const index = 2n ** BigInt(depth);
|
|
52
|
+
await expect(tree.updateLeaf(Buffer.alloc(32), index)).rejects.toThrow();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('updating non-empty leaf does not change tree size', async () => {
|
|
56
|
+
const depth = 32;
|
|
57
|
+
const maxIndex = 2 ** depth - 1;
|
|
58
|
+
|
|
59
|
+
const db = levelup(createMemDown());
|
|
60
|
+
const tree = await createDb(db, pedersen, 'test', depth);
|
|
61
|
+
|
|
62
|
+
const randomIndex = BigInt(Math.floor(Math.random() * maxIndex));
|
|
63
|
+
expect(tree.getNumLeaves(false)).toEqual(0n);
|
|
64
|
+
|
|
65
|
+
// Insert a leaf
|
|
66
|
+
await tree.updateLeaf(randomBytes(32), randomIndex);
|
|
67
|
+
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
68
|
+
|
|
69
|
+
// Update a leaf
|
|
70
|
+
await tree.updateLeaf(randomBytes(32), randomIndex);
|
|
71
|
+
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('deleting leaf decrements tree size', async () => {
|
|
75
|
+
const depth = 254;
|
|
76
|
+
const maxIndex = 2 ** depth - 1;
|
|
77
|
+
|
|
78
|
+
const db = levelup(createMemDown());
|
|
79
|
+
const tree = await createDb(db, pedersen, 'test', depth);
|
|
80
|
+
|
|
81
|
+
const randomIndex = BigInt(Math.floor(Math.random() * maxIndex));
|
|
82
|
+
expect(tree.getNumLeaves(false)).toEqual(0n);
|
|
83
|
+
|
|
84
|
+
// Insert a leaf
|
|
85
|
+
await tree.updateLeaf(randomBytes(32), randomIndex);
|
|
86
|
+
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
87
|
+
|
|
88
|
+
// Delete a leaf
|
|
89
|
+
await tree.updateLeaf(INITIAL_LEAF, randomIndex);
|
|
90
|
+
expect(tree.getNumLeaves(true)).toEqual(0n);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should have correct root and sibling path after in a "non-append-only" way', async () => {
|
|
94
|
+
const db = levelup(createMemDown());
|
|
95
|
+
const tree = await createDb(db, pedersen, 'test', 3);
|
|
96
|
+
|
|
97
|
+
const level2ZeroHash = pedersen.compress(INITIAL_LEAF, INITIAL_LEAF);
|
|
98
|
+
const level1ZeroHash = pedersen.compress(level2ZeroHash, level2ZeroHash);
|
|
99
|
+
|
|
100
|
+
expect(tree.getNumLeaves(false)).toEqual(0n);
|
|
101
|
+
expect(tree.getRoot(false)).toEqual(pedersen.compress(level1ZeroHash, level1ZeroHash));
|
|
102
|
+
|
|
103
|
+
// Insert leaf at index 3
|
|
104
|
+
let level1LeftHash: Buffer;
|
|
105
|
+
const leafAtIndex3 = randomBytes(32);
|
|
106
|
+
{
|
|
107
|
+
await tree.updateLeaf(leafAtIndex3, 3n);
|
|
108
|
+
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
109
|
+
const level2Hash = pedersen.compress(INITIAL_LEAF, leafAtIndex3);
|
|
110
|
+
level1LeftHash = pedersen.compress(level2ZeroHash, level2Hash);
|
|
111
|
+
const root = pedersen.compress(level1LeftHash, level1ZeroHash);
|
|
112
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
113
|
+
expect(await tree.getSiblingPath(3n, true)).toEqual(
|
|
114
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level2ZeroHash, level1ZeroHash]),
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Insert leaf at index 6
|
|
119
|
+
let level1RightHash: Buffer;
|
|
120
|
+
{
|
|
121
|
+
const leafAtIndex6 = randomBytes(32);
|
|
122
|
+
await tree.updateLeaf(leafAtIndex6, 6n);
|
|
123
|
+
expect(tree.getNumLeaves(true)).toEqual(2n);
|
|
124
|
+
const level2Hash = pedersen.compress(leafAtIndex6, INITIAL_LEAF);
|
|
125
|
+
level1RightHash = pedersen.compress(level2ZeroHash, level2Hash);
|
|
126
|
+
const root = pedersen.compress(level1LeftHash, level1RightHash);
|
|
127
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
128
|
+
expect(await tree.getSiblingPath(6n, true)).toEqual(
|
|
129
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level2ZeroHash, level1LeftHash]),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Insert leaf at index 2
|
|
134
|
+
const leafAtIndex2 = randomBytes(32);
|
|
135
|
+
{
|
|
136
|
+
await tree.updateLeaf(leafAtIndex2, 2n);
|
|
137
|
+
expect(tree.getNumLeaves(true)).toEqual(3n);
|
|
138
|
+
const level2Hash = pedersen.compress(leafAtIndex2, leafAtIndex3);
|
|
139
|
+
level1LeftHash = pedersen.compress(level2ZeroHash, level2Hash);
|
|
140
|
+
const root = pedersen.compress(level1LeftHash, level1RightHash);
|
|
141
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
142
|
+
expect(await tree.getSiblingPath(2n, true)).toEqual(
|
|
143
|
+
new SiblingPath(TEST_TREE_DEPTH, [leafAtIndex3, level2ZeroHash, level1RightHash]),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Updating leaf at index 3
|
|
148
|
+
{
|
|
149
|
+
const updatedLeafAtIndex3 = randomBytes(32);
|
|
150
|
+
await tree.updateLeaf(updatedLeafAtIndex3, 3n);
|
|
151
|
+
expect(tree.getNumLeaves(true)).toEqual(3n);
|
|
152
|
+
const level2Hash = pedersen.compress(leafAtIndex2, updatedLeafAtIndex3);
|
|
153
|
+
level1LeftHash = pedersen.compress(level2ZeroHash, level2Hash);
|
|
154
|
+
const root = pedersen.compress(level1LeftHash, level1RightHash);
|
|
155
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
156
|
+
expect(await tree.getSiblingPath(3n, true)).toEqual(
|
|
157
|
+
new SiblingPath(TEST_TREE_DEPTH, [leafAtIndex2, level2ZeroHash, level1RightHash]),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it.skip('measures time of inserting 1000 leaves at random positions for depth 254', async () => {
|
|
163
|
+
const depth = 254;
|
|
164
|
+
const maxIndex = 2 ** depth - 1;
|
|
165
|
+
|
|
166
|
+
const db = levelup(createMemDown());
|
|
167
|
+
const tree = await createDb(db, pedersen, 'test', depth);
|
|
168
|
+
|
|
169
|
+
const leaves = Array.from({ length: 1000 }).map(() => randomBytes(32));
|
|
170
|
+
const indices = Array.from({ length: 1000 }).map(() => BigInt(Math.floor(Math.random() * maxIndex)));
|
|
171
|
+
|
|
172
|
+
const start = Date.now();
|
|
173
|
+
await Promise.all(leaves.map((leaf, i) => tree.updateLeaf(leaf, indices[i])));
|
|
174
|
+
const end = Date.now();
|
|
175
|
+
log(`Inserting 1000 leaves at random positions for depth 254 took ${end - start}ms`);
|
|
176
|
+
}, 300_000);
|
|
177
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { UpdateOnlyTree } from '../interfaces/update_only_tree.js';
|
|
2
|
+
import { INITIAL_LEAF, TreeBase } from '../tree_base.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A Merkle tree implementation that uses a LevelDB database to store the tree.
|
|
6
|
+
*/
|
|
7
|
+
export class SparseTree extends TreeBase implements UpdateOnlyTree {
|
|
8
|
+
/**
|
|
9
|
+
* Updates a leaf in the tree.
|
|
10
|
+
* @param leaf - New contents of the leaf.
|
|
11
|
+
* @param index - Index of the leaf to be updated.
|
|
12
|
+
*/
|
|
13
|
+
public async updateLeaf(leaf: Buffer, index: bigint): Promise<void> {
|
|
14
|
+
if (index > this.maxIndex) {
|
|
15
|
+
throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const insertingZeroElement = leaf.equals(INITIAL_LEAF);
|
|
19
|
+
const originallyZeroElement = (await this.getLeafValue(index, true))?.equals(INITIAL_LEAF);
|
|
20
|
+
if (insertingZeroElement && originallyZeroElement) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
await this.addLeafToCacheAndHashToRoot(leaf, index);
|
|
24
|
+
if (insertingZeroElement) {
|
|
25
|
+
// Deleting element (originally non-zero and new value is zero)
|
|
26
|
+
this.cachedSize = (this.cachedSize ?? this.size) - 1n;
|
|
27
|
+
} else if (originallyZeroElement) {
|
|
28
|
+
// Inserting new element (originally zero and new value is non-zero)
|
|
29
|
+
this.cachedSize = (this.cachedSize ?? this.size) + 1n;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
import { default as levelup } from 'levelup';
|
|
2
|
+
import { Hasher, INITIAL_LEAF, MerkleTree, Pedersen, SiblingPath } from '../index.js';
|
|
3
|
+
import { StandardIndexedTree } from './standard_indexed_tree.js';
|
|
4
|
+
import { treeTestSuite } from '../test/test_suite.js';
|
|
5
|
+
|
|
6
|
+
import { createMemDown } from '../test/utils/create_mem_down.js';
|
|
7
|
+
import { newTree } from '../new_tree.js';
|
|
8
|
+
import { loadTree } from '../load_tree.js';
|
|
9
|
+
import { toBufferBE } from '@aztec/foundation/bigint-buffer';
|
|
10
|
+
import { IWasmModule } from '@aztec/foundation/wasm';
|
|
11
|
+
import { CircuitsWasm } from '@aztec/circuits.js';
|
|
12
|
+
|
|
13
|
+
const createDb = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string, depth: number) => {
|
|
14
|
+
return await newTree(StandardIndexedTree, levelUp, hasher, name, depth);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const createFromName = async (levelUp: levelup.LevelUp, hasher: Hasher, name: string) => {
|
|
18
|
+
return await loadTree(StandardIndexedTree, levelUp, hasher, name);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const createIndexedTreeLeaf = (value: number, nextIndex: number, nextValue: number) => {
|
|
22
|
+
return [toBufferBE(BigInt(value), 32), toBufferBE(BigInt(nextIndex), 32), toBufferBE(BigInt(nextValue), 32)];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const verifyCommittedState = async <N extends number>(
|
|
26
|
+
tree: MerkleTree,
|
|
27
|
+
root: Buffer,
|
|
28
|
+
siblingPathIndex: bigint,
|
|
29
|
+
emptySiblingPath: SiblingPath<N>,
|
|
30
|
+
) => {
|
|
31
|
+
expect(tree.getRoot(false)).toEqual(root);
|
|
32
|
+
expect(tree.getNumLeaves(false)).toEqual(1n);
|
|
33
|
+
expect(await tree.getSiblingPath(siblingPathIndex, false)).toEqual(emptySiblingPath);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const TEST_TREE_DEPTH = 3;
|
|
37
|
+
|
|
38
|
+
treeTestSuite('StandardIndexedTree', createDb, createFromName);
|
|
39
|
+
|
|
40
|
+
describe('StandardIndexedTreeSpecific', () => {
|
|
41
|
+
let wasm: IWasmModule;
|
|
42
|
+
let pedersen: Pedersen;
|
|
43
|
+
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
wasm = await CircuitsWasm.get();
|
|
46
|
+
pedersen = new Pedersen(wasm);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('produces the correct roots and sibling paths', async () => {
|
|
50
|
+
// Create a depth-3 indexed merkle tree
|
|
51
|
+
const db = levelup(createMemDown());
|
|
52
|
+
const tree = await createDb(db, pedersen, 'test', 3);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Initial state:
|
|
56
|
+
*
|
|
57
|
+
* index 0 1 2 3 4 5 6 7
|
|
58
|
+
* ---------------------------------------------------------------------
|
|
59
|
+
* val 0 0 0 0 0 0 0 0
|
|
60
|
+
* nextIdx 0 0 0 0 0 0 0 0
|
|
61
|
+
* nextVal 0 0 0 0 0 0 0 0.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
const initialLeafHash = pedersen.compressInputs(createIndexedTreeLeaf(0, 0, 0));
|
|
65
|
+
const level1ZeroHash = pedersen.compress(INITIAL_LEAF, INITIAL_LEAF);
|
|
66
|
+
const level2ZeroHash = pedersen.compress(level1ZeroHash, level1ZeroHash);
|
|
67
|
+
|
|
68
|
+
let index0Hash = initialLeafHash;
|
|
69
|
+
// 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
|
|
70
|
+
let e10 = pedersen.compress(index0Hash, INITIAL_LEAF);
|
|
71
|
+
let e20 = pedersen.compress(e10, level1ZeroHash);
|
|
72
|
+
|
|
73
|
+
const initialE20 = e20; // Kept for calculating committed state later
|
|
74
|
+
const initialE10 = e10;
|
|
75
|
+
|
|
76
|
+
let root = pedersen.compress(e20, level2ZeroHash);
|
|
77
|
+
const initialRoot = root;
|
|
78
|
+
|
|
79
|
+
const emptySiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]);
|
|
80
|
+
|
|
81
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
82
|
+
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
83
|
+
expect(await tree.getSiblingPath(0n, true)).toEqual(
|
|
84
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await verifyCommittedState(tree, initialRoot, 0n, emptySiblingPath);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Add new value 30:
|
|
91
|
+
*
|
|
92
|
+
* index 0 1 2 3 4 5 6 7
|
|
93
|
+
* ---------------------------------------------------------------------
|
|
94
|
+
* val 0 30 0 0 0 0 0 0
|
|
95
|
+
* nextIdx 1 0 0 0 0 0 0 0
|
|
96
|
+
* nextVal 30 0 0 0 0 0 0 0.
|
|
97
|
+
*/
|
|
98
|
+
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 1, 30));
|
|
99
|
+
let index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 0, 0));
|
|
100
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
101
|
+
e20 = pedersen.compress(e10, level1ZeroHash);
|
|
102
|
+
root = pedersen.compress(e20, level2ZeroHash);
|
|
103
|
+
|
|
104
|
+
await tree.appendLeaves([toBufferBE(30n, 32)]);
|
|
105
|
+
|
|
106
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
107
|
+
expect(tree.getNumLeaves(true)).toEqual(2n);
|
|
108
|
+
expect(await tree.getSiblingPath(1n, true)).toEqual(
|
|
109
|
+
new SiblingPath(TEST_TREE_DEPTH, [index0Hash, level1ZeroHash, level2ZeroHash]),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// ensure the committed state is correct
|
|
113
|
+
const initialSiblingPath = new SiblingPath(TEST_TREE_DEPTH, [initialLeafHash, level1ZeroHash, level2ZeroHash]);
|
|
114
|
+
await verifyCommittedState(tree, initialRoot, 1n, initialSiblingPath);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Add new value 10:
|
|
118
|
+
*
|
|
119
|
+
* index 0 1 2 3 4 5 6 7
|
|
120
|
+
* ---------------------------------------------------------------------
|
|
121
|
+
* val 0 30 10 0 0 0 0 0
|
|
122
|
+
* nextIdx 2 0 1 0 0 0 0 0
|
|
123
|
+
* nextVal 10 0 30 0 0 0 0 0.
|
|
124
|
+
*/
|
|
125
|
+
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 2, 10));
|
|
126
|
+
let index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 1, 30));
|
|
127
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
128
|
+
let e11 = pedersen.compress(index2Hash, INITIAL_LEAF);
|
|
129
|
+
e20 = pedersen.compress(e10, e11);
|
|
130
|
+
root = pedersen.compress(e20, level2ZeroHash);
|
|
131
|
+
|
|
132
|
+
await tree.appendLeaves([toBufferBE(10n, 32)]);
|
|
133
|
+
|
|
134
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
135
|
+
expect(tree.getNumLeaves(true)).toEqual(3n);
|
|
136
|
+
expect(await tree.getSiblingPath(2n, true)).toEqual(
|
|
137
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e10, level2ZeroHash]),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// ensure the committed state is correct
|
|
141
|
+
await verifyCommittedState(
|
|
142
|
+
tree,
|
|
143
|
+
initialRoot,
|
|
144
|
+
2n,
|
|
145
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Add new value 20:
|
|
150
|
+
*
|
|
151
|
+
* index 0 1 2 3 4 5 6 7
|
|
152
|
+
* ---------------------------------------------------------------------
|
|
153
|
+
* val 0 30 10 20 0 0 0 0
|
|
154
|
+
* nextIdx 2 0 3 1 0 0 0 0
|
|
155
|
+
* nextVal 10 0 20 30 0 0 0 0.
|
|
156
|
+
*/
|
|
157
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
158
|
+
index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 3, 20));
|
|
159
|
+
const index3Hash = pedersen.compressInputs(createIndexedTreeLeaf(20, 1, 30));
|
|
160
|
+
e11 = pedersen.compress(index2Hash, index3Hash);
|
|
161
|
+
e20 = pedersen.compress(e10, e11);
|
|
162
|
+
root = pedersen.compress(e20, level2ZeroHash);
|
|
163
|
+
|
|
164
|
+
await tree.appendLeaves([toBufferBE(20n, 32)]);
|
|
165
|
+
|
|
166
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
167
|
+
expect(tree.getNumLeaves(true)).toEqual(4n);
|
|
168
|
+
expect(await tree.getSiblingPath(3n, true)).toEqual(
|
|
169
|
+
new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, level2ZeroHash]),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// ensure the committed state is correct
|
|
173
|
+
await verifyCommittedState(
|
|
174
|
+
tree,
|
|
175
|
+
initialRoot,
|
|
176
|
+
3n,
|
|
177
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Add new value 50:
|
|
182
|
+
*
|
|
183
|
+
* index 0 1 2 3 4 5 6 7
|
|
184
|
+
* ---------------------------------------------------------------------
|
|
185
|
+
* val 0 30 10 20 50 0 0 0
|
|
186
|
+
* nextIdx 2 4 3 1 0 0 0 0
|
|
187
|
+
* nextVal 10 50 20 30 0 0 0 0.
|
|
188
|
+
*/
|
|
189
|
+
index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 4, 50));
|
|
190
|
+
const index4Hash = pedersen.compressInputs(createIndexedTreeLeaf(50, 0, 0));
|
|
191
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
192
|
+
e20 = pedersen.compress(e10, e11);
|
|
193
|
+
const e12 = pedersen.compress(index4Hash, INITIAL_LEAF);
|
|
194
|
+
const e21 = pedersen.compress(e12, level1ZeroHash);
|
|
195
|
+
root = pedersen.compress(e20, e21);
|
|
196
|
+
|
|
197
|
+
await tree.appendLeaves([toBufferBE(50n, 32)]);
|
|
198
|
+
|
|
199
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
200
|
+
expect(tree.getNumLeaves(true)).toEqual(5n);
|
|
201
|
+
|
|
202
|
+
// ensure the committed state is correct
|
|
203
|
+
await verifyCommittedState(
|
|
204
|
+
tree,
|
|
205
|
+
initialRoot,
|
|
206
|
+
4n,
|
|
207
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, initialE20]),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// check all uncommitted hash paths
|
|
211
|
+
expect(await tree.getSiblingPath(0n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index1Hash, e11, e21]));
|
|
212
|
+
expect(await tree.getSiblingPath(1n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index0Hash, e11, e21]));
|
|
213
|
+
expect(await tree.getSiblingPath(2n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index3Hash, e10, e21]));
|
|
214
|
+
expect(await tree.getSiblingPath(3n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, e21]));
|
|
215
|
+
expect(await tree.getSiblingPath(4n, true)).toEqual(
|
|
216
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, e20]),
|
|
217
|
+
);
|
|
218
|
+
expect(await tree.getSiblingPath(5n, true)).toEqual(
|
|
219
|
+
new SiblingPath(TEST_TREE_DEPTH, [index4Hash, level1ZeroHash, e20]),
|
|
220
|
+
);
|
|
221
|
+
expect(await tree.getSiblingPath(6n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e12, e20]));
|
|
222
|
+
expect(await tree.getSiblingPath(7n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e12, e20]));
|
|
223
|
+
|
|
224
|
+
// check all committed hash paths
|
|
225
|
+
expect(await tree.getSiblingPath(0n, false)).toEqual(emptySiblingPath);
|
|
226
|
+
expect(await tree.getSiblingPath(1n, false)).toEqual(initialSiblingPath);
|
|
227
|
+
expect(await tree.getSiblingPath(2n, false)).toEqual(
|
|
228
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]),
|
|
229
|
+
);
|
|
230
|
+
expect(await tree.getSiblingPath(3n, false)).toEqual(
|
|
231
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, initialE10, level2ZeroHash]),
|
|
232
|
+
);
|
|
233
|
+
const e2SiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, initialE20]);
|
|
234
|
+
expect(await tree.getSiblingPath(4n, false)).toEqual(e2SiblingPath);
|
|
235
|
+
expect(await tree.getSiblingPath(5n, false)).toEqual(e2SiblingPath);
|
|
236
|
+
expect(await tree.getSiblingPath(6n, false)).toEqual(e2SiblingPath);
|
|
237
|
+
expect(await tree.getSiblingPath(7n, false)).toEqual(e2SiblingPath);
|
|
238
|
+
|
|
239
|
+
await tree.commit();
|
|
240
|
+
// check all committed hash paths equal uncommitted hash paths
|
|
241
|
+
for (let i = 0; i < 8; i++) {
|
|
242
|
+
expect(await tree.getSiblingPath(BigInt(i), false)).toEqual(await tree.getSiblingPath(BigInt(i), true));
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('Can append empty leaves and handle insertions', async () => {
|
|
247
|
+
// Create a depth-3 indexed merkle tree
|
|
248
|
+
const db = levelup(createMemDown());
|
|
249
|
+
const tree = await createDb(db, pedersen, 'test', 3);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Initial state:
|
|
253
|
+
*
|
|
254
|
+
* index 0 1 2 3 4 5 6 7
|
|
255
|
+
* ---------------------------------------------------------------------
|
|
256
|
+
* val 0 0 0 0 0 0 0 0
|
|
257
|
+
* nextIdx 0 0 0 0 0 0 0 0
|
|
258
|
+
* nextVal 0 0 0 0 0 0 0 0.
|
|
259
|
+
*/
|
|
260
|
+
|
|
261
|
+
const INITIAL_LEAF = toBufferBE(0n, 32);
|
|
262
|
+
const initialLeafHash = pedersen.compressInputs(createIndexedTreeLeaf(0, 0, 0));
|
|
263
|
+
const level1ZeroHash = pedersen.compress(INITIAL_LEAF, INITIAL_LEAF);
|
|
264
|
+
const level2ZeroHash = pedersen.compress(level1ZeroHash, level1ZeroHash);
|
|
265
|
+
let index0Hash = initialLeafHash;
|
|
266
|
+
|
|
267
|
+
let e10 = pedersen.compress(index0Hash, INITIAL_LEAF);
|
|
268
|
+
let e20 = pedersen.compress(e10, level1ZeroHash);
|
|
269
|
+
|
|
270
|
+
const inite10 = e10;
|
|
271
|
+
const inite20 = e20;
|
|
272
|
+
|
|
273
|
+
let root = pedersen.compress(e20, level2ZeroHash);
|
|
274
|
+
const initialRoot = root;
|
|
275
|
+
|
|
276
|
+
const emptySiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]);
|
|
277
|
+
const initialSiblingPath = new SiblingPath(TEST_TREE_DEPTH, [initialLeafHash, level1ZeroHash, level2ZeroHash]);
|
|
278
|
+
|
|
279
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
280
|
+
expect(tree.getNumLeaves(true)).toEqual(1n);
|
|
281
|
+
expect(await tree.getSiblingPath(0n, true)).toEqual(
|
|
282
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, level2ZeroHash]),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
await verifyCommittedState(tree, initialRoot, 0n, emptySiblingPath);
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Add new value 30:
|
|
289
|
+
*
|
|
290
|
+
* index 0 1 2 3 4 5 6 7
|
|
291
|
+
* ---------------------------------------------------------------------
|
|
292
|
+
* val 0 30 0 0 0 0 0 0
|
|
293
|
+
* nextIdx 1 0 0 0 0 0 0 0
|
|
294
|
+
* nextVal 30 0 0 0 0 0 0 0.
|
|
295
|
+
*/
|
|
296
|
+
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 1, 30));
|
|
297
|
+
let index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 0, 0));
|
|
298
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
299
|
+
e20 = pedersen.compress(e10, level1ZeroHash);
|
|
300
|
+
root = pedersen.compress(e20, level2ZeroHash);
|
|
301
|
+
|
|
302
|
+
await tree.appendLeaves([toBufferBE(30n, 32)]);
|
|
303
|
+
|
|
304
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
305
|
+
expect(tree.getNumLeaves(true)).toEqual(2n);
|
|
306
|
+
expect(await tree.getSiblingPath(1n, true)).toEqual(
|
|
307
|
+
new SiblingPath(TEST_TREE_DEPTH, [index0Hash, level1ZeroHash, level2ZeroHash]),
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
// ensure the committed state is correct
|
|
311
|
+
await verifyCommittedState(tree, initialRoot, 1n, initialSiblingPath);
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Add new value 10:
|
|
315
|
+
*
|
|
316
|
+
* index 0 1 2 3 4 5 6 7
|
|
317
|
+
* ---------------------------------------------------------------------
|
|
318
|
+
* val 0 30 10 0 0 0 0 0
|
|
319
|
+
* nextIdx 2 0 1 0 0 0 0 0
|
|
320
|
+
* nextVal 10 0 30 0 0 0 0 0.
|
|
321
|
+
*/
|
|
322
|
+
index0Hash = pedersen.compressInputs(createIndexedTreeLeaf(0, 2, 10));
|
|
323
|
+
let index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 1, 30));
|
|
324
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
325
|
+
let e11 = pedersen.compress(index2Hash, INITIAL_LEAF);
|
|
326
|
+
e20 = pedersen.compress(e10, e11);
|
|
327
|
+
root = pedersen.compress(e20, level2ZeroHash);
|
|
328
|
+
|
|
329
|
+
await tree.appendLeaves([toBufferBE(10n, 32)]);
|
|
330
|
+
|
|
331
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
332
|
+
expect(tree.getNumLeaves(true)).toEqual(3n);
|
|
333
|
+
expect(await tree.getSiblingPath(2n, true)).toEqual(
|
|
334
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e10, level2ZeroHash]),
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
// ensure the committed state is correct
|
|
338
|
+
await verifyCommittedState(
|
|
339
|
+
tree,
|
|
340
|
+
initialRoot,
|
|
341
|
+
2n,
|
|
342
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]),
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Add new value 20:
|
|
347
|
+
*
|
|
348
|
+
* index 0 1 2 3 4 5 6 7
|
|
349
|
+
* ---------------------------------------------------------------------
|
|
350
|
+
* val 0 30 10 20 0 0 0 0
|
|
351
|
+
* nextIdx 2 0 3 1 0 0 0 0
|
|
352
|
+
* nextVal 10 0 20 30 0 0 0 0.
|
|
353
|
+
*/
|
|
354
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
355
|
+
index2Hash = pedersen.compressInputs(createIndexedTreeLeaf(10, 3, 20));
|
|
356
|
+
const index3Hash = pedersen.compressInputs(createIndexedTreeLeaf(20, 1, 30));
|
|
357
|
+
e11 = pedersen.compress(index2Hash, index3Hash);
|
|
358
|
+
e20 = pedersen.compress(e10, e11);
|
|
359
|
+
root = pedersen.compress(e20, level2ZeroHash);
|
|
360
|
+
|
|
361
|
+
await tree.appendLeaves([toBufferBE(20n, 32)]);
|
|
362
|
+
|
|
363
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
364
|
+
expect(tree.getNumLeaves(true)).toEqual(4n);
|
|
365
|
+
expect(await tree.getSiblingPath(3n, true)).toEqual(
|
|
366
|
+
new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, level2ZeroHash]),
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
// ensure the committed state is correct
|
|
370
|
+
await verifyCommittedState(
|
|
371
|
+
tree,
|
|
372
|
+
initialRoot,
|
|
373
|
+
3n,
|
|
374
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]),
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
// Add 2 empty values
|
|
378
|
+
const emptyLeaves = [toBufferBE(0n, 32), toBufferBE(0n, 32)];
|
|
379
|
+
await tree.appendLeaves(emptyLeaves);
|
|
380
|
+
|
|
381
|
+
// The root should be the same but the size should have increased
|
|
382
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
383
|
+
expect(tree.getNumLeaves(true)).toEqual(6n);
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Add new value 50:
|
|
387
|
+
*
|
|
388
|
+
* index 0 1 2 3 4 5 6 7
|
|
389
|
+
* --------------------------------------------------------------------
|
|
390
|
+
* val 0 30 10 20 0 0 50 0
|
|
391
|
+
* nextIdx 2 6 3 1 0 0 0 0
|
|
392
|
+
* nextVal 10 50 20 30 0 0 0 0.
|
|
393
|
+
*/
|
|
394
|
+
index1Hash = pedersen.compressInputs(createIndexedTreeLeaf(30, 6, 50));
|
|
395
|
+
const index6Hash = pedersen.compressInputs(createIndexedTreeLeaf(50, 0, 0));
|
|
396
|
+
e10 = pedersen.compress(index0Hash, index1Hash);
|
|
397
|
+
e20 = pedersen.compress(e10, e11);
|
|
398
|
+
const e13 = pedersen.compress(index6Hash, INITIAL_LEAF);
|
|
399
|
+
const e21 = pedersen.compress(level1ZeroHash, e13);
|
|
400
|
+
root = pedersen.compress(e20, e21);
|
|
401
|
+
|
|
402
|
+
await tree.appendLeaves([toBufferBE(50n, 32)]);
|
|
403
|
+
|
|
404
|
+
expect(tree.getRoot(true)).toEqual(root);
|
|
405
|
+
expect(tree.getNumLeaves(true)).toEqual(7n);
|
|
406
|
+
|
|
407
|
+
// ensure the committed state is correct
|
|
408
|
+
await verifyCommittedState(
|
|
409
|
+
tree,
|
|
410
|
+
initialRoot,
|
|
411
|
+
6n,
|
|
412
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, inite20]),
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
// // check all uncommitted hash paths
|
|
416
|
+
expect(await tree.getSiblingPath(0n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index1Hash, e11, e21]));
|
|
417
|
+
expect(await tree.getSiblingPath(1n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index0Hash, e11, e21]));
|
|
418
|
+
expect(await tree.getSiblingPath(2n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index3Hash, e10, e21]));
|
|
419
|
+
expect(await tree.getSiblingPath(3n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [index2Hash, e10, e21]));
|
|
420
|
+
expect(await tree.getSiblingPath(4n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e13, e20]));
|
|
421
|
+
expect(await tree.getSiblingPath(5n, true)).toEqual(new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, e13, e20]));
|
|
422
|
+
expect(await tree.getSiblingPath(6n, true)).toEqual(
|
|
423
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, e20]),
|
|
424
|
+
);
|
|
425
|
+
expect(await tree.getSiblingPath(7n, true)).toEqual(
|
|
426
|
+
new SiblingPath(TEST_TREE_DEPTH, [index6Hash, level1ZeroHash, e20]),
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
// check all committed hash paths
|
|
430
|
+
expect(await tree.getSiblingPath(0n, false)).toEqual(emptySiblingPath);
|
|
431
|
+
expect(await tree.getSiblingPath(1n, false)).toEqual(initialSiblingPath);
|
|
432
|
+
expect(await tree.getSiblingPath(2n, false)).toEqual(
|
|
433
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]),
|
|
434
|
+
);
|
|
435
|
+
expect(await tree.getSiblingPath(3n, false)).toEqual(
|
|
436
|
+
new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, inite10, level2ZeroHash]),
|
|
437
|
+
);
|
|
438
|
+
const e2SiblingPath = new SiblingPath(TEST_TREE_DEPTH, [INITIAL_LEAF, level1ZeroHash, inite20]);
|
|
439
|
+
expect(await tree.getSiblingPath(4n, false)).toEqual(e2SiblingPath);
|
|
440
|
+
expect(await tree.getSiblingPath(5n, false)).toEqual(e2SiblingPath);
|
|
441
|
+
expect(await tree.getSiblingPath(6n, false)).toEqual(e2SiblingPath);
|
|
442
|
+
expect(await tree.getSiblingPath(7n, false)).toEqual(e2SiblingPath);
|
|
443
|
+
|
|
444
|
+
await tree.commit();
|
|
445
|
+
// check all committed hash paths equal uncommitted hash paths
|
|
446
|
+
for (let i = 0; i < 8; i++) {
|
|
447
|
+
expect(await tree.getSiblingPath(BigInt(i), false)).toEqual(await tree.getSiblingPath(BigInt(i), true));
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
});
|