@aztec/aztec-node 0.0.1-commit.fcb71a6 → 0.0.1-commit.fffb133c

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,14 +1,8 @@
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';
3
+ import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client';
9
4
  import {
10
5
  ARCHIVE_HEIGHT,
11
- INITIAL_L2_BLOCK_NUM,
12
6
  type L1_TO_L2_MSG_TREE_HEIGHT,
13
7
  type NOTE_HASH_TREE_HEIGHT,
14
8
  type NULLIFIER_TREE_HEIGHT,
@@ -19,7 +13,7 @@ import { createEthereumChain } from '@aztec/ethereum/chain';
19
13
  import { getPublicClient } from '@aztec/ethereum/client';
20
14
  import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
21
15
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
22
- import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
16
+ import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
23
17
  import { compactArray, pick } from '@aztec/foundation/collection';
24
18
  import { Fr } from '@aztec/foundation/curves/bn254';
25
19
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -36,14 +30,7 @@ import {
36
30
  } from '@aztec/node-lib/factories';
37
31
  import { type P2P, type P2PClientDeps, createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
38
32
  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';
33
+ import { GlobalVariableBuilder, SequencerClient, type SequencerPublisher } from '@aztec/sequencer-client';
47
34
  import { PublicProcessorFactory } from '@aztec/simulator/server';
48
35
  import {
49
36
  AttestationsBlockWatcher,
@@ -54,14 +41,8 @@ import {
54
41
  } from '@aztec/slasher';
55
42
  import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
56
43
  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';
44
+ import { type BlockParameter, type DataInBlock, L2Block, L2BlockHash, type L2BlockSource } from '@aztec/stdlib/block';
45
+ import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
65
46
  import type {
66
47
  ContractClassPublic,
67
48
  ContractDataSource,
@@ -116,10 +97,13 @@ import {
116
97
  trackSpan,
117
98
  } from '@aztec/telemetry-client';
118
99
  import {
100
+ FullNodeCheckpointsBuilder as CheckpointsBuilder,
101
+ FullNodeCheckpointsBuilder,
119
102
  NodeKeystoreAdapter,
120
103
  ValidatorClient,
121
104
  createBlockProposalHandler,
122
105
  createValidatorClient,
106
+ createValidatorForAcceptingTxs,
123
107
  } from '@aztec/validator-client';
124
108
  import { createWorldStateSynchronizer } from '@aztec/world-state';
125
109
 
@@ -135,6 +119,7 @@ import { NodeMetrics } from './node_metrics.js';
135
119
  */
136
120
  export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
137
121
  private metrics: NodeMetrics;
122
+ private initialHeaderHashPromise: Promise<L2BlockHash> | undefined = undefined;
138
123
 
139
124
  // Prevent two snapshot operations to happen simultaneously
140
125
  private isUploadingSnapshot = false;
@@ -191,7 +176,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
191
176
  logger?: Logger;
192
177
  publisher?: SequencerPublisher;
193
178
  dateProvider?: DateProvider;
194
- blobClient?: BlobClientInterface;
195
179
  p2pClientDeps?: P2PClientDeps<P2PClientType.Full>;
196
180
  } = {},
197
181
  options: {
@@ -271,24 +255,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
271
255
  );
272
256
  }
273
257
 
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
- });
258
+ const blobClient = await createBlobClientWithFileStores(config, createLogger('node:blob-client:client'));
292
259
 
293
260
  // attempt snapshot sync if possible
294
261
  await trySnapshotSync(config, log);
@@ -334,7 +301,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
334
301
  // We should really not be modifying the config object
335
302
  config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
336
303
 
337
- const blockBuilder = new BlockBuilder(
304
+ // Create FullNodeCheckpointsBuilder for validator and non-validator block proposal handling
305
+ const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
338
306
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
339
307
  worldStateSynchronizer,
340
308
  archiver,
@@ -346,16 +314,17 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
346
314
  const watchers: Watcher[] = [];
347
315
 
348
316
  // Create validator client if required
349
- const validatorClient = createValidatorClient(config, {
317
+ const validatorClient = await createValidatorClient(config, {
318
+ checkpointsBuilder: validatorCheckpointsBuilder,
319
+ worldState: worldStateSynchronizer,
350
320
  p2pClient,
351
321
  telemetry,
352
322
  dateProvider,
353
323
  epochCache,
354
- blockBuilder,
355
324
  blockSource: archiver,
356
325
  l1ToL2MessageSource: archiver,
357
326
  keyStoreManager,
358
- fileStoreBlobUploadClient: fileStoreUploadClient,
327
+ blobClient,
359
328
  });
360
329
 
361
330
  // If we have a validator client, register it as a source of offenses for the slasher,
@@ -373,7 +342,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
373
342
  if (!validatorClient && config.alwaysReexecuteBlockProposals) {
374
343
  log.info('Setting up block proposal reexecution for monitoring');
375
344
  createBlockProposalHandler(config, {
376
- blockBuilder,
345
+ checkpointsBuilder: validatorCheckpointsBuilder,
346
+ worldState: worldStateSynchronizer,
377
347
  epochCache,
378
348
  blockSource: archiver,
379
349
  l1ToL2MessageSource: archiver,
@@ -401,7 +371,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
401
371
  archiver,
402
372
  epochCache,
403
373
  p2pClient.getTxProvider(),
404
- blockBuilder,
374
+ validatorCheckpointsBuilder,
405
375
  config,
406
376
  );
407
377
  watchers.push(epochPruneWatcher);
@@ -466,6 +436,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
466
436
  // Create and start the sequencer client
467
437
  const checkpointsBuilder = new CheckpointsBuilder(
468
438
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
439
+ worldStateSynchronizer,
469
440
  archiver,
470
441
  dateProvider,
471
442
  telemetry,
@@ -594,13 +565,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
594
565
  }
595
566
 
596
567
  /**
597
- * Get a block specified by its number.
598
- * @param number - The block number being requested.
568
+ * Get a block specified by its block number, block hash, or 'latest'.
569
+ * @param block - The block parameter (block number, block hash, or 'latest').
599
570
  * @returns The requested block.
600
571
  */
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);
572
+ public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
573
+ if (L2BlockHash.isL2BlockHash(block)) {
574
+ return this.getBlockByHash(Fr.fromBuffer(block.toBuffer()));
575
+ }
576
+ const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
577
+ if (blockNumber === BlockNumber.ZERO) {
578
+ return this.buildInitialBlock();
579
+ }
580
+ return await this.blockSource.getL2Block(blockNumber);
604
581
  }
605
582
 
606
583
  /**
@@ -609,8 +586,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
609
586
  * @returns The requested block.
610
587
  */
611
588
  public async getBlockByHash(blockHash: Fr): Promise<L2Block | undefined> {
612
- const publishedBlock = await this.blockSource.getPublishedBlockByHash(blockHash);
613
- return publishedBlock?.block;
589
+ const initialBlockHash = await this.#getInitialHeaderHash();
590
+ if (blockHash.equals(Fr.fromBuffer(initialBlockHash.toBuffer()))) {
591
+ return this.buildInitialBlock();
592
+ }
593
+ return await this.blockSource.getL2BlockByHash(blockHash);
594
+ }
595
+
596
+ private buildInitialBlock(): L2Block {
597
+ const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
598
+ return L2Block.empty(initialHeader);
614
599
  }
615
600
 
616
601
  /**
@@ -619,8 +604,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
619
604
  * @returns The requested block.
620
605
  */
621
606
  public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
622
- const publishedBlock = await this.blockSource.getPublishedBlockByArchive(archive);
623
- return publishedBlock?.block;
607
+ return await this.blockSource.getL2BlockByArchive(archive);
624
608
  }
625
609
 
626
610
  /**
@@ -630,19 +614,23 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
630
614
  * @returns The blocks requested.
631
615
  */
632
616
  public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
633
- return (await this.blockSource.getBlocks(from, limit)) ?? [];
617
+ return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
634
618
  }
635
619
 
636
- public async getPublishedBlocks(from: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
637
- return (await this.blockSource.getPublishedBlocks(from, limit)) ?? [];
620
+ public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
621
+ return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
622
+ }
623
+
624
+ public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
625
+ return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
638
626
  }
639
627
 
640
628
  /**
641
- * Method to fetch the current base fees.
642
- * @returns The current base fees.
629
+ * Method to fetch the current min L2 fees.
630
+ * @returns The current min L2 fees.
643
631
  */
644
- public async getCurrentBaseFees(): Promise<GasFees> {
645
- return await this.globalVariableBuilder.getCurrentBaseFees();
632
+ public async getCurrentMinFees(): Promise<GasFees> {
633
+ return await this.globalVariableBuilder.getCurrentMinFees();
646
634
  }
647
635
 
648
636
  public async getMaxPriorityFees(): Promise<GasFees> {
@@ -665,6 +653,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
665
653
  return await this.blockSource.getProvenBlockNumber();
666
654
  }
667
655
 
656
+ public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
657
+ return await this.blockSource.getCheckpointedL2BlockNumber();
658
+ }
659
+
668
660
  /**
669
661
  * Method to fetch the version of the package.
670
662
  * @returns The node package version
@@ -697,12 +689,45 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
697
689
  return this.contractDataSource.getContract(address);
698
690
  }
699
691
 
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);
692
+ public async getPrivateLogsByTags(
693
+ tags: SiloedTag[],
694
+ page?: number,
695
+ referenceBlock?: L2BlockHash,
696
+ ): Promise<TxScopedL2Log[][]> {
697
+ if (referenceBlock) {
698
+ const initialBlockHash = await this.#getInitialHeaderHash();
699
+ if (!referenceBlock.equals(initialBlockHash)) {
700
+ const blockHashFr = Fr.fromBuffer(referenceBlock.toBuffer());
701
+ const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
702
+ if (!header) {
703
+ throw new Error(
704
+ `Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
705
+ );
706
+ }
707
+ }
708
+ }
709
+ return this.logsSource.getPrivateLogsByTags(tags, page);
710
+ }
711
+
712
+ public async getPublicLogsByTagsFromContract(
713
+ contractAddress: AztecAddress,
714
+ tags: Tag[],
715
+ page?: number,
716
+ referenceBlock?: L2BlockHash,
717
+ ): Promise<TxScopedL2Log[][]> {
718
+ if (referenceBlock) {
719
+ const initialBlockHash = await this.#getInitialHeaderHash();
720
+ if (!referenceBlock.equals(initialBlockHash)) {
721
+ const blockHashFr = Fr.fromBuffer(referenceBlock.toBuffer());
722
+ const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
723
+ if (!header) {
724
+ throw new Error(
725
+ `Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
726
+ );
727
+ }
728
+ }
729
+ }
730
+ return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
706
731
  }
707
732
 
708
733
  /**
@@ -749,21 +774,26 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
749
774
  }
750
775
 
751
776
  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
- }
777
+ // Check the tx pool status first. If the tx is known to the pool (pending or mined), we'll use that
778
+ // as a fallback if we don't find a settled receipt in the archiver.
779
+ const txPoolStatus = await this.p2pClient.getTxStatus(txHash);
780
+ const isKnownToPool = txPoolStatus === 'pending' || txPoolStatus === 'mined';
760
781
 
782
+ // Then get the actual tx from the archiver, which tracks every tx in a mined block.
761
783
  const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
784
+
762
785
  if (settledTxReceipt) {
763
- txReceipt = settledTxReceipt;
786
+ // If the archiver has the receipt then return it.
787
+ return settledTxReceipt;
788
+ } else if (isKnownToPool) {
789
+ // If the tx is in the pool but not in the archiver, it's pending.
790
+ // This handles race conditions between archiver and p2p, where the archiver
791
+ // has pruned the block in which a tx was mined, but p2p has not caught up yet.
792
+ return new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
793
+ } else {
794
+ // Otherwise, if we don't know the tx, we consider it dropped.
795
+ return new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
764
796
  }
765
-
766
- return txReceipt;
767
797
  }
768
798
 
769
799
  public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
@@ -788,6 +818,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
788
818
  this.log.info(`Stopped Aztec Node`);
789
819
  }
790
820
 
821
+ /**
822
+ * Returns the blob client used by this node.
823
+ * @internal - Exposed for testing purposes only.
824
+ */
825
+ public getBlobClient(): BlobClientInterface | undefined {
826
+ return this.blobClient;
827
+ }
828
+
791
829
  /**
792
830
  * Method to retrieve pending txs.
793
831
  * @param limit - The number of items to returns
@@ -820,20 +858,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
820
858
  return compactArray(await Promise.all(txHashes.map(txHash => this.getTxByHash(txHash))));
821
859
  }
822
860
 
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
861
  public async findLeavesIndexes(
832
- blockNumber: BlockParameter,
862
+ block: BlockParameter,
833
863
  treeId: MerkleTreeId,
834
864
  leafValues: Fr[],
835
865
  ): Promise<(DataInBlock<bigint> | undefined)[]> {
836
- const committedDb = await this.#getWorldState(blockNumber);
866
+ const committedDb = await this.#getWorldState(block);
837
867
  const maybeIndices = await committedDb.findLeafIndices(
838
868
  treeId,
839
869
  leafValues.map(x => x.toBuffer()),
@@ -891,39 +921,27 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
891
921
  });
892
922
  }
893
923
 
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
924
  public async getNullifierSiblingPath(
901
- blockNumber: BlockParameter,
925
+ block: BlockParameter,
902
926
  leafIndex: bigint,
903
927
  ): Promise<SiblingPath<typeof NULLIFIER_TREE_HEIGHT>> {
904
- const committedDb = await this.#getWorldState(blockNumber);
928
+ const committedDb = await this.#getWorldState(block);
905
929
  return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
906
930
  }
907
931
 
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
932
  public async getNoteHashSiblingPath(
915
- blockNumber: BlockParameter,
933
+ block: BlockParameter,
916
934
  leafIndex: bigint,
917
935
  ): Promise<SiblingPath<typeof NOTE_HASH_TREE_HEIGHT>> {
918
- const committedDb = await this.#getWorldState(blockNumber);
936
+ const committedDb = await this.#getWorldState(block);
919
937
  return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
920
938
  }
921
939
 
922
940
  public async getArchiveMembershipWitness(
923
- blockNumber: BlockParameter,
941
+ block: BlockParameter,
924
942
  archive: Fr,
925
943
  ): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
926
- const committedDb = await this.#getWorldState(blockNumber);
944
+ const committedDb = await this.#getWorldState(block);
927
945
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [archive]);
928
946
  return pathAndIndex === undefined
929
947
  ? undefined
@@ -931,10 +949,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
931
949
  }
932
950
 
933
951
  public async getNoteHashMembershipWitness(
934
- blockNumber: BlockParameter,
952
+ block: BlockParameter,
935
953
  noteHash: Fr,
936
954
  ): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
937
- const committedDb = await this.#getWorldState(blockNumber);
955
+ const committedDb = await this.#getWorldState(block);
938
956
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
939
957
  MerkleTreeId.NOTE_HASH_TREE,
940
958
  [noteHash],
@@ -944,17 +962,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
944
962
  : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
945
963
  }
946
964
 
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
965
  public async getL1ToL2MessageMembershipWitness(
954
- blockNumber: BlockParameter,
966
+ block: BlockParameter,
955
967
  l1ToL2Message: Fr,
956
968
  ): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
957
- const db = await this.#getWorldState(blockNumber);
969
+ const db = await this.#getWorldState(block);
958
970
  const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
959
971
  if (!witness) {
960
972
  return undefined;
@@ -982,56 +994,52 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
982
994
  }
983
995
 
984
996
  /**
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).
997
+ * Returns all the L2 to L1 messages in an epoch.
998
+ * @param epoch - The epoch at which to get the data.
999
+ * @returns The L2 to L1 messages (empty array if the epoch is not found).
988
1000
  */
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),
1001
+ public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
1002
+ // Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
1003
+ const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
1004
+ const blocksInCheckpoints: L2Block[][] = [];
1005
+ let previousSlotNumber = SlotNumber.ZERO;
1006
+ let checkpointIndex = -1;
1007
+ for (const checkpointedBlock of checkpointedBlocks) {
1008
+ const block = checkpointedBlock.block;
1009
+ const slotNumber = block.header.globalVariables.slotNumber;
1010
+ if (slotNumber !== previousSlotNumber) {
1011
+ checkpointIndex++;
1012
+ blocksInCheckpoints.push([]);
1013
+ previousSlotNumber = slotNumber;
1014
+ }
1015
+ blocksInCheckpoints[checkpointIndex].push(block);
1016
+ }
1017
+ return blocksInCheckpoints.map(blocks =>
1018
+ blocks.map(block => block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs)),
992
1019
  );
993
- return block?.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
994
1020
  }
995
1021
 
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
1022
  public async getArchiveSiblingPath(
1003
- blockNumber: BlockParameter,
1023
+ block: BlockParameter,
1004
1024
  leafIndex: bigint,
1005
1025
  ): Promise<SiblingPath<typeof ARCHIVE_HEIGHT>> {
1006
- const committedDb = await this.#getWorldState(blockNumber);
1026
+ const committedDb = await this.#getWorldState(block);
1007
1027
  return committedDb.getSiblingPath(MerkleTreeId.ARCHIVE, leafIndex);
1008
1028
  }
1009
1029
 
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
1030
  public async getPublicDataSiblingPath(
1017
- blockNumber: BlockParameter,
1031
+ block: BlockParameter,
1018
1032
  leafIndex: bigint,
1019
1033
  ): Promise<SiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>> {
1020
- const committedDb = await this.#getWorldState(blockNumber);
1034
+ const committedDb = await this.#getWorldState(block);
1021
1035
  return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
1022
1036
  }
1023
1037
 
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
1038
  public async getNullifierMembershipWitness(
1031
- blockNumber: BlockParameter,
1039
+ block: BlockParameter,
1032
1040
  nullifier: Fr,
1033
1041
  ): Promise<NullifierMembershipWitness | undefined> {
1034
- const db = await this.#getWorldState(blockNumber);
1042
+ const db = await this.#getWorldState(block);
1035
1043
  const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
1036
1044
  if (!witness) {
1037
1045
  return undefined;
@@ -1048,7 +1056,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1048
1056
 
1049
1057
  /**
1050
1058
  * 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.
1059
+ * @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
1052
1060
  * @param nullifier - Nullifier we try to find the low nullifier witness for.
1053
1061
  * @returns The low nullifier membership witness (if found).
1054
1062
  * @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
@@ -1061,10 +1069,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1061
1069
  * TODO: This is a confusing behavior and we should eventually address that.
1062
1070
  */
1063
1071
  public async getLowNullifierMembershipWitness(
1064
- blockNumber: BlockParameter,
1072
+ block: BlockParameter,
1065
1073
  nullifier: Fr,
1066
1074
  ): Promise<NullifierMembershipWitness | undefined> {
1067
- const committedDb = await this.#getWorldState(blockNumber);
1075
+ const committedDb = await this.#getWorldState(block);
1068
1076
  const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
1069
1077
  if (!findResult) {
1070
1078
  return undefined;
@@ -1079,8 +1087,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1079
1087
  return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
1080
1088
  }
1081
1089
 
1082
- async getPublicDataWitness(blockNumber: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
1083
- const committedDb = await this.#getWorldState(blockNumber);
1090
+ async getPublicDataWitness(block: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
1091
+ const committedDb = await this.#getWorldState(block);
1084
1092
  const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1085
1093
  if (!lowLeafResult) {
1086
1094
  return undefined;
@@ -1094,19 +1102,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1094
1102
  }
1095
1103
  }
1096
1104
 
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);
1105
+ public async getPublicStorageAt(block: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
1106
+ const committedDb = await this.#getWorldState(block);
1110
1107
  const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
1111
1108
 
1112
1109
  const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
@@ -1120,24 +1117,23 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1120
1117
  return preimage.leaf.value;
1121
1118
  }
1122
1119
 
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);
1120
+ public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
1121
+ if (L2BlockHash.isL2BlockHash(block)) {
1122
+ const initialBlockHash = await this.#getInitialHeaderHash();
1123
+ if (block.equals(initialBlockHash)) {
1124
+ // Block source doesn't handle initial header so we need to handle the case separately.
1125
+ return this.worldStateSynchronizer.getCommitted().getInitialHeader();
1126
+ }
1127
+ const blockHashFr = Fr.fromBuffer(block.toBuffer());
1128
+ return this.blockSource.getBlockHeaderByHash(blockHashFr);
1129
+ } else {
1130
+ // Block source doesn't handle initial header so we need to handle the case separately.
1131
+ const blockNumber = block === 'latest' ? await this.getBlockNumber() : (block as BlockNumber);
1132
+ if (blockNumber === BlockNumber.ZERO) {
1133
+ return this.worldStateSynchronizer.getCommitted().getInitialHeader();
1134
+ }
1135
+ return this.blockSource.getBlockHeader(block);
1136
+ }
1141
1137
  }
1142
1138
 
1143
1139
  /**
@@ -1246,7 +1242,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1246
1242
  l1ChainId: this.l1ChainId,
1247
1243
  rollupVersion: this.version,
1248
1244
  setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()),
1249
- gasFees: await this.getCurrentBaseFees(),
1245
+ gasFees: await this.getCurrentMinFees(),
1250
1246
  skipFeeEnforcement,
1251
1247
  txsPermitted: !this.config.disableTransactions,
1252
1248
  });
@@ -1318,7 +1314,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1318
1314
  }
1319
1315
 
1320
1316
  // And it has an L2 block hash
1321
- const l2BlockHash = await archiver.getL2Tips().then(tips => tips.latest.hash);
1317
+ const l2BlockHash = await archiver.getL2Tips().then(tips => tips.proposed.hash);
1322
1318
  if (!l2BlockHash) {
1323
1319
  this.metrics.recordSnapshotError();
1324
1320
  throw new Error(`Archiver has no latest L2 block hash downloaded. Cannot start snapshot.`);
@@ -1352,7 +1348,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1352
1348
  throw new Error('Archiver implementation does not support rollbacks.');
1353
1349
  }
1354
1350
 
1355
- const finalizedBlock = await archiver.getL2Tips().then(tips => tips.finalized.number);
1351
+ const finalizedBlock = await archiver.getL2Tips().then(tips => tips.finalized.block.number);
1356
1352
  if (targetBlock < finalizedBlock) {
1357
1353
  if (force) {
1358
1354
  this.log.warn(`Clearing world state database to allow rolling back behind finalized block ${finalizedBlock}`);
@@ -1413,16 +1409,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1413
1409
  }
1414
1410
  }
1415
1411
 
1412
+ #getInitialHeaderHash(): Promise<L2BlockHash> {
1413
+ if (!this.initialHeaderHashPromise) {
1414
+ this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
1415
+ }
1416
+ return this.initialHeaderHashPromise;
1417
+ }
1418
+
1416
1419
  /**
1417
1420
  * 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.
1421
+ * @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
1419
1422
  * @returns An instance of a committed MerkleTreeOperations
1420
1423
  */
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
-
1424
+ async #getWorldState(block: BlockParameter) {
1426
1425
  let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
1427
1426
  try {
1428
1427
  // Attempt to sync the world state if necessary
@@ -1431,15 +1430,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1431
1430
  this.log.error(`Error getting world state: ${err}`);
1432
1431
  }
1433
1432
 
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}`);
1433
+ if (block === 'latest') {
1434
+ this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
1437
1435
  return this.worldStateSynchronizer.getCommitted();
1438
- } else if (blockNumber <= blockSyncedTo) {
1436
+ }
1437
+
1438
+ if (L2BlockHash.isL2BlockHash(block)) {
1439
+ const initialBlockHash = await this.#getInitialHeaderHash();
1440
+ if (block.equals(initialBlockHash)) {
1441
+ // Block source doesn't handle initial header so we need to handle the case separately.
1442
+ return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
1443
+ }
1444
+
1445
+ const blockHashFr = Fr.fromBuffer(block.toBuffer());
1446
+ const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
1447
+ if (!header) {
1448
+ throw new Error(
1449
+ `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.`,
1450
+ );
1451
+ }
1452
+ const blockNumber = header.getBlockNumber();
1439
1453
  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`);
1454
+ return this.worldStateSynchronizer.getSnapshot(blockNumber);
1455
+ }
1456
+
1457
+ // Block number provided
1458
+ {
1459
+ const blockNumber = block as BlockNumber;
1460
+
1461
+ if (blockNumber > blockSyncedTo) {
1462
+ throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
1463
+ }
1464
+
1465
+ this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1466
+ return this.worldStateSynchronizer.getSnapshot(blockNumber);
1443
1467
  }
1444
1468
  }
1445
1469