@aztec/aztec-node 0.0.1-commit.1142ef1 → 0.0.1-commit.18ccd8f0

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.
@@ -3,7 +3,6 @@ import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@azte
3
3
  import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client';
4
4
  import {
5
5
  ARCHIVE_HEIGHT,
6
- INITIAL_L2_BLOCK_NUM,
7
6
  type L1_TO_L2_MSG_TREE_HEIGHT,
8
7
  type NOTE_HASH_TREE_HEIGHT,
9
8
  type NULLIFIER_TREE_HEIGHT,
@@ -31,7 +30,7 @@ import {
31
30
  } from '@aztec/node-lib/factories';
32
31
  import { type P2P, type P2PClientDeps, createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
33
32
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
34
- import { BlockBuilder, GlobalVariableBuilder, SequencerClient, type SequencerPublisher } from '@aztec/sequencer-client';
33
+ import { GlobalVariableBuilder, SequencerClient, type SequencerPublisher } from '@aztec/sequencer-client';
35
34
  import { PublicProcessorFactory } from '@aztec/simulator/server';
36
35
  import {
37
36
  AttestationsBlockWatcher,
@@ -42,15 +41,7 @@ import {
42
41
  } from '@aztec/slasher';
43
42
  import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
44
43
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
45
- import {
46
- type BlockParameter,
47
- type DataInBlock,
48
- type L2Block,
49
- L2BlockHash,
50
- L2BlockNew,
51
- type L2BlockSource,
52
- type PublishedL2Block,
53
- } from '@aztec/stdlib/block';
44
+ import { BlockHash, type BlockParameter, type DataInBlock, L2Block, type L2BlockSource } from '@aztec/stdlib/block';
54
45
  import type { PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
55
46
  import type {
56
47
  ContractClassPublic,
@@ -128,6 +119,7 @@ import { NodeMetrics } from './node_metrics.js';
128
119
  */
129
120
  export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
130
121
  private metrics: NodeMetrics;
122
+ private initialHeaderHashPromise: Promise<BlockHash> | undefined = undefined;
131
123
 
132
124
  // Prevent two snapshot operations to happen simultaneously
133
125
  private isUploadingSnapshot = false;
@@ -309,18 +301,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
309
301
  // We should really not be modifying the config object
310
302
  config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
311
303
 
312
- // Create BlockBuilder for EpochPruneWatcher (slasher functionality)
313
- const blockBuilder = new BlockBuilder(
314
- { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
315
- worldStateSynchronizer,
316
- archiver,
317
- dateProvider,
318
- telemetry,
319
- );
320
-
321
304
  // Create FullNodeCheckpointsBuilder for validator and non-validator block proposal handling
322
305
  const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
323
306
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
307
+ worldStateSynchronizer,
324
308
  archiver,
325
309
  dateProvider,
326
310
  telemetry,
@@ -330,7 +314,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
330
314
  const watchers: Watcher[] = [];
331
315
 
332
316
  // Create validator client if required
333
- const validatorClient = createValidatorClient(config, {
317
+ const validatorClient = await createValidatorClient(config, {
334
318
  checkpointsBuilder: validatorCheckpointsBuilder,
335
319
  worldState: worldStateSynchronizer,
336
320
  p2pClient,
@@ -387,7 +371,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
387
371
  archiver,
388
372
  epochCache,
389
373
  p2pClient.getTxProvider(),
390
- blockBuilder,
374
+ validatorCheckpointsBuilder,
391
375
  config,
392
376
  );
393
377
  watchers.push(epochPruneWatcher);
@@ -452,6 +436,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
452
436
  // Create and start the sequencer client
453
437
  const checkpointsBuilder = new CheckpointsBuilder(
454
438
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration) },
439
+ worldStateSynchronizer,
455
440
  archiver,
456
441
  dateProvider,
457
442
  telemetry,
@@ -580,13 +565,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
580
565
  }
581
566
 
582
567
  /**
583
- * Get a block specified by its number.
584
- * @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').
585
570
  * @returns The requested block.
586
571
  */
587
- public async getBlock(number: BlockParameter): Promise<L2Block | undefined> {
588
- const blockNumber = number === 'latest' ? await this.getBlockNumber() : (number as BlockNumber);
589
- return await this.blockSource.getBlock(blockNumber);
572
+ public async getBlock(block: BlockParameter): Promise<L2Block | undefined> {
573
+ if (BlockHash.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);
590
581
  }
591
582
 
592
583
  /**
@@ -595,8 +586,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
595
586
  * @returns The requested block.
596
587
  */
597
588
  public async getBlockByHash(blockHash: Fr): Promise<L2Block | undefined> {
598
- const publishedBlock = await this.blockSource.getPublishedBlockByHash(blockHash);
599
- 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);
600
599
  }
601
600
 
602
601
  /**
@@ -605,8 +604,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
605
604
  * @returns The requested block.
606
605
  */
607
606
  public async getBlockByArchive(archive: Fr): Promise<L2Block | undefined> {
608
- const publishedBlock = await this.blockSource.getPublishedBlockByArchive(archive);
609
- return publishedBlock?.block;
607
+ return await this.blockSource.getL2BlockByArchive(archive);
610
608
  }
611
609
 
612
610
  /**
@@ -616,23 +614,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
616
614
  * @returns The blocks requested.
617
615
  */
618
616
  public async getBlocks(from: BlockNumber, limit: number): Promise<L2Block[]> {
619
- return (await this.blockSource.getBlocks(from, limit)) ?? [];
620
- }
621
-
622
- public async getPublishedBlocks(from: BlockNumber, limit: number): Promise<PublishedL2Block[]> {
623
- return (await this.blockSource.getPublishedBlocks(from, limit)) ?? [];
617
+ return (await this.blockSource.getBlocks(from, BlockNumber(limit))) ?? [];
624
618
  }
625
619
 
626
- public async getPublishedCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
627
- return (await this.blockSource.getPublishedCheckpoints(from, limit)) ?? [];
620
+ public async getCheckpoints(from: CheckpointNumber, limit: number): Promise<PublishedCheckpoint[]> {
621
+ return (await this.blockSource.getCheckpoints(from, limit)) ?? [];
628
622
  }
629
623
 
630
- public async getL2BlocksNew(from: BlockNumber, limit: number): Promise<L2BlockNew[]> {
631
- return (await this.blockSource.getL2BlocksNew(from, limit)) ?? [];
632
- }
633
-
634
- public async getCheckpointedBlocks(from: BlockNumber, limit: number, proven?: boolean) {
635
- return (await this.blockSource.getCheckpointedBlocks(from, limit, proven)) ?? [];
624
+ public async getCheckpointedBlocks(from: BlockNumber, limit: number) {
625
+ return (await this.blockSource.getCheckpointedBlocks(from, limit)) ?? [];
636
626
  }
637
627
 
638
628
  /**
@@ -663,6 +653,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
663
653
  return await this.blockSource.getProvenBlockNumber();
664
654
  }
665
655
 
656
+ public async getCheckpointedBlockNumber(): Promise<BlockNumber> {
657
+ return await this.blockSource.getCheckpointedL2BlockNumber();
658
+ }
659
+
666
660
  /**
667
661
  * Method to fetch the version of the package.
668
662
  * @returns The node package version
@@ -695,12 +689,45 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
695
689
  return this.contractDataSource.getContract(address);
696
690
  }
697
691
 
698
- public getPrivateLogsByTags(tags: SiloedTag[]): Promise<TxScopedL2Log[][]> {
699
- return this.logsSource.getPrivateLogsByTags(tags);
700
- }
701
-
702
- public getPublicLogsByTagsFromContract(contractAddress: AztecAddress, tags: Tag[]): Promise<TxScopedL2Log[][]> {
703
- return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags);
692
+ public async getPrivateLogsByTags(
693
+ tags: SiloedTag[],
694
+ page?: number,
695
+ referenceBlock?: BlockHash,
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?: BlockHash,
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);
704
731
  }
705
732
 
706
733
  /**
@@ -747,21 +774,26 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
747
774
  }
748
775
 
749
776
  public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
750
- let txReceipt = new TxReceipt(txHash, TxStatus.DROPPED, 'Tx dropped by P2P node.');
751
-
752
- // We first check if the tx is in pending (instead of first checking if it is mined) because if we first check
753
- // for mined and then for pending there could be a race condition where the tx is mined between the two checks
754
- // and we would incorrectly return a TxReceipt with status DROPPED
755
- if ((await this.p2pClient.getTxStatus(txHash)) === 'pending') {
756
- txReceipt = new TxReceipt(txHash, TxStatus.PENDING, '');
757
- }
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';
758
781
 
782
+ // Then get the actual tx from the archiver, which tracks every tx in a mined block.
759
783
  const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
784
+
760
785
  if (settledTxReceipt) {
761
- 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');
762
796
  }
763
-
764
- return txReceipt;
765
797
  }
766
798
 
767
799
  public getTxEffect(txHash: TxHash): Promise<IndexedTxEffect | undefined> {
@@ -826,20 +858,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
826
858
  return compactArray(await Promise.all(txHashes.map(txHash => this.getTxByHash(txHash))));
827
859
  }
828
860
 
829
- /**
830
- * Find the indexes of the given leaves in the given tree along with a block metadata pointing to the block in which
831
- * the leaves were inserted.
832
- * @param blockNumber - The block number at which to get the data or 'latest' for latest data.
833
- * @param treeId - The tree to search in.
834
- * @param leafValues - The values to search for.
835
- * @returns The indices of leaves and the block metadata of a block in which the leaves were inserted.
836
- */
837
861
  public async findLeavesIndexes(
838
- blockNumber: BlockParameter,
862
+ block: BlockParameter,
839
863
  treeId: MerkleTreeId,
840
864
  leafValues: Fr[],
841
865
  ): Promise<(DataInBlock<bigint> | undefined)[]> {
842
- const committedDb = await this.#getWorldState(blockNumber);
866
+ const committedDb = await this.#getWorldState(block);
843
867
  const maybeIndices = await committedDb.findLeafIndices(
844
868
  treeId,
845
869
  leafValues.map(x => x.toBuffer()),
@@ -891,45 +915,33 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
891
915
  }
892
916
  return {
893
917
  l2BlockNumber: BlockNumber(Number(blockNumber)),
894
- l2BlockHash: L2BlockHash.fromField(blockHash),
918
+ l2BlockHash: BlockHash.fromField(blockHash),
895
919
  data: index,
896
920
  };
897
921
  });
898
922
  }
899
923
 
900
- /**
901
- * Returns a sibling path for the given index in the nullifier tree.
902
- * @param blockNumber - The block number at which to get the data.
903
- * @param leafIndex - The index of the leaf for which the sibling path is required.
904
- * @returns The sibling path for the leaf index.
905
- */
906
924
  public async getNullifierSiblingPath(
907
- blockNumber: BlockParameter,
925
+ block: BlockParameter,
908
926
  leafIndex: bigint,
909
927
  ): Promise<SiblingPath<typeof NULLIFIER_TREE_HEIGHT>> {
910
- const committedDb = await this.#getWorldState(blockNumber);
928
+ const committedDb = await this.#getWorldState(block);
911
929
  return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
912
930
  }
913
931
 
914
- /**
915
- * Returns a sibling path for the given index in the data tree.
916
- * @param blockNumber - The block number at which to get the data.
917
- * @param leafIndex - The index of the leaf for which the sibling path is required.
918
- * @returns The sibling path for the leaf index.
919
- */
920
932
  public async getNoteHashSiblingPath(
921
- blockNumber: BlockParameter,
933
+ block: BlockParameter,
922
934
  leafIndex: bigint,
923
935
  ): Promise<SiblingPath<typeof NOTE_HASH_TREE_HEIGHT>> {
924
- const committedDb = await this.#getWorldState(blockNumber);
936
+ const committedDb = await this.#getWorldState(block);
925
937
  return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
926
938
  }
927
939
 
928
940
  public async getArchiveMembershipWitness(
929
- blockNumber: BlockParameter,
941
+ block: BlockParameter,
930
942
  archive: Fr,
931
943
  ): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
932
- const committedDb = await this.#getWorldState(blockNumber);
944
+ const committedDb = await this.#getWorldState(block);
933
945
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [archive]);
934
946
  return pathAndIndex === undefined
935
947
  ? undefined
@@ -937,10 +949,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
937
949
  }
938
950
 
939
951
  public async getNoteHashMembershipWitness(
940
- blockNumber: BlockParameter,
952
+ block: BlockParameter,
941
953
  noteHash: Fr,
942
954
  ): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
943
- const committedDb = await this.#getWorldState(blockNumber);
955
+ const committedDb = await this.#getWorldState(block);
944
956
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.NOTE_HASH_TREE>(
945
957
  MerkleTreeId.NOTE_HASH_TREE,
946
958
  [noteHash],
@@ -950,17 +962,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
950
962
  : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
951
963
  }
952
964
 
953
- /**
954
- * Returns the index and a sibling path for a leaf in the committed l1 to l2 data tree.
955
- * @param blockNumber - The block number at which to get the data.
956
- * @param l1ToL2Message - The l1ToL2Message to get the index / sibling path for.
957
- * @returns A tuple of the index and the sibling path of the L1ToL2Message (undefined if not found).
958
- */
959
965
  public async getL1ToL2MessageMembershipWitness(
960
- blockNumber: BlockParameter,
966
+ block: BlockParameter,
961
967
  l1ToL2Message: Fr,
962
968
  ): Promise<[bigint, SiblingPath<typeof L1_TO_L2_MSG_TREE_HEIGHT>] | undefined> {
963
- const db = await this.#getWorldState(blockNumber);
969
+ const db = await this.#getWorldState(block);
964
970
  const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [l1ToL2Message]);
965
971
  if (!witness) {
966
972
  return undefined;
@@ -993,12 +999,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
993
999
  * @returns The L2 to L1 messages (empty array if the epoch is not found).
994
1000
  */
995
1001
  public async getL2ToL1Messages(epoch: EpochNumber): Promise<Fr[][][][]> {
996
- // Assumes `getBlocksForEpoch` returns blocks in ascending order of block number.
997
- const blocks = await this.blockSource.getBlocksForEpoch(epoch);
1002
+ // Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
1003
+ const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
998
1004
  const blocksInCheckpoints: L2Block[][] = [];
999
1005
  let previousSlotNumber = SlotNumber.ZERO;
1000
1006
  let checkpointIndex = -1;
1001
- for (const block of blocks) {
1007
+ for (const checkpointedBlock of checkpointedBlocks) {
1008
+ const block = checkpointedBlock.block;
1002
1009
  const slotNumber = block.header.globalVariables.slotNumber;
1003
1010
  if (slotNumber !== previousSlotNumber) {
1004
1011
  checkpointIndex++;
@@ -1012,45 +1019,27 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1012
1019
  );
1013
1020
  }
1014
1021
 
1015
- /**
1016
- * Returns a sibling path for a leaf in the committed blocks tree.
1017
- * @param blockNumber - The block number at which to get the data.
1018
- * @param leafIndex - Index of the leaf in the tree.
1019
- * @returns The sibling path.
1020
- */
1021
1022
  public async getArchiveSiblingPath(
1022
- blockNumber: BlockParameter,
1023
+ block: BlockParameter,
1023
1024
  leafIndex: bigint,
1024
1025
  ): Promise<SiblingPath<typeof ARCHIVE_HEIGHT>> {
1025
- const committedDb = await this.#getWorldState(blockNumber);
1026
+ const committedDb = await this.#getWorldState(block);
1026
1027
  return committedDb.getSiblingPath(MerkleTreeId.ARCHIVE, leafIndex);
1027
1028
  }
1028
1029
 
1029
- /**
1030
- * Returns a sibling path for a leaf in the committed public data tree.
1031
- * @param blockNumber - The block number at which to get the data.
1032
- * @param leafIndex - Index of the leaf in the tree.
1033
- * @returns The sibling path.
1034
- */
1035
1030
  public async getPublicDataSiblingPath(
1036
- blockNumber: BlockParameter,
1031
+ block: BlockParameter,
1037
1032
  leafIndex: bigint,
1038
1033
  ): Promise<SiblingPath<typeof PUBLIC_DATA_TREE_HEIGHT>> {
1039
- const committedDb = await this.#getWorldState(blockNumber);
1034
+ const committedDb = await this.#getWorldState(block);
1040
1035
  return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
1041
1036
  }
1042
1037
 
1043
- /**
1044
- * Returns a nullifier membership witness for a given nullifier at a given block.
1045
- * @param blockNumber - The block number at which to get the index.
1046
- * @param nullifier - Nullifier we try to find witness for.
1047
- * @returns The nullifier membership witness (if found).
1048
- */
1049
1038
  public async getNullifierMembershipWitness(
1050
- blockNumber: BlockParameter,
1039
+ block: BlockParameter,
1051
1040
  nullifier: Fr,
1052
1041
  ): Promise<NullifierMembershipWitness | undefined> {
1053
- const db = await this.#getWorldState(blockNumber);
1042
+ const db = await this.#getWorldState(block);
1054
1043
  const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]);
1055
1044
  if (!witness) {
1056
1045
  return undefined;
@@ -1067,7 +1056,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1067
1056
 
1068
1057
  /**
1069
1058
  * Returns a low nullifier membership witness for a given nullifier at a given block.
1070
- * @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.
1071
1060
  * @param nullifier - Nullifier we try to find the low nullifier witness for.
1072
1061
  * @returns The low nullifier membership witness (if found).
1073
1062
  * @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
@@ -1080,10 +1069,10 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1080
1069
  * TODO: This is a confusing behavior and we should eventually address that.
1081
1070
  */
1082
1071
  public async getLowNullifierMembershipWitness(
1083
- blockNumber: BlockParameter,
1072
+ block: BlockParameter,
1084
1073
  nullifier: Fr,
1085
1074
  ): Promise<NullifierMembershipWitness | undefined> {
1086
- const committedDb = await this.#getWorldState(blockNumber);
1075
+ const committedDb = await this.#getWorldState(block);
1087
1076
  const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
1088
1077
  if (!findResult) {
1089
1078
  return undefined;
@@ -1098,8 +1087,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1098
1087
  return new NullifierMembershipWitness(BigInt(index), preimageData as NullifierLeafPreimage, siblingPath);
1099
1088
  }
1100
1089
 
1101
- async getPublicDataWitness(blockNumber: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
1102
- const committedDb = await this.#getWorldState(blockNumber);
1090
+ async getPublicDataWitness(block: BlockParameter, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
1091
+ const committedDb = await this.#getWorldState(block);
1103
1092
  const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
1104
1093
  if (!lowLeafResult) {
1105
1094
  return undefined;
@@ -1113,19 +1102,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1113
1102
  }
1114
1103
  }
1115
1104
 
1116
- /**
1117
- * Gets the storage value at the given contract storage slot.
1118
- *
1119
- * @remarks The storage slot here refers to the slot as it is defined in Noir not the index in the merkle tree.
1120
- * Aztec's version of `eth_getStorageAt`.
1121
- *
1122
- * @param contract - Address of the contract to query.
1123
- * @param slot - Slot to query.
1124
- * @param blockNumber - The block number at which to get the data or 'latest'.
1125
- * @returns Storage value at the given contract slot.
1126
- */
1127
- public async getPublicStorageAt(blockNumber: BlockParameter, contract: AztecAddress, slot: Fr): Promise<Fr> {
1128
- 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);
1129
1107
  const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
1130
1108
 
1131
1109
  const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
@@ -1139,24 +1117,23 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1139
1117
  return preimage.leaf.value;
1140
1118
  }
1141
1119
 
1142
- /**
1143
- * Returns the currently committed block header, or the initial header if no blocks have been produced.
1144
- * @returns The current committed block header.
1145
- */
1146
- public async getBlockHeader(blockNumber: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
1147
- return blockNumber === BlockNumber.ZERO ||
1148
- (blockNumber === 'latest' && (await this.blockSource.getBlockNumber()) === BlockNumber.ZERO)
1149
- ? this.worldStateSynchronizer.getCommitted().getInitialHeader()
1150
- : this.blockSource.getBlockHeader(blockNumber === 'latest' ? blockNumber : (blockNumber as BlockNumber));
1151
- }
1152
-
1153
- /**
1154
- * Get a block header specified by its hash.
1155
- * @param blockHash - The block hash being requested.
1156
- * @returns The requested block header.
1157
- */
1158
- public async getBlockHeaderByHash(blockHash: Fr): Promise<BlockHeader | undefined> {
1159
- return await this.blockSource.getBlockHeaderByHash(blockHash);
1120
+ public async getBlockHeader(block: BlockParameter = 'latest'): Promise<BlockHeader | undefined> {
1121
+ if (BlockHash.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
+ }
1160
1137
  }
1161
1138
 
1162
1139
  /**
@@ -1206,6 +1183,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1206
1183
  this.contractDataSource,
1207
1184
  new DateProvider(),
1208
1185
  this.telemetry,
1186
+ this.log.getBindings(),
1209
1187
  );
1210
1188
 
1211
1189
  this.log.verbose(`Simulating public calls for tx ${txHash}`, {
@@ -1259,16 +1237,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1259
1237
  // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1260
1238
  const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1261
1239
  const blockNumber = BlockNumber((await this.blockSource.getBlockNumber()) + 1);
1262
- const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, {
1263
- timestamp: nextSlotTimestamp,
1264
- blockNumber,
1265
- l1ChainId: this.l1ChainId,
1266
- rollupVersion: this.version,
1267
- setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()),
1268
- gasFees: await this.getCurrentMinFees(),
1269
- skipFeeEnforcement,
1270
- txsPermitted: !this.config.disableTransactions,
1271
- });
1240
+ const validator = createValidatorForAcceptingTxs(
1241
+ db,
1242
+ this.contractDataSource,
1243
+ verifier,
1244
+ {
1245
+ timestamp: nextSlotTimestamp,
1246
+ blockNumber,
1247
+ l1ChainId: this.l1ChainId,
1248
+ rollupVersion: this.version,
1249
+ setupAllowList: this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions()),
1250
+ gasFees: await this.getCurrentMinFees(),
1251
+ skipFeeEnforcement,
1252
+ txsPermitted: !this.config.disableTransactions,
1253
+ },
1254
+ this.log.getBindings(),
1255
+ );
1272
1256
 
1273
1257
  return await validator.validateTx(tx);
1274
1258
  }
@@ -1432,16 +1416,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1432
1416
  }
1433
1417
  }
1434
1418
 
1419
+ #getInitialHeaderHash(): Promise<BlockHash> {
1420
+ if (!this.initialHeaderHashPromise) {
1421
+ this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
1422
+ }
1423
+ return this.initialHeaderHashPromise;
1424
+ }
1425
+
1435
1426
  /**
1436
1427
  * Returns an instance of MerkleTreeOperations having first ensured the world state is fully synched
1437
- * @param blockNumber - The block number at which to get the data.
1428
+ * @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
1438
1429
  * @returns An instance of a committed MerkleTreeOperations
1439
1430
  */
1440
- async #getWorldState(blockNumber: BlockParameter) {
1441
- if (typeof blockNumber === 'number' && blockNumber < INITIAL_L2_BLOCK_NUM - 1) {
1442
- throw new Error('Invalid block number to get world state for: ' + blockNumber);
1443
- }
1444
-
1431
+ async #getWorldState(block: BlockParameter) {
1445
1432
  let blockSyncedTo: BlockNumber = BlockNumber.ZERO;
1446
1433
  try {
1447
1434
  // Attempt to sync the world state if necessary
@@ -1450,15 +1437,40 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
1450
1437
  this.log.error(`Error getting world state: ${err}`);
1451
1438
  }
1452
1439
 
1453
- // using a snapshot could be less efficient than using the committed db
1454
- if (blockNumber === 'latest' /*|| blockNumber === blockSyncedTo*/) {
1455
- this.log.debug(`Using committed db for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1440
+ if (block === 'latest') {
1441
+ this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
1456
1442
  return this.worldStateSynchronizer.getCommitted();
1457
- } else if (blockNumber <= blockSyncedTo) {
1443
+ }
1444
+
1445
+ if (BlockHash.isL2BlockHash(block)) {
1446
+ const initialBlockHash = await this.#getInitialHeaderHash();
1447
+ if (block.equals(initialBlockHash)) {
1448
+ // Block source doesn't handle initial header so we need to handle the case separately.
1449
+ return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
1450
+ }
1451
+
1452
+ const blockHashFr = Fr.fromBuffer(block.toBuffer());
1453
+ const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
1454
+ if (!header) {
1455
+ throw new Error(
1456
+ `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.`,
1457
+ );
1458
+ }
1459
+ const blockNumber = header.getBlockNumber();
1458
1460
  this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1459
- return this.worldStateSynchronizer.getSnapshot(blockNumber as BlockNumber);
1460
- } else {
1461
- throw new Error(`Block ${blockNumber} not yet synced`);
1461
+ return this.worldStateSynchronizer.getSnapshot(blockNumber);
1462
+ }
1463
+
1464
+ // Block number provided
1465
+ {
1466
+ const blockNumber = block as BlockNumber;
1467
+
1468
+ if (blockNumber > blockSyncedTo) {
1469
+ throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
1470
+ }
1471
+
1472
+ this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
1473
+ return this.worldStateSynchronizer.getSnapshot(blockNumber);
1462
1474
  }
1463
1475
  }
1464
1476
 
@@ -20,12 +20,7 @@ export async function createSentinel(
20
20
  if (!config.sentinelEnabled) {
21
21
  return undefined;
22
22
  }
23
- const kvStore = await createStore(
24
- 'sentinel',
25
- SentinelStore.SCHEMA_VERSION,
26
- config,
27
- createLogger('node:sentinel:lmdb'),
28
- );
23
+ const kvStore = await createStore('sentinel', SentinelStore.SCHEMA_VERSION, config, logger.getBindings());
29
24
  const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
30
25
  const storeHistoricProvenPerformanceLength = config.sentinelHistoricProvenPerformanceLengthInEpochs;
31
26
  const sentinelStore = new SentinelStore(kvStore, {