@aztec/merkle-tree 0.16.1 → 0.16.2
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/dest/index.d.ts +5 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +6 -2
- package/dest/interfaces/append_only_tree.d.ts +2 -1
- package/dest/interfaces/append_only_tree.d.ts.map +1 -1
- package/dest/interfaces/indexed_tree.d.ts +38 -17
- package/dest/interfaces/indexed_tree.d.ts.map +1 -1
- package/dest/interfaces/merkle_tree.d.ts +7 -0
- package/dest/interfaces/merkle_tree.d.ts.map +1 -1
- package/dest/interfaces/update_only_tree.d.ts +3 -3
- package/dest/interfaces/update_only_tree.d.ts.map +1 -1
- package/dest/load_tree.d.ts +2 -1
- package/dest/load_tree.d.ts.map +1 -1
- package/dest/load_tree.js +1 -2
- package/dest/new_tree.d.ts +1 -1
- package/dest/new_tree.d.ts.map +1 -1
- package/dest/new_tree.js +2 -2
- package/dest/snapshots/append_only_snapshot.d.ts +30 -0
- package/dest/snapshots/append_only_snapshot.d.ts.map +1 -0
- package/dest/snapshots/append_only_snapshot.js +200 -0
- package/dest/snapshots/base_full_snapshot.d.ts +50 -0
- package/dest/snapshots/base_full_snapshot.d.ts.map +1 -0
- package/dest/snapshots/base_full_snapshot.js +179 -0
- package/dest/snapshots/full_snapshot.d.ts +22 -0
- package/dest/snapshots/full_snapshot.d.ts.map +1 -0
- package/dest/snapshots/full_snapshot.js +21 -0
- package/dest/snapshots/indexed_tree_snapshot.d.ts +15 -0
- package/dest/snapshots/indexed_tree_snapshot.d.ts.map +1 -0
- package/dest/snapshots/indexed_tree_snapshot.js +75 -0
- package/dest/snapshots/snapshot_builder.d.ts +76 -0
- package/dest/snapshots/snapshot_builder.d.ts.map +1 -0
- package/dest/snapshots/snapshot_builder.js +2 -0
- package/dest/snapshots/snapshot_builder_test_suite.d.ts +5 -0
- package/dest/snapshots/snapshot_builder_test_suite.d.ts.map +1 -0
- package/dest/snapshots/snapshot_builder_test_suite.js +163 -0
- package/dest/sparse_tree/sparse_tree.d.ts +5 -0
- package/dest/sparse_tree/sparse_tree.d.ts.map +1 -1
- package/dest/sparse_tree/sparse_tree.js +18 -1
- package/dest/standard_indexed_tree/standard_indexed_tree.d.ts +111 -81
- package/dest/standard_indexed_tree/standard_indexed_tree.d.ts.map +1 -1
- package/dest/standard_indexed_tree/standard_indexed_tree.js +225 -259
- package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.d.ts.map +1 -1
- package/dest/standard_indexed_tree/test/standard_indexed_tree_with_append.js +13 -19
- package/dest/standard_tree/standard_tree.d.ts +5 -0
- package/dest/standard_tree/standard_tree.d.ts.map +1 -1
- package/dest/standard_tree/standard_tree.js +24 -1
- package/dest/tree_base.d.ts +9 -4
- package/dest/tree_base.d.ts.map +1 -1
- package/dest/tree_base.js +16 -7
- package/package.json +4 -3
- package/src/index.ts +5 -1
- package/src/interfaces/append_only_tree.ts +2 -1
- package/src/interfaces/indexed_tree.ts +50 -28
- package/src/interfaces/merkle_tree.ts +8 -0
- package/src/interfaces/update_only_tree.ts +3 -4
- package/src/load_tree.ts +2 -2
- package/src/new_tree.ts +2 -2
- package/src/snapshots/append_only_snapshot.ts +243 -0
- package/src/snapshots/base_full_snapshot.ts +232 -0
- package/src/snapshots/full_snapshot.ts +26 -0
- package/src/snapshots/indexed_tree_snapshot.ts +108 -0
- package/src/snapshots/snapshot_builder.ts +84 -0
- package/src/snapshots/snapshot_builder_test_suite.ts +218 -0
- package/src/sparse_tree/sparse_tree.ts +16 -0
- package/src/standard_indexed_tree/standard_indexed_tree.ts +325 -304
- package/src/standard_indexed_tree/test/standard_indexed_tree_with_append.ts +23 -21
- package/src/standard_tree/standard_tree.ts +21 -0
- package/src/tree_base.ts +28 -7
|
@@ -1,83 +1,107 @@
|
|
|
1
1
|
import { toBigIntBE, toBufferBE } from '@aztec/foundation/bigint-buffer';
|
|
2
2
|
import { createDebugLogger } from '@aztec/foundation/log';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { IndexedTreeLeaf, IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
|
|
4
|
+
import { Hasher, SiblingPath } from '@aztec/types';
|
|
5
|
+
|
|
6
|
+
import { LevelUp } from 'levelup';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
BatchInsertionResult,
|
|
10
|
+
IndexedTree,
|
|
11
|
+
IndexedTreeSnapshot,
|
|
12
|
+
IndexedTreeSnapshotBuilder,
|
|
13
|
+
LowLeafWitnessData,
|
|
14
|
+
} from '../index.js';
|
|
6
15
|
import { TreeBase } from '../tree_base.js';
|
|
7
16
|
|
|
8
17
|
const log = createDebugLogger('aztec:standard-indexed-tree');
|
|
9
18
|
|
|
10
|
-
const indexToKeyLeaf = (name: string, index: bigint) => {
|
|
11
|
-
return `${name}:leaf:${toBufferBE(index, 32).toString('hex')}`;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const keyLeafToIndex = (key: string): bigint => {
|
|
15
|
-
const index = key.split(':')[2];
|
|
16
|
-
return toBigIntBE(Buffer.from(index, 'hex'));
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const zeroLeaf: LeafData = {
|
|
20
|
-
value: 0n,
|
|
21
|
-
nextValue: 0n,
|
|
22
|
-
nextIndex: 0n,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
19
|
/**
|
|
26
|
-
*
|
|
20
|
+
* Factory for creating leaf preimages.
|
|
27
21
|
*/
|
|
28
|
-
export interface
|
|
22
|
+
export interface PreimageFactory {
|
|
29
23
|
/**
|
|
30
|
-
*
|
|
24
|
+
* Creates a new preimage from a leaf.
|
|
25
|
+
* @param leaf - Leaf to create a preimage from.
|
|
26
|
+
* @param nextKey - Next key of the leaf.
|
|
27
|
+
* @param nextIndex - Next index of the leaf.
|
|
31
28
|
*/
|
|
32
|
-
|
|
29
|
+
fromLeaf(leaf: IndexedTreeLeaf, nextKey: bigint, nextIndex: bigint): IndexedTreeLeafPreimage;
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new preimage from a buffer.
|
|
32
|
+
* @param buffer - Buffer to create a preimage from.
|
|
33
|
+
*/
|
|
34
|
+
fromBuffer(buffer: Buffer): IndexedTreeLeafPreimage;
|
|
35
|
+
/**
|
|
36
|
+
* Creates an empty preimage.
|
|
37
|
+
*/
|
|
38
|
+
empty(): IndexedTreeLeafPreimage;
|
|
39
|
+
/**
|
|
40
|
+
* Creates a copy of a preimage.
|
|
41
|
+
* @param preimage - Preimage to be cloned.
|
|
42
|
+
*/
|
|
43
|
+
clone(preimage: IndexedTreeLeafPreimage): IndexedTreeLeafPreimage;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Factory for creating leaves.
|
|
48
|
+
*/
|
|
49
|
+
export interface LeafFactory {
|
|
33
50
|
/**
|
|
34
|
-
*
|
|
51
|
+
* Creates a new leaf from a buffer.
|
|
52
|
+
* @param key - Key of the leaf.
|
|
35
53
|
*/
|
|
36
|
-
|
|
54
|
+
buildDummy(key: bigint): IndexedTreeLeaf;
|
|
37
55
|
/**
|
|
38
|
-
*
|
|
56
|
+
* Creates a new leaf from a buffer.
|
|
57
|
+
* @param buffer - Buffer to create a leaf from.
|
|
39
58
|
*/
|
|
40
|
-
|
|
59
|
+
fromBuffer(buffer: Buffer): IndexedTreeLeaf;
|
|
41
60
|
}
|
|
42
61
|
|
|
62
|
+
export const buildDbKeyForPreimage = (name: string, index: bigint) => {
|
|
63
|
+
return `${name}:leaf_by_index:${toBufferBE(index, 32).toString('hex')}`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const buildDbKeyForLeafIndex = (name: string, key: bigint) => {
|
|
67
|
+
return `${name}:leaf_index_by_leaf_key:${toBufferBE(key, 32).toString('hex')}`;
|
|
68
|
+
};
|
|
69
|
+
|
|
43
70
|
/**
|
|
44
71
|
* Pre-compute empty witness.
|
|
45
72
|
* @param treeHeight - Height of tree for sibling path.
|
|
46
73
|
* @returns An empty witness.
|
|
47
74
|
*/
|
|
48
|
-
function getEmptyLowLeafWitness<N extends number>(
|
|
75
|
+
function getEmptyLowLeafWitness<N extends number>(
|
|
76
|
+
treeHeight: N,
|
|
77
|
+
leafPreimageFactory: PreimageFactory,
|
|
78
|
+
): LowLeafWitnessData<N> {
|
|
49
79
|
return {
|
|
50
|
-
|
|
80
|
+
leafPreimage: leafPreimageFactory.empty(),
|
|
51
81
|
index: 0n,
|
|
52
82
|
siblingPath: new SiblingPath(treeHeight, Array(treeHeight).fill(toBufferBE(0n, 32))),
|
|
53
83
|
};
|
|
54
84
|
}
|
|
55
85
|
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
57
|
-
const encodeTreeValue = (leafData: LeafData) => {
|
|
58
|
-
const valueAsBuffer = toBufferBE(leafData.value, 32);
|
|
59
|
-
const indexAsBuffer = toBufferBE(leafData.nextIndex, 32);
|
|
60
|
-
const nextValueAsBuffer = toBufferBE(leafData.nextValue, 32);
|
|
61
|
-
return Buffer.concat([valueAsBuffer, indexAsBuffer, nextValueAsBuffer]);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const decodeTreeValue = (buf: Buffer) => {
|
|
65
|
-
const value = toBigIntBE(buf.subarray(0, 32));
|
|
66
|
-
const nextIndex = toBigIntBE(buf.subarray(32, 64));
|
|
67
|
-
const nextValue = toBigIntBE(buf.subarray(64, 96));
|
|
68
|
-
return {
|
|
69
|
-
value,
|
|
70
|
-
nextIndex,
|
|
71
|
-
nextValue,
|
|
72
|
-
} as LeafData;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
86
|
/**
|
|
76
|
-
*
|
|
87
|
+
* Standard implementation of an indexed tree.
|
|
77
88
|
*/
|
|
78
89
|
export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
79
|
-
|
|
80
|
-
protected
|
|
90
|
+
#snapshotBuilder = new IndexedTreeSnapshotBuilder(this.db, this, this.leafPreimageFactory);
|
|
91
|
+
protected cachedLeafPreimages: { [key: string]: IndexedTreeLeafPreimage } = {};
|
|
92
|
+
|
|
93
|
+
public constructor(
|
|
94
|
+
db: LevelUp,
|
|
95
|
+
hasher: Hasher,
|
|
96
|
+
name: string,
|
|
97
|
+
depth: number,
|
|
98
|
+
size: bigint = 0n,
|
|
99
|
+
protected leafPreimageFactory: PreimageFactory,
|
|
100
|
+
protected leafFactory: LeafFactory,
|
|
101
|
+
root?: Buffer,
|
|
102
|
+
) {
|
|
103
|
+
super(db, hasher, name, depth, size, root);
|
|
104
|
+
}
|
|
81
105
|
|
|
82
106
|
/**
|
|
83
107
|
* Appends the given leaves to the tree.
|
|
@@ -85,7 +109,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
85
109
|
* @returns Empty promise.
|
|
86
110
|
* @remarks Use batchInsert method instead.
|
|
87
111
|
*/
|
|
88
|
-
|
|
112
|
+
appendLeaves(_leaves: Buffer[]): Promise<void> {
|
|
89
113
|
throw new Error('Not implemented');
|
|
90
114
|
}
|
|
91
115
|
|
|
@@ -113,88 +137,149 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
113
137
|
* @param includeUncommitted - Indicates whether to include uncommitted leaves in the computation.
|
|
114
138
|
* @returns The value of the leaf at the given index or undefined if the leaf is empty.
|
|
115
139
|
*/
|
|
116
|
-
public getLeafValue(index: bigint, includeUncommitted: boolean): Promise<Buffer | undefined> {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
return Promise.resolve(undefined);
|
|
120
|
-
}
|
|
121
|
-
return Promise.resolve(toBufferBE(leaf.value, 32));
|
|
140
|
+
public async getLeafValue(index: bigint, includeUncommitted: boolean): Promise<Buffer | undefined> {
|
|
141
|
+
const preimage = await this.getLatestLeafPreimageCopy(index, includeUncommitted);
|
|
142
|
+
return preimage && preimage.toBuffer();
|
|
122
143
|
}
|
|
123
144
|
|
|
124
145
|
/**
|
|
125
146
|
* Finds the index of the largest leaf whose value is less than or equal to the provided value.
|
|
126
|
-
* @param
|
|
147
|
+
* @param newKey - The new key to be inserted into the tree.
|
|
127
148
|
* @param includeUncommitted - If true, the uncommitted changes are included in the search.
|
|
128
149
|
* @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`.
|
|
129
150
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
151
|
+
async findIndexOfPreviousKey(
|
|
152
|
+
newKey: bigint,
|
|
132
153
|
includeUncommitted: boolean,
|
|
133
|
-
):
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const numLeaves = this.getNumLeaves(includeUncommitted);
|
|
144
|
-
const diff: bigint[] = [];
|
|
145
|
-
|
|
146
|
-
for (let i = 0; i < numLeaves; i++) {
|
|
147
|
-
const storedLeaf = this.getLatestLeafDataCopy(i, includeUncommitted)!;
|
|
148
|
-
|
|
149
|
-
// The stored leaf can be undefined if it addresses an empty leaf
|
|
150
|
-
// If the leaf is empty we do the same as if the leaf was larger
|
|
151
|
-
if (storedLeaf === undefined) {
|
|
152
|
-
diff.push(newValue);
|
|
153
|
-
} else if (storedLeaf.value > newValue) {
|
|
154
|
-
diff.push(newValue);
|
|
155
|
-
} else if (storedLeaf.value === newValue) {
|
|
156
|
-
return { index: i, alreadyPresent: true };
|
|
157
|
-
} else {
|
|
158
|
-
diff.push(newValue - storedLeaf.value);
|
|
154
|
+
): Promise<
|
|
155
|
+
| {
|
|
156
|
+
/**
|
|
157
|
+
* The index of the found leaf.
|
|
158
|
+
*/
|
|
159
|
+
index: bigint;
|
|
160
|
+
/**
|
|
161
|
+
* A flag indicating if the corresponding leaf's value is equal to `newValue`.
|
|
162
|
+
*/
|
|
163
|
+
alreadyPresent: boolean;
|
|
159
164
|
}
|
|
165
|
+
| undefined
|
|
166
|
+
> {
|
|
167
|
+
let lowLeafIndex = await this.getDbLowLeafIndex(newKey);
|
|
168
|
+
let lowLeafPreimage = lowLeafIndex !== undefined ? await this.getDbPreimage(lowLeafIndex) : undefined;
|
|
169
|
+
|
|
170
|
+
if (includeUncommitted) {
|
|
171
|
+
const cachedLowLeafIndex = this.getCachedLowLeafIndex(newKey);
|
|
172
|
+
if (cachedLowLeafIndex !== undefined) {
|
|
173
|
+
const cachedLowLeafPreimage = this.getCachedPreimage(cachedLowLeafIndex)!;
|
|
174
|
+
if (!lowLeafPreimage || cachedLowLeafPreimage.getKey() > lowLeafPreimage.getKey()) {
|
|
175
|
+
lowLeafIndex = cachedLowLeafIndex;
|
|
176
|
+
lowLeafPreimage = cachedLowLeafPreimage;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (lowLeafIndex === undefined || !lowLeafPreimage) {
|
|
182
|
+
return undefined;
|
|
160
183
|
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
index: lowLeafIndex,
|
|
187
|
+
alreadyPresent: lowLeafPreimage.getKey() === newKey,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private getCachedLowLeafIndex(key: bigint): bigint | undefined {
|
|
192
|
+
const indexes = Object.getOwnPropertyNames(this.cachedLeafPreimages);
|
|
193
|
+
const lowLeafIndexes = indexes
|
|
194
|
+
.map(index => ({
|
|
195
|
+
index: BigInt(index),
|
|
196
|
+
key: this.cachedLeafPreimages[index].getKey(),
|
|
197
|
+
}))
|
|
198
|
+
.filter(({ key: candidateKey }) => candidateKey <= key)
|
|
199
|
+
.sort((a, b) => Number(b.key - a.key));
|
|
200
|
+
return lowLeafIndexes[0]?.index;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private getCachedLeafIndex(key: bigint): bigint | undefined {
|
|
204
|
+
const index = Object.keys(this.cachedLeafPreimages).find(index => {
|
|
205
|
+
return this.cachedLeafPreimages[index].getKey() === key;
|
|
206
|
+
});
|
|
207
|
+
if (index) {
|
|
208
|
+
return BigInt(index);
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private async getDbLowLeafIndex(key: bigint): Promise<bigint | undefined> {
|
|
214
|
+
return await new Promise<bigint | undefined>((resolve, reject) => {
|
|
215
|
+
let lowLeafIndex: bigint | undefined;
|
|
216
|
+
this.db
|
|
217
|
+
.createReadStream({
|
|
218
|
+
gte: buildDbKeyForLeafIndex(this.getName(), 0n),
|
|
219
|
+
lte: buildDbKeyForLeafIndex(this.getName(), key),
|
|
220
|
+
limit: 1,
|
|
221
|
+
reverse: true,
|
|
222
|
+
})
|
|
223
|
+
.on('data', data => {
|
|
224
|
+
lowLeafIndex = toBigIntBE(data.value);
|
|
225
|
+
})
|
|
226
|
+
.on('close', function () {})
|
|
227
|
+
.on('end', function () {
|
|
228
|
+
resolve(lowLeafIndex);
|
|
229
|
+
})
|
|
230
|
+
.on('error', function () {
|
|
231
|
+
log.error('stream error');
|
|
232
|
+
reject();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private async getDbPreimage(index: bigint): Promise<IndexedTreeLeafPreimage | undefined> {
|
|
238
|
+
const dbPreimage = await this.db
|
|
239
|
+
.get(buildDbKeyForPreimage(this.getName(), index))
|
|
240
|
+
.then(data => this.leafPreimageFactory.fromBuffer(data))
|
|
241
|
+
.catch(() => undefined);
|
|
242
|
+
return dbPreimage;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private getCachedPreimage(index: bigint): IndexedTreeLeafPreimage | undefined {
|
|
246
|
+
return this.cachedLeafPreimages[index.toString()];
|
|
163
247
|
}
|
|
164
248
|
|
|
165
249
|
/**
|
|
166
|
-
* Gets the latest
|
|
167
|
-
* @param index - Index of the leaf of which to obtain the
|
|
250
|
+
* Gets the latest LeafPreimage copy.
|
|
251
|
+
* @param index - Index of the leaf of which to obtain the LeafPreimage copy.
|
|
168
252
|
* @param includeUncommitted - If true, the uncommitted changes are included in the search.
|
|
169
|
-
* @returns A copy of the leaf
|
|
253
|
+
* @returns A copy of the leaf preimage at the given index or undefined if the leaf was not found.
|
|
170
254
|
*/
|
|
171
|
-
public
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
: undefined;
|
|
255
|
+
public async getLatestLeafPreimageCopy(
|
|
256
|
+
index: bigint,
|
|
257
|
+
includeUncommitted: boolean,
|
|
258
|
+
): Promise<IndexedTreeLeafPreimage | undefined> {
|
|
259
|
+
const preimage = !includeUncommitted
|
|
260
|
+
? await this.getDbPreimage(index)
|
|
261
|
+
: this.getCachedPreimage(index) ?? (await this.getDbPreimage(index));
|
|
262
|
+
return preimage && this.leafPreimageFactory.clone(preimage);
|
|
180
263
|
}
|
|
181
264
|
|
|
182
265
|
/**
|
|
183
|
-
*
|
|
184
|
-
* @param
|
|
185
|
-
* @
|
|
266
|
+
* Returns the index of a leaf given its value, or undefined if no leaf with that value is found.
|
|
267
|
+
* @param value - The leaf value to look for.
|
|
268
|
+
* @param includeUncommitted - Indicates whether to include uncommitted data.
|
|
269
|
+
* @returns The index of the first leaf found with a given value (undefined if not found).
|
|
186
270
|
*/
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
271
|
+
public async findLeafIndex(value: Buffer, includeUncommitted: boolean): Promise<bigint | undefined> {
|
|
272
|
+
const leaf = this.leafFactory.fromBuffer(value);
|
|
273
|
+
let index = await this.db
|
|
274
|
+
.get(buildDbKeyForLeafIndex(this.getName(), leaf.getKey()))
|
|
275
|
+
.then(data => toBigIntBE(data))
|
|
276
|
+
.catch(() => undefined);
|
|
277
|
+
|
|
278
|
+
if (includeUncommitted && index === undefined) {
|
|
279
|
+
const cachedIndex = this.getCachedLeafIndex(leaf.getKey());
|
|
280
|
+
index = cachedIndex;
|
|
196
281
|
}
|
|
197
|
-
return
|
|
282
|
+
return index;
|
|
198
283
|
}
|
|
199
284
|
|
|
200
285
|
/**
|
|
@@ -216,66 +301,31 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
216
301
|
throw new Error(`Prefilled size must be at least 1!`);
|
|
217
302
|
}
|
|
218
303
|
|
|
219
|
-
const leaves:
|
|
304
|
+
const leaves: IndexedTreeLeafPreimage[] = [];
|
|
220
305
|
for (let i = 0n; i < prefilledSize; i++) {
|
|
221
|
-
const newLeaf =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
nextValue: i + 1n,
|
|
225
|
-
};
|
|
226
|
-
leaves.push(newLeaf);
|
|
306
|
+
const newLeaf = this.leafFactory.buildDummy(i);
|
|
307
|
+
const newLeafPreimage = this.leafPreimageFactory.fromLeaf(newLeaf, i + 1n, i + 1n);
|
|
308
|
+
leaves.push(newLeafPreimage);
|
|
227
309
|
}
|
|
228
310
|
|
|
229
|
-
// Make the first leaf have 0 value
|
|
230
|
-
leaves[0].value = 0n;
|
|
231
|
-
|
|
232
311
|
// Make the last leaf point to the first leaf
|
|
233
|
-
leaves[prefilledSize - 1]
|
|
234
|
-
leaves[prefilledSize - 1].nextValue = 0n;
|
|
312
|
+
leaves[prefilledSize - 1] = this.leafPreimageFactory.fromLeaf(leaves[prefilledSize - 1].asLeaf(), 0n, 0n);
|
|
235
313
|
|
|
236
314
|
await this.encodeAndAppendLeaves(leaves, true);
|
|
237
315
|
await this.commit();
|
|
238
316
|
}
|
|
239
317
|
|
|
240
|
-
/**
|
|
241
|
-
* Loads Merkle tree data from a database and assigns them to this object.
|
|
242
|
-
*/
|
|
243
|
-
public async initFromDb(): Promise<void> {
|
|
244
|
-
const startingIndex = 0n;
|
|
245
|
-
const values: LeafData[] = [];
|
|
246
|
-
const promise = new Promise<void>((resolve, reject) => {
|
|
247
|
-
this.db
|
|
248
|
-
.createReadStream({
|
|
249
|
-
gte: indexToKeyLeaf(this.getName(), startingIndex),
|
|
250
|
-
lte: indexToKeyLeaf(this.getName(), 2n ** BigInt(this.getDepth())),
|
|
251
|
-
})
|
|
252
|
-
.on('data', function (data) {
|
|
253
|
-
const index = keyLeafToIndex(data.key.toString('utf-8'));
|
|
254
|
-
values[Number(index)] = decodeTreeValue(data.value);
|
|
255
|
-
})
|
|
256
|
-
.on('close', function () {})
|
|
257
|
-
.on('end', function () {
|
|
258
|
-
resolve();
|
|
259
|
-
})
|
|
260
|
-
.on('error', function () {
|
|
261
|
-
log.error('stream error');
|
|
262
|
-
reject();
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
await promise;
|
|
266
|
-
this.leaves = values;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
318
|
/**
|
|
270
319
|
* Commits all the leaves to the database and removes them from a cache.
|
|
271
320
|
*/
|
|
272
321
|
private async commitLeaves(): Promise<void> {
|
|
273
322
|
const batch = this.db.batch();
|
|
274
|
-
const keys = Object.getOwnPropertyNames(this.
|
|
323
|
+
const keys = Object.getOwnPropertyNames(this.cachedLeafPreimages);
|
|
275
324
|
for (const key of keys) {
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
this.
|
|
325
|
+
const leaf = this.cachedLeafPreimages[key];
|
|
326
|
+
const index = BigInt(key);
|
|
327
|
+
batch.put(buildDbKeyForPreimage(this.getName(), index), leaf.toBuffer());
|
|
328
|
+
batch.put(buildDbKeyForLeafIndex(this.getName(), leaf.getKey()), toBufferBE(index, 32));
|
|
279
329
|
}
|
|
280
330
|
await batch.write();
|
|
281
331
|
this.clearCachedLeaves();
|
|
@@ -285,20 +335,21 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
285
335
|
* Clears the cache.
|
|
286
336
|
*/
|
|
287
337
|
private clearCachedLeaves() {
|
|
288
|
-
this.
|
|
338
|
+
this.cachedLeafPreimages = {};
|
|
289
339
|
}
|
|
290
340
|
|
|
291
341
|
/**
|
|
292
342
|
* Updates a leaf in the tree.
|
|
293
|
-
* @param
|
|
343
|
+
* @param preimage - New contents of the leaf.
|
|
294
344
|
* @param index - Index of the leaf to be updated.
|
|
295
345
|
*/
|
|
296
|
-
protected async updateLeaf(
|
|
346
|
+
protected async updateLeaf(preimage: IndexedTreeLeafPreimage, index: bigint) {
|
|
297
347
|
if (index > this.maxIndex) {
|
|
298
348
|
throw Error(`Index out of bounds. Index ${index}, max index: ${this.maxIndex}.`);
|
|
299
349
|
}
|
|
300
350
|
|
|
301
|
-
|
|
351
|
+
this.cachedLeafPreimages[index.toString()] = preimage;
|
|
352
|
+
const encodedLeaf = this.encodeLeaf(preimage, true);
|
|
302
353
|
await this.addLeafToCacheAndHashToRoot(encodedLeaf, index);
|
|
303
354
|
const numLeaves = this.getNumLeaves(true);
|
|
304
355
|
if (index >= numLeaves) {
|
|
@@ -317,8 +368,6 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
317
368
|
*
|
|
318
369
|
* This offers massive circuit performance savings over doing incremental insertions.
|
|
319
370
|
*
|
|
320
|
-
* A description of the algorithm can be found here: https://colab.research.google.com/drive/1A0gizduSi4FIiIJZ8OylwIpO9-OTqV-R
|
|
321
|
-
*
|
|
322
371
|
* WARNING: This function has side effects, it will insert values into the tree.
|
|
323
372
|
*
|
|
324
373
|
* Assumptions:
|
|
@@ -338,81 +387,78 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
338
387
|
* roots.
|
|
339
388
|
*
|
|
340
389
|
* This become tricky when two items that are being batch inserted need to update the same low nullifier, or need to use
|
|
341
|
-
* a value that is part of the same batch insertion as their low nullifier.
|
|
342
|
-
*
|
|
390
|
+
* a value that is part of the same batch insertion as their low nullifier. What we do to avoid this case is to
|
|
391
|
+
* update the existing leaves in the tree with the nullifiers in high to low order, ensuring that this case never occurs.
|
|
392
|
+
* The circuit has to sort the nullifiers (or take a hint of the sorted nullifiers and prove that it's a valid permutation).
|
|
393
|
+
* Then we just batch insert the new nullifiers in the original order.
|
|
343
394
|
*
|
|
344
395
|
* The following example will illustrate attempting to insert 2,3,20,19 into a tree already containing 0,5,10,15
|
|
345
396
|
*
|
|
346
397
|
* The example will explore two cases. In each case the values low nullifier will exist within the batch insertion,
|
|
347
398
|
* One where the low nullifier comes before the item in the set (2,3), and one where it comes after (20,19).
|
|
348
399
|
*
|
|
400
|
+
* First, we sort the nullifiers high to low, that's 20,19,3,2
|
|
401
|
+
*
|
|
349
402
|
* The original tree: Pending insertion subtree
|
|
350
403
|
*
|
|
351
|
-
* index 0 2 3
|
|
404
|
+
* index 0 1 2 3 - - - -
|
|
352
405
|
* ------------------------------------- ----------------------------
|
|
353
406
|
* val 0 5 10 15 - - - -
|
|
354
407
|
* nextIdx 1 2 3 0 - - - -
|
|
355
408
|
* nextVal 5 10 15 0 - - - -
|
|
356
409
|
*
|
|
357
410
|
*
|
|
358
|
-
* Inserting
|
|
359
|
-
* 1. Find the low nullifier (
|
|
411
|
+
* Inserting 20:
|
|
412
|
+
* 1. Find the low nullifier (3) - provide inclusion proof
|
|
360
413
|
* 2. Update its pointers
|
|
361
|
-
* 3. Insert
|
|
414
|
+
* 3. Insert 20 into the pending subtree
|
|
362
415
|
*
|
|
363
|
-
* index 0 2 3
|
|
416
|
+
* index 0 1 2 3 - - 6 -
|
|
364
417
|
* ------------------------------------- ----------------------------
|
|
365
|
-
* val 0 5 10 15
|
|
366
|
-
* nextIdx
|
|
367
|
-
* nextVal
|
|
418
|
+
* val 0 5 10 15 - - 20 -
|
|
419
|
+
* nextIdx 1 2 3 6 - - 0 -
|
|
420
|
+
* nextVal 5 10 15 20 - - 0 -
|
|
368
421
|
*
|
|
369
|
-
* Inserting
|
|
370
|
-
* 1.
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
* - Index 0 has a val 0 and nextVal of 2. This is NOT enough to prove non inclusion of 2.
|
|
374
|
-
* - Our existing tree is in a state where we cannot prove non inclusion of 3.
|
|
375
|
-
* We do not provide a non inclusion proof to out circuit, but prompt it to look within the insertion subtree.
|
|
376
|
-
* 2. Update pending insertion subtree
|
|
377
|
-
* 3. Insert 3 into pending subtree
|
|
422
|
+
* Inserting 19:
|
|
423
|
+
* 1. Find the low nullifier (3) - provide inclusion proof
|
|
424
|
+
* 2. Update its pointers
|
|
425
|
+
* 3. Insert 19 into the pending subtree
|
|
378
426
|
*
|
|
379
|
-
*
|
|
380
|
-
* index 0 2 3 4 5 6 - -
|
|
427
|
+
* index 0 1 2 3 - - 6 7
|
|
381
428
|
* ------------------------------------- ----------------------------
|
|
382
|
-
* val 0 5 10 15
|
|
383
|
-
* nextIdx
|
|
384
|
-
* nextVal
|
|
429
|
+
* val 0 5 10 15 - - 20 19
|
|
430
|
+
* nextIdx 1 2 3 7 - - 0 6
|
|
431
|
+
* nextVal 5 10 15 19 - - 0 20
|
|
385
432
|
*
|
|
386
|
-
* Inserting
|
|
387
|
-
* 1. Find the low nullifier (
|
|
433
|
+
* Inserting 3:
|
|
434
|
+
* 1. Find the low nullifier (0) - provide inclusion proof
|
|
388
435
|
* 2. Update its pointers
|
|
389
|
-
* 3. Insert
|
|
436
|
+
* 3. Insert 3 into the pending subtree
|
|
390
437
|
*
|
|
391
|
-
* index 0 2 3
|
|
438
|
+
* index 0 1 2 3 - 5 6 7
|
|
392
439
|
* ------------------------------------- ----------------------------
|
|
393
|
-
* val 0 5 10 15
|
|
394
|
-
* nextIdx 5 2 3 7
|
|
395
|
-
* nextVal
|
|
440
|
+
* val 0 5 10 15 - 3 20 19
|
|
441
|
+
* nextIdx 5 2 3 7 - 1 0 6
|
|
442
|
+
* nextVal 3 10 15 19 - 5 0 20
|
|
396
443
|
*
|
|
397
|
-
* Inserting
|
|
398
|
-
* 1.
|
|
399
|
-
* We can provide an inclusion proof of this intermediate tree state.
|
|
444
|
+
* Inserting 2:
|
|
445
|
+
* 1. Find the low nullifier (0) - provide inclusion proof
|
|
400
446
|
* 2. Update its pointers
|
|
401
|
-
* 3. Insert
|
|
447
|
+
* 3. Insert 2 into the pending subtree
|
|
402
448
|
*
|
|
403
|
-
* index 0 2 3
|
|
449
|
+
* index 0 1 2 3 4 5 6 7
|
|
404
450
|
* ------------------------------------- ----------------------------
|
|
405
|
-
* val 0 5 10 15 2 3 20
|
|
406
|
-
* nextIdx
|
|
407
|
-
* nextVal 2 10 15 19 3 5 0
|
|
451
|
+
* val 0 5 10 15 2 3 20 19
|
|
452
|
+
* nextIdx 4 2 3 7 5 1 0 6
|
|
453
|
+
* nextVal 2 10 15 19 3 5 0 20
|
|
408
454
|
*
|
|
409
455
|
* Perform subtree insertion
|
|
410
456
|
*
|
|
411
|
-
* index 0 2 3 4 5 6 7
|
|
457
|
+
* index 0 1 2 3 4 5 6 7
|
|
412
458
|
* ---------------------------------------------------------------------
|
|
413
|
-
* val 0 5 10 15 2 3 20
|
|
414
|
-
* nextIdx
|
|
415
|
-
* nextVal 2 10 15 19 3 5 0
|
|
459
|
+
* val 0 5 10 15 2 3 20 19
|
|
460
|
+
* nextIdx 4 2 3 7 5 1 0 6
|
|
461
|
+
* nextVal 2 10 15 19 3 5 0 20
|
|
416
462
|
*
|
|
417
463
|
* TODO: this implementation will change once the zero value is changed from h(0,0,0). Changes incoming over the next sprint
|
|
418
464
|
* @param leaves - Values to insert into the tree.
|
|
@@ -426,107 +472,70 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
426
472
|
>(
|
|
427
473
|
leaves: Buffer[],
|
|
428
474
|
subtreeHeight: SubtreeHeight,
|
|
429
|
-
): Promise<
|
|
430
|
-
|
|
431
|
-
| [undefined, SiblingPath<SubtreeSiblingPathHeight>]
|
|
432
|
-
> {
|
|
433
|
-
// Keep track of touched low leaves
|
|
434
|
-
const touched = new Map<number, bigint[]>();
|
|
435
|
-
|
|
436
|
-
const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight);
|
|
475
|
+
): Promise<BatchInsertionResult<TreeHeight, SubtreeSiblingPathHeight>> {
|
|
476
|
+
const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory);
|
|
437
477
|
// Accumulators
|
|
438
|
-
const lowLeavesWitnesses: LowLeafWitnessData<TreeHeight>[] =
|
|
439
|
-
const pendingInsertionSubtree:
|
|
478
|
+
const lowLeavesWitnesses: LowLeafWitnessData<TreeHeight>[] = leaves.map(() => emptyLowLeafWitness);
|
|
479
|
+
const pendingInsertionSubtree: IndexedTreeLeafPreimage[] = leaves.map(() => this.leafPreimageFactory.empty());
|
|
440
480
|
|
|
441
481
|
// Start info
|
|
442
482
|
const startInsertionIndex = this.getNumLeaves(true);
|
|
443
483
|
|
|
484
|
+
const leavesToInsert = leaves.map(leaf => this.leafFactory.fromBuffer(leaf));
|
|
485
|
+
const sortedDescendingLeafTuples = leavesToInsert
|
|
486
|
+
.map((leaf, index) => ({ leaf, index }))
|
|
487
|
+
.sort((a, b) => Number(b.leaf.getKey() - a.leaf.getKey()));
|
|
488
|
+
const sortedDescendingLeaves = sortedDescendingLeafTuples.map(leafTuple => leafTuple.leaf);
|
|
489
|
+
|
|
444
490
|
// Get insertion path for each leaf
|
|
445
|
-
for (let i = 0; i <
|
|
446
|
-
const
|
|
491
|
+
for (let i = 0; i < leavesToInsert.length; i++) {
|
|
492
|
+
const newLeaf = sortedDescendingLeaves[i];
|
|
493
|
+
const originalIndex = leavesToInsert.indexOf(newLeaf);
|
|
447
494
|
|
|
448
|
-
|
|
449
|
-
if (newValue === 0n) {
|
|
450
|
-
pendingInsertionSubtree.push(zeroLeaf);
|
|
451
|
-
lowLeavesWitnesses.push(emptyLowLeafWitness);
|
|
495
|
+
if (newLeaf.isEmpty()) {
|
|
452
496
|
continue;
|
|
453
497
|
}
|
|
454
498
|
|
|
455
|
-
const indexOfPrevious = this.
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
continue;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
if (
|
|
468
|
-
pendingInsertionSubtree[j].value < newValue &&
|
|
469
|
-
(pendingInsertionSubtree[j].nextValue > newValue || pendingInsertionSubtree[j].nextValue === 0n)
|
|
470
|
-
) {
|
|
471
|
-
// add the new value to the pending low nullifiers
|
|
472
|
-
const currentLowLeaf: LeafData = {
|
|
473
|
-
value: newValue,
|
|
474
|
-
nextValue: pendingInsertionSubtree[j].nextValue,
|
|
475
|
-
nextIndex: pendingInsertionSubtree[j].nextIndex,
|
|
476
|
-
};
|
|
477
|
-
|
|
478
|
-
pendingInsertionSubtree.push(currentLowLeaf);
|
|
479
|
-
|
|
480
|
-
// Update the pending low leaf to point at the new value
|
|
481
|
-
pendingInsertionSubtree[j].nextValue = newValue;
|
|
482
|
-
pendingInsertionSubtree[j].nextIndex = startInsertionIndex + BigInt(i);
|
|
483
|
-
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
499
|
+
const indexOfPrevious = await this.findIndexOfPreviousKey(newLeaf.getKey(), true);
|
|
500
|
+
if (indexOfPrevious === undefined) {
|
|
501
|
+
return {
|
|
502
|
+
lowLeavesWitnessData: undefined,
|
|
503
|
+
sortedNewLeaves: sortedDescendingLeafTuples.map(leafTuple => leafTuple.leaf.toBuffer()),
|
|
504
|
+
sortedNewLeavesIndexes: sortedDescendingLeafTuples.map(leafTuple => leafTuple.index),
|
|
505
|
+
newSubtreeSiblingPath: await this.getSubtreeSiblingPath(subtreeHeight, true),
|
|
506
|
+
};
|
|
507
|
+
}
|
|
487
508
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
// Update the touched mapping
|
|
492
|
-
if (prevNodes) {
|
|
493
|
-
prevNodes.push(newValue);
|
|
494
|
-
touched.set(indexOfPrevious.index, prevNodes);
|
|
495
|
-
} else {
|
|
496
|
-
touched.set(indexOfPrevious.index, [newValue]);
|
|
497
|
-
}
|
|
509
|
+
// get the low leaf (existence checked in getting index)
|
|
510
|
+
const lowLeafPreimage = (await this.getLatestLeafPreimageCopy(indexOfPrevious.index, true))!;
|
|
511
|
+
const siblingPath = await this.getSiblingPath<TreeHeight>(BigInt(indexOfPrevious.index), true);
|
|
498
512
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const siblingPath = await this.getSiblingPath<TreeHeight>(BigInt(indexOfPrevious.index), true);
|
|
505
|
-
|
|
506
|
-
const witness: LowLeafWitnessData<TreeHeight> = {
|
|
507
|
-
leafData: { ...lowLeaf },
|
|
508
|
-
index: BigInt(indexOfPrevious.index),
|
|
509
|
-
siblingPath,
|
|
510
|
-
};
|
|
513
|
+
const witness: LowLeafWitnessData<TreeHeight> = {
|
|
514
|
+
leafPreimage: lowLeafPreimage,
|
|
515
|
+
index: BigInt(indexOfPrevious.index),
|
|
516
|
+
siblingPath,
|
|
517
|
+
};
|
|
511
518
|
|
|
512
|
-
|
|
513
|
-
|
|
519
|
+
// Update the running paths
|
|
520
|
+
lowLeavesWitnesses[i] = witness;
|
|
514
521
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
522
|
+
const currentPendingPreimageLeaf = this.leafPreimageFactory.fromLeaf(
|
|
523
|
+
newLeaf,
|
|
524
|
+
lowLeafPreimage.getNextKey(),
|
|
525
|
+
lowLeafPreimage.getNextIndex(),
|
|
526
|
+
);
|
|
520
527
|
|
|
521
|
-
|
|
528
|
+
pendingInsertionSubtree[originalIndex] = currentPendingPreimageLeaf;
|
|
522
529
|
|
|
523
|
-
|
|
524
|
-
|
|
530
|
+
const newLowLeafPreimage = this.leafPreimageFactory.fromLeaf(
|
|
531
|
+
lowLeafPreimage.asLeaf(),
|
|
532
|
+
newLeaf.getKey(),
|
|
533
|
+
startInsertionIndex + BigInt(originalIndex),
|
|
534
|
+
);
|
|
525
535
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
}
|
|
536
|
+
const lowLeafIndex = indexOfPrevious.index;
|
|
537
|
+
this.cachedLeafPreimages[lowLeafIndex.toString()] = newLowLeafPreimage;
|
|
538
|
+
await this.updateLeaf(newLowLeafPreimage, lowLeafIndex);
|
|
530
539
|
}
|
|
531
540
|
|
|
532
541
|
const newSubtreeSiblingPath = await this.getSubtreeSiblingPath<SubtreeHeight, SubtreeSiblingPathHeight>(
|
|
@@ -538,7 +547,13 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
538
547
|
// Note: In this case we set `hash0Leaf` param to false because batch insertion algorithm use forced null leaf
|
|
539
548
|
// inclusion. See {@link encodeLeaf} for a more through param explanation.
|
|
540
549
|
await this.encodeAndAppendLeaves(pendingInsertionSubtree, false);
|
|
541
|
-
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
lowLeavesWitnessData: lowLeavesWitnesses,
|
|
553
|
+
sortedNewLeaves: sortedDescendingLeafTuples.map(leafTuple => leafTuple.leaf.toBuffer()),
|
|
554
|
+
sortedNewLeavesIndexes: sortedDescendingLeafTuples.map(leafTuple => leafTuple.index),
|
|
555
|
+
newSubtreeSiblingPath,
|
|
556
|
+
};
|
|
542
557
|
}
|
|
543
558
|
|
|
544
559
|
async getSubtreeSiblingPath<SubtreeHeight extends number, SubtreeSiblingPathHeight extends number>(
|
|
@@ -552,21 +567,29 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
552
567
|
return fullSiblingPath.getSubtreeSiblingPath(subtreeHeight);
|
|
553
568
|
}
|
|
554
569
|
|
|
570
|
+
snapshot(blockNumber: number): Promise<IndexedTreeSnapshot> {
|
|
571
|
+
return this.#snapshotBuilder.snapshot(blockNumber);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
getSnapshot(block: number): Promise<IndexedTreeSnapshot> {
|
|
575
|
+
return this.#snapshotBuilder.getSnapshot(block);
|
|
576
|
+
}
|
|
577
|
+
|
|
555
578
|
/**
|
|
556
579
|
* Encodes leaves and appends them to a tree.
|
|
557
|
-
* @param
|
|
580
|
+
* @param preimages - Leaves to encode.
|
|
558
581
|
* @param hash0Leaf - Indicates whether 0 value leaf should be hashed. See {@link encodeLeaf}.
|
|
559
582
|
* @returns Empty promise
|
|
560
583
|
*/
|
|
561
|
-
private async encodeAndAppendLeaves(
|
|
562
|
-
const startInsertionIndex =
|
|
584
|
+
private async encodeAndAppendLeaves(preimages: IndexedTreeLeafPreimage[], hash0Leaf: boolean): Promise<void> {
|
|
585
|
+
const startInsertionIndex = this.getNumLeaves(true);
|
|
563
586
|
|
|
564
|
-
const
|
|
565
|
-
this.
|
|
566
|
-
return this.encodeLeaf(
|
|
587
|
+
const hashedLeaves = preimages.map((preimage, i) => {
|
|
588
|
+
this.cachedLeafPreimages[(startInsertionIndex + BigInt(i)).toString()] = preimage;
|
|
589
|
+
return this.encodeLeaf(preimage, hash0Leaf);
|
|
567
590
|
});
|
|
568
591
|
|
|
569
|
-
await super.appendLeaves(
|
|
592
|
+
await super.appendLeaves(hashedLeaves);
|
|
570
593
|
}
|
|
571
594
|
|
|
572
595
|
/**
|
|
@@ -577,14 +600,12 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree {
|
|
|
577
600
|
* nullifier it is improbable that a valid nullifier would be 0.
|
|
578
601
|
* @returns Leaf encoded in a buffer.
|
|
579
602
|
*/
|
|
580
|
-
private encodeLeaf(leaf:
|
|
603
|
+
private encodeLeaf(leaf: IndexedTreeLeafPreimage, hash0Leaf: boolean): Buffer {
|
|
581
604
|
let encodedLeaf;
|
|
582
|
-
if (!hash0Leaf && leaf.
|
|
605
|
+
if (!hash0Leaf && leaf.getKey() == 0n) {
|
|
583
606
|
encodedLeaf = toBufferBE(0n, 32);
|
|
584
607
|
} else {
|
|
585
|
-
encodedLeaf = this.hasher.hashInputs(
|
|
586
|
-
[leaf.value, leaf.nextIndex, leaf.nextValue].map(val => toBufferBE(val, 32)),
|
|
587
|
-
);
|
|
608
|
+
encodedLeaf = this.hasher.hashInputs(leaf.toHashInputs());
|
|
588
609
|
}
|
|
589
610
|
return encodedLeaf;
|
|
590
611
|
}
|