@aztec/aztec-node 0.0.1-commit.f5d02921e → 0.0.1-commit.f7ea82942

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.
@@ -386,6 +386,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
386
386
  import { EthAddress } from '@aztec/foundation/eth-address';
387
387
  import { BadRequestError } from '@aztec/foundation/json-rpc';
388
388
  import { createLogger } from '@aztec/foundation/log';
389
+ import { retryUntil } from '@aztec/foundation/retry';
389
390
  import { count } from '@aztec/foundation/string';
390
391
  import { DateProvider, Timer } from '@aztec/foundation/timer';
391
392
  import { MembershipWitness } from '@aztec/foundation/trees';
@@ -396,7 +397,7 @@ import { createP2PClient, createTxValidatorForAcceptingTxsOverRPC, getDefaultAll
396
397
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
397
398
  import { createProverNode } from '@aztec/prover-node';
398
399
  import { createKeyStoreForProver } from '@aztec/prover-node/config';
399
- import { GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client';
400
+ import { FeeProviderImpl, GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client';
400
401
  import { PublicProcessorFactory } from '@aztec/simulator/server';
401
402
  import { AttestationsBlockWatcher, EpochPruneWatcher, createSlasher } from '@aztec/slasher';
402
403
  import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
@@ -439,6 +440,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
439
440
  l1ChainId;
440
441
  version;
441
442
  globalVariableBuilder;
443
+ feeProvider;
442
444
  epochCache;
443
445
  packageVersion;
444
446
  peerProofVerifier;
@@ -463,7 +465,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
463
465
  // Prevent two snapshot operations to happen simultaneously
464
466
  isUploadingSnapshot;
465
467
  tracer;
466
- constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion, peerProofVerifier, rpcProofVerifier, telemetry = getTelemetryClient(), log = createLogger('node'), blobClient, validatorClient, keyStoreManager, debugLogStore = new NullDebugLogStore()){
468
+ constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, feeProvider, epochCache, packageVersion, peerProofVerifier, rpcProofVerifier, telemetry = getTelemetryClient(), log = createLogger('node'), blobClient, validatorClient, keyStoreManager, debugLogStore = new NullDebugLogStore()){
467
469
  this.config = config;
468
470
  this.p2pClient = p2pClient;
469
471
  this.blockSource = blockSource;
@@ -479,6 +481,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
479
481
  this.l1ChainId = l1ChainId;
480
482
  this.version = version;
481
483
  this.globalVariableBuilder = globalVariableBuilder;
484
+ this.feeProvider = feeProvider;
482
485
  this.epochCache = epochCache;
483
486
  this.packageVersion = packageVersion;
484
487
  this.peerProofVerifier = peerProofVerifier;
@@ -593,234 +596,252 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
593
596
  const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, {
594
597
  dateProvider
595
598
  });
596
- const archiver = await createArchiver(config, {
597
- blobClient,
598
- epochCache,
599
- telemetry,
600
- dateProvider
601
- }, {
602
- blockUntilSync: !config.skipArchiverInitialSync
603
- });
604
- // now create the merkle trees and the world state synchronizer
605
- const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.prefilledPublicData, telemetry);
606
- const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
607
- let peerProofVerifier;
608
- let rpcProofVerifier;
609
- if (useRealVerifiers) {
610
- peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
611
- const rpcVerifier = await BBCircuitVerifier.new(config);
612
- rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
613
- } else {
614
- peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
615
- rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
616
- }
617
- let debugLogStore;
618
- if (!config.realProofs) {
619
- log.warn(`Aztec node is accepting fake proofs`);
620
- debugLogStore = new InMemoryDebugLogStore();
621
- log.info('Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served');
622
- } else {
623
- debugLogStore = new NullDebugLogStore();
624
- }
625
- const proverOnly = config.enableProverNode && config.disableValidator;
626
- if (proverOnly) {
627
- log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
628
- }
629
- // create the tx pool and the p2p client, which will need the l2 block source
630
- const p2pClient = await createP2PClient(config, archiver, peerProofVerifier, worldStateSynchronizer, epochCache, packageVersion, dateProvider, telemetry, deps.p2pClientDeps);
631
- // We'll accumulate sentinel watchers here
632
- const watchers = [];
633
- // Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
634
- // Override maxTxsPerCheckpoint with the validator-specific limit if set.
635
- const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder({
636
- ...config,
637
- l1GenesisTime,
638
- slotDuration: Number(slotDuration),
639
- rollupManaLimit,
640
- maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint
641
- }, worldStateSynchronizer, archiver, dateProvider, telemetry);
642
- let validatorClient;
643
- if (!proverOnly) {
644
- // Create validator client if required
645
- validatorClient = await createValidatorClient(config, {
646
- checkpointsBuilder: validatorCheckpointsBuilder,
647
- worldState: worldStateSynchronizer,
648
- p2pClient,
649
- telemetry,
650
- dateProvider,
651
- epochCache,
652
- blockSource: archiver,
653
- l1ToL2MessageSource: archiver,
654
- keyStoreManager,
599
+ // Track started resources so we can clean up on partial failure during node creation.
600
+ const started = [];
601
+ try {
602
+ const archiver = await createArchiver(config, {
655
603
  blobClient,
656
- slashingProtectionDb: deps.slashingProtectionDb
604
+ epochCache,
605
+ telemetry,
606
+ dateProvider
607
+ }, {
608
+ blockUntilSync: !config.skipArchiverInitialSync
657
609
  });
658
- // If we have a validator client, register it as a source of offenses for the slasher,
659
- // and have it register callbacks on the p2p client *before* we start it, otherwise messages
660
- // like attestations or auths will fail.
661
- if (validatorClient) {
662
- watchers.push(validatorClient);
663
- const vc = validatorClient;
664
- const getValidatorAddresses = ()=>vc.getValidatorAddresses().map((a)=>a.toString());
665
- validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
666
- if (!options.dontStartSequencer) {
667
- await validatorClient.registerHandlers();
668
- }
610
+ started.push(archiver);
611
+ // now create the merkle trees and the world state synchronizer
612
+ const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
613
+ started.push(worldStateSynchronizer);
614
+ const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
615
+ let peerProofVerifier;
616
+ let rpcProofVerifier;
617
+ if (useRealVerifiers) {
618
+ peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
619
+ const rpcVerifier = await BBCircuitVerifier.new(config);
620
+ rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
621
+ } else {
622
+ peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
623
+ rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
669
624
  }
670
- }
671
- // If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
672
- // for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
673
- // while non-reexecution is used for validating the proposals and collecting their txs.
674
- // Checkpoint proposals rebuild blobs if the blob client can upload blobs.
675
- if (!validatorClient) {
676
- const reexecute = !!config.alwaysReexecuteBlockProposals;
677
- log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
678
- createProposalHandler(config, {
679
- checkpointsBuilder: validatorCheckpointsBuilder,
680
- worldState: worldStateSynchronizer,
681
- epochCache,
682
- blockSource: archiver,
683
- l1ToL2MessageSource: archiver,
684
- p2pClient,
685
- blobClient,
686
- dateProvider,
687
- telemetry
688
- }).register(p2pClient, reexecute, archiver);
689
- }
690
- // Start world state and wait for it to sync to the archiver.
691
- await worldStateSynchronizer.start();
692
- // Start p2p. Note that it depends on world state to be running.
693
- await p2pClient.start();
694
- let validatorsSentinel;
695
- let epochPruneWatcher;
696
- let attestationsBlockWatcher;
697
- if (!proverOnly) {
698
- validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
699
- if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
700
- watchers.push(validatorsSentinel);
625
+ started.push(peerProofVerifier, rpcProofVerifier);
626
+ let debugLogStore;
627
+ if (!config.realProofs) {
628
+ log.warn(`Aztec node is accepting fake proofs`);
629
+ debugLogStore = new InMemoryDebugLogStore();
630
+ log.info('Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served');
631
+ } else {
632
+ debugLogStore = new NullDebugLogStore();
701
633
  }
702
- if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
703
- epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), validatorCheckpointsBuilder, config);
704
- watchers.push(epochPruneWatcher);
634
+ const globalVariableBuilderConfig = {
635
+ l1Contracts: config.l1Contracts,
636
+ ethereumSlotDuration: config.ethereumSlotDuration,
637
+ rollupVersion: BigInt(config.rollupVersion),
638
+ l1GenesisTime,
639
+ slotDuration: Number(slotDuration)
640
+ };
641
+ const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
642
+ const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
643
+ const proverOnly = config.enableProverNode && config.disableValidator;
644
+ if (proverOnly) {
645
+ log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
646
+ }
647
+ // create the tx pool and the p2p client, which will need the l2 block source
648
+ const p2pClient = await createP2PClient(config, archiver, peerProofVerifier, worldStateSynchronizer, epochCache, feeProvider, packageVersion, dateProvider, telemetry, deps.p2pClientDeps);
649
+ started.push(p2pClient);
650
+ // We'll accumulate sentinel watchers here
651
+ const watchers = [];
652
+ // Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
653
+ // Override maxTxsPerCheckpoint with the validator-specific limit if set.
654
+ const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder({
655
+ ...config,
656
+ l1GenesisTime,
657
+ slotDuration: Number(slotDuration),
658
+ rollupManaLimit,
659
+ maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint
660
+ }, worldStateSynchronizer, archiver, dateProvider, telemetry);
661
+ let validatorClient;
662
+ if (!proverOnly) {
663
+ // Create validator client if required
664
+ validatorClient = await createValidatorClient(config, {
665
+ checkpointsBuilder: validatorCheckpointsBuilder,
666
+ worldState: worldStateSynchronizer,
667
+ p2pClient,
668
+ telemetry,
669
+ dateProvider,
670
+ epochCache,
671
+ blockSource: archiver,
672
+ l1ToL2MessageSource: archiver,
673
+ keyStoreManager,
674
+ blobClient,
675
+ slashingProtectionDb: deps.slashingProtectionDb
676
+ });
677
+ // If we have a validator client, register it as a source of offenses for the slasher,
678
+ // and have it register callbacks on the p2p client *before* we start it, otherwise messages
679
+ // like attestations or auths will fail.
680
+ if (validatorClient) {
681
+ watchers.push(validatorClient);
682
+ const vc = validatorClient;
683
+ const getValidatorAddresses = ()=>vc.getValidatorAddresses().map((a)=>a.toString());
684
+ validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
685
+ if (!options.dontStartSequencer) {
686
+ await validatorClient.registerHandlers();
687
+ }
688
+ }
705
689
  }
706
- // We assume we want to slash for invalid attestations unless all max penalties are set to 0
707
- if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
708
- attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
709
- watchers.push(attestationsBlockWatcher);
690
+ // If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
691
+ // for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
692
+ // while non-reexecution is used for validating the proposals and collecting their txs.
693
+ // Checkpoint proposals rebuild blobs if the blob client can upload blobs.
694
+ if (!validatorClient) {
695
+ const reexecute = !!config.alwaysReexecuteBlockProposals;
696
+ log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
697
+ createProposalHandler(config, {
698
+ checkpointsBuilder: validatorCheckpointsBuilder,
699
+ worldState: worldStateSynchronizer,
700
+ epochCache,
701
+ blockSource: archiver,
702
+ l1ToL2MessageSource: archiver,
703
+ p2pClient,
704
+ blobClient,
705
+ dateProvider,
706
+ telemetry
707
+ }).register(p2pClient, reexecute, archiver);
710
708
  }
711
- }
712
- // Start p2p-related services once the archiver has completed sync
713
- void archiver.waitForInitialSync().then(async ()=>{
709
+ // Start world state and wait for it to sync to the archiver.
710
+ await worldStateSynchronizer.start();
711
+ // Start p2p. Note that it depends on world state to be running.
714
712
  await p2pClient.start();
715
- await validatorsSentinel?.start();
716
- await epochPruneWatcher?.start();
717
- await attestationsBlockWatcher?.start();
718
- log.info(`All p2p services started`);
719
- }).catch((err)=>log.error('Failed to start p2p services after archiver sync', err));
720
- const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, {
721
- l1Contracts: config.l1Contracts,
722
- ethereumSlotDuration: config.ethereumSlotDuration,
723
- rollupVersion: BigInt(config.rollupVersion),
724
- l1GenesisTime,
725
- slotDuration: Number(slotDuration)
726
- });
727
- // Validator enabled, create/start relevant service
728
- let sequencer;
729
- let slasherClient;
730
- if (!config.disableValidator && validatorClient) {
731
- // We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
732
- // as they are executed when the node is selected as proposer.
733
- const validatorAddresses = keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses() : [];
734
- slasherClient = await createSlasher(config, config.l1Contracts, getPublicClient(config), watchers, dateProvider, epochCache, validatorAddresses, undefined);
735
- await slasherClient.start();
736
- const l1TxUtils = config.sequencerPublisherForwarderAddress ? await createForwarderL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), config.sequencerPublisherForwarderAddress, {
737
- ...config,
738
- scope: 'sequencer'
739
- }, {
740
- telemetry,
741
- logger: log.createChild('l1-tx-utils'),
742
- dateProvider,
743
- kzg: Blob.getViemKzgInstance()
744
- }) : await createL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), {
745
- ...config,
746
- scope: 'sequencer'
747
- }, {
748
- telemetry,
749
- logger: log.createChild('l1-tx-utils'),
750
- dateProvider,
751
- kzg: Blob.getViemKzgInstance()
752
- });
753
- // Create a funder L1TxUtils from the keystore funding account (if configured)
754
- const fundingSigner = keyStoreManager?.createFundingSigner();
755
- let funderL1TxUtils;
756
- if (fundingSigner) {
757
- const [funder] = await createL1TxUtilsFromSigners(publicClient, [
758
- fundingSigner
759
- ], {
713
+ let validatorsSentinel;
714
+ let epochPruneWatcher;
715
+ let attestationsBlockWatcher;
716
+ if (!proverOnly) {
717
+ validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
718
+ if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
719
+ watchers.push(validatorsSentinel);
720
+ }
721
+ if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
722
+ epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), validatorCheckpointsBuilder, config);
723
+ watchers.push(epochPruneWatcher);
724
+ }
725
+ // We assume we want to slash for invalid attestations unless all max penalties are set to 0
726
+ if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
727
+ attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
728
+ watchers.push(attestationsBlockWatcher);
729
+ }
730
+ }
731
+ // Start p2p-related services once the archiver has completed sync
732
+ void archiver.waitForInitialSync().then(async ()=>{
733
+ await validatorsSentinel?.start();
734
+ await epochPruneWatcher?.start();
735
+ await attestationsBlockWatcher?.start();
736
+ log.info(`All p2p services started`);
737
+ }).catch((err)=>log.error('Failed to start p2p services after archiver sync', err));
738
+ // Validator enabled, create/start relevant service
739
+ let sequencer;
740
+ let slasherClient;
741
+ if (!config.disableValidator && validatorClient) {
742
+ // We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
743
+ // as they are executed when the node is selected as proposer.
744
+ const validatorAddresses = keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses() : [];
745
+ slasherClient = await createSlasher(config, config.l1Contracts, getPublicClient(config), watchers, dateProvider, epochCache, validatorAddresses, undefined);
746
+ await slasherClient.start();
747
+ started.push(slasherClient);
748
+ const l1TxUtils = config.sequencerPublisherForwarderAddress ? await createForwarderL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), config.sequencerPublisherForwarderAddress, {
760
749
  ...config,
761
750
  scope: 'sequencer'
762
751
  }, {
763
752
  telemetry,
764
- logger: log.createChild('l1-tx-utils:funder'),
765
- dateProvider
753
+ logger: log.createChild('l1-tx-utils'),
754
+ dateProvider,
755
+ kzg: Blob.getViemKzgInstance()
756
+ }) : await createL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), {
757
+ ...config,
758
+ scope: 'sequencer'
759
+ }, {
760
+ telemetry,
761
+ logger: log.createChild('l1-tx-utils'),
762
+ dateProvider,
763
+ kzg: Blob.getViemKzgInstance()
764
+ });
765
+ // Create a funder L1TxUtils from the keystore funding account (if configured)
766
+ const fundingSigner = keyStoreManager?.createFundingSigner();
767
+ let funderL1TxUtils;
768
+ if (fundingSigner) {
769
+ const [funder] = await createL1TxUtilsFromSigners(publicClient, [
770
+ fundingSigner
771
+ ], {
772
+ ...config,
773
+ scope: 'sequencer'
774
+ }, {
775
+ telemetry,
776
+ logger: log.createChild('l1-tx-utils:funder'),
777
+ dateProvider
778
+ });
779
+ funderL1TxUtils = funder;
780
+ }
781
+ // Create and start the sequencer client
782
+ const checkpointsBuilder = new CheckpointsBuilder({
783
+ ...config,
784
+ l1GenesisTime,
785
+ slotDuration: Number(slotDuration),
786
+ rollupManaLimit
787
+ }, worldStateSynchronizer, archiver, dateProvider, telemetry, debugLogStore);
788
+ sequencer = await SequencerClient.new(config, {
789
+ ...deps,
790
+ epochCache,
791
+ l1TxUtils,
792
+ funderL1TxUtils,
793
+ validatorClient,
794
+ p2pClient,
795
+ worldStateSynchronizer,
796
+ slasherClient,
797
+ checkpointsBuilder,
798
+ l2BlockSource: archiver,
799
+ l1ToL2MessageSource: archiver,
800
+ telemetry,
801
+ dateProvider,
802
+ blobClient,
803
+ nodeKeyStore: keyStoreManager,
804
+ globalVariableBuilder
766
805
  });
767
- funderL1TxUtils = funder;
768
806
  }
769
- // Create and start the sequencer client
770
- const checkpointsBuilder = new CheckpointsBuilder({
771
- ...config,
772
- l1GenesisTime,
773
- slotDuration: Number(slotDuration),
774
- rollupManaLimit
775
- }, worldStateSynchronizer, archiver, dateProvider, telemetry, debugLogStore);
776
- sequencer = await SequencerClient.new(config, {
777
- ...deps,
778
- epochCache,
779
- l1TxUtils,
780
- funderL1TxUtils,
781
- validatorClient,
782
- p2pClient,
783
- worldStateSynchronizer,
784
- slasherClient,
785
- checkpointsBuilder,
786
- l2BlockSource: archiver,
787
- l1ToL2MessageSource: archiver,
788
- telemetry,
789
- dateProvider,
790
- blobClient,
791
- nodeKeyStore: keyStoreManager,
792
- globalVariableBuilder
793
- });
794
- }
795
- if (!options.dontStartSequencer && sequencer) {
796
- await sequencer.start();
797
- log.verbose(`Sequencer started`);
798
- } else if (sequencer) {
799
- log.warn(`Sequencer created but not started`);
800
- }
801
- // Create prover node subsystem if enabled
802
- let proverNode;
803
- if (config.enableProverNode) {
804
- proverNode = await createProverNode(config, {
805
- ...deps.proverNodeDeps,
806
- telemetry,
807
- dateProvider,
808
- archiver,
809
- worldStateSynchronizer,
810
- p2pClient,
811
- epochCache,
812
- blobClient,
813
- keyStoreManager
814
- });
815
- if (!options.dontStartProverNode) {
816
- await proverNode.start();
817
- log.info(`Prover node subsystem started`);
818
- } else {
819
- log.info(`Prover node subsystem created but not started`);
807
+ if (!options.dontStartSequencer && sequencer) {
808
+ await sequencer.start();
809
+ started.push(sequencer);
810
+ log.verbose(`Sequencer started`);
811
+ } else if (sequencer) {
812
+ log.warn(`Sequencer created but not started`);
813
+ }
814
+ // Create prover node subsystem if enabled
815
+ let proverNode;
816
+ if (config.enableProverNode) {
817
+ proverNode = await createProverNode(config, {
818
+ ...deps.proverNodeDeps,
819
+ telemetry,
820
+ dateProvider,
821
+ archiver,
822
+ worldStateSynchronizer,
823
+ p2pClient,
824
+ epochCache,
825
+ blobClient,
826
+ keyStoreManager
827
+ });
828
+ if (!options.dontStartProverNode) {
829
+ await proverNode.start();
830
+ started.push(proverNode);
831
+ log.info(`Prover node subsystem started`);
832
+ } else {
833
+ log.info(`Prover node subsystem created but not started`);
834
+ }
835
+ }
836
+ const node = new AztecNodeService(config, p2pClient, archiver, archiver, archiver, archiver, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, ethereumChain.chainInfo.id, config.rollupVersion, globalVariableBuilder, feeProvider, epochCache, packageVersion, peerProofVerifier, rpcProofVerifier, telemetry, log, blobClient, validatorClient, keyStoreManager, debugLogStore);
837
+ return node;
838
+ } catch (err) {
839
+ log.error('Failed during node creation, stopping started resources', err);
840
+ for (const resource of started.reverse()){
841
+ await tryStop(resource);
820
842
  }
843
+ throw err;
821
844
  }
822
- const node = new AztecNodeService(config, p2pClient, archiver, archiver, archiver, archiver, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, ethereumChain.chainInfo.id, config.rollupVersion, globalVariableBuilder, epochCache, packageVersion, peerProofVerifier, rpcProofVerifier, telemetry, log, blobClient, validatorClient, keyStoreManager, debugLogStore);
823
- return node;
824
845
  }
825
846
  /**
826
847
  * Returns the sequencer client instance.
@@ -934,11 +955,11 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
934
955
  getCheckpointsDataForEpoch(epochNumber) {
935
956
  return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
936
957
  }
937
- /**
938
- * Method to fetch the current min L2 fees.
939
- * @returns The current min L2 fees.
940
- */ async getCurrentMinFees() {
941
- return await this.globalVariableBuilder.getCurrentMinFees();
958
+ async getCurrentMinFees() {
959
+ return await this.feeProvider.getCurrentMinFees();
960
+ }
961
+ /** Returns predicted min fees for the current slot and next N slots. */ async getPredictedMinFees(manaUsage) {
962
+ return await this.feeProvider.getPredictedMinFees(manaUsage);
942
963
  }
943
964
  async getMaxPriorityFees() {
944
965
  for await (const tx of this.p2pClient.iteratePendingTxs()){
@@ -1177,13 +1198,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1177
1198
  if (blockNumber === undefined) {
1178
1199
  throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
1179
1200
  }
1180
- const blockHash = blockNumberToHash.get(blockNumber);
1181
- if (blockHash === undefined) {
1201
+ const l2BlockHash = blockNumberToHash.get(blockNumber);
1202
+ if (l2BlockHash === undefined) {
1182
1203
  throw new Error(`Block hash not found for block number ${blockNumber}`);
1183
1204
  }
1184
1205
  return {
1185
1206
  l2BlockNumber: blockNumber,
1186
- l2BlockHash: new BlockHash(blockHash),
1207
+ l2BlockHash,
1187
1208
  data: index
1188
1209
  };
1189
1210
  });
@@ -1497,7 +1518,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1497
1518
  });
1498
1519
  return Promise.resolve();
1499
1520
  }
1500
- async rollbackTo(targetBlock, force) {
1521
+ async rollbackTo(targetBlock, force, resumeSync = true) {
1501
1522
  const archiver = this.blockSource;
1502
1523
  if (!('rollbackTo' in archiver)) {
1503
1524
  throw new Error('Archiver implementation does not support rollbacks.');
@@ -1525,9 +1546,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1525
1546
  this.log.error(`Error during rollback`, err);
1526
1547
  throw err;
1527
1548
  } finally{
1528
- this.log.info(`Resuming world state and archiver sync.`);
1529
- this.worldStateSynchronizer.resumeSync();
1530
- archiver.resume();
1549
+ if (resumeSync) {
1550
+ this.log.info(`Resuming world state and archiver sync.`);
1551
+ this.worldStateSynchronizer.resumeSync();
1552
+ archiver.resume();
1553
+ } else {
1554
+ this.log.info(`Sync left paused after rollback (resumeSync=false).`);
1555
+ }
1531
1556
  }
1532
1557
  }
1533
1558
  async pauseSync() {
@@ -1541,18 +1566,12 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1541
1566
  this.blockSource.resume();
1542
1567
  return Promise.resolve();
1543
1568
  }
1544
- getSlashPayloads() {
1545
- if (!this.slasherClient) {
1546
- throw new Error(`Slasher client not enabled`);
1547
- }
1548
- return this.slasherClient.getSlashPayloads();
1549
- }
1550
1569
  getSlashOffenses(round) {
1551
1570
  if (!this.slasherClient) {
1552
1571
  throw new Error(`Slasher client not enabled`);
1553
1572
  }
1554
1573
  if (round === 'all') {
1555
- return this.slasherClient.getPendingOffenses();
1574
+ return this.slasherClient.getOffenses();
1556
1575
  } else {
1557
1576
  return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
1558
1577
  }
@@ -1624,6 +1643,33 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1624
1643
  this.keyStoreManager = newManager;
1625
1644
  this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
1626
1645
  }
1646
+ async mineBlock() {
1647
+ if (!this.sequencer) {
1648
+ throw new BadRequestError('Cannot mine block: no sequencer is running');
1649
+ }
1650
+ const currentBlockNumber = await this.getBlockNumber();
1651
+ // Use slot duration + 50% buffer as the timeout so this works on running networks too
1652
+ const { slotDuration } = await this.blockSource.getL1Constants();
1653
+ const timeoutSeconds = Math.ceil(slotDuration * 1.5);
1654
+ // Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
1655
+ const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
1656
+ this.sequencer.updateConfig({
1657
+ minTxsPerBlock: 0
1658
+ });
1659
+ try {
1660
+ // Trigger the sequencer to produce a block immediately
1661
+ void this.sequencer.trigger();
1662
+ // Wait for the new L2 block to appear
1663
+ await retryUntil(async ()=>{
1664
+ const newBlockNumber = await this.getBlockNumber();
1665
+ return newBlockNumber > currentBlockNumber ? true : undefined;
1666
+ }, 'mineBlock', timeoutSeconds, 0.1);
1667
+ } finally{
1668
+ this.sequencer.updateConfig({
1669
+ minTxsPerBlock: originalMinTxsPerBlock
1670
+ });
1671
+ }
1672
+ }
1627
1673
  #getInitialHeaderHash() {
1628
1674
  if (!this.initialHeaderHashPromise) {
1629
1675
  this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
@@ -1651,14 +1697,17 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1651
1697
  if (BlockHash.isBlockHash(block)) {
1652
1698
  const initialBlockHash = await this.#getInitialHeaderHash();
1653
1699
  if (block.equals(initialBlockHash)) {
1654
- // Block source doesn't handle initial header so we need to handle the case separately.
1655
- return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
1656
- }
1657
- const header = await this.blockSource.getBlockHeaderByHash(block);
1658
- if (!header) {
1659
- throw new Error(`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.`);
1700
+ // Block 0 is a first-class historical block: its state lives in the trees' persisted
1701
+ // block-0 payload. Resolving the genesis hash to block number 0 lets the snapshot path
1702
+ // pin reads to genesis state even after the node has advanced past it.
1703
+ blockNumber = BlockNumber.ZERO;
1704
+ } else {
1705
+ const header = await this.blockSource.getBlockHeaderByHash(block);
1706
+ if (!header) {
1707
+ throw new Error(`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.`);
1708
+ }
1709
+ blockNumber = header.getBlockNumber();
1660
1710
  }
1661
- blockNumber = header.getBlockNumber();
1662
1711
  } else {
1663
1712
  blockNumber = block;
1664
1713
  }
@@ -1671,8 +1720,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
1671
1720
  // Double-check world-state synced to the same block hash as was requested
1672
1721
  if (BlockHash.isBlockHash(block)) {
1673
1722
  const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
1674
- if (!blockHash || !new BlockHash(blockHash).equals(block)) {
1675
- throw new Error(`Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`);
1723
+ if (!blockHash || !block.equals(blockHash)) {
1724
+ const initialBlockHash = await this.#getInitialHeaderHash();
1725
+ throw new Error(`Block hash ${block.toString()} not found in world state at block number ${blockNumber} (world state has ${blockHash?.toString() ?? 'no hash'} at that index, genesis header hash is ${initialBlockHash.toString()}). If the node API has been queried with anchor block hash possibly a reorg has occurred.`);
1676
1726
  }
1677
1727
  }
1678
1728
  return snapshot;