@aztec/world-state 0.1.0-alpha43 → 0.1.0-alpha44

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.
@@ -1,10 +1,10 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { SerialQueue } from '@aztec/foundation/fifo';
2
2
  import { createDebugLogger } from '@aztec/foundation/log';
3
3
  import { L2Block, L2BlockDownloader, L2BlockSource } from '@aztec/types';
4
4
 
5
- import { MerkleTreeDb, MerkleTreeOperations, computeGlobalVariablesHash } from '../index.js';
5
+ import { MerkleTreeOperations, MerkleTrees } from '../index.js';
6
6
  import { MerkleTreeOperationsFacade } from '../merkle-tree/merkle_tree_operations_facade.js';
7
- import { getConfigEnvVars } from './config.js';
7
+ import { WorldStateConfig } from './config.js';
8
8
  import { WorldStateRunningState, WorldStateStatus, WorldStateSynchroniser } from './world_state_synchroniser.js';
9
9
 
10
10
  /**
@@ -19,19 +19,17 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
19
19
  private l2BlockDownloader: L2BlockDownloader;
20
20
  private syncPromise: Promise<void> = Promise.resolve();
21
21
  private syncResolve?: () => void = undefined;
22
+ private jobQueue = new SerialQueue();
22
23
  private stopping = false;
23
24
  private runningPromise: Promise<void> = Promise.resolve();
24
25
  private currentState: WorldStateRunningState = WorldStateRunningState.IDLE;
25
26
 
26
- /** The latest Global Variables hash for the HEAD of the chain. */
27
- public latestGlobalVariablesHash: Fr = Fr.ZERO;
28
-
29
27
  constructor(
30
- private merkleTreeDb: MerkleTreeDb,
28
+ private merkleTreeDb: MerkleTrees,
31
29
  private l2BlockSource: L2BlockSource,
30
+ config: WorldStateConfig,
32
31
  private log = createDebugLogger('aztec:world_state'),
33
32
  ) {
34
- const config = getConfigEnvVars();
35
33
  this.l2BlockDownloader = new L2BlockDownloader(
36
34
  l2BlockSource,
37
35
  config.l2QueueSize,
@@ -57,7 +55,6 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
57
55
 
58
56
  // get the current latest block number
59
57
  this.latestBlockNumberAtStart = await this.l2BlockSource.getBlockHeight();
60
- this.latestGlobalVariablesHash = await computeGlobalVariablesHash();
61
58
 
62
59
  const blockToDownloadFrom = this.currentL2BlockNum + 1;
63
60
 
@@ -78,10 +75,10 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
78
75
  // start looking for further blocks
79
76
  const blockProcess = async () => {
80
77
  while (!this.stopping) {
81
- const blocks = await this.l2BlockDownloader.getL2Blocks();
82
- await this.handleL2Blocks(blocks);
78
+ await this.jobQueue.put(() => this.collectAndProcessBlocks());
83
79
  }
84
80
  };
81
+ this.jobQueue.start();
85
82
  this.runningPromise = blockProcess();
86
83
  this.l2BlockDownloader.start(blockToDownloadFrom);
87
84
  this.log(`Started block downloader from block ${blockToDownloadFrom}`);
@@ -92,6 +89,8 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
92
89
  this.log('Stopping world state...');
93
90
  this.stopping = true;
94
91
  await this.l2BlockDownloader.stop();
92
+ await this.jobQueue.cancel();
93
+ await this.merkleTreeDb.stop();
95
94
  await this.runningPromise;
96
95
  this.setCurrentState(WorldStateRunningState.STOPPED);
97
96
  }
@@ -104,6 +103,56 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
104
103
  return Promise.resolve(status);
105
104
  }
106
105
 
106
+ /**
107
+ * Forces an immediate sync
108
+ * @param blockHeight - The minimum block height that we must sync to
109
+ * @returns A promise that resolves once the sync has completed.
110
+ */
111
+ public async syncImmediate(blockHeight?: number): Promise<void> {
112
+ if (this.currentState !== WorldStateRunningState.RUNNING) {
113
+ throw new Error(`World State is not running, unable to perform sync`);
114
+ }
115
+ // If we have been given a block height to sync to and we have reached that height
116
+ // then return.
117
+ if (blockHeight !== undefined && blockHeight <= this.currentL2BlockNum) {
118
+ return;
119
+ }
120
+ const blockToSyncTo = blockHeight === undefined ? 'latest' : `${blockHeight}`;
121
+ this.log(`World State at block ${this.currentL2BlockNum}, told to sync to block ${blockToSyncTo}...`);
122
+ // ensure any outstanding block updates are completed first.
123
+ await this.jobQueue.syncPoint();
124
+ while (true) {
125
+ // Check the block height again
126
+ if (blockHeight !== undefined && blockHeight <= this.currentL2BlockNum) {
127
+ return;
128
+ }
129
+ // Poll for more blocks
130
+ const numBlocks = await this.l2BlockDownloader.pollImmediate();
131
+ this.log(`Block download immediate poll yielded ${numBlocks} blocks`);
132
+ if (numBlocks) {
133
+ // More blocks were received, process them and go round again
134
+ await this.jobQueue.put(() => this.collectAndProcessBlocks());
135
+ continue;
136
+ }
137
+ // No blocks are available, if we have been given a block height then we can't achieve it
138
+ if (blockHeight !== undefined) {
139
+ throw new Error(
140
+ `Unable to sync to block height ${blockHeight}, currently synced to block ${this.currentL2BlockNum}`,
141
+ );
142
+ }
143
+ return;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Checks for the availability of new blocks and processes them.
149
+ */
150
+ private async collectAndProcessBlocks() {
151
+ // This request for blocks will timeout after 1 second if no blocks are received
152
+ const blocks = await this.l2BlockDownloader.getL2Blocks(1);
153
+ await this.handleL2Blocks(blocks);
154
+ }
155
+
107
156
  /**
108
157
  * Handles a list of L2 blocks (i.e. Inserts the new commitments into the merkle tree).
109
158
  * @param l2Blocks - The L2 blocks to handle.
@@ -121,8 +170,6 @@ export class ServerWorldStateSynchroniser implements WorldStateSynchroniser {
121
170
  private async handleL2Block(l2Block: L2Block) {
122
171
  await this.merkleTreeDb.handleL2Block(l2Block);
123
172
  this.currentL2BlockNum = l2Block.number;
124
- this.latestGlobalVariablesHash = await computeGlobalVariablesHash(l2Block.globalVariables);
125
- this.log(`Synced global variables with hash ${this.latestGlobalVariablesHash.toString()}`);
126
173
  if (
127
174
  this.currentState === WorldStateRunningState.SYNCHING &&
128
175
  this.currentL2BlockNum >= this.latestBlockNumberAtStart
@@ -1,5 +1,3 @@
1
- import { Fr } from '@aztec/circuits.js';
2
-
3
1
  import { MerkleTreeOperations } from '../index.js';
4
2
 
5
3
  /**
@@ -47,6 +45,13 @@ export interface WorldStateSynchroniser {
47
45
  */
48
46
  stop(): Promise<void>;
49
47
 
48
+ /**
49
+ * Forces an immediate sync to an optionally provided minimum block height
50
+ * @param blockHeight - The minimum block height that we must sync to
51
+ * @returns A promise that resolves once the sync has completed.
52
+ */
53
+ syncImmediate(blockHeight?: number): Promise<void>;
54
+
50
55
  /**
51
56
  * Returns an instance of MerkleTreeOperations that will include uncommitted data.
52
57
  * @returns An instance of MerkleTreeOperations that will include uncommitted data.
@@ -58,9 +63,4 @@ export interface WorldStateSynchroniser {
58
63
  * @returns An instance of MerkleTreeOperations that will not include uncommitted data.
59
64
  */
60
65
  getCommitted(): MerkleTreeOperations;
61
-
62
- /**
63
- * The latest Global Variables hash for the HEAD of the chain.
64
- */
65
- latestGlobalVariablesHash: Fr;
66
66
  }
@@ -1,4 +1,5 @@
1
- import { GlobalVariables, MAX_NEW_NULLIFIERS_PER_TX } from '@aztec/circuits.js';
1
+ import { MAX_NEW_NULLIFIERS_PER_TX } from '@aztec/circuits.js';
2
+ import { Fr } from '@aztec/foundation/fields';
2
3
  import { createDebugLogger } from '@aztec/foundation/log';
3
4
  import { LeafData, LowLeafWitnessData } from '@aztec/merkle-tree';
4
5
  import { L2Block, MerkleTreeId, SiblingPath } from '@aztec/types';
@@ -115,14 +116,14 @@ export interface MerkleTreeOperations {
115
116
  /**
116
117
  * Gets the current roots of the commitment trees.
117
118
  */
118
- getTreeRoots(): CurrentTreeRoots;
119
+ getTreeRoots(): Promise<CurrentTreeRoots>;
119
120
 
120
121
  /**
121
122
  * Gets sibling path for a leaf.
122
123
  * @param treeId - The tree to be queried for a sibling path.
123
124
  * @param index - The index of the leaf for which a sibling path should be returned.
124
125
  */
125
- getSiblingPath(treeId: MerkleTreeId, index: bigint): Promise<SiblingPath<number>>;
126
+ getSiblingPath<N extends number>(treeId: MerkleTreeId, index: bigint): Promise<SiblingPath<N>>;
126
127
 
127
128
  /**
128
129
  * Returns the previous index for a given value in an indexed tree.
@@ -175,9 +176,20 @@ export interface MerkleTreeOperations {
175
176
  /**
176
177
  * Inserts the new block hash into the new block hashes tree.
177
178
  * This includes all of the current roots of all of the data trees and the current blocks global vars.
178
- * @param globalVariables - The global variables to insert into the block hash.
179
+ * @param globalVariablesHash - The global variables hash to insert into the block hash.
179
180
  */
180
- updateHistoricBlocksTree(globalVariables: GlobalVariables): Promise<void>;
181
+ updateHistoricBlocksTree(globalVariablesHash: Fr): Promise<void>;
182
+
183
+ /**
184
+ * Updates the latest global variables hash
185
+ * @param globalVariablesHash - The latest global variables hash
186
+ */
187
+ updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise<void>;
188
+
189
+ /**
190
+ * Gets the global variables hash from the previous block
191
+ */
192
+ getLatestGlobalVariablesHash(): Promise<Fr>;
181
193
 
182
194
  /**
183
195
  * Batch insert multiple leaves into the tree.
@@ -10,7 +10,8 @@ import {
10
10
  PRIVATE_DATA_TREE_HEIGHT,
11
11
  PUBLIC_DATA_TREE_HEIGHT,
12
12
  } from '@aztec/circuits.js';
13
- import { computeBlockHashWithGlobals } from '@aztec/circuits.js/abis';
13
+ import { computeBlockHash } from '@aztec/circuits.js/abis';
14
+ import { Committable } from '@aztec/foundation/committable';
14
15
  import { SerialQueue } from '@aztec/foundation/fifo';
15
16
  import { createDebugLogger } from '@aztec/foundation/log';
16
17
  import { IWasmModule } from '@aztec/foundation/wasm';
@@ -31,6 +32,7 @@ import { L2Block, MerkleTreeId, SiblingPath, merkleTreeIds } from '@aztec/types'
31
32
  import { default as levelup } from 'levelup';
32
33
 
33
34
  import { MerkleTreeOperationsFacade } from '../merkle-tree/merkle_tree_operations_facade.js';
35
+ import { computeGlobalVariablesHash } from '../utils.js';
34
36
  import {
35
37
  CurrentTreeRoots,
36
38
  INITIAL_NULLIFIER_TREE_SIZE,
@@ -46,9 +48,12 @@ import {
46
48
  */
47
49
  export class MerkleTrees implements MerkleTreeDb {
48
50
  private trees: (AppendOnlyTree | UpdateOnlyTree)[] = [];
51
+ private latestGlobalVariablesHash: Committable<Fr>;
49
52
  private jobQueue = new SerialQueue();
50
53
 
51
- constructor(private db: levelup.LevelUp, private log = createDebugLogger('aztec:merkle_trees')) {}
54
+ constructor(private db: levelup.LevelUp, private log = createDebugLogger('aztec:merkle_trees')) {
55
+ this.latestGlobalVariablesHash = new Committable(Fr.zero());
56
+ }
52
57
 
53
58
  /**
54
59
  * Initialises the collection of Merkle Trees.
@@ -105,8 +110,10 @@ export class MerkleTrees implements MerkleTreeDb {
105
110
  this.jobQueue.start();
106
111
 
107
112
  // The first leaf in the blocks tree contains the empty roots of the other trees and empty global variables.
108
- await this.updateHistoricBlocksTree(GlobalVariables.empty(), true);
109
- await historicBlocksTree.commit();
113
+ const initialGlobalVariablesHash = await computeGlobalVariablesHash(GlobalVariables.empty());
114
+ await this._updateLatestGlobalVariablesHash(initialGlobalVariablesHash);
115
+ await this._updateHistoricBlocksTree(initialGlobalVariablesHash, true);
116
+ await this._commit();
110
117
  }
111
118
 
112
119
  /**
@@ -147,12 +154,27 @@ export class MerkleTrees implements MerkleTreeDb {
147
154
  /**
148
155
  * Inserts into the roots trees (CONTRACT_TREE_ROOTS_TREE, PRIVATE_DATA_TREE_ROOTS_TREE, L1_TO_L2_MESSAGES_TREE_ROOTS_TREE)
149
156
  * the current roots of the corresponding trees (CONTRACT_TREE, PRIVATE_DATA_TREE, L1_TO_L2_MESSAGES_TREE).
150
- * @param globals - The global variables to use for hashing.
157
+ * @param globalsHash - The current global variables hash.
158
+ * @param includeUncommitted - Indicates whether to include uncommitted data.
159
+ */
160
+ public async updateHistoricBlocksTree(globalsHash: Fr, includeUncommitted: boolean) {
161
+ await this.synchronise(() => this._updateHistoricBlocksTree(globalsHash, includeUncommitted));
162
+ }
163
+
164
+ /**
165
+ * Updates the latest global variables hash
166
+ * @param globalVariablesHash - The latest global variables hash
167
+ */
168
+ public async updateLatestGlobalVariablesHash(globalVariablesHash: Fr) {
169
+ return await this.synchronise(() => this._updateLatestGlobalVariablesHash(globalVariablesHash));
170
+ }
171
+
172
+ /**
173
+ * Gets the global variables hash from the previous block
151
174
  * @param includeUncommitted - Indicates whether to include uncommitted data.
152
175
  */
153
- public async updateHistoricBlocksTree(globals: GlobalVariables, includeUncommitted: boolean) {
154
- const blockHash = await this.getCurrentBlockHash(globals, includeUncommitted);
155
- await this.appendLeaves(MerkleTreeId.BLOCKS_TREE, [blockHash.toBuffer()]);
176
+ public async getLatestGlobalVariablesHash(includeUncommitted: boolean): Promise<Fr> {
177
+ return await this.synchronise(() => this._getGlobalVariablesHash(includeUncommitted));
156
178
  }
157
179
 
158
180
  /**
@@ -170,8 +192,8 @@ export class MerkleTrees implements MerkleTreeDb {
170
192
  * @param includeUncommitted - Indicates whether to include uncommitted data.
171
193
  * @returns The current roots of the trees.
172
194
  */
173
- public getTreeRoots(includeUncommitted: boolean): CurrentTreeRoots {
174
- const roots = this.getAllTreeRoots(includeUncommitted);
195
+ public async getTreeRoots(includeUncommitted: boolean): Promise<CurrentTreeRoots> {
196
+ const roots = await this.synchronise(() => Promise.resolve(this._getAllTreeRoots(includeUncommitted)));
175
197
 
176
198
  return {
177
199
  privateDataTreeRoot: roots[0],
@@ -183,14 +205,14 @@ export class MerkleTrees implements MerkleTreeDb {
183
205
  };
184
206
  }
185
207
 
186
- async getCurrentBlockHash(globals: GlobalVariables, includeUncommitted: boolean): Promise<Fr> {
187
- const roots = this.getAllTreeRoots(includeUncommitted).map(root => Fr.fromBuffer(root));
208
+ private async _getCurrentBlockHash(globalsHash: Fr, includeUncommitted: boolean): Promise<Fr> {
209
+ const roots = (await this._getAllTreeRoots(includeUncommitted)).map(root => Fr.fromBuffer(root));
188
210
  const wasm = await CircuitsWasm.get();
189
- return computeBlockHashWithGlobals(wasm, globals, roots[0], roots[1], roots[2], roots[3], roots[4]);
211
+ return computeBlockHash(wasm, globalsHash, roots[0], roots[1], roots[2], roots[3], roots[4]);
190
212
  }
191
213
 
192
- getAllTreeRoots(includeUncommitted: boolean): Buffer[] {
193
- return [
214
+ private _getAllTreeRoots(includeUncommitted: boolean): Promise<Buffer[]> {
215
+ const roots = [
194
216
  MerkleTreeId.PRIVATE_DATA_TREE,
195
217
  MerkleTreeId.NULLIFIER_TREE,
196
218
  MerkleTreeId.CONTRACT_TREE,
@@ -198,6 +220,8 @@ export class MerkleTrees implements MerkleTreeDb {
198
220
  MerkleTreeId.PUBLIC_DATA_TREE,
199
221
  MerkleTreeId.BLOCKS_TREE,
200
222
  ].map(tree => this.trees[tree].getRoot(includeUncommitted));
223
+
224
+ return Promise.resolve(roots);
201
225
  }
202
226
 
203
227
  /**
@@ -377,6 +401,20 @@ export class MerkleTrees implements MerkleTreeDb {
377
401
  return await this.jobQueue.put(fn);
378
402
  }
379
403
 
404
+ private _updateLatestGlobalVariablesHash(globalVariablesHash: Fr): Promise<void> {
405
+ this.latestGlobalVariablesHash.set(globalVariablesHash);
406
+ return Promise.resolve();
407
+ }
408
+
409
+ private _getGlobalVariablesHash(includeUncommitted: boolean): Promise<Fr> {
410
+ return Promise.resolve(this.latestGlobalVariablesHash.get(includeUncommitted));
411
+ }
412
+
413
+ private async _updateHistoricBlocksTree(globalsHash: Fr, includeUncommitted: boolean) {
414
+ const blockHash = await this._getCurrentBlockHash(globalsHash, includeUncommitted);
415
+ await this._appendLeaves(MerkleTreeId.BLOCKS_TREE, [blockHash.toBuffer()]);
416
+ }
417
+
380
418
  /**
381
419
  * Returns the tree info for the specified tree id.
382
420
  * @param treeId - Id of the tree to get information from.
@@ -451,6 +489,7 @@ export class MerkleTrees implements MerkleTreeDb {
451
489
  for (const tree of this.trees) {
452
490
  await tree.commit();
453
491
  }
492
+ this.latestGlobalVariablesHash.commit();
454
493
  }
455
494
 
456
495
  /**
@@ -461,6 +500,7 @@ export class MerkleTrees implements MerkleTreeDb {
461
500
  for (const tree of this.trees) {
462
501
  await tree.rollback();
463
502
  }
503
+ this.latestGlobalVariablesHash.rollback();
464
504
  }
465
505
 
466
506
  /**
@@ -514,7 +554,11 @@ export class MerkleTrees implements MerkleTreeDb {
514
554
  }
515
555
 
516
556
  // Sync and add the block to the historic blocks tree
517
- const blockHash = await this.getCurrentBlockHash(l2Block.globalVariables, true);
557
+ const globalVariablesHash = await computeGlobalVariablesHash(l2Block.globalVariables);
558
+ await this._updateLatestGlobalVariablesHash(globalVariablesHash);
559
+ this.log(`Synced global variables with hash ${this.latestGlobalVariablesHash.toString()}`);
560
+
561
+ const blockHash = await this._getCurrentBlockHash(globalVariablesHash, true);
518
562
  await this._appendLeaves(MerkleTreeId.BLOCKS_TREE, [blockHash.toBuffer()]);
519
563
 
520
564
  await this._commit();