@aztec/aztec-node 0.0.1-commit.5914bae → 0.0.1-commit.59a0419c6
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.
- package/dest/aztec-node/config.d.ts +2 -2
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +1 -1
- package/dest/aztec-node/server.d.ts +15 -14
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +312 -239
- package/dest/sentinel/factory.d.ts +2 -2
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +6 -6
- package/package.json +26 -27
- package/src/aztec-node/config.ts +1 -1
- package/src/aztec-node/server.ts +384 -317
- package/src/sentinel/factory.ts +1 -1
- package/src/sentinel/sentinel.ts +6 -6
|
@@ -372,7 +372,8 @@ function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
|
|
|
372
372
|
}
|
|
373
373
|
var _dec, _initProto;
|
|
374
374
|
import { createArchiver } from '@aztec/archiver';
|
|
375
|
-
import { BBCircuitVerifier,
|
|
375
|
+
import { BBCircuitVerifier, BatchChonkVerifier, QueuedIVCVerifier } from '@aztec/bb-prover';
|
|
376
|
+
import { TestCircuitVerifier } from '@aztec/bb-prover/test';
|
|
376
377
|
import { createBlobClientWithFileStores } from '@aztec/blob-client/client';
|
|
377
378
|
import { Blob } from '@aztec/blob-lib';
|
|
378
379
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
@@ -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,9 +440,11 @@ _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;
|
|
447
|
+
rpcProofVerifier;
|
|
445
448
|
telemetry;
|
|
446
449
|
log;
|
|
447
450
|
blobClient;
|
|
@@ -462,7 +465,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
462
465
|
// Prevent two snapshot operations to happen simultaneously
|
|
463
466
|
isUploadingSnapshot;
|
|
464
467
|
tracer;
|
|
465
|
-
constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion,
|
|
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()){
|
|
466
469
|
this.config = config;
|
|
467
470
|
this.p2pClient = p2pClient;
|
|
468
471
|
this.blockSource = blockSource;
|
|
@@ -478,9 +481,11 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
478
481
|
this.l1ChainId = l1ChainId;
|
|
479
482
|
this.version = version;
|
|
480
483
|
this.globalVariableBuilder = globalVariableBuilder;
|
|
484
|
+
this.feeProvider = feeProvider;
|
|
481
485
|
this.epochCache = epochCache;
|
|
482
486
|
this.packageVersion = packageVersion;
|
|
483
|
-
this.
|
|
487
|
+
this.peerProofVerifier = peerProofVerifier;
|
|
488
|
+
this.rpcProofVerifier = rpcProofVerifier;
|
|
484
489
|
this.telemetry = telemetry;
|
|
485
490
|
this.log = log;
|
|
486
491
|
this.blobClient = blobClient;
|
|
@@ -500,6 +505,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
500
505
|
throw new Error('debugLogStore should never be enabled when realProofs are set');
|
|
501
506
|
}
|
|
502
507
|
}
|
|
508
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */ getProofVerifier() {
|
|
509
|
+
return this.rpcProofVerifier;
|
|
510
|
+
}
|
|
503
511
|
async getWorldStateSyncStatus() {
|
|
504
512
|
const status = await this.worldStateSynchronizer.status();
|
|
505
513
|
return status.syncSummary;
|
|
@@ -588,205 +596,252 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
588
596
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, {
|
|
589
597
|
dateProvider
|
|
590
598
|
});
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
dateProvider
|
|
596
|
-
}, {
|
|
597
|
-
blockUntilSync: !config.skipArchiverInitialSync
|
|
598
|
-
});
|
|
599
|
-
// now create the merkle trees and the world state synchronizer
|
|
600
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.prefilledPublicData, telemetry);
|
|
601
|
-
const circuitVerifier = config.realProofs || config.debugForceTxProofVerification ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
602
|
-
let debugLogStore;
|
|
603
|
-
if (!config.realProofs) {
|
|
604
|
-
log.warn(`Aztec node is accepting fake proofs`);
|
|
605
|
-
debugLogStore = new InMemoryDebugLogStore();
|
|
606
|
-
log.info('Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served');
|
|
607
|
-
} else {
|
|
608
|
-
debugLogStore = new NullDebugLogStore();
|
|
609
|
-
}
|
|
610
|
-
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
611
|
-
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
612
|
-
if (proverOnly) {
|
|
613
|
-
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
614
|
-
}
|
|
615
|
-
// create the tx pool and the p2p client, which will need the l2 block source
|
|
616
|
-
const p2pClient = await createP2PClient(config, archiver, proofVerifier, worldStateSynchronizer, epochCache, packageVersion, dateProvider, telemetry, deps.p2pClientDeps);
|
|
617
|
-
// We'll accumulate sentinel watchers here
|
|
618
|
-
const watchers = [];
|
|
619
|
-
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
620
|
-
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
621
|
-
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder({
|
|
622
|
-
...config,
|
|
623
|
-
l1GenesisTime,
|
|
624
|
-
slotDuration: Number(slotDuration),
|
|
625
|
-
rollupManaLimit,
|
|
626
|
-
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint
|
|
627
|
-
}, worldStateSynchronizer, archiver, dateProvider, telemetry);
|
|
628
|
-
let validatorClient;
|
|
629
|
-
if (!proverOnly) {
|
|
630
|
-
// Create validator client if required
|
|
631
|
-
validatorClient = await createValidatorClient(config, {
|
|
632
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
633
|
-
worldState: worldStateSynchronizer,
|
|
634
|
-
p2pClient,
|
|
635
|
-
telemetry,
|
|
636
|
-
dateProvider,
|
|
637
|
-
epochCache,
|
|
638
|
-
blockSource: archiver,
|
|
639
|
-
l1ToL2MessageSource: archiver,
|
|
640
|
-
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, {
|
|
641
603
|
blobClient,
|
|
642
|
-
slashingProtectionDb: deps.slashingProtectionDb
|
|
643
|
-
});
|
|
644
|
-
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
645
|
-
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
646
|
-
// like attestations or auths will fail.
|
|
647
|
-
if (validatorClient) {
|
|
648
|
-
watchers.push(validatorClient);
|
|
649
|
-
if (!options.dontStartSequencer) {
|
|
650
|
-
await validatorClient.registerHandlers();
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
655
|
-
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
656
|
-
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
657
|
-
// Checkpoint proposals are handled if the blob client can upload blobs.
|
|
658
|
-
if (!validatorClient) {
|
|
659
|
-
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
660
|
-
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
661
|
-
createProposalHandler(config, {
|
|
662
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
663
|
-
worldState: worldStateSynchronizer,
|
|
664
604
|
epochCache,
|
|
665
|
-
blockSource: archiver,
|
|
666
|
-
l1ToL2MessageSource: archiver,
|
|
667
|
-
p2pClient,
|
|
668
|
-
blobClient,
|
|
669
|
-
dateProvider,
|
|
670
|
-
telemetry
|
|
671
|
-
}).register(p2pClient, reexecute);
|
|
672
|
-
}
|
|
673
|
-
// Start world state and wait for it to sync to the archiver.
|
|
674
|
-
await worldStateSynchronizer.start();
|
|
675
|
-
// Start p2p. Note that it depends on world state to be running.
|
|
676
|
-
await p2pClient.start();
|
|
677
|
-
let validatorsSentinel;
|
|
678
|
-
let epochPruneWatcher;
|
|
679
|
-
let attestationsBlockWatcher;
|
|
680
|
-
if (!proverOnly) {
|
|
681
|
-
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
682
|
-
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
683
|
-
watchers.push(validatorsSentinel);
|
|
684
|
-
}
|
|
685
|
-
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
686
|
-
epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), validatorCheckpointsBuilder, config);
|
|
687
|
-
watchers.push(epochPruneWatcher);
|
|
688
|
-
}
|
|
689
|
-
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
690
|
-
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
691
|
-
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
692
|
-
watchers.push(attestationsBlockWatcher);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
// Start p2p-related services once the archiver has completed sync
|
|
696
|
-
void archiver.waitForInitialSync().then(async ()=>{
|
|
697
|
-
await p2pClient.start();
|
|
698
|
-
await validatorsSentinel?.start();
|
|
699
|
-
await epochPruneWatcher?.start();
|
|
700
|
-
await attestationsBlockWatcher?.start();
|
|
701
|
-
log.info(`All p2p services started`);
|
|
702
|
-
}).catch((err)=>log.error('Failed to start p2p services after archiver sync', err));
|
|
703
|
-
const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, {
|
|
704
|
-
l1Contracts: config.l1Contracts,
|
|
705
|
-
ethereumSlotDuration: config.ethereumSlotDuration,
|
|
706
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
707
|
-
l1GenesisTime,
|
|
708
|
-
slotDuration: Number(slotDuration)
|
|
709
|
-
});
|
|
710
|
-
// Validator enabled, create/start relevant service
|
|
711
|
-
let sequencer;
|
|
712
|
-
let slasherClient;
|
|
713
|
-
if (!config.disableValidator && validatorClient) {
|
|
714
|
-
// We create a slasher only if we have a sequencer, since all slashing actions go through the sequencer publisher
|
|
715
|
-
// as they are executed when the node is selected as proposer.
|
|
716
|
-
const validatorAddresses = keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses() : [];
|
|
717
|
-
slasherClient = await createSlasher(config, config.l1Contracts, getPublicClient(config), watchers, dateProvider, epochCache, validatorAddresses, undefined);
|
|
718
|
-
await slasherClient.start();
|
|
719
|
-
const l1TxUtils = config.sequencerPublisherForwarderAddress ? await createForwarderL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), config.sequencerPublisherForwarderAddress, {
|
|
720
|
-
...config,
|
|
721
|
-
scope: 'sequencer'
|
|
722
|
-
}, {
|
|
723
605
|
telemetry,
|
|
724
|
-
|
|
725
|
-
dateProvider,
|
|
726
|
-
kzg: Blob.getViemKzgInstance()
|
|
727
|
-
}) : await createL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), {
|
|
728
|
-
...config,
|
|
729
|
-
scope: 'sequencer'
|
|
606
|
+
dateProvider
|
|
730
607
|
}, {
|
|
731
|
-
|
|
732
|
-
logger: log.createChild('l1-tx-utils'),
|
|
733
|
-
dateProvider,
|
|
734
|
-
kzg: Blob.getViemKzgInstance()
|
|
608
|
+
blockUntilSync: !config.skipArchiverInitialSync
|
|
735
609
|
});
|
|
736
|
-
|
|
737
|
-
|
|
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);
|
|
624
|
+
}
|
|
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();
|
|
633
|
+
}
|
|
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({
|
|
738
655
|
...config,
|
|
739
656
|
l1GenesisTime,
|
|
740
657
|
slotDuration: Number(slotDuration),
|
|
741
|
-
rollupManaLimit
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
validatorClient,
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
+
}
|
|
689
|
+
}
|
|
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);
|
|
708
|
+
}
|
|
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.
|
|
712
|
+
await p2pClient.start();
|
|
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
|
+
}
|
|
786
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, {
|
|
749
|
+
...config,
|
|
750
|
+
scope: 'sequencer'
|
|
751
|
+
}, {
|
|
752
|
+
telemetry,
|
|
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
|
|
805
|
+
});
|
|
806
|
+
}
|
|
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);
|
|
842
|
+
}
|
|
843
|
+
throw err;
|
|
787
844
|
}
|
|
788
|
-
const node = new AztecNodeService(config, p2pClient, archiver, archiver, archiver, archiver, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, ethereumChain.chainInfo.id, config.rollupVersion, globalVariableBuilder, epochCache, packageVersion, proofVerifier, telemetry, log, blobClient, validatorClient, keyStoreManager, debugLogStore);
|
|
789
|
-
return node;
|
|
790
845
|
}
|
|
791
846
|
/**
|
|
792
847
|
* Returns the sequencer client instance.
|
|
@@ -900,11 +955,11 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
900
955
|
getCheckpointsDataForEpoch(epochNumber) {
|
|
901
956
|
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
902
957
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
return await this.
|
|
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);
|
|
908
963
|
}
|
|
909
964
|
async getMaxPriorityFees() {
|
|
910
965
|
for await (const tx of this.p2pClient.iteratePendingTxs()){
|
|
@@ -955,28 +1010,36 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
955
1010
|
return this.contractDataSource.getContract(address);
|
|
956
1011
|
}
|
|
957
1012
|
async getPrivateLogsByTags(tags, page, referenceBlock) {
|
|
1013
|
+
let upToBlockNumber;
|
|
958
1014
|
if (referenceBlock) {
|
|
959
1015
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
960
|
-
if (
|
|
1016
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
1017
|
+
upToBlockNumber = BlockNumber(0);
|
|
1018
|
+
} else {
|
|
961
1019
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
962
1020
|
if (!header) {
|
|
963
1021
|
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
964
1022
|
}
|
|
1023
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
965
1024
|
}
|
|
966
1025
|
}
|
|
967
|
-
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
1026
|
+
return this.logsSource.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
968
1027
|
}
|
|
969
1028
|
async getPublicLogsByTagsFromContract(contractAddress, tags, page, referenceBlock) {
|
|
1029
|
+
let upToBlockNumber;
|
|
970
1030
|
if (referenceBlock) {
|
|
971
1031
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
972
|
-
if (
|
|
1032
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
1033
|
+
upToBlockNumber = BlockNumber(0);
|
|
1034
|
+
} else {
|
|
973
1035
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
974
1036
|
if (!header) {
|
|
975
1037
|
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
976
1038
|
}
|
|
1039
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
977
1040
|
}
|
|
978
1041
|
}
|
|
979
|
-
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
1042
|
+
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
980
1043
|
}
|
|
981
1044
|
/**
|
|
982
1045
|
* Gets public logs based on the provided filter.
|
|
@@ -1049,7 +1112,10 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1049
1112
|
await tryStop(this.validatorsSentinel);
|
|
1050
1113
|
await tryStop(this.epochPruneWatcher);
|
|
1051
1114
|
await tryStop(this.slasherClient);
|
|
1052
|
-
await
|
|
1115
|
+
await Promise.all([
|
|
1116
|
+
tryStop(this.peerProofVerifier),
|
|
1117
|
+
tryStop(this.rpcProofVerifier)
|
|
1118
|
+
]);
|
|
1053
1119
|
await tryStop(this.sequencer);
|
|
1054
1120
|
await tryStop(this.proverNode);
|
|
1055
1121
|
await tryStop(this.p2pClient);
|
|
@@ -1132,32 +1198,26 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1132
1198
|
if (blockNumber === undefined) {
|
|
1133
1199
|
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1134
1200
|
}
|
|
1135
|
-
const
|
|
1136
|
-
if (
|
|
1201
|
+
const l2BlockHash = blockNumberToHash.get(blockNumber);
|
|
1202
|
+
if (l2BlockHash === undefined) {
|
|
1137
1203
|
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1138
1204
|
}
|
|
1139
1205
|
return {
|
|
1140
1206
|
l2BlockNumber: blockNumber,
|
|
1141
|
-
l2BlockHash
|
|
1207
|
+
l2BlockHash,
|
|
1142
1208
|
data: index
|
|
1143
1209
|
};
|
|
1144
1210
|
});
|
|
1145
1211
|
}
|
|
1146
1212
|
async getBlockHashMembershipWitness(referenceBlock, blockHash) {
|
|
1147
|
-
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1148
|
-
if (referenceBlock === BlockNumber.ZERO) {
|
|
1149
|
-
return undefined;
|
|
1150
|
-
}
|
|
1151
|
-
if (BlockHash.isBlockHash(referenceBlock)) {
|
|
1152
|
-
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1153
|
-
if (referenceBlock.equals(initialBlockHash)) {
|
|
1154
|
-
return undefined;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
1213
|
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1158
1214
|
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1159
1215
|
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1160
1216
|
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1217
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
1218
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1219
|
+
return undefined;
|
|
1220
|
+
}
|
|
1161
1221
|
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1162
1222
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
|
|
1163
1223
|
blockHash
|
|
@@ -1345,7 +1405,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1345
1405
|
}
|
|
1346
1406
|
async isValidTx(tx, { isSimulation, skipFeeEnforcement } = {}) {
|
|
1347
1407
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1348
|
-
const verifier = isSimulation ? undefined : this.
|
|
1408
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1349
1409
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1350
1410
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1351
1411
|
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
@@ -1387,7 +1447,18 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1387
1447
|
archiver.updateConfig(config);
|
|
1388
1448
|
}
|
|
1389
1449
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1390
|
-
|
|
1450
|
+
await Promise.all([
|
|
1451
|
+
tryStop(this.peerProofVerifier),
|
|
1452
|
+
tryStop(this.rpcProofVerifier)
|
|
1453
|
+
]);
|
|
1454
|
+
if (newConfig.realProofs) {
|
|
1455
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
1456
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
1457
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
1458
|
+
} else {
|
|
1459
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
1460
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
1461
|
+
}
|
|
1391
1462
|
}
|
|
1392
1463
|
this.config = newConfig;
|
|
1393
1464
|
}
|
|
@@ -1447,7 +1518,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1447
1518
|
});
|
|
1448
1519
|
return Promise.resolve();
|
|
1449
1520
|
}
|
|
1450
|
-
async rollbackTo(targetBlock, force) {
|
|
1521
|
+
async rollbackTo(targetBlock, force, resumeSync = true) {
|
|
1451
1522
|
const archiver = this.blockSource;
|
|
1452
1523
|
if (!('rollbackTo' in archiver)) {
|
|
1453
1524
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1475,9 +1546,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1475
1546
|
this.log.error(`Error during rollback`, err);
|
|
1476
1547
|
throw err;
|
|
1477
1548
|
} finally{
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
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
|
+
}
|
|
1481
1556
|
}
|
|
1482
1557
|
}
|
|
1483
1558
|
async pauseSync() {
|
|
@@ -1491,18 +1566,12 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1491
1566
|
this.blockSource.resume();
|
|
1492
1567
|
return Promise.resolve();
|
|
1493
1568
|
}
|
|
1494
|
-
getSlashPayloads() {
|
|
1495
|
-
if (!this.slasherClient) {
|
|
1496
|
-
throw new Error(`Slasher client not enabled`);
|
|
1497
|
-
}
|
|
1498
|
-
return this.slasherClient.getSlashPayloads();
|
|
1499
|
-
}
|
|
1500
1569
|
getSlashOffenses(round) {
|
|
1501
1570
|
if (!this.slasherClient) {
|
|
1502
1571
|
throw new Error(`Slasher client not enabled`);
|
|
1503
1572
|
}
|
|
1504
1573
|
if (round === 'all') {
|
|
1505
|
-
return this.slasherClient.
|
|
1574
|
+
return this.slasherClient.getOffenses();
|
|
1506
1575
|
} else {
|
|
1507
1576
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1508
1577
|
}
|
|
@@ -1628,14 +1697,17 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1628
1697
|
if (BlockHash.isBlockHash(block)) {
|
|
1629
1698
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1630
1699
|
if (block.equals(initialBlockHash)) {
|
|
1631
|
-
// Block
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
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();
|
|
1637
1710
|
}
|
|
1638
|
-
blockNumber = header.getBlockNumber();
|
|
1639
1711
|
} else {
|
|
1640
1712
|
blockNumber = block;
|
|
1641
1713
|
}
|
|
@@ -1648,8 +1720,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1648
1720
|
// Double-check world-state synced to the same block hash as was requested
|
|
1649
1721
|
if (BlockHash.isBlockHash(block)) {
|
|
1650
1722
|
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1651
|
-
if (!blockHash || !
|
|
1652
|
-
|
|
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.`);
|
|
1653
1726
|
}
|
|
1654
1727
|
}
|
|
1655
1728
|
return snapshot;
|