@aztec/aztec-node 0.0.1-commit.ec5f612 → 0.0.1-commit.ec7ac5448
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 +26 -26
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +260 -142
- package/dest/sentinel/factory.d.ts +2 -2
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +8 -8
- package/package.json +26 -27
- package/src/aztec-node/config.ts +1 -1
- package/src/aztec-node/server.ts +275 -150
- package/src/sentinel/factory.ts +1 -1
- package/src/sentinel/sentinel.ts +8 -8
|
@@ -372,19 +372,21 @@ 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';
|
|
379
380
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
380
|
-
import { getPublicClient } from '@aztec/ethereum/client';
|
|
381
|
+
import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client';
|
|
381
382
|
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
382
|
-
import { BlockNumber
|
|
383
|
-
import { compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
383
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
384
|
+
import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
384
385
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
385
386
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
386
387
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
387
388
|
import { createLogger } from '@aztec/foundation/log';
|
|
389
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
388
390
|
import { count } from '@aztec/foundation/string';
|
|
389
391
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
390
392
|
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
@@ -411,9 +413,9 @@ import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@az
|
|
|
411
413
|
import { PublicSimulationOutput, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
412
414
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
413
415
|
import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
414
|
-
import { FullNodeCheckpointsBuilder as CheckpointsBuilder, FullNodeCheckpointsBuilder, NodeKeystoreAdapter, ValidatorClient,
|
|
416
|
+
import { FullNodeCheckpointsBuilder as CheckpointsBuilder, FullNodeCheckpointsBuilder, NodeKeystoreAdapter, ValidatorClient, createProposalHandler, createValidatorClient } from '@aztec/validator-client';
|
|
415
417
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
416
|
-
import { createPublicClient
|
|
418
|
+
import { createPublicClient } from 'viem';
|
|
417
419
|
import { createSentinel } from '../sentinel/factory.js';
|
|
418
420
|
import { createKeyStoreForValidator } from './config.js';
|
|
419
421
|
import { NodeMetrics } from './node_metrics.js';
|
|
@@ -440,7 +442,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
440
442
|
globalVariableBuilder;
|
|
441
443
|
epochCache;
|
|
442
444
|
packageVersion;
|
|
443
|
-
|
|
445
|
+
peerProofVerifier;
|
|
446
|
+
rpcProofVerifier;
|
|
444
447
|
telemetry;
|
|
445
448
|
log;
|
|
446
449
|
blobClient;
|
|
@@ -461,7 +464,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
461
464
|
// Prevent two snapshot operations to happen simultaneously
|
|
462
465
|
isUploadingSnapshot;
|
|
463
466
|
tracer;
|
|
464
|
-
constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion,
|
|
467
|
+
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()){
|
|
465
468
|
this.config = config;
|
|
466
469
|
this.p2pClient = p2pClient;
|
|
467
470
|
this.blockSource = blockSource;
|
|
@@ -479,7 +482,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
479
482
|
this.globalVariableBuilder = globalVariableBuilder;
|
|
480
483
|
this.epochCache = epochCache;
|
|
481
484
|
this.packageVersion = packageVersion;
|
|
482
|
-
this.
|
|
485
|
+
this.peerProofVerifier = peerProofVerifier;
|
|
486
|
+
this.rpcProofVerifier = rpcProofVerifier;
|
|
483
487
|
this.telemetry = telemetry;
|
|
484
488
|
this.log = log;
|
|
485
489
|
this.blobClient = blobClient;
|
|
@@ -499,6 +503,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
499
503
|
throw new Error('debugLogStore should never be enabled when realProofs are set');
|
|
500
504
|
}
|
|
501
505
|
}
|
|
506
|
+
/** @internal Exposed for testing — returns the RPC proof verifier. */ getProofVerifier() {
|
|
507
|
+
return this.rpcProofVerifier;
|
|
508
|
+
}
|
|
502
509
|
async getWorldStateSyncStatus() {
|
|
503
510
|
const status = await this.worldStateSynchronizer.status();
|
|
504
511
|
return status.syncSummary;
|
|
@@ -559,9 +566,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
559
566
|
}
|
|
560
567
|
const publicClient = createPublicClient({
|
|
561
568
|
chain: ethereumChain.chainInfo,
|
|
562
|
-
transport:
|
|
563
|
-
|
|
564
|
-
|
|
569
|
+
transport: makeL1HttpTransport(config.l1RpcUrls, {
|
|
570
|
+
timeout: config.l1HttpTimeoutMS
|
|
571
|
+
}),
|
|
565
572
|
pollingInterval: config.viemPollingIntervalMS
|
|
566
573
|
});
|
|
567
574
|
const l1ContractsAddresses = await RegistryContract.collectAddresses(publicClient, config.l1Contracts.registryAddress, config.rollupVersion ?? 'canonical');
|
|
@@ -571,10 +578,11 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
571
578
|
...l1ContractsAddresses
|
|
572
579
|
};
|
|
573
580
|
const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString());
|
|
574
|
-
const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
|
|
581
|
+
const [l1GenesisTime, slotDuration, rollupVersionFromRollup, rollupManaLimit] = await Promise.all([
|
|
575
582
|
rollupContract.getL1GenesisTime(),
|
|
576
583
|
rollupContract.getSlotDuration(),
|
|
577
|
-
rollupContract.getVersion()
|
|
584
|
+
rollupContract.getVersion(),
|
|
585
|
+
rollupContract.getManaLimit().then(Number)
|
|
578
586
|
]);
|
|
579
587
|
config.rollupVersion ??= Number(rollupVersionFromRollup);
|
|
580
588
|
if (config.rollupVersion !== Number(rollupVersionFromRollup)) {
|
|
@@ -595,8 +603,18 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
595
603
|
blockUntilSync: !config.skipArchiverInitialSync
|
|
596
604
|
});
|
|
597
605
|
// now create the merkle trees and the world state synchronizer
|
|
598
|
-
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.
|
|
599
|
-
const
|
|
606
|
+
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
|
|
607
|
+
const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
|
|
608
|
+
let peerProofVerifier;
|
|
609
|
+
let rpcProofVerifier;
|
|
610
|
+
if (useRealVerifiers) {
|
|
611
|
+
peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
|
|
612
|
+
const rpcVerifier = await BBCircuitVerifier.new(config);
|
|
613
|
+
rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
|
|
614
|
+
} else {
|
|
615
|
+
peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
616
|
+
rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
617
|
+
}
|
|
600
618
|
let debugLogStore;
|
|
601
619
|
if (!config.realProofs) {
|
|
602
620
|
log.warn(`Aztec node is accepting fake proofs`);
|
|
@@ -605,22 +623,22 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
605
623
|
} else {
|
|
606
624
|
debugLogStore = new NullDebugLogStore();
|
|
607
625
|
}
|
|
608
|
-
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
609
626
|
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
610
627
|
if (proverOnly) {
|
|
611
628
|
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
612
629
|
}
|
|
613
630
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
614
|
-
const p2pClient = await createP2PClient(config, archiver,
|
|
615
|
-
// We should really not be modifying the config object
|
|
616
|
-
config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? await getDefaultAllowedSetupFunctions();
|
|
631
|
+
const p2pClient = await createP2PClient(config, archiver, peerProofVerifier, worldStateSynchronizer, epochCache, packageVersion, dateProvider, telemetry, deps.p2pClientDeps);
|
|
617
632
|
// We'll accumulate sentinel watchers here
|
|
618
633
|
const watchers = [];
|
|
619
|
-
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation
|
|
634
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation.
|
|
635
|
+
// Override maxTxsPerCheckpoint with the validator-specific limit if set.
|
|
620
636
|
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder({
|
|
621
637
|
...config,
|
|
622
638
|
l1GenesisTime,
|
|
623
|
-
slotDuration: Number(slotDuration)
|
|
639
|
+
slotDuration: Number(slotDuration),
|
|
640
|
+
rollupManaLimit,
|
|
641
|
+
maxTxsPerCheckpoint: config.validateMaxTxsPerCheckpoint
|
|
624
642
|
}, worldStateSynchronizer, archiver, dateProvider, telemetry);
|
|
625
643
|
let validatorClient;
|
|
626
644
|
if (!proverOnly) {
|
|
@@ -635,32 +653,40 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
635
653
|
blockSource: archiver,
|
|
636
654
|
l1ToL2MessageSource: archiver,
|
|
637
655
|
keyStoreManager,
|
|
638
|
-
blobClient
|
|
656
|
+
blobClient,
|
|
657
|
+
slashingProtectionDb: deps.slashingProtectionDb
|
|
639
658
|
});
|
|
640
659
|
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
641
660
|
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
642
661
|
// like attestations or auths will fail.
|
|
643
662
|
if (validatorClient) {
|
|
644
663
|
watchers.push(validatorClient);
|
|
664
|
+
const vc = validatorClient;
|
|
665
|
+
const getValidatorAddresses = ()=>vc.getValidatorAddresses().map((a)=>a.toString());
|
|
666
|
+
validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
|
|
645
667
|
if (!options.dontStartSequencer) {
|
|
646
668
|
await validatorClient.registerHandlers();
|
|
647
669
|
}
|
|
648
670
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
671
|
+
}
|
|
672
|
+
// If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
|
|
673
|
+
// for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
|
|
674
|
+
// while non-reexecution is used for validating the proposals and collecting their txs.
|
|
675
|
+
// Checkpoint proposals rebuild blobs if the blob client can upload blobs.
|
|
676
|
+
if (!validatorClient) {
|
|
677
|
+
const reexecute = !!config.alwaysReexecuteBlockProposals;
|
|
678
|
+
log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
|
|
679
|
+
createProposalHandler(config, {
|
|
680
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
681
|
+
worldState: worldStateSynchronizer,
|
|
682
|
+
epochCache,
|
|
683
|
+
blockSource: archiver,
|
|
684
|
+
l1ToL2MessageSource: archiver,
|
|
685
|
+
p2pClient,
|
|
686
|
+
blobClient,
|
|
687
|
+
dateProvider,
|
|
688
|
+
telemetry
|
|
689
|
+
}).register(p2pClient, reexecute, archiver);
|
|
664
690
|
}
|
|
665
691
|
// Start world state and wait for it to sync to the archiver.
|
|
666
692
|
await worldStateSynchronizer.start();
|
|
@@ -686,12 +712,18 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
686
712
|
}
|
|
687
713
|
// Start p2p-related services once the archiver has completed sync
|
|
688
714
|
void archiver.waitForInitialSync().then(async ()=>{
|
|
689
|
-
await p2pClient.start();
|
|
690
715
|
await validatorsSentinel?.start();
|
|
691
716
|
await epochPruneWatcher?.start();
|
|
692
717
|
await attestationsBlockWatcher?.start();
|
|
693
718
|
log.info(`All p2p services started`);
|
|
694
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
|
+
});
|
|
695
727
|
// Validator enabled, create/start relevant service
|
|
696
728
|
let sequencer;
|
|
697
729
|
let slasherClient;
|
|
@@ -718,16 +750,34 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
718
750
|
dateProvider,
|
|
719
751
|
kzg: Blob.getViemKzgInstance()
|
|
720
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
|
+
], {
|
|
760
|
+
...config,
|
|
761
|
+
scope: 'sequencer'
|
|
762
|
+
}, {
|
|
763
|
+
telemetry,
|
|
764
|
+
logger: log.createChild('l1-tx-utils:funder'),
|
|
765
|
+
dateProvider
|
|
766
|
+
});
|
|
767
|
+
funderL1TxUtils = funder;
|
|
768
|
+
}
|
|
721
769
|
// Create and start the sequencer client
|
|
722
770
|
const checkpointsBuilder = new CheckpointsBuilder({
|
|
723
771
|
...config,
|
|
724
772
|
l1GenesisTime,
|
|
725
|
-
slotDuration: Number(slotDuration)
|
|
773
|
+
slotDuration: Number(slotDuration),
|
|
774
|
+
rollupManaLimit
|
|
726
775
|
}, worldStateSynchronizer, archiver, dateProvider, telemetry, debugLogStore);
|
|
727
776
|
sequencer = await SequencerClient.new(config, {
|
|
728
777
|
...deps,
|
|
729
778
|
epochCache,
|
|
730
779
|
l1TxUtils,
|
|
780
|
+
funderL1TxUtils,
|
|
731
781
|
validatorClient,
|
|
732
782
|
p2pClient,
|
|
733
783
|
worldStateSynchronizer,
|
|
@@ -738,7 +788,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
738
788
|
telemetry,
|
|
739
789
|
dateProvider,
|
|
740
790
|
blobClient,
|
|
741
|
-
nodeKeyStore: keyStoreManager
|
|
791
|
+
nodeKeyStore: keyStoreManager,
|
|
792
|
+
globalVariableBuilder
|
|
742
793
|
});
|
|
743
794
|
}
|
|
744
795
|
if (!options.dontStartSequencer && sequencer) {
|
|
@@ -768,13 +819,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
768
819
|
log.info(`Prover node subsystem created but not started`);
|
|
769
820
|
}
|
|
770
821
|
}
|
|
771
|
-
const
|
|
772
|
-
...config,
|
|
773
|
-
rollupVersion: BigInt(config.rollupVersion),
|
|
774
|
-
l1GenesisTime,
|
|
775
|
-
slotDuration: Number(slotDuration)
|
|
776
|
-
});
|
|
777
|
-
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);
|
|
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);
|
|
778
823
|
return node;
|
|
779
824
|
}
|
|
780
825
|
/**
|
|
@@ -805,7 +850,10 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
805
850
|
return Promise.resolve(this.p2pClient.getEnr()?.encodeTxt());
|
|
806
851
|
}
|
|
807
852
|
async getAllowedPublicSetup() {
|
|
808
|
-
return
|
|
853
|
+
return [
|
|
854
|
+
...await getDefaultAllowedSetupFunctions(),
|
|
855
|
+
...this.config.txPublicSetupAllowListExtend ?? []
|
|
856
|
+
];
|
|
809
857
|
}
|
|
810
858
|
/**
|
|
811
859
|
* Method to determine if the node is ready to accept transactions.
|
|
@@ -883,6 +931,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
883
931
|
async getCheckpointedBlocks(from, limit) {
|
|
884
932
|
return await this.blockSource.getCheckpointedBlocks(from, limit) ?? [];
|
|
885
933
|
}
|
|
934
|
+
getCheckpointsDataForEpoch(epochNumber) {
|
|
935
|
+
return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
|
|
936
|
+
}
|
|
886
937
|
/**
|
|
887
938
|
* Method to fetch the current min L2 fees.
|
|
888
939
|
* @returns The current min L2 fees.
|
|
@@ -910,6 +961,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
910
961
|
async getCheckpointedBlockNumber() {
|
|
911
962
|
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
912
963
|
}
|
|
964
|
+
getCheckpointNumber() {
|
|
965
|
+
return this.blockSource.getCheckpointNumber();
|
|
966
|
+
}
|
|
913
967
|
/**
|
|
914
968
|
* Method to fetch the version of the package.
|
|
915
969
|
* @returns The node package version
|
|
@@ -935,28 +989,36 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
935
989
|
return this.contractDataSource.getContract(address);
|
|
936
990
|
}
|
|
937
991
|
async getPrivateLogsByTags(tags, page, referenceBlock) {
|
|
992
|
+
let upToBlockNumber;
|
|
938
993
|
if (referenceBlock) {
|
|
939
994
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
940
|
-
if (
|
|
995
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
996
|
+
upToBlockNumber = BlockNumber(0);
|
|
997
|
+
} else {
|
|
941
998
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
942
999
|
if (!header) {
|
|
943
1000
|
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
944
1001
|
}
|
|
1002
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
945
1003
|
}
|
|
946
1004
|
}
|
|
947
|
-
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
1005
|
+
return this.logsSource.getPrivateLogsByTags(tags, page, upToBlockNumber);
|
|
948
1006
|
}
|
|
949
1007
|
async getPublicLogsByTagsFromContract(contractAddress, tags, page, referenceBlock) {
|
|
1008
|
+
let upToBlockNumber;
|
|
950
1009
|
if (referenceBlock) {
|
|
951
1010
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
952
|
-
if (
|
|
1011
|
+
if (referenceBlock.equals(initialBlockHash)) {
|
|
1012
|
+
upToBlockNumber = BlockNumber(0);
|
|
1013
|
+
} else {
|
|
953
1014
|
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
954
1015
|
if (!header) {
|
|
955
1016
|
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
956
1017
|
}
|
|
1018
|
+
upToBlockNumber = header.globalVariables.blockNumber;
|
|
957
1019
|
}
|
|
958
1020
|
}
|
|
959
|
-
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
1021
|
+
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
|
|
960
1022
|
}
|
|
961
1023
|
/**
|
|
962
1024
|
* Gets public logs based on the provided filter.
|
|
@@ -1029,7 +1091,10 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1029
1091
|
await tryStop(this.validatorsSentinel);
|
|
1030
1092
|
await tryStop(this.epochPruneWatcher);
|
|
1031
1093
|
await tryStop(this.slasherClient);
|
|
1032
|
-
await
|
|
1094
|
+
await Promise.all([
|
|
1095
|
+
tryStop(this.peerProofVerifier),
|
|
1096
|
+
tryStop(this.rpcProofVerifier)
|
|
1097
|
+
]);
|
|
1033
1098
|
await tryStop(this.sequencer);
|
|
1034
1099
|
await tryStop(this.proverNode);
|
|
1035
1100
|
await tryStop(this.p2pClient);
|
|
@@ -1071,70 +1136,82 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1071
1136
|
return compactArray(await Promise.all(txHashes.map((txHash)=>this.getTxByHash(txHash))));
|
|
1072
1137
|
}
|
|
1073
1138
|
async findLeavesIndexes(referenceBlock, treeId, leafValues) {
|
|
1074
|
-
const committedDb = await this
|
|
1139
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1075
1140
|
const maybeIndices = await committedDb.findLeafIndices(treeId, leafValues.map((x)=>x.toBuffer()));
|
|
1076
|
-
//
|
|
1077
|
-
const
|
|
1078
|
-
// Now we find the block numbers for the indices
|
|
1079
|
-
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId,
|
|
1080
|
-
//
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1141
|
+
// Filter out undefined values to query block numbers only for found leaves
|
|
1142
|
+
const definedIndices = maybeIndices.filter((x)=>x !== undefined);
|
|
1143
|
+
// Now we find the block numbers for the defined indices
|
|
1144
|
+
const blockNumbers = await committedDb.getBlockNumbersForLeafIndices(treeId, definedIndices);
|
|
1145
|
+
// Build a map from leaf index to block number
|
|
1146
|
+
const indexToBlockNumber = new Map();
|
|
1147
|
+
for(let i = 0; i < definedIndices.length; i++){
|
|
1148
|
+
const blockNumber = blockNumbers[i];
|
|
1149
|
+
if (blockNumber === undefined) {
|
|
1150
|
+
throw new Error(`Block number is undefined for leaf index ${definedIndices[i]} in tree ${MerkleTreeId[treeId]}`);
|
|
1084
1151
|
}
|
|
1152
|
+
indexToBlockNumber.set(definedIndices[i], blockNumber);
|
|
1085
1153
|
}
|
|
1086
1154
|
// Get unique block numbers in order to optimize num calls to getLeafValue function.
|
|
1087
1155
|
const uniqueBlockNumbers = [
|
|
1088
|
-
...new Set(
|
|
1156
|
+
...new Set(indexToBlockNumber.values())
|
|
1089
1157
|
];
|
|
1090
|
-
// Now we obtain the block hashes from the archive tree
|
|
1091
|
-
// (note that block number corresponds to the leaf index in the archive tree).
|
|
1158
|
+
// Now we obtain the block hashes from the archive tree (block number = leaf index in archive tree).
|
|
1092
1159
|
const blockHashes = await Promise.all(uniqueBlockNumbers.map((blockNumber)=>{
|
|
1093
1160
|
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1094
1161
|
}));
|
|
1095
|
-
//
|
|
1162
|
+
// Build a map from block number to block hash
|
|
1163
|
+
const blockNumberToHash = new Map();
|
|
1096
1164
|
for(let i = 0; i < uniqueBlockNumbers.length; i++){
|
|
1097
|
-
|
|
1165
|
+
const blockHash = blockHashes[i];
|
|
1166
|
+
if (blockHash === undefined) {
|
|
1098
1167
|
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
1099
1168
|
}
|
|
1169
|
+
blockNumberToHash.set(uniqueBlockNumbers[i], blockHash);
|
|
1100
1170
|
}
|
|
1101
1171
|
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
1102
|
-
return maybeIndices.map((index
|
|
1172
|
+
return maybeIndices.map((index)=>{
|
|
1103
1173
|
if (index === undefined) {
|
|
1104
1174
|
return undefined;
|
|
1105
1175
|
}
|
|
1106
|
-
const blockNumber =
|
|
1176
|
+
const blockNumber = indexToBlockNumber.get(index);
|
|
1107
1177
|
if (blockNumber === undefined) {
|
|
1108
|
-
|
|
1178
|
+
throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
|
|
1109
1179
|
}
|
|
1110
|
-
const
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
return undefined;
|
|
1180
|
+
const blockHash = blockNumberToHash.get(blockNumber);
|
|
1181
|
+
if (blockHash === undefined) {
|
|
1182
|
+
throw new Error(`Block hash not found for block number ${blockNumber}`);
|
|
1114
1183
|
}
|
|
1115
1184
|
return {
|
|
1116
|
-
l2BlockNumber:
|
|
1185
|
+
l2BlockNumber: blockNumber,
|
|
1117
1186
|
l2BlockHash: new BlockHash(blockHash),
|
|
1118
1187
|
data: index
|
|
1119
1188
|
};
|
|
1120
1189
|
});
|
|
1121
1190
|
}
|
|
1122
1191
|
async getBlockHashMembershipWitness(referenceBlock, blockHash) {
|
|
1123
|
-
|
|
1192
|
+
// The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
|
|
1193
|
+
// which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
|
|
1194
|
+
// So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
|
|
1195
|
+
const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
|
|
1196
|
+
if (referenceBlockNumber === BlockNumber.ZERO) {
|
|
1197
|
+
// Block 0 (the initial block) has an empty archive, so no membership witness can exist.
|
|
1198
|
+
return undefined;
|
|
1199
|
+
}
|
|
1200
|
+
const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
|
|
1124
1201
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
|
|
1125
1202
|
blockHash
|
|
1126
1203
|
]);
|
|
1127
1204
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1128
1205
|
}
|
|
1129
1206
|
async getNoteHashMembershipWitness(referenceBlock, noteHash) {
|
|
1130
|
-
const committedDb = await this
|
|
1207
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1131
1208
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.NOTE_HASH_TREE, [
|
|
1132
1209
|
noteHash
|
|
1133
1210
|
]);
|
|
1134
1211
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1135
1212
|
}
|
|
1136
1213
|
async getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message) {
|
|
1137
|
-
const db = await this
|
|
1214
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1138
1215
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [
|
|
1139
1216
|
l1ToL2Message
|
|
1140
1217
|
]);
|
|
@@ -1147,9 +1224,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1147
1224
|
witness.path
|
|
1148
1225
|
];
|
|
1149
1226
|
}
|
|
1150
|
-
async
|
|
1227
|
+
async getL1ToL2MessageCheckpoint(l1ToL2Message) {
|
|
1151
1228
|
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
1152
|
-
return messageIndex ?
|
|
1229
|
+
return messageIndex ? InboxLeaf.checkpointNumberFromIndex(messageIndex) : undefined;
|
|
1153
1230
|
}
|
|
1154
1231
|
/**
|
|
1155
1232
|
* Returns whether an L1 to L2 message is synced by archiver and if it's ready to be included in a block.
|
|
@@ -1166,23 +1243,11 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1166
1243
|
*/ async getL2ToL1Messages(epoch) {
|
|
1167
1244
|
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
|
|
1168
1245
|
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
|
|
1169
|
-
const blocksInCheckpoints =
|
|
1170
|
-
let previousSlotNumber = SlotNumber.ZERO;
|
|
1171
|
-
let checkpointIndex = -1;
|
|
1172
|
-
for (const checkpointedBlock of checkpointedBlocks){
|
|
1173
|
-
const block = checkpointedBlock.block;
|
|
1174
|
-
const slotNumber = block.header.globalVariables.slotNumber;
|
|
1175
|
-
if (slotNumber !== previousSlotNumber) {
|
|
1176
|
-
checkpointIndex++;
|
|
1177
|
-
blocksInCheckpoints.push([]);
|
|
1178
|
-
previousSlotNumber = slotNumber;
|
|
1179
|
-
}
|
|
1180
|
-
blocksInCheckpoints[checkpointIndex].push(block);
|
|
1181
|
-
}
|
|
1246
|
+
const blocksInCheckpoints = chunkBy(checkpointedBlocks, (cb)=>cb.block.header.globalVariables.slotNumber).map((group)=>group.map((cb)=>cb.block));
|
|
1182
1247
|
return blocksInCheckpoints.map((blocks)=>blocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
|
|
1183
1248
|
}
|
|
1184
1249
|
async getNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1185
|
-
const db = await this
|
|
1250
|
+
const db = await this.getWorldState(referenceBlock);
|
|
1186
1251
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [
|
|
1187
1252
|
nullifier.toBuffer()
|
|
1188
1253
|
]);
|
|
@@ -1196,36 +1261,22 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1196
1261
|
}
|
|
1197
1262
|
return new NullifierMembershipWitness(index, leafPreimage, path);
|
|
1198
1263
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
* @param referenceBlock - The block parameter (block number, block hash, or 'latest') at which to get the data
|
|
1202
|
-
* (which contains the root of the nullifier tree in which we are searching for the nullifier).
|
|
1203
|
-
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1204
|
-
* @returns The low nullifier membership witness (if found).
|
|
1205
|
-
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
1206
|
-
* list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
|
|
1207
|
-
* we are trying to prove non-inclusion for.
|
|
1208
|
-
*
|
|
1209
|
-
* Note: This function returns the membership witness of the nullifier itself and not the low nullifier when
|
|
1210
|
-
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1211
|
-
* index of the nullifier itself when it already exists in the tree.
|
|
1212
|
-
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1213
|
-
*/ async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1214
|
-
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1264
|
+
async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1265
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1215
1266
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1216
1267
|
if (!findResult) {
|
|
1217
1268
|
return undefined;
|
|
1218
1269
|
}
|
|
1219
1270
|
const { index, alreadyPresent } = findResult;
|
|
1220
1271
|
if (alreadyPresent) {
|
|
1221
|
-
|
|
1272
|
+
throw new Error(`Cannot prove nullifier non-inclusion: nullifier ${nullifier.toBigInt()} already exists in the tree`);
|
|
1222
1273
|
}
|
|
1223
1274
|
const preimageData = await committedDb.getLeafPreimage(MerkleTreeId.NULLIFIER_TREE, index);
|
|
1224
1275
|
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
1225
1276
|
return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath);
|
|
1226
1277
|
}
|
|
1227
1278
|
async getPublicDataWitness(referenceBlock, leafSlot) {
|
|
1228
|
-
const committedDb = await this
|
|
1279
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1229
1280
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1230
1281
|
if (!lowLeafResult) {
|
|
1231
1282
|
return undefined;
|
|
@@ -1236,7 +1287,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1236
1287
|
}
|
|
1237
1288
|
}
|
|
1238
1289
|
async getPublicStorageAt(referenceBlock, contract, slot) {
|
|
1239
|
-
const committedDb = await this
|
|
1290
|
+
const committedDb = await this.getWorldState(referenceBlock);
|
|
1240
1291
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1241
1292
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1242
1293
|
if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
|
|
@@ -1315,7 +1366,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1315
1366
|
});
|
|
1316
1367
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1317
1368
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1318
|
-
const [processedTxs, failedTxs, _usedTxs, returns,
|
|
1369
|
+
const [processedTxs, failedTxs, _usedTxs, returns, debugLogs] = await processor.process([
|
|
1319
1370
|
tx
|
|
1320
1371
|
]);
|
|
1321
1372
|
// REFACTOR: Consider returning the error rather than throwing
|
|
@@ -1333,19 +1384,26 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1333
1384
|
}
|
|
1334
1385
|
async isValidTx(tx, { isSimulation, skipFeeEnforcement } = {}) {
|
|
1335
1386
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1336
|
-
const verifier = isSimulation ? undefined : this.
|
|
1387
|
+
const verifier = isSimulation ? undefined : this.rpcProofVerifier;
|
|
1337
1388
|
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1338
1389
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1339
1390
|
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
1391
|
+
const l1Constants = await this.blockSource.getL1Constants();
|
|
1340
1392
|
const validator = createTxValidatorForAcceptingTxsOverRPC(db, this.contractDataSource, verifier, {
|
|
1341
1393
|
timestamp: nextSlotTimestamp,
|
|
1342
1394
|
blockNumber,
|
|
1343
1395
|
l1ChainId: this.l1ChainId,
|
|
1344
1396
|
rollupVersion: this.version,
|
|
1345
|
-
setupAllowList:
|
|
1397
|
+
setupAllowList: [
|
|
1398
|
+
...await getDefaultAllowedSetupFunctions(),
|
|
1399
|
+
...this.config.txPublicSetupAllowListExtend ?? []
|
|
1400
|
+
],
|
|
1346
1401
|
gasFees: await this.getCurrentMinFees(),
|
|
1347
1402
|
skipFeeEnforcement,
|
|
1348
|
-
txsPermitted: !this.config.disableTransactions
|
|
1403
|
+
txsPermitted: !this.config.disableTransactions,
|
|
1404
|
+
rollupManaLimit: l1Constants.rollupManaLimit,
|
|
1405
|
+
maxBlockL2Gas: this.config.validateMaxL2BlockGas,
|
|
1406
|
+
maxBlockDAGas: this.config.validateMaxDABlockGas
|
|
1349
1407
|
}, this.log.getBindings());
|
|
1350
1408
|
return await validator.validateTx(tx);
|
|
1351
1409
|
}
|
|
@@ -1368,7 +1426,18 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1368
1426
|
archiver.updateConfig(config);
|
|
1369
1427
|
}
|
|
1370
1428
|
if (newConfig.realProofs !== this.config.realProofs) {
|
|
1371
|
-
|
|
1429
|
+
await Promise.all([
|
|
1430
|
+
tryStop(this.peerProofVerifier),
|
|
1431
|
+
tryStop(this.rpcProofVerifier)
|
|
1432
|
+
]);
|
|
1433
|
+
if (newConfig.realProofs) {
|
|
1434
|
+
this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
|
|
1435
|
+
const rpcVerifier = await BBCircuitVerifier.new(newConfig);
|
|
1436
|
+
this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
|
|
1437
|
+
} else {
|
|
1438
|
+
this.peerProofVerifier = new TestCircuitVerifier();
|
|
1439
|
+
this.rpcProofVerifier = new TestCircuitVerifier();
|
|
1440
|
+
}
|
|
1372
1441
|
}
|
|
1373
1442
|
this.config = newConfig;
|
|
1374
1443
|
}
|
|
@@ -1428,7 +1497,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1428
1497
|
});
|
|
1429
1498
|
return Promise.resolve();
|
|
1430
1499
|
}
|
|
1431
|
-
async rollbackTo(targetBlock, force) {
|
|
1500
|
+
async rollbackTo(targetBlock, force, resumeSync = true) {
|
|
1432
1501
|
const archiver = this.blockSource;
|
|
1433
1502
|
if (!('rollbackTo' in archiver)) {
|
|
1434
1503
|
throw new Error('Archiver implementation does not support rollbacks.');
|
|
@@ -1456,9 +1525,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1456
1525
|
this.log.error(`Error during rollback`, err);
|
|
1457
1526
|
throw err;
|
|
1458
1527
|
} finally{
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1528
|
+
if (resumeSync) {
|
|
1529
|
+
this.log.info(`Resuming world state and archiver sync.`);
|
|
1530
|
+
this.worldStateSynchronizer.resumeSync();
|
|
1531
|
+
archiver.resume();
|
|
1532
|
+
} else {
|
|
1533
|
+
this.log.info(`Sync left paused after rollback (resumeSync=false).`);
|
|
1534
|
+
}
|
|
1462
1535
|
}
|
|
1463
1536
|
}
|
|
1464
1537
|
async pauseSync() {
|
|
@@ -1472,18 +1545,12 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1472
1545
|
this.blockSource.resume();
|
|
1473
1546
|
return Promise.resolve();
|
|
1474
1547
|
}
|
|
1475
|
-
getSlashPayloads() {
|
|
1476
|
-
if (!this.slasherClient) {
|
|
1477
|
-
throw new Error(`Slasher client not enabled`);
|
|
1478
|
-
}
|
|
1479
|
-
return this.slasherClient.getSlashPayloads();
|
|
1480
|
-
}
|
|
1481
1548
|
getSlashOffenses(round) {
|
|
1482
1549
|
if (!this.slasherClient) {
|
|
1483
1550
|
throw new Error(`Slasher client not enabled`);
|
|
1484
1551
|
}
|
|
1485
1552
|
if (round === 'all') {
|
|
1486
|
-
return this.slasherClient.
|
|
1553
|
+
return this.slasherClient.getOffenses();
|
|
1487
1554
|
} else {
|
|
1488
1555
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1489
1556
|
}
|
|
@@ -1555,6 +1622,33 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1555
1622
|
this.keyStoreManager = newManager;
|
|
1556
1623
|
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1557
1624
|
}
|
|
1625
|
+
async mineBlock() {
|
|
1626
|
+
if (!this.sequencer) {
|
|
1627
|
+
throw new BadRequestError('Cannot mine block: no sequencer is running');
|
|
1628
|
+
}
|
|
1629
|
+
const currentBlockNumber = await this.getBlockNumber();
|
|
1630
|
+
// Use slot duration + 50% buffer as the timeout so this works on running networks too
|
|
1631
|
+
const { slotDuration } = await this.blockSource.getL1Constants();
|
|
1632
|
+
const timeoutSeconds = Math.ceil(slotDuration * 1.5);
|
|
1633
|
+
// Temporarily set minTxsPerBlock to 0 so the sequencer produces a block even with no txs
|
|
1634
|
+
const originalMinTxsPerBlock = this.sequencer.getSequencer().getConfig().minTxsPerBlock;
|
|
1635
|
+
this.sequencer.updateConfig({
|
|
1636
|
+
minTxsPerBlock: 0
|
|
1637
|
+
});
|
|
1638
|
+
try {
|
|
1639
|
+
// Trigger the sequencer to produce a block immediately
|
|
1640
|
+
void this.sequencer.trigger();
|
|
1641
|
+
// Wait for the new L2 block to appear
|
|
1642
|
+
await retryUntil(async ()=>{
|
|
1643
|
+
const newBlockNumber = await this.getBlockNumber();
|
|
1644
|
+
return newBlockNumber > currentBlockNumber ? true : undefined;
|
|
1645
|
+
}, 'mineBlock', timeoutSeconds, 0.1);
|
|
1646
|
+
} finally{
|
|
1647
|
+
this.sequencer.updateConfig({
|
|
1648
|
+
minTxsPerBlock: originalMinTxsPerBlock
|
|
1649
|
+
});
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1558
1652
|
#getInitialHeaderHash() {
|
|
1559
1653
|
if (!this.initialHeaderHashPromise) {
|
|
1560
1654
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1565,7 +1659,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1565
1659
|
* Returns an instance of MerkleTreeOperations having first ensured the world state is fully synched
|
|
1566
1660
|
* @param block - The block parameter (block number, block hash, or 'latest') at which to get the data.
|
|
1567
1661
|
* @returns An instance of a committed MerkleTreeOperations
|
|
1568
|
-
*/ async
|
|
1662
|
+
*/ async getWorldState(block) {
|
|
1569
1663
|
let blockSyncedTo = BlockNumber.ZERO;
|
|
1570
1664
|
try {
|
|
1571
1665
|
// Attempt to sync the world state if necessary
|
|
@@ -1577,6 +1671,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1577
1671
|
this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
|
|
1578
1672
|
return this.worldStateSynchronizer.getCommitted();
|
|
1579
1673
|
}
|
|
1674
|
+
// Get the block number, either directly from the parameter or by quering the archiver with the block hash
|
|
1675
|
+
let blockNumber;
|
|
1580
1676
|
if (BlockHash.isBlockHash(block)) {
|
|
1581
1677
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1582
1678
|
if (block.equals(initialBlockHash)) {
|
|
@@ -1587,19 +1683,41 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1587
1683
|
if (!header) {
|
|
1588
1684
|
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.`);
|
|
1589
1685
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
}
|
|
1594
|
-
//
|
|
1595
|
-
{
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1686
|
+
blockNumber = header.getBlockNumber();
|
|
1687
|
+
} else {
|
|
1688
|
+
blockNumber = block;
|
|
1689
|
+
}
|
|
1690
|
+
// Check it's within world state sync range
|
|
1691
|
+
if (blockNumber > blockSyncedTo) {
|
|
1692
|
+
throw new Error(`Queried block ${block} not yet synced by the node (node is synced upto ${blockSyncedTo}).`);
|
|
1693
|
+
}
|
|
1694
|
+
this.log.debug(`Using snapshot for block ${blockNumber}, world state synced upto ${blockSyncedTo}`);
|
|
1695
|
+
const snapshot = this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1696
|
+
// Double-check world-state synced to the same block hash as was requested
|
|
1697
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1698
|
+
const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
1699
|
+
if (!blockHash || !new BlockHash(blockHash).equals(block)) {
|
|
1700
|
+
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.`);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
return snapshot;
|
|
1704
|
+
}
|
|
1705
|
+
/** Resolves a block parameter to a block number. */ async resolveBlockNumber(block) {
|
|
1706
|
+
if (block === 'latest') {
|
|
1707
|
+
return BlockNumber(await this.blockSource.getBlockNumber());
|
|
1708
|
+
}
|
|
1709
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1710
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1711
|
+
if (block.equals(initialBlockHash)) {
|
|
1712
|
+
return BlockNumber.ZERO;
|
|
1713
|
+
}
|
|
1714
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1715
|
+
if (!header) {
|
|
1716
|
+
throw new Error(`Block hash ${block.toString()} not found.`);
|
|
1599
1717
|
}
|
|
1600
|
-
|
|
1601
|
-
return this.worldStateSynchronizer.getSnapshot(blockNumber);
|
|
1718
|
+
return header.getBlockNumber();
|
|
1602
1719
|
}
|
|
1720
|
+
return block;
|
|
1603
1721
|
}
|
|
1604
1722
|
/**
|
|
1605
1723
|
* Ensure we fully sync the world state
|