@aztec/world-state 0.0.1-commit.179ed20 → 0.0.1-commit.189eedb3

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.
Files changed (56) hide show
  1. package/dest/instrumentation/instrumentation.d.ts +4 -3
  2. package/dest/instrumentation/instrumentation.d.ts.map +1 -1
  3. package/dest/instrumentation/instrumentation.js +4 -9
  4. package/dest/native/fork_checkpoint.d.ts +7 -1
  5. package/dest/native/fork_checkpoint.d.ts.map +1 -1
  6. package/dest/native/fork_checkpoint.js +15 -3
  7. package/dest/native/ipc_world_state_instance.d.ts +87 -0
  8. package/dest/native/ipc_world_state_instance.d.ts.map +1 -0
  9. package/dest/native/ipc_world_state_instance.js +660 -0
  10. package/dest/native/merkle_trees_facade.d.ts +12 -8
  11. package/dest/native/merkle_trees_facade.d.ts.map +1 -1
  12. package/dest/native/merkle_trees_facade.js +63 -115
  13. package/dest/native/message.d.ts +14 -222
  14. package/dest/native/message.d.ts.map +1 -1
  15. package/dest/native/message.js +0 -42
  16. package/dest/native/native_world_state.d.ts +19 -7
  17. package/dest/native/native_world_state.d.ts.map +1 -1
  18. package/dest/native/native_world_state.js +113 -58
  19. package/dest/native/native_world_state_instance.d.ts +58 -41
  20. package/dest/native/native_world_state_instance.d.ts.map +1 -1
  21. package/dest/native/native_world_state_instance.js +5 -202
  22. package/dest/native/world_state_operation.d.ts +7 -0
  23. package/dest/native/world_state_operation.d.ts.map +1 -0
  24. package/dest/native/world_state_operation.js +5 -0
  25. package/dest/synchronizer/config.d.ts +3 -3
  26. package/dest/synchronizer/config.d.ts.map +1 -1
  27. package/dest/synchronizer/config.js +15 -13
  28. package/dest/synchronizer/factory.d.ts +5 -5
  29. package/dest/synchronizer/factory.d.ts.map +1 -1
  30. package/dest/synchronizer/factory.js +7 -6
  31. package/dest/synchronizer/server_world_state_synchronizer.d.ts +4 -4
  32. package/dest/synchronizer/server_world_state_synchronizer.d.ts.map +1 -1
  33. package/dest/synchronizer/server_world_state_synchronizer.js +90 -18
  34. package/dest/testing.d.ts +4 -3
  35. package/dest/testing.d.ts.map +1 -1
  36. package/dest/testing.js +10 -6
  37. package/dest/world-state-db/merkle_tree_db.d.ts +1 -10
  38. package/dest/world-state-db/merkle_tree_db.d.ts.map +1 -1
  39. package/package.json +14 -11
  40. package/src/instrumentation/instrumentation.ts +6 -18
  41. package/src/native/fork_checkpoint.ts +19 -3
  42. package/src/native/ipc_world_state_instance.ts +830 -0
  43. package/src/native/merkle_trees_facade.ts +89 -109
  44. package/src/native/message.ts +13 -287
  45. package/src/native/native_world_state.ts +129 -97
  46. package/src/native/native_world_state_instance.ts +96 -280
  47. package/src/native/world_state_operation.ts +33 -0
  48. package/src/synchronizer/config.ts +21 -15
  49. package/src/synchronizer/factory.ts +12 -11
  50. package/src/synchronizer/server_world_state_synchronizer.ts +95 -20
  51. package/src/testing.ts +8 -9
  52. package/src/world-state-db/merkle_tree_db.ts +0 -10
  53. package/dest/native/world_state_ops_queue.d.ts +0 -19
  54. package/dest/native/world_state_ops_queue.d.ts.map +0 -1
  55. package/dest/native/world_state_ops_queue.js +0 -146
  56. package/src/native/world_state_ops_queue.ts +0 -190
@@ -1,10 +1,11 @@
1
- import { GENESIS_BLOCK_HEADER_HASH, INITIAL_L2_BLOCK_NUM, INITIAL_L2_CHECKPOINT_NUM } from '@aztec/constants';
2
- import { BlockNumber } from '@aztec/foundation/branded-types';
1
+ import { INITIAL_CHECKPOINT_NUMBER } from '@aztec/constants';
2
+ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
3
3
  import type { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { type Logger, createLogger } from '@aztec/foundation/log';
5
5
  import { promiseWithResolvers } from '@aztec/foundation/promise';
6
6
  import { elapsed } from '@aztec/foundation/timer';
7
7
  import {
8
+ type BlockHash,
8
9
  GENESIS_CHECKPOINT_HEADER_HASH,
9
10
  type L2Block,
10
11
  type L2BlockId,
@@ -64,7 +65,7 @@ export class ServerWorldStateSynchronizer
64
65
  private readonly log: Logger = createLogger('world_state'),
65
66
  ) {
66
67
  this.merkleTreeCommitted = this.merkleTreeDb.getCommitted();
67
- this.historyToKeep = config.worldStateBlockHistory < 1 ? undefined : config.worldStateBlockHistory;
68
+ this.historyToKeep = config.worldStateCheckpointHistory < 1 ? undefined : config.worldStateCheckpointHistory;
68
69
  this.log.info(
69
70
  `Created world state synchroniser with block history of ${
70
71
  this.historyToKeep === undefined ? 'infinity' : this.historyToKeep
@@ -177,13 +178,10 @@ export class ServerWorldStateSynchronizer
177
178
  /**
178
179
  * Forces an immediate sync.
179
180
  * @param targetBlockNumber - The target block number that we must sync to. Will download unproven blocks if needed to reach it.
180
- * @param skipThrowIfTargetNotReached - Whether to skip throwing if the target block number is not reached.
181
+ * @param blockHash - If provided, verifies the block at targetBlockNumber matches this hash. On mismatch, triggers a resync (reorg detection).
181
182
  * @returns A promise that resolves with the block number the world state was synced to
182
183
  */
183
- public async syncImmediate(
184
- targetBlockNumber?: BlockNumber,
185
- skipThrowIfTargetNotReached?: boolean,
186
- ): Promise<BlockNumber> {
184
+ public async syncImmediate(targetBlockNumber?: BlockNumber, blockHash?: BlockHash): Promise<BlockNumber> {
187
185
  if (this.currentState !== WorldStateRunningState.RUNNING) {
188
186
  throw new Error(`World State is not running. Unable to perform sync.`);
189
187
  }
@@ -195,7 +193,19 @@ export class ServerWorldStateSynchronizer
195
193
  // If we have been given a block number to sync to and we have reached that number then return
196
194
  const currentBlockNumber = await this.getLatestBlockNumber();
197
195
  if (targetBlockNumber !== undefined && targetBlockNumber <= currentBlockNumber) {
198
- return currentBlockNumber;
196
+ if (blockHash === undefined) {
197
+ return currentBlockNumber;
198
+ }
199
+
200
+ // If a block hash was provided, verify we're on the expected fork
201
+ const currentHash = await this.getL2BlockHash(targetBlockNumber);
202
+ if (currentHash === blockHash.toString()) {
203
+ return currentBlockNumber;
204
+ }
205
+ // Hash mismatch: a reorg may have occurred, fall through to trigger sync
206
+ this.log.debug(
207
+ `World state block hash mismatch at ${targetBlockNumber} (expected ${blockHash}, got ${currentHash}). Triggering resync.`,
208
+ );
199
209
  }
200
210
  this.log.debug(`World State at ${currentBlockNumber} told to sync to ${targetBlockNumber ?? 'latest'}`);
201
211
 
@@ -213,7 +223,7 @@ export class ServerWorldStateSynchronizer
213
223
 
214
224
  // If we have been given a block number to sync to and we have not reached that number then fail
215
225
  const updatedBlockNumber = await this.getLatestBlockNumber();
216
- if (!skipThrowIfTargetNotReached && targetBlockNumber !== undefined && targetBlockNumber > updatedBlockNumber) {
226
+ if (targetBlockNumber !== undefined && targetBlockNumber > updatedBlockNumber) {
217
227
  throw new WorldStateSynchronizerError(
218
228
  `Unable to sync to block number ${targetBlockNumber} (last synced is ${updatedBlockNumber})`,
219
229
  {
@@ -227,6 +237,24 @@ export class ServerWorldStateSynchronizer
227
237
  );
228
238
  }
229
239
 
240
+ // If a block hash was provided, verify we're on the expected fork after syncing, throw otherwise
241
+ if (blockHash !== undefined && targetBlockNumber !== undefined) {
242
+ const updatedHash = await this.getL2BlockHash(targetBlockNumber);
243
+ if (updatedHash !== blockHash.toString()) {
244
+ throw new WorldStateSynchronizerError(
245
+ `Block hash mismatch at block ${targetBlockNumber} (expected ${blockHash} but got ${updatedHash})`,
246
+ {
247
+ cause: {
248
+ reason: 'block_hash_mismatch',
249
+ targetBlockNumber,
250
+ expectedHash: blockHash.toString(),
251
+ actualHash: updatedHash,
252
+ },
253
+ },
254
+ );
255
+ }
256
+ }
257
+
230
258
  return updatedBlockNumber;
231
259
  }
232
260
 
@@ -259,19 +287,24 @@ export class ServerWorldStateSynchronizer
259
287
  // but we use a block stream so we need to provide 'local' L2Tips.
260
288
  // We configure the block stream to ignore checkpoints and set checkpoint values to genesis here.
261
289
  const genesisCheckpointHeaderHash = GENESIS_CHECKPOINT_HEADER_HASH.toString();
290
+ const initialBlockHash = (await this.merkleTreeCommitted.getInitialHeader().hash()).toString();
262
291
  return {
263
292
  proposed: latestBlockId,
264
293
  checkpointed: {
265
- block: { number: INITIAL_L2_BLOCK_NUM, hash: GENESIS_BLOCK_HEADER_HASH.toString() },
266
- checkpoint: { number: INITIAL_L2_CHECKPOINT_NUM, hash: genesisCheckpointHeaderHash },
294
+ block: { number: BlockNumber.ZERO, hash: initialBlockHash },
295
+ checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
296
+ },
297
+ proposedCheckpoint: {
298
+ block: { number: BlockNumber.ZERO, hash: initialBlockHash },
299
+ checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
267
300
  },
268
301
  finalized: {
269
302
  block: { number: status.finalizedBlockNumber, hash: finalizedBlockHash ?? '' },
270
- checkpoint: { number: INITIAL_L2_CHECKPOINT_NUM, hash: genesisCheckpointHeaderHash },
303
+ checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
271
304
  },
272
305
  proven: {
273
306
  block: { number: provenBlockNumber, hash: provenBlockHash ?? '' },
274
- checkpoint: { number: INITIAL_L2_CHECKPOINT_NUM, hash: genesisCheckpointHeaderHash },
307
+ checkpoint: { number: INITIAL_CHECKPOINT_NUMBER, hash: genesisCheckpointHeaderHash },
275
308
  },
276
309
  };
277
310
  }
@@ -360,16 +393,56 @@ export class ServerWorldStateSynchronizer
360
393
 
361
394
  private async handleChainFinalized(blockNumber: BlockNumber) {
362
395
  this.log.verbose(`Finalized chain is now at block ${blockNumber}`);
396
+ // If the finalized block number is older than the oldest available block in world state,
397
+ // skip entirely. The finalized block number can jump backwards (e.g. when the finalization
398
+ // heuristic changes) and try to read block data that has already been pruned. When this
399
+ // happens, there is nothing useful to do — the native world state is already finalized
400
+ // past this point and pruning has already happened.
401
+ const currentSummary = await this.merkleTreeDb.getStatusSummary();
402
+ if (blockNumber < currentSummary.oldestHistoricalBlock || blockNumber < 1) {
403
+ this.log.trace(
404
+ `Finalized block ${blockNumber} is older than the oldest available block ${currentSummary.oldestHistoricalBlock}. Skipping.`,
405
+ );
406
+ return;
407
+ }
363
408
  const summary = await this.merkleTreeDb.setFinalized(blockNumber);
364
409
  if (this.historyToKeep === undefined) {
365
410
  return;
366
411
  }
367
- const newHistoricBlock = summary.finalizedBlockNumber - this.historyToKeep + 1;
368
- if (newHistoricBlock <= 1) {
412
+ const finalisedBlockData = await this.l2BlockSource.getBlockData({ number: summary.finalizedBlockNumber });
413
+ if (finalisedBlockData === undefined) {
414
+ this.log.warn(
415
+ `Failed to retrieve checkpointed block for finalized block number: ${summary.finalizedBlockNumber}`,
416
+ );
417
+ return;
418
+ }
419
+ // Compute the required historic checkpoint number
420
+ const newHistoricCheckpointNumber = finalisedBlockData.checkpointNumber - this.historyToKeep + 1;
421
+ if (newHistoricCheckpointNumber <= 1) {
422
+ return;
423
+ }
424
+ // Retrieve the historic checkpoint
425
+ const historicCheckpoint = await this.l2BlockSource.getCheckpoint({
426
+ number: CheckpointNumber(newHistoricCheckpointNumber),
427
+ });
428
+ if (!historicCheckpoint) {
429
+ this.log.warn(`Failed to retrieve checkpoint number ${newHistoricCheckpointNumber} from Archiver`);
369
430
  return;
370
431
  }
371
- this.log.verbose(`Pruning historic blocks to ${newHistoricBlock}`);
372
- const status = await this.merkleTreeDb.removeHistoricalBlocks(BlockNumber(newHistoricBlock));
432
+ if (historicCheckpoint.checkpoint.blocks.length === 0 || historicCheckpoint.checkpoint.blocks[0] === undefined) {
433
+ this.log.warn(`Retrieved checkpoint number ${newHistoricCheckpointNumber} has no blocks!`);
434
+ return;
435
+ }
436
+ // Find the block at the start of the checkpoint and remove blocks up to this one
437
+ const newHistoricBlock = historicCheckpoint.checkpoint.blocks[0];
438
+ if (newHistoricBlock.number <= currentSummary.oldestHistoricalBlock) {
439
+ this.log.debug(
440
+ `Historic block ${newHistoricBlock.number} is not newer than oldest available ${currentSummary.oldestHistoricalBlock}. Skipping prune.`,
441
+ );
442
+ return;
443
+ }
444
+ this.log.verbose(`Pruning historic blocks to ${newHistoricBlock.number}`);
445
+ const status = await this.merkleTreeDb.removeHistoricalBlocks(BlockNumber(newHistoricBlock.number));
373
446
  this.log.debug(`World state summary `, status.summary);
374
447
  }
375
448
 
@@ -380,9 +453,11 @@ export class ServerWorldStateSynchronizer
380
453
  }
381
454
 
382
455
  private async handleChainPruned(blockNumber: BlockNumber) {
383
- this.log.warn(`Chain pruned to block ${blockNumber}`);
456
+ this.log.info(`Chain pruned to block ${blockNumber}`);
384
457
  const status = await this.merkleTreeDb.unwindBlocks(blockNumber);
385
- this.provenBlockNumber = undefined;
458
+ if (this.provenBlockNumber !== undefined && this.provenBlockNumber > blockNumber) {
459
+ this.provenBlockNumber = undefined;
460
+ }
386
461
  this.instrumentation.updateWorldStateMetrics(status);
387
462
  }
388
463
 
package/src/testing.ts CHANGED
@@ -3,22 +3,19 @@ import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { computeFeePayerBalanceLeafSlot } from '@aztec/protocol-contracts/fee-juice';
4
4
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
5
  import { MerkleTreeId, PublicDataTreeLeaf } from '@aztec/stdlib/trees';
6
+ import type { GenesisData } from '@aztec/stdlib/world-state';
6
7
 
7
8
  import { NativeWorldStateService } from './native/index.js';
8
9
 
9
- async function generateGenesisValues(prefilledPublicData: PublicDataTreeLeaf[]) {
10
- if (!prefilledPublicData.length) {
10
+ async function generateGenesisValues(genesis: GenesisData) {
11
+ if (!genesis.prefilledPublicData.length && genesis.genesisTimestamp === 0n) {
11
12
  return {
12
13
  genesisArchiveRoot: new Fr(GENESIS_ARCHIVE_ROOT),
13
14
  };
14
15
  }
15
16
 
16
17
  // Create a temporary world state to compute the genesis values.
17
- const ws = await NativeWorldStateService.tmp(
18
- undefined /* rollupAddress */,
19
- true /* cleanupTmpDir */,
20
- prefilledPublicData,
21
- );
18
+ const ws = await NativeWorldStateService.tmp(/*cleanupTmpDir=*/ true, genesis);
22
19
  const genesisArchiveRoot = new Fr((await ws.getCommitted().getTreeInfo(MerkleTreeId.ARCHIVE)).root);
23
20
  await ws.close();
24
21
 
@@ -33,6 +30,7 @@ export async function getGenesisValues(
33
30
  initialAccounts: AztecAddress[],
34
31
  initialAccountFeeJuice = defaultInitialAccountFeeJuice,
35
32
  genesisPublicData: PublicDataTreeLeaf[] = [],
33
+ genesisTimestamp: bigint = 0n,
36
34
  ) {
37
35
  // Top up the accounts with fee juice.
38
36
  let prefilledPublicData = await Promise.all(
@@ -46,11 +44,12 @@ export async function getGenesisValues(
46
44
 
47
45
  prefilledPublicData.sort((a, b) => (b.slot.lt(a.slot) ? 1 : -1));
48
46
 
49
- const { genesisArchiveRoot } = await generateGenesisValues(prefilledPublicData);
47
+ const genesis: GenesisData = { prefilledPublicData, genesisTimestamp };
48
+ const { genesisArchiveRoot } = await generateGenesisValues(genesis);
50
49
 
51
50
  return {
52
51
  genesisArchiveRoot,
53
- prefilledPublicData,
52
+ genesis,
54
53
  fundingNeeded: BigInt(initialAccounts.length) * initialAccountFeeJuice.toBigInt(),
55
54
  };
56
55
  }
@@ -1,14 +1,12 @@
1
1
  import { MAX_NULLIFIERS_PER_TX, MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX } from '@aztec/constants';
2
2
  import type { BlockNumber } from '@aztec/foundation/branded-types';
3
3
  import type { Fr } from '@aztec/foundation/curves/bn254';
4
- import type { IndexedTreeSnapshot, TreeSnapshot } from '@aztec/merkle-tree';
5
4
  import type { L2Block } from '@aztec/stdlib/block';
6
5
  import type {
7
6
  ForkMerkleTreeOperations,
8
7
  MerkleTreeReadOperations,
9
8
  ReadonlyWorldStateAccess,
10
9
  } from '@aztec/stdlib/interfaces/server';
11
- import type { MerkleTreeId } from '@aztec/stdlib/trees';
12
10
 
13
11
  import type { WorldStateStatusFull, WorldStateStatusSummary } from '../native/message.js';
14
12
 
@@ -31,14 +29,6 @@ export const INITIAL_NULLIFIER_TREE_SIZE = 2 * MAX_NULLIFIERS_PER_TX;
31
29
 
32
30
  export const INITIAL_PUBLIC_DATA_TREE_SIZE = 2 * MAX_TOTAL_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX;
33
31
 
34
- export type TreeSnapshots = {
35
- [MerkleTreeId.NULLIFIER_TREE]: IndexedTreeSnapshot;
36
- [MerkleTreeId.NOTE_HASH_TREE]: TreeSnapshot<Fr>;
37
- [MerkleTreeId.PUBLIC_DATA_TREE]: IndexedTreeSnapshot;
38
- [MerkleTreeId.L1_TO_L2_MESSAGE_TREE]: TreeSnapshot<Fr>;
39
- [MerkleTreeId.ARCHIVE]: TreeSnapshot<Fr>;
40
- };
41
-
42
32
  export interface MerkleTreeAdminDatabase extends ForkMerkleTreeOperations, ReadonlyWorldStateAccess {
43
33
  /**
44
34
  * Handles a single L2 block (i.e. Inserts the new note hashes into the merkle tree).
@@ -1,19 +0,0 @@
1
- import { WorldStateMessageType } from './message.js';
2
- export declare const MUTATING_MSG_TYPES: Set<WorldStateMessageType>;
3
- export declare class WorldStateOpsQueue {
4
- private requests;
5
- private inFlightMutatingCount;
6
- private inFlightCount;
7
- private stopPromise?;
8
- private stopResolve?;
9
- private requestId;
10
- private ops;
11
- execute(request: () => Promise<any>, messageType: WorldStateMessageType, committedOnly: boolean): Promise<any>;
12
- private executeMutating;
13
- private executeNonMutatingUncommitted;
14
- private executeNonMutatingCommitted;
15
- private checkAndEnqueue;
16
- private sendEnqueuedRequest;
17
- stop(): Promise<void>;
18
- }
19
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid29ybGRfc3RhdGVfb3BzX3F1ZXVlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbmF0aXZlL3dvcmxkX3N0YXRlX29wc19xdWV1ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUF5QnJELGVBQU8sTUFBTSxrQkFBa0IsNEJBZ0I3QixDQUFDO0FBR0gscUJBQWEsa0JBQWtCO0lBQzdCLE9BQU8sQ0FBQyxRQUFRLENBQXNCO0lBQ3RDLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBSztJQUNsQyxPQUFPLENBQUMsYUFBYSxDQUFLO0lBQzFCLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBZ0I7SUFDcEMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFhO0lBQ2pDLE9BQU8sQ0FBQyxTQUFTLENBQUs7SUFDdEIsT0FBTyxDQUFDLEdBQUcsQ0FBd0M7SUFJNUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsRUFBRSxXQUFXLEVBQUUscUJBQXFCLEVBQUUsYUFBYSxFQUFFLE9BQU8sZ0JBc0JyRztJQUdELE9BQU8sQ0FBQyxlQUFlO0lBV3ZCLE9BQU8sQ0FBQyw2QkFBNkI7SUFZckMsT0FBTyxDQUFDLDJCQUEyQjtJQVVuQyxPQUFPLENBQUMsZUFBZTtJQXlDdkIsT0FBTyxDQUFDLG1CQUFtQjtJQWdCcEIsSUFBSSxrQkFnQlY7Q0FDRiJ9
@@ -1 +0,0 @@
1
- {"version":3,"file":"world_state_ops_queue.d.ts","sourceRoot":"","sources":["../../src/native/world_state_ops_queue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAyBrD,eAAO,MAAM,kBAAkB,4BAgB7B,CAAC;AAGH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,GAAG,CAAwC;IAI5C,OAAO,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,aAAa,EAAE,OAAO,gBAsBrG;IAGD,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,6BAA6B;IAYrC,OAAO,CAAC,2BAA2B;IAUnC,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,mBAAmB;IAgBpB,IAAI,kBAgBV;CACF"}
@@ -1,146 +0,0 @@
1
- import { promiseWithResolvers } from '@aztec/foundation/promise';
2
- import { WorldStateMessageType } from './message.js';
3
- // These are the set of message types that implement mutating operations
4
- // Messages of these types require exclusive access to their given forks
5
- export const MUTATING_MSG_TYPES = new Set([
6
- WorldStateMessageType.APPEND_LEAVES,
7
- WorldStateMessageType.BATCH_INSERT,
8
- WorldStateMessageType.SEQUENTIAL_INSERT,
9
- WorldStateMessageType.UPDATE_ARCHIVE,
10
- WorldStateMessageType.COMMIT,
11
- WorldStateMessageType.ROLLBACK,
12
- WorldStateMessageType.SYNC_BLOCK,
13
- WorldStateMessageType.CREATE_FORK,
14
- WorldStateMessageType.DELETE_FORK,
15
- WorldStateMessageType.FINALIZE_BLOCKS,
16
- WorldStateMessageType.UNWIND_BLOCKS,
17
- WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
18
- WorldStateMessageType.CREATE_CHECKPOINT,
19
- WorldStateMessageType.COMMIT_CHECKPOINT,
20
- WorldStateMessageType.REVERT_CHECKPOINT
21
- ]);
22
- // This class implements the per-fork operation queue
23
- export class WorldStateOpsQueue {
24
- requests = [];
25
- inFlightMutatingCount = 0;
26
- inFlightCount = 0;
27
- stopPromise;
28
- stopResolve;
29
- requestId = 0;
30
- ops = new Map();
31
- // The primary public api, this is where an operation is queued
32
- // We return a promise that will ultimately be resolved/rejected with the response/error generated by the 'request' argument
33
- execute(request, messageType, committedOnly) {
34
- if (this.stopResolve !== undefined) {
35
- throw new Error('Unable to send request to world state, queue already stopped');
36
- }
37
- const op = {
38
- requestId: this.requestId++,
39
- mutating: MUTATING_MSG_TYPES.has(messageType),
40
- request,
41
- promise: promiseWithResolvers()
42
- };
43
- this.ops.set(op.requestId, op);
44
- // Perform the appropriate action based upon the queueing rules
45
- if (op.mutating) {
46
- this.executeMutating(op);
47
- } else if (committedOnly === false) {
48
- this.executeNonMutatingUncommitted(op);
49
- } else {
50
- this.executeNonMutatingCommitted(op);
51
- }
52
- return op.promise.promise;
53
- }
54
- // Mutating requests need exclusive access
55
- executeMutating(op) {
56
- // If nothing is in flight then we send the request immediately
57
- // Otherwise add to the queue
58
- if (this.inFlightCount === 0) {
59
- this.sendEnqueuedRequest(op);
60
- } else {
61
- this.requests.push(op);
62
- }
63
- }
64
- // Non mutating requests including uncommitted state
65
- executeNonMutatingUncommitted(op) {
66
- // If there are no mutating requests in flight and there is nothing queued
67
- // then send the request immediately
68
- // If a mutating request is in flight then we must wait
69
- // If a mutating request is not in flight but something is queued then it must be a mutating request
70
- if (this.inFlightMutatingCount == 0 && this.requests.length == 0) {
71
- this.sendEnqueuedRequest(op);
72
- } else {
73
- this.requests.push(op);
74
- }
75
- }
76
- executeNonMutatingCommitted(op) {
77
- // This is a non-mutating request for committed data
78
- // It can always be sent
79
- op.request().then(op.promise.resolve, op.promise.reject).finally(()=>{
80
- this.ops.delete(op.requestId);
81
- });
82
- }
83
- checkAndEnqueue(completedOp) {
84
- // As request has completed
85
- // First we decrements the relevant in flight counters
86
- if (completedOp.mutating) {
87
- --this.inFlightMutatingCount;
88
- }
89
- --this.inFlightCount;
90
- // If there are still requests in flight then do nothing further
91
- if (this.inFlightCount != 0) {
92
- return;
93
- }
94
- // No requests in flight, send next queued requests
95
- // We loop and send:
96
- // 1 mutating request if it is next in the queue
97
- // As many non-mutating requests as we encounter until
98
- // we exhaust the queue or we reach a mutating request
99
- while(this.requests.length > 0){
100
- const next = this.requests[0];
101
- if (next.mutating) {
102
- if (this.inFlightCount == 0) {
103
- // send the mutating request
104
- this.requests.shift();
105
- this.sendEnqueuedRequest(next);
106
- }
107
- break;
108
- } else {
109
- // not mutating, send and go round again
110
- this.requests.shift();
111
- this.sendEnqueuedRequest(next);
112
- }
113
- }
114
- // If the queue is empty, there is nothing in flight and we have been told to stop, then resolve the stop promise
115
- if (this.inFlightCount == 0 && this.stopResolve !== undefined) {
116
- this.stopResolve();
117
- }
118
- }
119
- sendEnqueuedRequest(op) {
120
- // Here we increment the in flight counts before sending
121
- ++this.inFlightCount;
122
- if (op.mutating) {
123
- ++this.inFlightMutatingCount;
124
- }
125
- // Make the request and pass the response/error through to the stored promise
126
- op.request().then(op.promise.resolve, op.promise.reject).finally(()=>{
127
- this.checkAndEnqueue(op);
128
- this.ops.delete(op.requestId);
129
- });
130
- }
131
- stop() {
132
- // If there is already a stop promise then return it
133
- if (this.stopPromise) {
134
- return this.stopPromise;
135
- }
136
- // Otherwise create a new one and capture the resolve method
137
- this.stopPromise = new Promise((resolve)=>{
138
- this.stopResolve = resolve;
139
- });
140
- // If no outstanding requests then immediately resolve the promise
141
- if (this.requests.length == 0 && this.inFlightCount == 0 && this.stopResolve !== undefined) {
142
- this.stopResolve();
143
- }
144
- return this.stopPromise;
145
- }
146
- }
@@ -1,190 +0,0 @@
1
- import { promiseWithResolvers } from '@aztec/foundation/promise';
2
-
3
- import { WorldStateMessageType } from './message.js';
4
-
5
- /**
6
- * This is the implementation for queueing requests to the world state.
7
- * Requests need to be queued for the world state to ensure that writes are correctly ordered
8
- * and reads return the correct data.
9
- * Due to the nature of the NAPI we can't really do this there.
10
- *
11
- * The rules for queueing are as follows:
12
- *
13
- * 1. Reads of committed state never need to be queued. LMDB uses MVCC to ensure readers see a consistent view of the DB.
14
- * 2. Reads of uncommitted state can happen concurrently with other reads of uncommitted state on the same fork (or reads of committed state)
15
- * 3. All writes require exclusive access to their respective fork
16
- *
17
- */
18
-
19
- type WorldStateOp = {
20
- requestId: number;
21
- mutating: boolean;
22
- request: () => Promise<any>;
23
- promise: PromiseWithResolvers<any>;
24
- };
25
-
26
- // These are the set of message types that implement mutating operations
27
- // Messages of these types require exclusive access to their given forks
28
- export const MUTATING_MSG_TYPES = new Set([
29
- WorldStateMessageType.APPEND_LEAVES,
30
- WorldStateMessageType.BATCH_INSERT,
31
- WorldStateMessageType.SEQUENTIAL_INSERT,
32
- WorldStateMessageType.UPDATE_ARCHIVE,
33
- WorldStateMessageType.COMMIT,
34
- WorldStateMessageType.ROLLBACK,
35
- WorldStateMessageType.SYNC_BLOCK,
36
- WorldStateMessageType.CREATE_FORK,
37
- WorldStateMessageType.DELETE_FORK,
38
- WorldStateMessageType.FINALIZE_BLOCKS,
39
- WorldStateMessageType.UNWIND_BLOCKS,
40
- WorldStateMessageType.REMOVE_HISTORICAL_BLOCKS,
41
- WorldStateMessageType.CREATE_CHECKPOINT,
42
- WorldStateMessageType.COMMIT_CHECKPOINT,
43
- WorldStateMessageType.REVERT_CHECKPOINT,
44
- ]);
45
-
46
- // This class implements the per-fork operation queue
47
- export class WorldStateOpsQueue {
48
- private requests: WorldStateOp[] = [];
49
- private inFlightMutatingCount = 0;
50
- private inFlightCount = 0;
51
- private stopPromise?: Promise<void>;
52
- private stopResolve?: () => void;
53
- private requestId = 0;
54
- private ops: Map<number, WorldStateOp> = new Map();
55
-
56
- // The primary public api, this is where an operation is queued
57
- // We return a promise that will ultimately be resolved/rejected with the response/error generated by the 'request' argument
58
- public execute(request: () => Promise<any>, messageType: WorldStateMessageType, committedOnly: boolean) {
59
- if (this.stopResolve !== undefined) {
60
- throw new Error('Unable to send request to world state, queue already stopped');
61
- }
62
-
63
- const op: WorldStateOp = {
64
- requestId: this.requestId++,
65
- mutating: MUTATING_MSG_TYPES.has(messageType),
66
- request,
67
- promise: promiseWithResolvers(),
68
- };
69
- this.ops.set(op.requestId, op);
70
-
71
- // Perform the appropriate action based upon the queueing rules
72
- if (op.mutating) {
73
- this.executeMutating(op);
74
- } else if (committedOnly === false) {
75
- this.executeNonMutatingUncommitted(op);
76
- } else {
77
- this.executeNonMutatingCommitted(op);
78
- }
79
- return op.promise.promise;
80
- }
81
-
82
- // Mutating requests need exclusive access
83
- private executeMutating(op: WorldStateOp) {
84
- // If nothing is in flight then we send the request immediately
85
- // Otherwise add to the queue
86
- if (this.inFlightCount === 0) {
87
- this.sendEnqueuedRequest(op);
88
- } else {
89
- this.requests.push(op);
90
- }
91
- }
92
-
93
- // Non mutating requests including uncommitted state
94
- private executeNonMutatingUncommitted(op: WorldStateOp) {
95
- // If there are no mutating requests in flight and there is nothing queued
96
- // then send the request immediately
97
- // If a mutating request is in flight then we must wait
98
- // If a mutating request is not in flight but something is queued then it must be a mutating request
99
- if (this.inFlightMutatingCount == 0 && this.requests.length == 0) {
100
- this.sendEnqueuedRequest(op);
101
- } else {
102
- this.requests.push(op);
103
- }
104
- }
105
-
106
- private executeNonMutatingCommitted(op: WorldStateOp) {
107
- // This is a non-mutating request for committed data
108
- // It can always be sent
109
- op.request()
110
- .then(op.promise.resolve, op.promise.reject)
111
- .finally(() => {
112
- this.ops.delete(op.requestId);
113
- });
114
- }
115
-
116
- private checkAndEnqueue(completedOp: WorldStateOp) {
117
- // As request has completed
118
- // First we decrements the relevant in flight counters
119
- if (completedOp.mutating) {
120
- --this.inFlightMutatingCount;
121
- }
122
- --this.inFlightCount;
123
-
124
- // If there are still requests in flight then do nothing further
125
- if (this.inFlightCount != 0) {
126
- return;
127
- }
128
-
129
- // No requests in flight, send next queued requests
130
- // We loop and send:
131
- // 1 mutating request if it is next in the queue
132
- // As many non-mutating requests as we encounter until
133
- // we exhaust the queue or we reach a mutating request
134
- while (this.requests.length > 0) {
135
- const next = this.requests[0];
136
- if (next.mutating) {
137
- if (this.inFlightCount == 0) {
138
- // send the mutating request
139
- this.requests.shift();
140
- this.sendEnqueuedRequest(next);
141
- }
142
- // this request is mutating, we need to stop here
143
- break;
144
- } else {
145
- // not mutating, send and go round again
146
- this.requests.shift();
147
- this.sendEnqueuedRequest(next);
148
- }
149
- }
150
-
151
- // If the queue is empty, there is nothing in flight and we have been told to stop, then resolve the stop promise
152
- if (this.inFlightCount == 0 && this.stopResolve !== undefined) {
153
- this.stopResolve();
154
- }
155
- }
156
-
157
- private sendEnqueuedRequest(op: WorldStateOp) {
158
- // Here we increment the in flight counts before sending
159
- ++this.inFlightCount;
160
- if (op.mutating) {
161
- ++this.inFlightMutatingCount;
162
- }
163
-
164
- // Make the request and pass the response/error through to the stored promise
165
- op.request()
166
- .then(op.promise.resolve, op.promise.reject)
167
- .finally(() => {
168
- this.checkAndEnqueue(op);
169
- this.ops.delete(op.requestId);
170
- });
171
- }
172
-
173
- public stop() {
174
- // If there is already a stop promise then return it
175
- if (this.stopPromise) {
176
- return this.stopPromise;
177
- }
178
-
179
- // Otherwise create a new one and capture the resolve method
180
- this.stopPromise = new Promise(resolve => {
181
- this.stopResolve = resolve;
182
- });
183
-
184
- // If no outstanding requests then immediately resolve the promise
185
- if (this.requests.length == 0 && this.inFlightCount == 0 && this.stopResolve !== undefined) {
186
- this.stopResolve();
187
- }
188
- return this.stopPromise;
189
- }
190
- }