@aztec/aztec-node 5.0.0-nightly.20260421 → 5.0.0-nightly.20260423

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.
@@ -313,326 +313,344 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
313
313
 
314
314
  const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider });
315
315
 
316
- const archiver = await createArchiver(
317
- config,
318
- { blobClient, epochCache, telemetry, dateProvider },
319
- { blockUntilSync: !config.skipArchiverInitialSync },
320
- );
321
-
322
- // now create the merkle trees and the world state synchronizer
323
- const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
324
- const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
325
- let peerProofVerifier: ClientProtocolCircuitVerifier;
326
- let rpcProofVerifier: ClientProtocolCircuitVerifier;
327
- if (useRealVerifiers) {
328
- peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
329
- const rpcVerifier = await BBCircuitVerifier.new(config);
330
- rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
331
- } else {
332
- peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
333
- rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
334
- }
335
-
336
- let debugLogStore: DebugLogStore;
337
- if (!config.realProofs) {
338
- log.warn(`Aztec node is accepting fake proofs`);
339
-
340
- debugLogStore = new InMemoryDebugLogStore();
341
- log.info(
342
- 'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
316
+ // Track started resources so we can clean up on partial failure during node creation.
317
+ const started: { stop?(): Promise<void> | void }[] = [];
318
+ try {
319
+ const archiver = await createArchiver(
320
+ config,
321
+ { blobClient, epochCache, telemetry, dateProvider },
322
+ { blockUntilSync: !config.skipArchiverInitialSync },
343
323
  );
344
- } else {
345
- debugLogStore = new NullDebugLogStore();
346
- }
347
-
348
- const proverOnly = config.enableProverNode && config.disableValidator;
349
- if (proverOnly) {
350
- log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
351
- }
324
+ started.push(archiver);
325
+
326
+ // now create the merkle trees and the world state synchronizer
327
+ const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
328
+ started.push(worldStateSynchronizer);
329
+ const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
330
+ let peerProofVerifier: ClientProtocolCircuitVerifier;
331
+ let rpcProofVerifier: ClientProtocolCircuitVerifier;
332
+ if (useRealVerifiers) {
333
+ peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
334
+ const rpcVerifier = await BBCircuitVerifier.new(config);
335
+ rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
336
+ } else {
337
+ peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
338
+ rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
339
+ }
340
+ started.push(peerProofVerifier, rpcProofVerifier);
352
341
 
353
- // create the tx pool and the p2p client, which will need the l2 block source
354
- const p2pClient = await createP2PClient(
355
- config,
356
- archiver,
357
- peerProofVerifier,
358
- worldStateSynchronizer,
359
- epochCache,
360
- packageVersion,
361
- dateProvider,
362
- telemetry,
363
- deps.p2pClientDeps,
364
- );
342
+ let debugLogStore: DebugLogStore;
343
+ if (!config.realProofs) {
344
+ log.warn(`Aztec node is accepting fake proofs`);
365
345
 
366
- // We'll accumulate sentinel watchers here
367
- const watchers: Watcher[] = [];
346
+ debugLogStore = new InMemoryDebugLogStore();
347
+ log.info(
348
+ 'Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served',
349
+ );
350
+ } else {
351
+ debugLogStore = new NullDebugLogStore();
352
+ }
368
353
 
369
- // Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
370
- // Override maxTxsPerCheckpoint with the validator-specific limit if set.
371
- const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
372
- {
373
- ...config,
354
+ const globalVariableBuilderConfig = {
355
+ l1Contracts: config.l1Contracts,
356
+ ethereumSlotDuration: config.ethereumSlotDuration,
357
+ rollupVersion: BigInt(config.rollupVersion),
374
358
  l1GenesisTime,
375
359
  slotDuration: Number(slotDuration),
376
- rollupManaLimit,
377
- maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
378
- },
379
- worldStateSynchronizer,
380
- archiver,
381
- dateProvider,
382
- telemetry,
383
- );
384
-
385
- let validatorClient: ValidatorClient | undefined;
386
-
387
- if (!proverOnly) {
388
- // Create validator client if required
389
- validatorClient = await createValidatorClient(config, {
390
- checkpointsBuilder: validatorCheckpointsBuilder,
391
- worldState: worldStateSynchronizer,
392
- p2pClient,
393
- telemetry,
394
- dateProvider,
395
- epochCache,
396
- blockSource: archiver,
397
- l1ToL2MessageSource: archiver,
398
- keyStoreManager,
399
- blobClient,
400
- slashingProtectionDb: deps.slashingProtectionDb,
401
- });
402
-
403
- // If we have a validator client, register it as a source of offenses for the slasher,
404
- // and have it register callbacks on the p2p client *before* we start it, otherwise messages
405
- // like attestations or auths will fail.
406
- if (validatorClient) {
407
- watchers.push(validatorClient);
360
+ };
408
361
 
409
- const vc = validatorClient;
410
- const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
411
- validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
362
+ const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
363
+ const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
412
364
 
413
- if (!options.dontStartSequencer) {
414
- await validatorClient.registerHandlers();
415
- }
365
+ const proverOnly = config.enableProverNode && config.disableValidator;
366
+ if (proverOnly) {
367
+ log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
416
368
  }
417
- }
418
369
 
419
- // If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
420
- // for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
421
- // while non-reexecution is used for validating the proposals and collecting their txs.
422
- // Checkpoint proposals rebuild blobs if the blob client can upload blobs.
423
- if (!validatorClient) {
424
- const reexecute = !!config.alwaysReexecuteBlockProposals;
425
- log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
426
- createProposalHandler(config, {
427
- checkpointsBuilder: validatorCheckpointsBuilder,
428
- worldState: worldStateSynchronizer,
370
+ // create the tx pool and the p2p client, which will need the l2 block source
371
+ const p2pClient = await createP2PClient(
372
+ config,
373
+ archiver,
374
+ peerProofVerifier,
375
+ worldStateSynchronizer,
429
376
  epochCache,
430
- blockSource: archiver,
431
- l1ToL2MessageSource: archiver,
432
- p2pClient,
433
- blobClient,
377
+ feeProvider,
378
+ packageVersion,
434
379
  dateProvider,
435
380
  telemetry,
436
- }).register(p2pClient, reexecute, archiver);
437
- }
438
-
439
- // Start world state and wait for it to sync to the archiver.
440
- await worldStateSynchronizer.start();
441
-
442
- // Start p2p. Note that it depends on world state to be running.
443
- await p2pClient.start();
381
+ deps.p2pClientDeps,
382
+ );
383
+ started.push(p2pClient);
384
+
385
+ // We'll accumulate sentinel watchers here
386
+ const watchers: Watcher[] = [];
387
+
388
+ // Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
389
+ // Override maxTxsPerCheckpoint with the validator-specific limit if set.
390
+ const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder(
391
+ {
392
+ ...config,
393
+ l1GenesisTime,
394
+ slotDuration: Number(slotDuration),
395
+ rollupManaLimit,
396
+ maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint,
397
+ },
398
+ worldStateSynchronizer,
399
+ archiver,
400
+ dateProvider,
401
+ telemetry,
402
+ );
444
403
 
445
- let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
446
- let epochPruneWatcher: EpochPruneWatcher | undefined;
447
- let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
404
+ let validatorClient: ValidatorClient | undefined;
448
405
 
449
- if (!proverOnly) {
450
- validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
451
- if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
452
- watchers.push(validatorsSentinel);
406
+ if (!proverOnly) {
407
+ // Create validator client if required
408
+ validatorClient = await createValidatorClient(config, {
409
+ checkpointsBuilder: validatorCheckpointsBuilder,
410
+ worldState: worldStateSynchronizer,
411
+ p2pClient,
412
+ telemetry,
413
+ dateProvider,
414
+ epochCache,
415
+ blockSource: archiver,
416
+ l1ToL2MessageSource: archiver,
417
+ keyStoreManager,
418
+ blobClient,
419
+ slashingProtectionDb: deps.slashingProtectionDb,
420
+ });
421
+
422
+ // If we have a validator client, register it as a source of offenses for the slasher,
423
+ // and have it register callbacks on the p2p client *before* we start it, otherwise messages
424
+ // like attestations or auths will fail.
425
+ if (validatorClient) {
426
+ watchers.push(validatorClient);
427
+
428
+ const vc = validatorClient;
429
+ const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
430
+ validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
431
+
432
+ if (!options.dontStartSequencer) {
433
+ await validatorClient.registerHandlers();
434
+ }
435
+ }
453
436
  }
454
437
 
455
- if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
456
- epochPruneWatcher = new EpochPruneWatcher(
457
- archiver,
458
- archiver,
438
+ // If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
439
+ // for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
440
+ // while non-reexecution is used for validating the proposals and collecting their txs.
441
+ // Checkpoint proposals rebuild blobs if the blob client can upload blobs.
442
+ if (!validatorClient) {
443
+ const reexecute = !!config.alwaysReexecuteBlockProposals;
444
+ log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
445
+ createProposalHandler(config, {
446
+ checkpointsBuilder: validatorCheckpointsBuilder,
447
+ worldState: worldStateSynchronizer,
459
448
  epochCache,
460
- p2pClient.getTxProvider(),
461
- validatorCheckpointsBuilder,
462
- config,
463
- );
464
- watchers.push(epochPruneWatcher);
449
+ blockSource: archiver,
450
+ l1ToL2MessageSource: archiver,
451
+ p2pClient,
452
+ blobClient,
453
+ dateProvider,
454
+ telemetry,
455
+ }).register(p2pClient, reexecute, archiver);
465
456
  }
466
457
 
467
- // We assume we want to slash for invalid attestations unless all max penalties are set to 0
468
- if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
469
- attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
470
- watchers.push(attestationsBlockWatcher);
471
- }
472
- }
458
+ // Start world state and wait for it to sync to the archiver.
459
+ await worldStateSynchronizer.start();
473
460
 
474
- // Start p2p-related services once the archiver has completed sync
475
- void archiver
476
- .waitForInitialSync()
477
- .then(async () => {
478
- await validatorsSentinel?.start();
479
- await epochPruneWatcher?.start();
480
- await attestationsBlockWatcher?.start();
481
- log.info(`All p2p services started`);
482
- })
483
- .catch(err => log.error('Failed to start p2p services after archiver sync', err));
484
-
485
- const globalVariableBuilderConfig = {
486
- l1Contracts: config.l1Contracts,
487
- ethereumSlotDuration: config.ethereumSlotDuration,
488
- rollupVersion: BigInt(config.rollupVersion),
489
- l1GenesisTime,
490
- slotDuration: Number(slotDuration),
491
- };
461
+ // Start p2p. Note that it depends on world state to be running.
462
+ await p2pClient.start();
492
463
 
493
- const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
494
- const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
464
+ let validatorsSentinel: Awaited<ReturnType<typeof createSentinel>> | undefined;
465
+ let epochPruneWatcher: EpochPruneWatcher | undefined;
466
+ let attestationsBlockWatcher: AttestationsBlockWatcher | undefined;
495
467
 
496
- // Validator enabled, create/start relevant service
497
- let sequencer: SequencerClient | undefined;
498
- let slasherClient: SlasherClientInterface | undefined;
499
- if (!config.disableValidator && validatorClient) {
500
- // We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
501
- // as they are executed when the node is selected as proposer.
502
- const validatorAddresses = keyStoreManager
503
- ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
504
- : [];
468
+ if (!proverOnly) {
469
+ validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
470
+ if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
471
+ watchers.push(validatorsSentinel);
472
+ }
505
473
 
506
- slasherClient = await createSlasher(
507
- config,
508
- config.l1Contracts,
509
- getPublicClient(config),
510
- watchers,
511
- dateProvider,
512
- epochCache,
513
- validatorAddresses,
514
- undefined, // logger
515
- );
516
- await slasherClient.start();
474
+ if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
475
+ epochPruneWatcher = new EpochPruneWatcher(
476
+ archiver,
477
+ archiver,
478
+ epochCache,
479
+ p2pClient.getTxProvider(),
480
+ validatorCheckpointsBuilder,
481
+ config,
482
+ );
483
+ watchers.push(epochPruneWatcher);
484
+ }
517
485
 
518
- const l1TxUtils = config.sequencerPublisherForwarderAddress
519
- ? await createForwarderL1TxUtilsFromSigners(
520
- publicClient,
521
- keyStoreManager!.createAllValidatorPublisherSigners(),
522
- config.sequencerPublisherForwarderAddress,
523
- { ...config, scope: 'sequencer' },
524
- { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
525
- )
526
- : await createL1TxUtilsFromSigners(
486
+ // We assume we want to slash for invalid attestations unless all max penalties are set to 0
487
+ if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
488
+ attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
489
+ watchers.push(attestationsBlockWatcher);
490
+ }
491
+ }
492
+
493
+ // Start p2p-related services once the archiver has completed sync
494
+ void archiver
495
+ .waitForInitialSync()
496
+ .then(async () => {
497
+ await validatorsSentinel?.start();
498
+ await epochPruneWatcher?.start();
499
+ await attestationsBlockWatcher?.start();
500
+ log.info(`All p2p services started`);
501
+ })
502
+ .catch(err => log.error('Failed to start p2p services after archiver sync', err));
503
+
504
+ // Validator enabled, create/start relevant service
505
+ let sequencer: SequencerClient | undefined;
506
+ let slasherClient: SlasherClientInterface | undefined;
507
+ if (!config.disableValidator && validatorClient) {
508
+ // We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
509
+ // as they are executed when the node is selected as proposer.
510
+ const validatorAddresses = keyStoreManager
511
+ ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses()
512
+ : [];
513
+
514
+ slasherClient = await createSlasher(
515
+ config,
516
+ config.l1Contracts,
517
+ getPublicClient(config),
518
+ watchers,
519
+ dateProvider,
520
+ epochCache,
521
+ validatorAddresses,
522
+ undefined, // logger
523
+ );
524
+ await slasherClient.start();
525
+ started.push(slasherClient);
526
+
527
+ const l1TxUtils = config.sequencerPublisherForwarderAddress
528
+ ? await createForwarderL1TxUtilsFromSigners(
529
+ publicClient,
530
+ keyStoreManager!.createAllValidatorPublisherSigners(),
531
+ config.sequencerPublisherForwarderAddress,
532
+ { ...config, scope: 'sequencer' },
533
+ { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
534
+ )
535
+ : await createL1TxUtilsFromSigners(
536
+ publicClient,
537
+ keyStoreManager!.createAllValidatorPublisherSigners(),
538
+ { ...config, scope: 'sequencer' },
539
+ { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
540
+ );
541
+
542
+ // Create a funder L1TxUtils from the keystore funding account (if configured)
543
+ const fundingSigner = keyStoreManager?.createFundingSigner();
544
+ let funderL1TxUtils: L1TxUtils | undefined;
545
+ if (fundingSigner) {
546
+ const [funder] = await createL1TxUtilsFromSigners(
527
547
  publicClient,
528
- keyStoreManager!.createAllValidatorPublisherSigners(),
548
+ [fundingSigner],
529
549
  { ...config, scope: 'sequencer' },
530
- { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
550
+ { telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
531
551
  );
552
+ funderL1TxUtils = funder;
553
+ }
532
554
 
533
- // Create a funder L1TxUtils from the keystore funding account (if configured)
534
- const fundingSigner = keyStoreManager?.createFundingSigner();
535
- let funderL1TxUtils: L1TxUtils | undefined;
536
- if (fundingSigner) {
537
- const [funder] = await createL1TxUtilsFromSigners(
538
- publicClient,
539
- [fundingSigner],
540
- { ...config, scope: 'sequencer' },
541
- { telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
555
+ // Create and start the sequencer client
556
+ const checkpointsBuilder = new CheckpointsBuilder(
557
+ { ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
558
+ worldStateSynchronizer,
559
+ archiver,
560
+ dateProvider,
561
+ telemetry,
562
+ debugLogStore,
542
563
  );
543
- funderL1TxUtils = funder;
564
+
565
+ sequencer = await SequencerClient.new(config, {
566
+ ...deps,
567
+ epochCache,
568
+ l1TxUtils,
569
+ funderL1TxUtils,
570
+ validatorClient,
571
+ p2pClient,
572
+ worldStateSynchronizer,
573
+ slasherClient,
574
+ checkpointsBuilder,
575
+ l2BlockSource: archiver,
576
+ l1ToL2MessageSource: archiver,
577
+ telemetry,
578
+ dateProvider,
579
+ blobClient,
580
+ nodeKeyStore: keyStoreManager!,
581
+ globalVariableBuilder,
582
+ });
544
583
  }
545
584
 
546
- // Create and start the sequencer client
547
- const checkpointsBuilder = new CheckpointsBuilder(
548
- { ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
549
- worldStateSynchronizer,
550
- archiver,
551
- dateProvider,
552
- telemetry,
553
- debugLogStore,
554
- );
585
+ if (!options.dontStartSequencer && sequencer) {
586
+ await sequencer.start();
587
+ started.push(sequencer);
588
+ log.verbose(`Sequencer started`);
589
+ } else if (sequencer) {
590
+ log.warn(`Sequencer created but not started`);
591
+ }
555
592
 
556
- sequencer = await SequencerClient.new(config, {
557
- ...deps,
558
- epochCache,
559
- l1TxUtils,
560
- funderL1TxUtils,
561
- validatorClient,
593
+ // Create prover node subsystem if enabled
594
+ let proverNode: ProverNode | undefined;
595
+ if (config.enableProverNode) {
596
+ proverNode = await createProverNode(config, {
597
+ ...deps.proverNodeDeps,
598
+ telemetry,
599
+ dateProvider,
600
+ archiver,
601
+ worldStateSynchronizer,
602
+ p2pClient,
603
+ epochCache,
604
+ blobClient,
605
+ keyStoreManager,
606
+ });
607
+
608
+ if (!options.dontStartProverNode) {
609
+ await proverNode.start();
610
+ started.push(proverNode);
611
+ log.info(`Prover node subsystem started`);
612
+ } else {
613
+ log.info(`Prover node subsystem created but not started`);
614
+ }
615
+ }
616
+
617
+ const node = new AztecNodeService(
618
+ config,
562
619
  p2pClient,
620
+ archiver,
621
+ archiver,
622
+ archiver,
623
+ archiver,
563
624
  worldStateSynchronizer,
625
+ sequencer,
626
+ proverNode,
564
627
  slasherClient,
565
- checkpointsBuilder,
566
- l2BlockSource: archiver,
567
- l1ToL2MessageSource: archiver,
568
- telemetry,
569
- dateProvider,
570
- blobClient,
571
- nodeKeyStore: keyStoreManager!,
628
+ validatorsSentinel,
629
+ epochPruneWatcher,
630
+ ethereumChain.chainInfo.id,
631
+ config.rollupVersion,
572
632
  globalVariableBuilder,
573
- });
574
- }
575
-
576
- if (!options.dontStartSequencer && sequencer) {
577
- await sequencer.start();
578
- log.verbose(`Sequencer started`);
579
- } else if (sequencer) {
580
- log.warn(`Sequencer created but not started`);
581
- }
582
-
583
- // Create prover node subsystem if enabled
584
- let proverNode: ProverNode | undefined;
585
- if (config.enableProverNode) {
586
- proverNode = await createProverNode(config, {
587
- ...deps.proverNodeDeps,
588
- telemetry,
589
- dateProvider,
590
- archiver,
591
- worldStateSynchronizer,
592
- p2pClient,
633
+ feeProvider,
593
634
  epochCache,
635
+ packageVersion,
636
+ peerProofVerifier,
637
+ rpcProofVerifier,
638
+ telemetry,
639
+ log,
594
640
  blobClient,
641
+ validatorClient,
595
642
  keyStoreManager,
596
- });
643
+ debugLogStore,
644
+ );
597
645
 
598
- if (!options.dontStartProverNode) {
599
- await proverNode.start();
600
- log.info(`Prover node subsystem started`);
601
- } else {
602
- log.info(`Prover node subsystem created but not started`);
646
+ return node;
647
+ } catch (err) {
648
+ log.error('Failed during node creation, stopping started resources', err);
649
+ for (const resource of started.reverse()) {
650
+ await tryStop(resource);
603
651
  }
652
+ throw err;
604
653
  }
605
-
606
- const node = new AztecNodeService(
607
- config,
608
- p2pClient,
609
- archiver,
610
- archiver,
611
- archiver,
612
- archiver,
613
- worldStateSynchronizer,
614
- sequencer,
615
- proverNode,
616
- slasherClient,
617
- validatorsSentinel,
618
- epochPruneWatcher,
619
- ethereumChain.chainInfo.id,
620
- config.rollupVersion,
621
- globalVariableBuilder,
622
- feeProvider,
623
- epochCache,
624
- packageVersion,
625
- peerProofVerifier,
626
- rpcProofVerifier,
627
- telemetry,
628
- log,
629
- blobClient,
630
- validatorClient,
631
- keyStoreManager,
632
- debugLogStore,
633
- );
634
-
635
- return node;
636
654
  }
637
655
 
638
656
  /**
@@ -1714,18 +1732,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1714
1732
  if (BlockHash.isBlockHash(block)) {
1715
1733
  const initialBlockHash = await this.#getInitialHeaderHash();
1716
1734
  if (block.equals(initialBlockHash)) {
1717
- // Block source doesn't handle initial header so we need to handle the case separately.
1718
- return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
1719
- }
1720
-
1721
- const header = await this.blockSource.getBlockHeaderByHash(block);
1722
- if (!header) {
1723
- throw new Error(
1724
- `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.`,
1725
- );
1735
+ // Block 0 is a first-class historical block: its state lives in the trees' persisted
1736
+ // block-0 payload. Resolving the genesis hash to block number 0 lets the snapshot path
1737
+ // pin reads to genesis state even after the node has advanced past it.
1738
+ blockNumber = BlockNumber.ZERO;
1739
+ } else {
1740
+ const header = await this.blockSource.getBlockHeaderByHash(block);
1741
+ if (!header) {
1742
+ throw new Error(
1743
+ `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.`,
1744
+ );
1745
+ }
1746
+ blockNumber = header.getBlockNumber();
1726
1747
  }
1727
-
1728
- blockNumber = header.getBlockNumber();
1729
1748
  } else {
1730
1749
  blockNumber = block as BlockNumber;
1731
1750
  }
@@ -1742,8 +1761,9 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1742
1761
  if (BlockHash.isBlockHash(block)) {
1743
1762
  const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
1744
1763
  if (!blockHash || !block.equals(blockHash)) {
1764
+ const initialBlockHash = await this.#getInitialHeaderHash();
1745
1765
  throw new Error(
1746
- `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.`,
1766
+ `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.`,
1747
1767
  );
1748
1768
  }
1749
1769
  }