@aztec/aztec-node 0.0.1-commit.6230efd → 0.0.1-commit.6b90f3f5

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,25 +1,13 @@
1
1
  import { Archiver, createArchiver } from '@aztec/archiver';
2
2
  import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
3
- import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client';
4
- import {
5
- type BlobFileStoreMetadata,
6
- createReadOnlyFileStoreBlobClients,
7
- createWritableFileStoreBlobClient,
8
- } from '@aztec/blob-client/filestore';
9
- import {
10
- ARCHIVE_HEIGHT,
11
- INITIAL_L2_BLOCK_NUM,
12
- type L1_TO_L2_MSG_TREE_HEIGHT,
13
- type NOTE_HASH_TREE_HEIGHT,
14
- type NULLIFIER_TREE_HEIGHT,
15
- type PUBLIC_DATA_TREE_HEIGHT,
16
- } from '@aztec/constants';
3
+ import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client';
4
+ import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
17
5
  import { EpochCache, type EpochCacheInterface } from '@aztec/epoch-cache';
18
6
  import { createEthereumChain } from '@aztec/ethereum/chain';
19
7
  import { getPublicClient } from '@aztec/ethereum/client';
20
8
  import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
21
9
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
22
- import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
10
+ import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
23
11
  import { compactArray, pick } from '@aztec/foundation/collection';
24
12
  import { Fr } from '@aztec/foundation/curves/bn254';
25
13
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -36,14 +24,7 @@ import {
36
24
  } from '@aztec/node-lib/factories';
37
25
  import { type P2P, type P2PClientDeps, createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
38
26
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
39
- import {
40
- BlockBuilder,
41
- GlobalVariableBuilder,
42
- SequencerClient,
43
- type SequencerPublisher,
44
- createValidatorForAcceptingTxs,
45
- } from '@aztec/sequencer-client';
46
- import { CheckpointsBuilder } from '@aztec/sequencer-client';
27
+ import { GlobalVariableBuilder, SequencerClient, type SequencerPublisher } from '@aztec/sequencer-client';
47
28
  import { PublicProcessorFactory } from '@aztec/simulator/server';
48
29
  import {
49
30
  AttestationsBlockWatcher,
@@ -54,14 +35,8 @@ import {
54
35
  } from '@aztec/slasher';
55
36
  import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
56
37
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
57
- import {
58
- type BlockParameter,
59
- type DataInBlock,
60
- type L2Block,
61
- L2BlockHash,
62
- type L2BlockSource,
63
- type PublishedL2Block,
64
- } from '@aztec/stdlib/block';
38
+ import { BlockHash, type BlockParameter, type DataInBlock, L2Block, type L2BlockSource } from '@aztec/stdlib/block';
39
+ import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
65
40
  import type {
66
41
  ContractClassPublic,
67
42
  ContractDataSource,
@@ -116,10 +91,13 @@ import {
116
91
  trackSpan,
117
92
  } from '@aztec/telemetry-client';
118
93
  import {
94
+ FullNodeCheckpointsBuilder as CheckpointsBuilder,
95
+ FullNodeCheckpointsBuilder,
119
96
  NodeKeystoreAdapter,
120
97
  ValidatorClient,
121
98
  createBlockProposalHandler,
122
99
  createValidatorClient,
100
+ createValidatorForAcceptingTxs,
123
101
  } from '@aztec/validator-client';
124
102
  import { createWorldStateSynchronizer } from '@aztec/world-state';
125
103
 
@@ -135,6 +113,7 @@ import { NodeMetrics } from './node_metrics.js';
135
113
  */
136
114
  export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
137
115
  private metrics: NodeMetrics;
116
+ private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
138
117
 
139
118
  // Prevent two snapshot operations to happen simultaneously
140
119
  private isUploadingSnapshot = false;
@@ -191,7 +170,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
191
170
  logger?: Logger;
192
171
  publisher?: SequencerPublisher;
193
172
  dateProvider?: DateProvider;
194
- blobClient?: BlobClientInterface;
195
173
  p2pClientDeps?: P2PClientDeps<P2PClientType.Full>;
196
174
  } = {},
197
175
  options: {
@@ -271,24 +249,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
271
249
  );
272
250
  }
273
251
 
274
- const blobFileStoreMetadata: BlobFileStoreMetadata = {
275
- l1ChainId: config.l1ChainId,
276
- rollupVersion: config.rollupVersion,
277
- rollupAddress: config.l1Contracts.rollupAddress.toString(),
278
- };
279
-
280
- const [fileStoreClients, fileStoreUploadClient] = await Promise.all([
281
- createReadOnlyFileStoreBlobClients(config.blobFileStoreUrls, blobFileStoreMetadata, log),
282
- createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, blobFileStoreMetadata, log),
283
- ]);
284
-
285
- const blobClient =
286
- deps.blobClient ??
287
- createBlobClient(config, {
288
- logger: createLogger('node:blob-client:client'),
289
- fileStoreClients,
290
- fileStoreUploadClient,
291
- });
252
+ const blobClient = await createBlobClientWithFileStores(config, createLogger('node:blob-client:client'));
292
253
 
293
254
  // attempt snapshot sync if possible
294
255
  await trySnapshotSync(config, log);
@@ -334,7 +295,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
334
295
  // We should really not be modifying the config object
335
296
  config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
336
297
 
337
- const blockBuilder = new BlockBuilder(
298
+ // Create FullNodeCheckpointsBuilder for validator and non-validator block proposal handling
299
+ const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
338
300
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
339
301
  worldStateSynchronizer,
340
302
  archiver,
@@ -346,16 +308,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
346
308
  const watchers: Watcher[] = [];
347
309
 
348
310
  // Create validator client if required
349
- const validatorClient = createValidatorClient(config, {
311
+ const validatorClient = await createValidatorClient(config, {
312
+ checkpointsBuilder: validatorCheckpointsBuilder,
313
+ worldState: worldStateSynchronizer,
350
314
  p2pClient,
351
315
  telemetry,
352
316
  dateProvider,
353
317
  epochCache,
354
- blockBuilder,
355
318
  blockSource: archiver,
356
319
  l1ToL2MessageSource: archiver,
357
320
  keyStoreManager,
358
- fileStoreBlobUploadClient: fileStoreUploadClient,
321
+ blobClient,
359
322
  });
360
323
 
361
324
  // If we have a validator client, register it as a source of offenses for the slasher,
@@ -373,7 +336,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
373
336
  if (!validatorClient && config.alwaysReexecuteBlockProposals) {
374
337
  log.info('Setting up block proposal reexecution for monitoring');
375
338
  createBlockProposalHandler(config, {
376
- blockBuilder,
339
+ checkpointsBuilder: validatorCheckpointsBuilder,
340
+ worldState: worldStateSynchronizer,
377
341
  epochCache,
378
342
  blockSource: archiver,
379
343
  l1ToL2MessageSource: archiver,
@@ -401,7 +365,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
401
365
  archiver,
402
366
  epochCache,
403
367
  p2pClient.getTxProvider(),
404
- blockBuilder,
368
+ validatorCheckpointsBuilder,
405
369
  config,
406
370
  );
407
371
  watchers.push(epochPruneWatcher);
@@ -466,6 +430,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
466
430
  // Create and start the sequencer client
467
431
  const checkpointsBuilder = new CheckpointsBuilder(
468
432
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
433
+ worldStateSynchronizer,
469
434
  archiver,
470
435
  dateProvider,
471
436
  telemetry,
@@ -594,13 +559,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
594
559
  }
595
560
 
596
561
  /**
597
- * Get a block specified by its number.
598
- * @param number - The block number being requested.
562
+ * Get a block specified by its block number, block hash, or 'latest'.
563
+ * @param block - The block parameter (block number, block hash, or 'latest').
599
564
  * @returns The requested block.
600
565
  */
601
- public async getBlock(number: BlockParameter): Promise<L2Block | undefined> {
602
- const blockNumber = number === 'latest' ? await this.getBlockNumber() : (number as BlockNumber);
603
- return await this.blockSource.getBlock(blockNumber);
566
+ public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
567
+ if (BlockHash.isBlockHash(block)) {
568
+ return this.getBlockByHash(block);
569
+ }
570
+ const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
571
+ if (blockNumber === BlockNumber.ZERO) {
572
+ return this.buildInitialBlock();
573
+ }
574
+ return await this.blockSource.getL2Block(blockNumber);
604
575
  }
605
576
 
606
577
  /**
@@ -608,9 +579,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
608
579
  * @param blockHash - The block hash being requested.
609
580
  * @returns The requested block.
610
581
  */
611
- public async getBlockByHash(blockHash: Fr): Promise<L2Block | undefined> {
612
- const publishedBlock = await this.blockSource.getPublishedBlockByHash(blockHash);
613
- return publishedBlock?.block;
582
+ public async getBlockByHash(blockHash: BlockHash): Promise<L2Block | undefined> {
583
+ const initialBlockHash = await this.#getInitialHeaderHash();
584
+ if (blockHash.equals(initialBlockHash)) {
585
+ return this.buildInitialBlock();
586
+ }
587
+ return await this.blockSource.getL2BlockByHash(blockHash);
588
+ }
589
+
590
+ private buildInitialBlock(): L2Block {
591
+ const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
592
+ return L2Block.empty(initialHeader);
614
593
  }
615
594
 
616
595
  /**
@@ -619,8 +598,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
619
598
  * @returns The requested block.
620
599
  */
621
600
  public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
622
- const publishedBlock = await this.blockSource.getPublishedBlockByArchive(archive);
623
- return publishedBlock?.block;
601
+ return await this.blockSource.getL2BlockByArchive(archive);
624
602
  }
625
603
 
626
604
  /**
@@ -630,19 +608,23 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
630
608
  * @returns The blocks requested.
631
609
  */
632
610
  public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
633
- return (await this.blockSource.getBlocks(from, limit)) ?? [];
611
+ return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
612
+ }
613
+
614
+ public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
615
+ return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
634
616
  }
635
617
 
636
- public async getPublishedBlocks(from: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
637
- return (await this.blockSource.getPublishedBlocks(from, limit)) ?? [];
618
+ public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
619
+ return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
638
620
  }
639
621
 
640
622
  /**
641
- * Method to fetch the current base fees.
642
- * @returns The current base fees.
623
+ * Method to fetch the current min L2 fees.
624
+ * @returns The current min L2 fees.
643
625
  */
644
- public async getCurrentBaseFees(): Promise<GasFees> {
645
- return await this.globalVariableBuilder.getCurrentBaseFees();
626
+ public async getCurrentMinFees(): Promise<GasFees> {
627
+ return await this.globalVariableBuilder.getCurrentMinFees();
646
628
  }
647
629
 
648
630
  public async getMaxPriorityFees(): Promise<GasFees> {
@@ -665,6 +647,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
665
647
  return await this.blockSource.getProvenBlockNumber();
666
648
  }
667
649
 
650
+ public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
651
+ return await this.blockSource.getCheckpointedL2BlockNumber();
652
+ }
653
+
668
654
  /**
669
655
  * Method to fetch the version of the package.
670
656
  * @returns The node package version
@@ -697,12 +683,43 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
697
683
  return this.contractDataSource.getContract(address);
698
684
  }
699
685
 
700
- public getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
701
- return this.logsSource.getPrivateLogsByTags(tags);
702
- }
703
-
704
- public getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
705
- return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags);
686
+ public async getPrivateLogsByTags(
687
+ tags: SiloedTag[],
688
+ page?: number,
689
+ referenceBlock?: BlockHash,
690
+ ): Promise<TxScopedL2Log[][]> {
691
+ if (referenceBlock) {
692
+ const initialBlockHash = await this.#getInitialHeaderHash();
693
+ if (!referenceBlock.equals(initialBlockHash)) {
694
+ const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
695
+ if (!header) {
696
+ throw new Error(
697
+ `Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
698
+ );
699
+ }
700
+ }
701
+ }
702
+ return this.logsSource.getPrivateLogsByTags(tags, page);
703
+ }
704
+
705
+ public async getPublicLogsByTagsFromContract(
706
+ contractAddress: AztecAddress,
707
+ tags: Tag[],
708
+ page?: number,
709
+ referenceBlock?: BlockHash,
710
+ ): Promise<TxScopedL2Log[][]> {
711
+ if (referenceBlock) {
712
+ const initialBlockHash = await this.#getInitialHeaderHash();
713
+ if (!referenceBlock.equals(initialBlockHash)) {
714
+ const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
715
+ if (!header) {
716
+ throw new Error(
717
+ `Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
718
+ );
719
+ }
720
+ }
721
+ }
722
+ return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
706
723
  }
707
724
 
708
725
  /**
@@ -749,21 +766,26 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
749
766
  }
750
767
 
751
768
  public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
752
- let txReceipt = new TxReceipt(txHash, TxStatus.DROPPED, 'Tx dropped by P2P node.');
753
-
754
- // We first check if the tx is in pending (instead of first checking if it is mined) because if we first check
755
- // for mined and then for pending there could be a race condition where the tx is mined between the two checks
756
- // and we would incorrectly return a TxReceipt with status DROPPED
757
- if ((await this.p2pClient.getTxStatus(txHash)) === 'pending') {
758
- txReceipt = new TxReceipt(txHash, TxStatus.PENDING, '');
759
- }
769
+ // Check the tx pool status first. If the tx is known to the pool (pending or mined), we'll use that
770
+ // as a fallback if we don't find a settled receipt in the archiver.
771
+ const txPoolStatus = await this.p2pClient.getTxStatus(txHash);
772
+ const isKnownToPool = txPoolStatus === 'pending' || txPoolStatus === 'mined';
760
773
 
774
+ // Then get the actual tx from the archiver, which tracks every tx in a mined block.
761
775
  const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
776
+
762
777
  if (settledTxReceipt) {
763
- txReceipt = settledTxReceipt;
778
+ // If the archiver has the receipt then return it.
779
+ return settledTxReceipt;
780
+ } else if (isKnownToPool) {
781
+ // If the tx is in the pool but not in the archiver, it's pending.
782
+ // This handles race conditions between archiver and p2p, where the archiver
783
+ // has pruned the block in which a tx was mined, but p2p has not caught up yet.
784
+ return new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
785
+ } else {
786
+ // Otherwise, if we don't know the tx, we consider it dropped.
787
+ return new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
764
788
  }
765
-
766
- return txReceipt;
767
789
  }
768
790
 
769
791
  public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
@@ -788,6 +810,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
788
810
  this.log.info(`Stopped Aztec Node`);
789
811
  }
790
812
 
813
+ /**
814
+ * Returns the blob client used by this node.
815
+ * @internal - Exposed for testing purposes only.
816
+ */
817
+ public getBlobClient(): BlobClientInterface | undefined {
818
+ return this.blobClient;
819
+ }
820
+
791
821
  /**
792
822
  * Method to retrieve pending txs.
793
823
  * @param limit - The number of items to returns
@@ -820,20 +850,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
820
850
  return compactArray(await Promise.all(txHashes.map(txHash => this.getTxByHash(txHash))));
821
851
  }
822
852
 
823
- /**
824
- * Find the indexes of the given leaves in the given tree along with a block metadata pointing to the block in which
825
- * the leaves were inserted.
826
- * @param blockNumber - The block number at which to get the data or 'latest' for latest data.
827
- * @param treeId - The tree to search in.
828
- * @param leafValues - The values to search for.
829
- * @returns The indices of leaves and the block metadata of a block in which the leaves were inserted.
830
- */
831
853
  public async findLeavesIndexes(
832
- blockNumber: BlockParameter,
854
+ block: BlockParameter,
833
855
  treeId: MerkleTreeId,
834
856
  leafValues: Fr[],
835
857
  ): Promise<(DataInBlock<bigint> | undefined)[]> {
836
- const committedDb = await this.#getWorldState(blockNumber);
858
+ const committedDb = await this.#getWorldState(block);
837
859
  const maybeIndices = await committedDb.findLeafIndices(
838
860
  treeId,
839
861
  leafValues.map(x => x.toBuffer()),
@@ -885,45 +907,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
885
907
  }
886
908
  return {
887
909
  l2BlockNumber: BlockNumber(Number(blockNumber)),
888
- l2BlockHash: L2BlockHash.fromField(blockHash),
910
+ l2BlockHash: new BlockHash(blockHash),
889
911
  data: index,
890
912
  };
891
913
  });
892
914
  }
893
915
 
894
- /**
895
- * Returns a sibling path for the given index in the nullifier tree.
896
- * @param blockNumber - The block number at which to get the data.
897
- * @param leafIndex - The index of the leaf for which the sibling path is required.
898
- * @returns The sibling path for the leaf index.
899
- */
900
- public async getNullifierSiblingPath(
901
- blockNumber: BlockParameter,
902
- leafIndex: bigint,
903
- ): Promise<SiblingPath<typeof NULLIFIER_TREE_HEIGHT>> {
904
- const committedDb = await this.#getWorldState(blockNumber);
905
- return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
906
- }
907
-
908
- /**
909
- * Returns a sibling path for the given index in the data tree.
910
- * @param blockNumber - The block number at which to get the data.
911
- * @param leafIndex - The index of the leaf for which the sibling path is required.
912
- * @returns The sibling path for the leaf index.
913
- */
914
- public async getNoteHashSiblingPath(
915
- blockNumber: BlockParameter,
916
- leafIndex: bigint,
917
- ): Promise<SiblingPath<typeof NOTE_HASH_TREE_HEIGHT>> {
918
- const committedDb = await this.#getWorldState(blockNumber);
919
- return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
920
- }
921
-
922
916
  public async getArchiveMembershipWitness(
923
- blockNumber: BlockParameter,
917
+ block: BlockParameter,
924
918
  archive: Fr,
925
919
  ): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
926
- const committedDb = await this.#getWorldState(blockNumber);
920
+ const committedDb = await this.#getWorldState(block);
927
921
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [archive]);
928
922
  return pathAndIndex === undefined
929
923
  ? undefined
@@ -931,10 +925,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
931
925
  }
932
926
 
933
927
  public async getNoteHashMembershipWitness(
934
- blockNumber: BlockParameter,
928
+ block: BlockParameter,
935
929
  noteHash: Fr,
936
930
  ): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
937
- const committedDb = await this.#getWorldState(blockNumber);
931
+ const committedDb = await this.#getWorldState(block);
938
932
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
939
933
  MerkleTreeId.NOTE_HASH_TREE,
940
934
  [noteHash],
@@ -944,17 +938,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
944
938
  : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
945
939
  }
946
940
 
947
- /**
948
- * Returns the index and a sibling path for a leaf in the committed l1 to l2 data tree.
949
- * @param blockNumber - The block number at which to get the data.
950
- * @param l1ToL2Message - The l1ToL2Message to get the index / sibling path for.
951
- * @returns A tuple of the index and the sibling path of the L1ToL2Message (undefined if not found).
952
- */
953
941
  public async getL1ToL2MessageMembershipWitness(
954
- blockNumber: BlockParameter,
942
+ block: BlockParameter,
955
943
  l1ToL2Message: Fr,
956
944
  ): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
957
- const db = await this.#getWorldState(blockNumber);
945
+ const db = await this.#getWorldState(block);
958
946
  const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
959
947
  if (!witness) {
960
948
  return undefined;
@@ -982,56 +970,36 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
982
970
  }
983
971
 
984
972
  /**
985
- * Returns all the L2 to L1 messages in a block.
986
- * @param blockNumber - The block number at which to get the data.
987
- * @returns The L2 to L1 messages (undefined if the block number is not found).
973
+ * Returns all the L2 to L1 messages in an epoch.
974
+ * @param epoch - The epoch at which to get the data.
975
+ * @returns The L2 to L1 messages (empty array if the epoch is not found).
988
976
  */
989
- public async getL2ToL1Messages(blockNumber: BlockParameter): Promise<Fr[][] | undefined> {
990
- const block = await this.blockSource.getBlock(
991
- blockNumber === 'latest' ? await this.getBlockNumber() : (blockNumber as BlockNumber),
977
+ public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
978
+ // Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
979
+ const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
980
+ const blocksInCheckpoints: L2Block[][] = [];
981
+ let previousSlotNumber = SlotNumber.ZERO;
982
+ let checkpointIndex = -1;
983
+ for (const checkpointedBlock of checkpointedBlocks) {
984
+ const block = checkpointedBlock.block;
985
+ const slotNumber = block.header.globalVariables.slotNumber;
986
+ if (slotNumber !== previousSlotNumber) {
987
+ checkpointIndex++;
988
+ blocksInCheckpoints.push([]);
989
+ previousSlotNumber = slotNumber;
990
+ }
991
+ blocksInCheckpoints[checkpointIndex].push(block);
992
+ }
993
+ return blocksInCheckpoints.map(blocks =>
994
+ blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
992
995
  );
993
- return block?.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
994
996
  }
995
997
 
996
- /**
997
- * Returns a sibling path for a leaf in the committed blocks tree.
998
- * @param blockNumber - The block number at which to get the data.
999
- * @param leafIndex - Index of the leaf in the tree.
1000
- * @returns The sibling path.
1001
- */
1002
- public async getArchiveSiblingPath(
1003
- blockNumber: BlockParameter,
1004
- leafIndex: bigint,
1005
- ): Promise<SiblingPath<typeof ARCHIVE_HEIGHT>> {
1006
- const committedDb = await this.#getWorldState(blockNumber);
1007
- return committedDb.getSiblingPath(MerkleTreeId.ARCHIVE, leafIndex);
1008
- }
1009
-
1010
- /**
1011
- * Returns a sibling path for a leaf in the committed public data tree.
1012
- * @param blockNumber - The block number at which to get the data.
1013
- * @param leafIndex - Index of the leaf in the tree.
1014
- * @returns The sibling path.
1015
- */
1016
- public async getPublicDataSiblingPath(
1017
- blockNumber: BlockParameter,
1018
- leafIndex: bigint,
1019
- ): Promise<SiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>> {
1020
- const committedDb = await this.#getWorldState(blockNumber);
1021
- return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
1022
- }
1023
-
1024
- /**
1025
- * Returns a nullifier membership witness for a given nullifier at a given block.
1026
- * @param blockNumber - The block number at which to get the index.
1027
- * @param nullifier - Nullifier we try to find witness for.
1028
- * @returns The nullifier membership witness (if found).
1029
- */
1030
998
  public async getNullifierMembershipWitness(
1031
- blockNumber: BlockParameter,
999
+ block: BlockParameter,
1032
1000
  nullifier: Fr,
1033
1001
  ): Promise<NullifierMembershipWitness | undefined> {
1034
- const db = await this.#getWorldState(blockNumber);
1002
+ const db = await this.#getWorldState(block);
1035
1003
  const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
1036
1004
  if (!witness) {
1037
1005
  return undefined;
@@ -1048,7 +1016,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1048
1016
 
1049
1017
  /**
1050
1018
  * Returns a low nullifier membership witness for a given nullifier at a given block.
1051
- * @param blockNumber - The block number at which to get the index.
1019
+ * @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
1052
1020
  * @param nullifier - Nullifier we try to find the low nullifier witness for.
1053
1021
  * @returns The low nullifier membership witness (if found).
1054
1022
  * @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
@@ -1061,10 +1029,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1061
1029
  * TODO: This is a confusing behavior and we should eventually address that.
1062
1030
  */
1063
1031
  public async getLowNullifierMembershipWitness(
1064
- blockNumber: BlockParameter,
1032
+ block: BlockParameter,
1065
1033
  nullifier: Fr,
1066
1034
  ): Promise<NullifierMembershipWitness | undefined> {
1067
- const committedDb = await this.#getWorldState(blockNumber);
1035
+ const committedDb = await this.#getWorldState(block);
1068
1036
  const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
1069
1037
  if (!findResult) {
1070
1038
  return undefined;
@@ -1079,8 +1047,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1079
1047
  return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
1080
1048
  }
1081
1049
 
1082
- async getPublicDataWitness(blockNumber: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
1083
- const committedDb = await this.#getWorldState(blockNumber);
1050
+ async getPublicDataWitness(block: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
1051
+ const committedDb = await this.#getWorldState(block);
1084
1052
  const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1085
1053
  if (!lowLeafResult) {
1086
1054
  return undefined;
@@ -1094,19 +1062,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1094
1062
  }
1095
1063
  }
1096
1064
 
1097
- /**
1098
- * Gets the storage value at the given contract storage slot.
1099
- *
1100
- * @remarks The storage slot here refers to the slot as it is defined in Noir not the index in the merkle tree.
1101
- * Aztec's version of `eth_getStorageAt`.
1102
- *
1103
- * @param contract - Address of the contract to query.
1104
- * @param slot - Slot to query.
1105
- * @param blockNumber - The block number at which to get the data or 'latest'.
1106
- * @returns Storage value at the given contract slot.
1107
- */
1108
- public async getPublicStorageAt(blockNumber: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
1109
- const committedDb = await this.#getWorldState(blockNumber);
1065
+ public async getPublicStorageAt(block: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
1066
+ const committedDb = await this.#getWorldState(block);
1110
1067
  const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
1111
1068
 
1112
1069
  const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
@@ -1120,24 +1077,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1120
1077
  return preimage.leaf.value;
1121
1078
  }
1122
1079
 
1123
- /**
1124
- * Returns the currently committed block header, or the initial header if no blocks have been produced.
1125
- * @returns The current committed block header.
1126
- */
1127
- public async getBlockHeader(blockNumber: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
1128
- return blockNumber === BlockNumber.ZERO ||
1129
- (blockNumber === 'latest' && (await this.blockSource.getBlockNumber()) === BlockNumber.ZERO)
1130
- ? this.worldStateSynchronizer.getCommitted().getInitialHeader()
1131
- : this.blockSource.getBlockHeader(blockNumber === 'latest' ? blockNumber : (blockNumber as BlockNumber));
1132
- }
1133
-
1134
- /**
1135
- * Get a block header specified by its hash.
1136
- * @param blockHash - The block hash being requested.
1137
- * @returns The requested block header.
1138
- */
1139
- public async getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
1140
- return await this.blockSource.getBlockHeaderByHash(blockHash);
1080
+ public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
1081
+ if (BlockHash.isBlockHash(block)) {
1082
+ const initialBlockHash = await this.#getInitialHeaderHash();
1083
+ if (block.equals(initialBlockHash)) {
1084
+ // Block source doesn't handle initial header so we need to handle the case separately.
1085
+ return this.worldStateSynchronizer.getCommitted().getInitialHeader();
1086
+ }
1087
+ return this.blockSource.getBlockHeaderByHash(block);
1088
+ } else {
1089
+ // Block source doesn't handle initial header so we need to handle the case separately.
1090
+ const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
1091
+ if (blockNumber === BlockNumber.ZERO) {
1092
+ return this.worldStateSynchronizer.getCommitted().getInitialHeader();
1093
+ }
1094
+ return this.blockSource.getBlockHeader(block);
1095
+ }
1141
1096
  }
1142
1097
 
1143
1098
  /**
@@ -1187,6 +1142,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1187
1142
  this.contractDataSource,
1188
1143
  new DateProvider(),
1189
1144
  this.telemetry,
1145
+ this.log.getBindings(),
1190
1146
  );
1191
1147
 
1192
1148
  this.log.verbose(`Simulating public calls for tx ${txHash}`, {
@@ -1240,16 +1196,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1240
1196
  // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1241
1197
  const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1242
1198
  const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
1243
- const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, {
1244
- timestamp: nextSlotTimestamp,
1245
- blockNumber,
1246
- l1ChainId: this.l1ChainId,
1247
- rollupVersion: this.version,
1248
- setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()),
1249
- gasFees: await this.getCurrentBaseFees(),
1250
- skipFeeEnforcement,
1251
- txsPermitted: !this.config.disableTransactions,
1252
- });
1199
+ const validator = createValidatorForAcceptingTxs(
1200
+ db,
1201
+ this.contractDataSource,
1202
+ verifier,
1203
+ {
1204
+ timestamp: nextSlotTimestamp,
1205
+ blockNumber,
1206
+ l1ChainId: this.l1ChainId,
1207
+ rollupVersion: this.version,
1208
+ setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()),
1209
+ gasFees: await this.getCurrentMinFees(),
1210
+ skipFeeEnforcement,
1211
+ txsPermitted: !this.config.disableTransactions,
1212
+ },
1213
+ this.log.getBindings(),
1214
+ );
1253
1215
 
1254
1216
  return await validator.validateTx(tx);
1255
1217
  }
@@ -1318,7 +1280,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1318
1280
  }
1319
1281
 
1320
1282
  // And it has an L2 block hash
1321
- const l2BlockHash = await archiver.getL2Tips().then(tips => tips.latest.hash);
1283
+ const l2BlockHash = await archiver.getL2Tips().then(tips => tips.proposed.hash);
1322
1284
  if (!l2BlockHash) {
1323
1285
  this.metrics.recordSnapshotError();
1324
1286
  throw new Error(`Archiver has no latest L2 block hash downloaded. Cannot start snapshot.`);
@@ -1352,7 +1314,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1352
1314
  throw new Error('Archiver implementation does not support rollbacks.');
1353
1315
  }
1354
1316
 
1355
- const finalizedBlock = await archiver.getL2Tips().then(tips => tips.finalized.number);
1317
+ const finalizedBlock = await archiver.getL2Tips().then(tips => tips.finalized.block.number);
1356
1318
  if (targetBlock < finalizedBlock) {
1357
1319
  if (force) {
1358
1320
  this.log.warn(`Clearing world state database to allow rolling back behind finalized block ${finalizedBlock}`);
@@ -1413,16 +1375,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1413
1375
  }
1414
1376
  }
1415
1377
 
1378
+ #getInitialHeaderHash(): Promise<BlockHash> {
1379
+ if (!this.initialHeaderHashPromise) {
1380
+ this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
1381
+ }
1382
+ return this.initialHeaderHashPromise;
1383
+ }
1384
+
1416
1385
  /**
1417
1386
  * Returns an instance of MerkleTreeOperations having first ensured the world state is fully synched
1418
- * @param blockNumber - The block number at which to get the data.
1387
+ * @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
1419
1388
  * @returns An instance of a committed MerkleTreeOperations
1420
1389
  */
1421
- async #getWorldState(blockNumber: BlockParameter) {
1422
- if (typeof blockNumber === 'number' && blockNumber < INITIAL_L2_BLOCK_NUM - 1) {
1423
- throw new Error('Invalid block number to get world state for: ' + blockNumber);
1424
- }
1425
-
1390
+ async #getWorldState(block: BlockParameter) {
1426
1391
  let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
1427
1392
  try {
1428
1393
  // Attempt to sync the world state if necessary
@@ -1431,15 +1396,39 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1431
1396
  this.log.error(`Error getting world state: ${err}`);
1432
1397
  }
1433
1398
 
1434
- // using a snapshot could be less efficient than using the committed db
1435
- if (blockNumber === 'latest' /*|| blockNumber === blockSyncedTo*/) {
1436
- this.log.debug(`Using committed db for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1399
+ if (block === 'latest') {
1400
+ this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
1437
1401
  return this.worldStateSynchronizer.getCommitted();
1438
- } else if (blockNumber <= blockSyncedTo) {
1402
+ }
1403
+
1404
+ if (BlockHash.isBlockHash(block)) {
1405
+ const initialBlockHash = await this.#getInitialHeaderHash();
1406
+ if (block.equals(initialBlockHash)) {
1407
+ // Block source doesn't handle initial header so we need to handle the case separately.
1408
+ return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
1409
+ }
1410
+
1411
+ const header = await this.blockSource.getBlockHeaderByHash(block);
1412
+ if (!header) {
1413
+ throw new Error(
1414
+ `Block hash ${block.toString()} not found when querying world state. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
1415
+ );
1416
+ }
1417
+ const blockNumber = header.getBlockNumber();
1439
1418
  this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1440
- return this.worldStateSynchronizer.getSnapshot(blockNumber as BlockNumber);
1441
- } else {
1442
- throw new Error(`Block ${blockNumber} not yet synced`);
1419
+ return this.worldStateSynchronizer.getSnapshot(blockNumber);
1420
+ }
1421
+
1422
+ // Block number provided
1423
+ {
1424
+ const blockNumber = block as BlockNumber;
1425
+
1426
+ if (blockNumber > blockSyncedTo) {
1427
+ throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
1428
+ }
1429
+
1430
+ this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1431
+ return this.worldStateSynchronizer.getSnapshot(blockNumber);
1443
1432
  }
1444
1433
  }
1445
1434