@aztec/world-state 0.22.0 → 0.24.0
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/package.json +7 -7
- package/src/index.ts +3 -0
- package/src/synchronizer/config.ts +27 -0
- package/src/synchronizer/index.ts +2 -0
- package/src/synchronizer/server_world_state_synchronizer.ts +215 -0
- package/src/synchronizer/world_state_synchronizer.ts +73 -0
- package/src/world-state-db/index.ts +3 -0
- package/src/world-state-db/merkle_tree_db.ts +50 -0
- package/src/world-state-db/merkle_tree_operations.ts +178 -0
- package/src/world-state-db/merkle_tree_operations_facade.ts +182 -0
- package/src/world-state-db/merkle_tree_snapshot_operations_facade.ts +157 -0
- package/src/world-state-db/merkle_trees.ts +574 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/circuit-types';
|
|
2
|
+
import { Header, NullifierLeafPreimage, StateReference } from '@aztec/circuits.js';
|
|
3
|
+
import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
|
|
4
|
+
import { BatchInsertionResult } from '@aztec/merkle-tree';
|
|
5
|
+
|
|
6
|
+
import { MerkleTreeDb } from './merkle_tree_db.js';
|
|
7
|
+
import { HandleL2BlockResult, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Wraps a MerkleTreeDbOperations to call all functions with a preset includeUncommitted flag.
|
|
11
|
+
*/
|
|
12
|
+
export class MerkleTreeOperationsFacade implements MerkleTreeOperations {
|
|
13
|
+
constructor(private trees: MerkleTreeDb, private includeUncommitted: boolean) {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns the tree info for the specified tree id.
|
|
17
|
+
* @param treeId - Id of the tree to get information from.
|
|
18
|
+
* @param includeUncommitted - Indicates whether to include uncommitted data.
|
|
19
|
+
* @returns The tree info for the specified tree.
|
|
20
|
+
*/
|
|
21
|
+
getTreeInfo(treeId: MerkleTreeId): Promise<TreeInfo> {
|
|
22
|
+
return this.trees.getTreeInfo(treeId, this.includeUncommitted);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the current state reference.
|
|
27
|
+
* @returns The current state reference.
|
|
28
|
+
*/
|
|
29
|
+
getStateReference(): Promise<StateReference> {
|
|
30
|
+
return this.trees.getStateReference(this.includeUncommitted);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Builds the initial header.
|
|
35
|
+
* @returns The initial header.
|
|
36
|
+
*/
|
|
37
|
+
buildInitialHeader(): Promise<Header> {
|
|
38
|
+
return this.trees.buildInitialHeader(this.includeUncommitted);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Appends a set of leaf values to the tree.
|
|
43
|
+
* @param treeId - Id of the tree to append leaves to.
|
|
44
|
+
* @param leaves - The set of leaves to be appended.
|
|
45
|
+
* @returns The tree info of the specified tree.
|
|
46
|
+
*/
|
|
47
|
+
appendLeaves(treeId: MerkleTreeId, leaves: Buffer[]): Promise<void> {
|
|
48
|
+
return this.trees.appendLeaves(treeId, leaves);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns the sibling path for a requested leaf index.
|
|
53
|
+
* @param treeId - Id of the tree to get the sibling path from.
|
|
54
|
+
* @param index - The index of the leaf for which a sibling path is required.
|
|
55
|
+
* @returns A promise with the sibling path of the specified leaf index.
|
|
56
|
+
*/
|
|
57
|
+
async getSiblingPath<N extends number>(treeId: MerkleTreeId, index: bigint): Promise<SiblingPath<N>> {
|
|
58
|
+
const path = await this.trees.getSiblingPath(treeId, index, this.includeUncommitted);
|
|
59
|
+
return path as unknown as SiblingPath<N>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Finds the index of the largest leaf whose value is less than or equal to the provided value.
|
|
64
|
+
* @param treeId - The ID of the tree to search.
|
|
65
|
+
* @param value - The value to be inserted into the tree.
|
|
66
|
+
* @param includeUncommitted - If true, the uncommitted changes are included in the search.
|
|
67
|
+
* @returns The found leaf index and a flag indicating if the corresponding leaf's value is equal to `newValue`.
|
|
68
|
+
*/
|
|
69
|
+
getPreviousValueIndex(
|
|
70
|
+
treeId: MerkleTreeId.NULLIFIER_TREE,
|
|
71
|
+
value: bigint,
|
|
72
|
+
): Promise<
|
|
73
|
+
| {
|
|
74
|
+
/**
|
|
75
|
+
* The index of the found leaf.
|
|
76
|
+
*/
|
|
77
|
+
index: bigint;
|
|
78
|
+
/**
|
|
79
|
+
* A flag indicating if the corresponding leaf's value is equal to `newValue`.
|
|
80
|
+
*/
|
|
81
|
+
alreadyPresent: boolean;
|
|
82
|
+
}
|
|
83
|
+
| undefined
|
|
84
|
+
> {
|
|
85
|
+
return this.trees.getPreviousValueIndex(treeId, value, this.includeUncommitted);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Updates a leaf in a tree at a given index.
|
|
90
|
+
* @param treeId - The ID of the tree.
|
|
91
|
+
* @param leaf - The new leaf value.
|
|
92
|
+
* @param index - The index to insert into.
|
|
93
|
+
* @returns Empty promise.
|
|
94
|
+
*/
|
|
95
|
+
updateLeaf(treeId: MerkleTreeId.NULLIFIER_TREE, leaf: NullifierLeafPreimage, index: bigint): Promise<void> {
|
|
96
|
+
return this.trees.updateLeaf(treeId, leaf, index);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Gets the leaf data at a given index and tree.
|
|
101
|
+
* @param treeId - The ID of the tree get the leaf from.
|
|
102
|
+
* @param index - The index of the leaf to get.
|
|
103
|
+
* @returns Leaf preimage.
|
|
104
|
+
*/
|
|
105
|
+
async getLeafPreimage(
|
|
106
|
+
treeId: MerkleTreeId.NULLIFIER_TREE,
|
|
107
|
+
index: bigint,
|
|
108
|
+
): Promise<IndexedTreeLeafPreimage | undefined> {
|
|
109
|
+
const preimage = await this.trees.getLeafPreimage(treeId, index, this.includeUncommitted);
|
|
110
|
+
return preimage as IndexedTreeLeafPreimage | undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Returns the index of a leaf given its value, or undefined if no leaf with that value is found.
|
|
115
|
+
* @param treeId - The ID of the tree.
|
|
116
|
+
* @param value - The leaf value to look for.
|
|
117
|
+
* @returns The index of the first leaf found with a given value (undefined if not found).
|
|
118
|
+
*/
|
|
119
|
+
findLeafIndex(treeId: MerkleTreeId, value: Buffer): Promise<bigint | undefined> {
|
|
120
|
+
return this.trees.findLeafIndex(treeId, value, this.includeUncommitted);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets the value at the given index.
|
|
125
|
+
* @param treeId - The ID of the tree to get the leaf value from.
|
|
126
|
+
* @param index - The index of the leaf.
|
|
127
|
+
* @param includeUncommitted - Indicates whether to include uncommitted changes.
|
|
128
|
+
* @returns Leaf value at the given index (undefined if not found).
|
|
129
|
+
*/
|
|
130
|
+
getLeafValue(treeId: MerkleTreeId, index: bigint): Promise<Buffer | undefined> {
|
|
131
|
+
return this.trees.getLeafValue(treeId, index, this.includeUncommitted);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Inserts the new block hash into the archive.
|
|
136
|
+
* This includes all of the current roots of all of the data trees and the current blocks global vars.
|
|
137
|
+
* @param header - The header to insert into the archive.
|
|
138
|
+
*/
|
|
139
|
+
public updateArchive(header: Header): Promise<void> {
|
|
140
|
+
return this.trees.updateArchive(header, this.includeUncommitted);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Handles a single L2 block (i.e. Inserts the new commitments into the merkle tree).
|
|
145
|
+
* @param block - The L2 block to handle.
|
|
146
|
+
* @returns Whether the block handled was produced by this same node.
|
|
147
|
+
*/
|
|
148
|
+
public handleL2Block(block: L2Block): Promise<HandleL2BlockResult> {
|
|
149
|
+
return this.trees.handleL2Block(block);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Commits all pending updates.
|
|
154
|
+
* @returns Empty promise.
|
|
155
|
+
*/
|
|
156
|
+
public async commit(): Promise<void> {
|
|
157
|
+
return await this.trees.commit();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Rolls back all pending updates.
|
|
162
|
+
* @returns Empty promise.
|
|
163
|
+
*/
|
|
164
|
+
public async rollback(): Promise<void> {
|
|
165
|
+
return await this.trees.rollback();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Batch insert multiple leaves into the tree.
|
|
170
|
+
* @param treeId - The ID of the tree.
|
|
171
|
+
* @param leaves - Leaves to insert into the tree.
|
|
172
|
+
* @param subtreeHeight - Height of the subtree.
|
|
173
|
+
* @returns The data for the leaves to be updated when inserting the new ones.
|
|
174
|
+
*/
|
|
175
|
+
public batchInsert<TreeHeight extends number, SubtreeSiblingPathHeight extends number>(
|
|
176
|
+
treeId: MerkleTreeId,
|
|
177
|
+
leaves: Buffer[],
|
|
178
|
+
subtreeHeight: number,
|
|
179
|
+
): Promise<BatchInsertionResult<TreeHeight, SubtreeSiblingPathHeight>> {
|
|
180
|
+
return this.trees.batchInsert(treeId, leaves, subtreeHeight);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { MerkleTreeId, SiblingPath } from '@aztec/circuit-types';
|
|
2
|
+
import { AppendOnlyTreeSnapshot, Fr, Header, PartialStateReference, StateReference } from '@aztec/circuits.js';
|
|
3
|
+
import { IndexedTreeLeafPreimage } from '@aztec/foundation/trees';
|
|
4
|
+
import { BatchInsertionResult, IndexedTreeSnapshot, TreeSnapshot } from '@aztec/merkle-tree';
|
|
5
|
+
|
|
6
|
+
import { MerkleTreeDb } from './merkle_tree_db.js';
|
|
7
|
+
import { HandleL2BlockResult, MerkleTreeOperations, TreeInfo } from './merkle_tree_operations.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Merkle tree operations on readonly tree snapshots.
|
|
11
|
+
*/
|
|
12
|
+
export class MerkleTreeSnapshotOperationsFacade implements MerkleTreeOperations {
|
|
13
|
+
#treesDb: MerkleTreeDb;
|
|
14
|
+
#blockNumber: number;
|
|
15
|
+
#treeSnapshots: ReadonlyArray<TreeSnapshot | IndexedTreeSnapshot> = [];
|
|
16
|
+
|
|
17
|
+
constructor(trees: MerkleTreeDb, blockNumber: number) {
|
|
18
|
+
this.#treesDb = trees;
|
|
19
|
+
this.#blockNumber = blockNumber;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async #getTreeSnapshot(merkleTreeId: number): Promise<TreeSnapshot | IndexedTreeSnapshot> {
|
|
23
|
+
if (this.#treeSnapshots[merkleTreeId]) {
|
|
24
|
+
return this.#treeSnapshots[merkleTreeId];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.#treeSnapshots = await this.#treesDb.getSnapshot(this.#blockNumber);
|
|
28
|
+
return this.#treeSnapshots[merkleTreeId]!;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async findLeafIndex(treeId: MerkleTreeId, value: Buffer): Promise<bigint | undefined> {
|
|
32
|
+
const tree = await this.#getTreeSnapshot(treeId);
|
|
33
|
+
return tree.findLeafIndex(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getLeafPreimage(
|
|
37
|
+
treeId: MerkleTreeId.NULLIFIER_TREE,
|
|
38
|
+
index: bigint,
|
|
39
|
+
): Promise<IndexedTreeLeafPreimage | undefined> {
|
|
40
|
+
const snapshot = (await this.#getTreeSnapshot(treeId)) as IndexedTreeSnapshot;
|
|
41
|
+
return snapshot.getLatestLeafPreimageCopy(BigInt(index));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async getLeafValue(treeId: MerkleTreeId, index: bigint): Promise<Buffer | undefined> {
|
|
45
|
+
const snapshot = await this.#getTreeSnapshot(treeId);
|
|
46
|
+
return snapshot.getLeafValue(BigInt(index));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async getPreviousValueIndex(
|
|
50
|
+
treeId: MerkleTreeId.NULLIFIER_TREE,
|
|
51
|
+
value: bigint,
|
|
52
|
+
): Promise<
|
|
53
|
+
| {
|
|
54
|
+
/**
|
|
55
|
+
* The index of the found leaf.
|
|
56
|
+
*/
|
|
57
|
+
index: bigint;
|
|
58
|
+
/**
|
|
59
|
+
* A flag indicating if the corresponding leaf's value is equal to `newValue`.
|
|
60
|
+
*/
|
|
61
|
+
alreadyPresent: boolean;
|
|
62
|
+
}
|
|
63
|
+
| undefined
|
|
64
|
+
> {
|
|
65
|
+
const snapshot = (await this.#getTreeSnapshot(treeId)) as IndexedTreeSnapshot;
|
|
66
|
+
return snapshot.findIndexOfPreviousKey(value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async getSiblingPath<N extends number>(treeId: MerkleTreeId, index: bigint): Promise<SiblingPath<N>> {
|
|
70
|
+
const snapshot = await this.#getTreeSnapshot(treeId);
|
|
71
|
+
return snapshot.getSiblingPath(index);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getTreeInfo(treeId: MerkleTreeId): Promise<TreeInfo> {
|
|
75
|
+
const snapshot = await this.#getTreeSnapshot(treeId);
|
|
76
|
+
return {
|
|
77
|
+
depth: snapshot.getDepth(),
|
|
78
|
+
root: snapshot.getRoot(),
|
|
79
|
+
size: snapshot.getNumLeaves(),
|
|
80
|
+
treeId,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async getStateReference(): Promise<StateReference> {
|
|
85
|
+
const snapshots = await Promise.all([
|
|
86
|
+
this.#getTreeSnapshot(MerkleTreeId.CONTRACT_TREE),
|
|
87
|
+
this.#getTreeSnapshot(MerkleTreeId.NULLIFIER_TREE),
|
|
88
|
+
this.#getTreeSnapshot(MerkleTreeId.NOTE_HASH_TREE),
|
|
89
|
+
this.#getTreeSnapshot(MerkleTreeId.PUBLIC_DATA_TREE),
|
|
90
|
+
this.#getTreeSnapshot(MerkleTreeId.L1_TO_L2_MESSAGE_TREE),
|
|
91
|
+
this.#getTreeSnapshot(MerkleTreeId.ARCHIVE),
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
return new StateReference(
|
|
95
|
+
new AppendOnlyTreeSnapshot(
|
|
96
|
+
Fr.fromBuffer(snapshots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE].getRoot()),
|
|
97
|
+
Number(snapshots[MerkleTreeId.L1_TO_L2_MESSAGE_TREE].getNumLeaves()),
|
|
98
|
+
),
|
|
99
|
+
new PartialStateReference(
|
|
100
|
+
new AppendOnlyTreeSnapshot(
|
|
101
|
+
Fr.fromBuffer(snapshots[MerkleTreeId.NOTE_HASH_TREE].getRoot()),
|
|
102
|
+
Number(snapshots[MerkleTreeId.NOTE_HASH_TREE].getNumLeaves()),
|
|
103
|
+
),
|
|
104
|
+
new AppendOnlyTreeSnapshot(
|
|
105
|
+
Fr.fromBuffer(snapshots[MerkleTreeId.NULLIFIER_TREE].getRoot()),
|
|
106
|
+
Number(snapshots[MerkleTreeId.NULLIFIER_TREE].getNumLeaves()),
|
|
107
|
+
),
|
|
108
|
+
new AppendOnlyTreeSnapshot(
|
|
109
|
+
Fr.fromBuffer(snapshots[MerkleTreeId.CONTRACT_TREE].getRoot()),
|
|
110
|
+
Number(snapshots[MerkleTreeId.CONTRACT_TREE].getNumLeaves()),
|
|
111
|
+
),
|
|
112
|
+
new AppendOnlyTreeSnapshot(
|
|
113
|
+
Fr.fromBuffer(snapshots[MerkleTreeId.PUBLIC_DATA_TREE].getRoot()),
|
|
114
|
+
Number(snapshots[MerkleTreeId.PUBLIC_DATA_TREE].getNumLeaves()),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
appendLeaves(): Promise<void> {
|
|
121
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
batchInsert<TreeHeight extends number, SubtreeSiblingPathHeight extends number>(): Promise<
|
|
125
|
+
BatchInsertionResult<TreeHeight, SubtreeSiblingPathHeight>
|
|
126
|
+
> {
|
|
127
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
updateArchive(): Promise<void> {
|
|
131
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
commit(): Promise<void> {
|
|
135
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
handleL2Block(): Promise<HandleL2BlockResult> {
|
|
139
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
rollback(): Promise<void> {
|
|
143
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
updateHistoricArchive(): Promise<void> {
|
|
147
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
updateLeaf(): Promise<void> {
|
|
151
|
+
return Promise.reject(new Error('Tree snapshot operations are read-only'));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
buildInitialHeader(): Promise<Header> {
|
|
155
|
+
throw new Error('Building initial header not supported on snapshot.');
|
|
156
|
+
}
|
|
157
|
+
}
|