@aztec/aztec-node 0.0.1-commit.d431d1c → 0.0.1-commit.dbf9cec
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 +7 -4
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +10 -2
- package/dest/aztec-node/node_metrics.d.ts +1 -1
- package/dest/aztec-node/node_metrics.d.ts.map +1 -1
- package/dest/aztec-node/node_metrics.js +8 -4
- package/dest/aztec-node/server.d.ts +39 -30
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +307 -151
- package/dest/sentinel/factory.d.ts +1 -1
- package/dest/sentinel/factory.d.ts.map +1 -1
- package/dest/sentinel/factory.js +1 -1
- package/dest/sentinel/sentinel.d.ts +2 -2
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +53 -27
- package/dest/sentinel/store.d.ts +2 -2
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +11 -7
- package/package.json +27 -25
- package/src/aztec-node/config.ts +24 -8
- package/src/aztec-node/node_metrics.ts +12 -5
- package/src/aztec-node/server.ts +402 -216
- package/src/sentinel/factory.ts +1 -6
- package/src/sentinel/sentinel.ts +56 -23
- package/src/sentinel/store.ts +12 -12
|
@@ -374,12 +374,13 @@ var _dec, _initProto;
|
|
|
374
374
|
import { createArchiver } from '@aztec/archiver';
|
|
375
375
|
import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
|
|
376
376
|
import { createBlobClientWithFileStores } from '@aztec/blob-client/client';
|
|
377
|
+
import { Blob } from '@aztec/blob-lib';
|
|
377
378
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
378
379
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
379
380
|
import { getPublicClient } from '@aztec/ethereum/client';
|
|
380
381
|
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
381
382
|
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
382
|
-
import { compactArray, pick } from '@aztec/foundation/collection';
|
|
383
|
+
import { compactArray, pick, unique } from '@aztec/foundation/collection';
|
|
383
384
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
384
385
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
385
386
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
@@ -389,26 +390,28 @@ import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
|
389
390
|
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
390
391
|
import { KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
391
392
|
import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
|
|
392
|
-
import {
|
|
393
|
-
import { createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
|
|
393
|
+
import { createForwarderL1TxUtilsFromSigners, createL1TxUtilsFromSigners } from '@aztec/node-lib/factories';
|
|
394
|
+
import { createP2PClient, createTxValidatorForAcceptingTxsOverRPC, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
|
|
394
395
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
396
|
+
import { createProverNode } from '@aztec/prover-node';
|
|
397
|
+
import { createKeyStoreForProver } from '@aztec/prover-node/config';
|
|
395
398
|
import { GlobalVariableBuilder, SequencerClient } from '@aztec/sequencer-client';
|
|
396
399
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
397
400
|
import { AttestationsBlockWatcher, EpochPruneWatcher, createSlasher } from '@aztec/slasher';
|
|
398
401
|
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
399
402
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
400
|
-
import {
|
|
403
|
+
import { BlockHash, L2Block } from '@aztec/stdlib/block';
|
|
401
404
|
import { GasFees } from '@aztec/stdlib/gas';
|
|
402
405
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
403
406
|
import { AztecNodeAdminConfigSchema } from '@aztec/stdlib/interfaces/client';
|
|
404
407
|
import { tryStop } from '@aztec/stdlib/interfaces/server';
|
|
408
|
+
import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
|
|
405
409
|
import { InboxLeaf } from '@aztec/stdlib/messaging';
|
|
406
|
-
import { P2PClientType } from '@aztec/stdlib/p2p';
|
|
407
410
|
import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
|
|
408
411
|
import { PublicSimulationOutput, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
|
|
409
412
|
import { getPackageVersion } from '@aztec/stdlib/update-checker';
|
|
410
413
|
import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
|
|
411
|
-
import { FullNodeCheckpointsBuilder as CheckpointsBuilder, FullNodeCheckpointsBuilder, NodeKeystoreAdapter, ValidatorClient, createBlockProposalHandler, createValidatorClient
|
|
414
|
+
import { FullNodeCheckpointsBuilder as CheckpointsBuilder, FullNodeCheckpointsBuilder, NodeKeystoreAdapter, ValidatorClient, createBlockProposalHandler, createValidatorClient } from '@aztec/validator-client';
|
|
412
415
|
import { createWorldStateSynchronizer } from '@aztec/world-state';
|
|
413
416
|
import { createPublicClient, fallback, http } from 'viem';
|
|
414
417
|
import { createSentinel } from '../sentinel/factory.js';
|
|
@@ -428,6 +431,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
428
431
|
l1ToL2MessageSource;
|
|
429
432
|
worldStateSynchronizer;
|
|
430
433
|
sequencer;
|
|
434
|
+
proverNode;
|
|
431
435
|
slasherClient;
|
|
432
436
|
validatorsSentinel;
|
|
433
437
|
epochPruneWatcher;
|
|
@@ -440,6 +444,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
440
444
|
telemetry;
|
|
441
445
|
log;
|
|
442
446
|
blobClient;
|
|
447
|
+
validatorClient;
|
|
448
|
+
keyStoreManager;
|
|
449
|
+
debugLogStore;
|
|
443
450
|
static{
|
|
444
451
|
({ e: [_initProto] } = _apply_decs_2203_r(this, [
|
|
445
452
|
[
|
|
@@ -454,7 +461,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
454
461
|
// Prevent two snapshot operations to happen simultaneously
|
|
455
462
|
isUploadingSnapshot;
|
|
456
463
|
tracer;
|
|
457
|
-
constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion, proofVerifier, telemetry = getTelemetryClient(), log = createLogger('node'), blobClient){
|
|
464
|
+
constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, proverNode, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion, proofVerifier, telemetry = getTelemetryClient(), log = createLogger('node'), blobClient, validatorClient, keyStoreManager, debugLogStore = new NullDebugLogStore()){
|
|
458
465
|
this.config = config;
|
|
459
466
|
this.p2pClient = p2pClient;
|
|
460
467
|
this.blockSource = blockSource;
|
|
@@ -463,6 +470,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
463
470
|
this.l1ToL2MessageSource = l1ToL2MessageSource;
|
|
464
471
|
this.worldStateSynchronizer = worldStateSynchronizer;
|
|
465
472
|
this.sequencer = sequencer;
|
|
473
|
+
this.proverNode = proverNode;
|
|
466
474
|
this.slasherClient = slasherClient;
|
|
467
475
|
this.validatorsSentinel = validatorsSentinel;
|
|
468
476
|
this.epochPruneWatcher = epochPruneWatcher;
|
|
@@ -475,12 +483,21 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
475
483
|
this.telemetry = telemetry;
|
|
476
484
|
this.log = log;
|
|
477
485
|
this.blobClient = blobClient;
|
|
486
|
+
this.validatorClient = validatorClient;
|
|
487
|
+
this.keyStoreManager = keyStoreManager;
|
|
488
|
+
this.debugLogStore = debugLogStore;
|
|
478
489
|
this.initialHeaderHashPromise = (_initProto(this), undefined);
|
|
479
490
|
this.isUploadingSnapshot = false;
|
|
480
491
|
this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
|
|
481
492
|
this.tracer = telemetry.getTracer('AztecNodeService');
|
|
482
493
|
this.log.info(`Aztec Node version: ${this.packageVersion}`);
|
|
483
494
|
this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config.l1Contracts);
|
|
495
|
+
// A defensive check that protects us against introducing a bug in the complex `createAndSync` function. We must
|
|
496
|
+
// never have debugLogStore enabled when not in test mode because then we would be accumulating debug logs in
|
|
497
|
+
// memory which could be a DoS vector on the sequencer (since no fees are paid for debug logs).
|
|
498
|
+
if (debugLogStore.isEnabled && config.realProofs) {
|
|
499
|
+
throw new Error('debugLogStore should never be enabled when realProofs are set');
|
|
500
|
+
}
|
|
484
501
|
}
|
|
485
502
|
async getWorldStateSyncStatus() {
|
|
486
503
|
const status = await this.worldStateSynchronizer.status();
|
|
@@ -502,16 +519,27 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
502
519
|
const telemetry = deps.telemetry ?? getTelemetryClient();
|
|
503
520
|
const dateProvider = deps.dateProvider ?? new DateProvider();
|
|
504
521
|
const ethereumChain = createEthereumChain(config.l1RpcUrls, config.l1ChainId);
|
|
505
|
-
// Build a key store from file if given or from environment otherwise
|
|
522
|
+
// Build a key store from file if given or from environment otherwise.
|
|
523
|
+
// We keep the raw KeyStore available so we can merge with prover keys if enableProverNode is set.
|
|
506
524
|
let keyStoreManager;
|
|
507
525
|
const keyStoreProvided = config.keyStoreDirectory !== undefined && config.keyStoreDirectory.length > 0;
|
|
508
526
|
if (keyStoreProvided) {
|
|
509
527
|
const keyStores = loadKeystores(config.keyStoreDirectory);
|
|
510
528
|
keyStoreManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
511
529
|
} else {
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
530
|
+
const rawKeyStores = [];
|
|
531
|
+
const validatorKeyStore = createKeyStoreForValidator(config);
|
|
532
|
+
if (validatorKeyStore) {
|
|
533
|
+
rawKeyStores.push(validatorKeyStore);
|
|
534
|
+
}
|
|
535
|
+
if (config.enableProverNode) {
|
|
536
|
+
const proverKeyStore = createKeyStoreForProver(config);
|
|
537
|
+
if (proverKeyStore) {
|
|
538
|
+
rawKeyStores.push(proverKeyStore);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (rawKeyStores.length > 0) {
|
|
542
|
+
keyStoreManager = new KeystoreManager(rawKeyStores.length === 1 ? rawKeyStores[0] : mergeKeystores(rawKeyStores));
|
|
515
543
|
}
|
|
516
544
|
}
|
|
517
545
|
await keyStoreManager?.validateSigners();
|
|
@@ -520,8 +548,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
520
548
|
if (keyStoreManager === undefined) {
|
|
521
549
|
throw new Error('Failed to create key store, a requirement for running a validator');
|
|
522
550
|
}
|
|
523
|
-
if (!keyStoreProvided) {
|
|
524
|
-
log.warn(
|
|
551
|
+
if (!keyStoreProvided && process.env.NODE_ENV !== 'test') {
|
|
552
|
+
log.warn("Keystore created from env: it's recommended to use a file-based key store for production");
|
|
525
553
|
}
|
|
526
554
|
ValidatorClient.validateKeyStoreConfiguration(keyStoreManager, log);
|
|
527
555
|
}
|
|
@@ -552,7 +580,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
552
580
|
if (config.rollupVersion !== Number(rollupVersionFromRollup)) {
|
|
553
581
|
log.warn(`Registry looked up and returned a rollup with version (${config.rollupVersion}), but this does not match with version detected from the rollup directly: (${rollupVersionFromRollup}).`);
|
|
554
582
|
}
|
|
555
|
-
const blobClient = await createBlobClientWithFileStores(config,
|
|
583
|
+
const blobClient = await createBlobClientWithFileStores(config, log.createChild('blob-client'));
|
|
556
584
|
// attempt snapshot sync if possible
|
|
557
585
|
await trySnapshotSync(config, log);
|
|
558
586
|
const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, {
|
|
@@ -569,77 +597,92 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
569
597
|
// now create the merkle trees and the world state synchronizer
|
|
570
598
|
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.prefilledPublicData, telemetry);
|
|
571
599
|
const circuitVerifier = config.realProofs || config.debugForceTxProofVerification ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
600
|
+
let debugLogStore;
|
|
572
601
|
if (!config.realProofs) {
|
|
573
602
|
log.warn(`Aztec node is accepting fake proofs`);
|
|
603
|
+
debugLogStore = new InMemoryDebugLogStore();
|
|
604
|
+
log.info('Aztec node started in test mode (realProofs set to false) hence debug logs from public functions will be collected and served');
|
|
605
|
+
} else {
|
|
606
|
+
debugLogStore = new NullDebugLogStore();
|
|
574
607
|
}
|
|
575
608
|
const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
|
|
609
|
+
const proverOnly = config.enableProverNode && config.disableValidator;
|
|
610
|
+
if (proverOnly) {
|
|
611
|
+
log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
|
|
612
|
+
}
|
|
576
613
|
// create the tx pool and the p2p client, which will need the l2 block source
|
|
577
|
-
const p2pClient = await createP2PClient(
|
|
614
|
+
const p2pClient = await createP2PClient(config, archiver, proofVerifier, worldStateSynchronizer, epochCache, packageVersion, dateProvider, telemetry, deps.p2pClientDeps);
|
|
578
615
|
// We should really not be modifying the config object
|
|
579
616
|
config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? await getDefaultAllowedSetupFunctions();
|
|
580
|
-
//
|
|
617
|
+
// We'll accumulate sentinel watchers here
|
|
618
|
+
const watchers = [];
|
|
619
|
+
// Create FullNodeCheckpointsBuilder for block proposal handling and tx validation
|
|
581
620
|
const validatorCheckpointsBuilder = new FullNodeCheckpointsBuilder({
|
|
582
621
|
...config,
|
|
583
622
|
l1GenesisTime,
|
|
584
623
|
slotDuration: Number(slotDuration)
|
|
585
624
|
}, worldStateSynchronizer, archiver, dateProvider, telemetry);
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
591
|
-
worldState: worldStateSynchronizer,
|
|
592
|
-
p2pClient,
|
|
593
|
-
telemetry,
|
|
594
|
-
dateProvider,
|
|
595
|
-
epochCache,
|
|
596
|
-
blockSource: archiver,
|
|
597
|
-
l1ToL2MessageSource: archiver,
|
|
598
|
-
keyStoreManager,
|
|
599
|
-
blobClient
|
|
600
|
-
});
|
|
601
|
-
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
602
|
-
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
603
|
-
// like attestations or auths will fail.
|
|
604
|
-
if (validatorClient) {
|
|
605
|
-
watchers.push(validatorClient);
|
|
606
|
-
if (!options.dontStartSequencer) {
|
|
607
|
-
await validatorClient.registerHandlers();
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// If there's no validator client but alwaysReexecuteBlockProposals is enabled,
|
|
611
|
-
// create a BlockProposalHandler to reexecute block proposals for monitoring
|
|
612
|
-
if (!validatorClient && config.alwaysReexecuteBlockProposals) {
|
|
613
|
-
log.info('Setting up block proposal reexecution for monitoring');
|
|
614
|
-
createBlockProposalHandler(config, {
|
|
625
|
+
let validatorClient;
|
|
626
|
+
if (!proverOnly) {
|
|
627
|
+
// Create validator client if required
|
|
628
|
+
validatorClient = await createValidatorClient(config, {
|
|
615
629
|
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
616
630
|
worldState: worldStateSynchronizer,
|
|
631
|
+
p2pClient,
|
|
632
|
+
telemetry,
|
|
633
|
+
dateProvider,
|
|
617
634
|
epochCache,
|
|
618
635
|
blockSource: archiver,
|
|
619
636
|
l1ToL2MessageSource: archiver,
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
637
|
+
keyStoreManager,
|
|
638
|
+
blobClient
|
|
639
|
+
});
|
|
640
|
+
// If we have a validator client, register it as a source of offenses for the slasher,
|
|
641
|
+
// and have it register callbacks on the p2p client *before* we start it, otherwise messages
|
|
642
|
+
// like attestations or auths will fail.
|
|
643
|
+
if (validatorClient) {
|
|
644
|
+
watchers.push(validatorClient);
|
|
645
|
+
if (!options.dontStartSequencer) {
|
|
646
|
+
await validatorClient.registerHandlers();
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
// If there's no validator client but alwaysReexecuteBlockProposals is enabled,
|
|
650
|
+
// create a BlockProposalHandler to reexecute block proposals for monitoring
|
|
651
|
+
if (!validatorClient && config.alwaysReexecuteBlockProposals) {
|
|
652
|
+
log.info('Setting up block proposal reexecution for monitoring');
|
|
653
|
+
createBlockProposalHandler(config, {
|
|
654
|
+
checkpointsBuilder: validatorCheckpointsBuilder,
|
|
655
|
+
worldState: worldStateSynchronizer,
|
|
656
|
+
epochCache,
|
|
657
|
+
blockSource: archiver,
|
|
658
|
+
l1ToL2MessageSource: archiver,
|
|
659
|
+
p2pClient,
|
|
660
|
+
dateProvider,
|
|
661
|
+
telemetry
|
|
662
|
+
}).registerForReexecution(p2pClient);
|
|
663
|
+
}
|
|
624
664
|
}
|
|
625
665
|
// Start world state and wait for it to sync to the archiver.
|
|
626
666
|
await worldStateSynchronizer.start();
|
|
627
667
|
// Start p2p. Note that it depends on world state to be running.
|
|
628
668
|
await p2pClient.start();
|
|
629
|
-
|
|
630
|
-
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
631
|
-
watchers.push(validatorsSentinel);
|
|
632
|
-
}
|
|
669
|
+
let validatorsSentinel;
|
|
633
670
|
let epochPruneWatcher;
|
|
634
|
-
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
635
|
-
epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), validatorCheckpointsBuilder, config);
|
|
636
|
-
watchers.push(epochPruneWatcher);
|
|
637
|
-
}
|
|
638
|
-
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
639
671
|
let attestationsBlockWatcher;
|
|
640
|
-
if (
|
|
641
|
-
|
|
642
|
-
|
|
672
|
+
if (!proverOnly) {
|
|
673
|
+
validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
674
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
675
|
+
watchers.push(validatorsSentinel);
|
|
676
|
+
}
|
|
677
|
+
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
678
|
+
epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), validatorCheckpointsBuilder, config);
|
|
679
|
+
watchers.push(epochPruneWatcher);
|
|
680
|
+
}
|
|
681
|
+
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
682
|
+
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
683
|
+
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
684
|
+
watchers.push(attestationsBlockWatcher);
|
|
685
|
+
}
|
|
643
686
|
}
|
|
644
687
|
// Start p2p-related services once the archiver has completed sync
|
|
645
688
|
void archiver.waitForInitialSync().then(async ()=>{
|
|
@@ -658,27 +701,29 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
658
701
|
const validatorAddresses = keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses() : [];
|
|
659
702
|
slasherClient = await createSlasher(config, config.l1Contracts, getPublicClient(config), watchers, dateProvider, epochCache, validatorAddresses, undefined);
|
|
660
703
|
await slasherClient.start();
|
|
661
|
-
const l1TxUtils = config.
|
|
704
|
+
const l1TxUtils = config.sequencerPublisherForwarderAddress ? await createForwarderL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), config.sequencerPublisherForwarderAddress, {
|
|
662
705
|
...config,
|
|
663
706
|
scope: 'sequencer'
|
|
664
707
|
}, {
|
|
665
708
|
telemetry,
|
|
666
709
|
logger: log.createChild('l1-tx-utils'),
|
|
667
|
-
dateProvider
|
|
668
|
-
|
|
710
|
+
dateProvider,
|
|
711
|
+
kzg: Blob.getViemKzgInstance()
|
|
712
|
+
}) : await createL1TxUtilsFromSigners(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), {
|
|
669
713
|
...config,
|
|
670
714
|
scope: 'sequencer'
|
|
671
715
|
}, {
|
|
672
716
|
telemetry,
|
|
673
717
|
logger: log.createChild('l1-tx-utils'),
|
|
674
|
-
dateProvider
|
|
718
|
+
dateProvider,
|
|
719
|
+
kzg: Blob.getViemKzgInstance()
|
|
675
720
|
});
|
|
676
721
|
// Create and start the sequencer client
|
|
677
722
|
const checkpointsBuilder = new CheckpointsBuilder({
|
|
678
723
|
...config,
|
|
679
724
|
l1GenesisTime,
|
|
680
725
|
slotDuration: Number(slotDuration)
|
|
681
|
-
}, worldStateSynchronizer, archiver, dateProvider, telemetry);
|
|
726
|
+
}, worldStateSynchronizer, archiver, dateProvider, telemetry, debugLogStore);
|
|
682
727
|
sequencer = await SequencerClient.new(config, {
|
|
683
728
|
...deps,
|
|
684
729
|
epochCache,
|
|
@@ -702,13 +747,35 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
702
747
|
} else if (sequencer) {
|
|
703
748
|
log.warn(`Sequencer created but not started`);
|
|
704
749
|
}
|
|
750
|
+
// Create prover node subsystem if enabled
|
|
751
|
+
let proverNode;
|
|
752
|
+
if (config.enableProverNode) {
|
|
753
|
+
proverNode = await createProverNode(config, {
|
|
754
|
+
...deps.proverNodeDeps,
|
|
755
|
+
telemetry,
|
|
756
|
+
dateProvider,
|
|
757
|
+
archiver,
|
|
758
|
+
worldStateSynchronizer,
|
|
759
|
+
p2pClient,
|
|
760
|
+
epochCache,
|
|
761
|
+
blobClient,
|
|
762
|
+
keyStoreManager
|
|
763
|
+
});
|
|
764
|
+
if (!options.dontStartProverNode) {
|
|
765
|
+
await proverNode.start();
|
|
766
|
+
log.info(`Prover node subsystem started`);
|
|
767
|
+
} else {
|
|
768
|
+
log.info(`Prover node subsystem created but not started`);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
705
771
|
const globalVariableBuilder = new GlobalVariableBuilder({
|
|
706
772
|
...config,
|
|
707
773
|
rollupVersion: BigInt(config.rollupVersion),
|
|
708
774
|
l1GenesisTime,
|
|
709
775
|
slotDuration: Number(slotDuration)
|
|
710
776
|
});
|
|
711
|
-
|
|
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);
|
|
778
|
+
return node;
|
|
712
779
|
}
|
|
713
780
|
/**
|
|
714
781
|
* Returns the sequencer client instance.
|
|
@@ -716,6 +783,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
716
783
|
*/ getSequencer() {
|
|
717
784
|
return this.sequencer;
|
|
718
785
|
}
|
|
786
|
+
/** Returns the prover node subsystem, if enabled. */ getProverNode() {
|
|
787
|
+
return this.proverNode;
|
|
788
|
+
}
|
|
719
789
|
getBlockSource() {
|
|
720
790
|
return this.blockSource;
|
|
721
791
|
}
|
|
@@ -758,7 +828,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
758
828
|
rollupVersion,
|
|
759
829
|
enr,
|
|
760
830
|
l1ContractAddresses: contractAddresses,
|
|
761
|
-
protocolContractAddresses: protocolContractAddresses
|
|
831
|
+
protocolContractAddresses: protocolContractAddresses,
|
|
832
|
+
realProofs: !!this.config.realProofs
|
|
762
833
|
};
|
|
763
834
|
return nodeInfo;
|
|
764
835
|
}
|
|
@@ -767,14 +838,14 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
767
838
|
* @param block - The block parameter (block number, block hash, or 'latest').
|
|
768
839
|
* @returns The requested block.
|
|
769
840
|
*/ async getBlock(block) {
|
|
770
|
-
if (
|
|
771
|
-
return this.getBlockByHash(
|
|
841
|
+
if (BlockHash.isBlockHash(block)) {
|
|
842
|
+
return this.getBlockByHash(block);
|
|
772
843
|
}
|
|
773
844
|
const blockNumber = block === 'latest' ? await this.getBlockNumber() : block;
|
|
774
845
|
if (blockNumber === BlockNumber.ZERO) {
|
|
775
846
|
return this.buildInitialBlock();
|
|
776
847
|
}
|
|
777
|
-
return await this.blockSource.
|
|
848
|
+
return await this.blockSource.getL2Block(blockNumber);
|
|
778
849
|
}
|
|
779
850
|
/**
|
|
780
851
|
* Get a block specified by its hash.
|
|
@@ -782,21 +853,21 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
782
853
|
* @returns The requested block.
|
|
783
854
|
*/ async getBlockByHash(blockHash) {
|
|
784
855
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
785
|
-
if (blockHash.equals(
|
|
856
|
+
if (blockHash.equals(initialBlockHash)) {
|
|
786
857
|
return this.buildInitialBlock();
|
|
787
858
|
}
|
|
788
|
-
return await this.blockSource.
|
|
859
|
+
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
789
860
|
}
|
|
790
861
|
buildInitialBlock() {
|
|
791
862
|
const initialHeader = this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
792
|
-
return
|
|
863
|
+
return L2Block.empty(initialHeader);
|
|
793
864
|
}
|
|
794
865
|
/**
|
|
795
866
|
* Get a block specified by its archive root.
|
|
796
867
|
* @param archive - The archive root being requested.
|
|
797
868
|
* @returns The requested block.
|
|
798
869
|
*/ async getBlockByArchive(archive) {
|
|
799
|
-
return await this.blockSource.
|
|
870
|
+
return await this.blockSource.getL2BlockByArchive(archive);
|
|
800
871
|
}
|
|
801
872
|
/**
|
|
802
873
|
* Method to request blocks. Will attempt to return all requested blocks but will return only those available.
|
|
@@ -804,19 +875,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
804
875
|
* @param limit - The maximum number of blocks to obtain.
|
|
805
876
|
* @returns The blocks requested.
|
|
806
877
|
*/ async getBlocks(from, limit) {
|
|
807
|
-
return await this.blockSource.
|
|
808
|
-
}
|
|
809
|
-
async getPublishedBlocks(from, limit) {
|
|
810
|
-
return await this.blockSource.getPublishedBlocks(from, limit) ?? [];
|
|
811
|
-
}
|
|
812
|
-
async getPublishedCheckpoints(from, limit) {
|
|
813
|
-
return await this.blockSource.getPublishedCheckpoints(from, limit) ?? [];
|
|
878
|
+
return await this.blockSource.getBlocks(from, BlockNumber(limit)) ?? [];
|
|
814
879
|
}
|
|
815
|
-
async
|
|
816
|
-
return await this.blockSource.
|
|
880
|
+
async getCheckpoints(from, limit) {
|
|
881
|
+
return await this.blockSource.getCheckpoints(from, limit) ?? [];
|
|
817
882
|
}
|
|
818
|
-
async getCheckpointedBlocks(from, limit
|
|
819
|
-
return await this.blockSource.getCheckpointedBlocks(from, limit
|
|
883
|
+
async getCheckpointedBlocks(from, limit) {
|
|
884
|
+
return await this.blockSource.getCheckpointedBlocks(from, limit) ?? [];
|
|
820
885
|
}
|
|
821
886
|
/**
|
|
822
887
|
* Method to fetch the current min L2 fees.
|
|
@@ -842,6 +907,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
842
907
|
async getProvenBlockNumber() {
|
|
843
908
|
return await this.blockSource.getProvenBlockNumber();
|
|
844
909
|
}
|
|
910
|
+
async getCheckpointedBlockNumber() {
|
|
911
|
+
return await this.blockSource.getCheckpointedL2BlockNumber();
|
|
912
|
+
}
|
|
845
913
|
/**
|
|
846
914
|
* Method to fetch the version of the package.
|
|
847
915
|
* @returns The node package version
|
|
@@ -866,10 +934,28 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
866
934
|
getContract(address) {
|
|
867
935
|
return this.contractDataSource.getContract(address);
|
|
868
936
|
}
|
|
869
|
-
getPrivateLogsByTags(tags, page) {
|
|
937
|
+
async getPrivateLogsByTags(tags, page, referenceBlock) {
|
|
938
|
+
if (referenceBlock) {
|
|
939
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
940
|
+
if (!referenceBlock.equals(initialBlockHash)) {
|
|
941
|
+
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
942
|
+
if (!header) {
|
|
943
|
+
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
}
|
|
870
947
|
return this.logsSource.getPrivateLogsByTags(tags, page);
|
|
871
948
|
}
|
|
872
|
-
getPublicLogsByTagsFromContract(contractAddress, tags, page) {
|
|
949
|
+
async getPublicLogsByTagsFromContract(contractAddress, tags, page, referenceBlock) {
|
|
950
|
+
if (referenceBlock) {
|
|
951
|
+
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
952
|
+
if (!referenceBlock.equals(initialBlockHash)) {
|
|
953
|
+
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
954
|
+
if (!header) {
|
|
955
|
+
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
873
959
|
return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
|
|
874
960
|
}
|
|
875
961
|
/**
|
|
@@ -905,24 +991,33 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
905
991
|
throw new Error(`Invalid tx: ${reason}`);
|
|
906
992
|
}
|
|
907
993
|
await this.p2pClient.sendTx(tx);
|
|
908
|
-
|
|
909
|
-
this.
|
|
994
|
+
const duration = timer.ms();
|
|
995
|
+
this.metrics.receivedTx(duration, true);
|
|
996
|
+
this.log.info(`Received tx ${txHash} in ${duration}ms`, {
|
|
910
997
|
txHash
|
|
911
998
|
});
|
|
912
999
|
}
|
|
913
1000
|
async getTxReceipt(txHash) {
|
|
914
|
-
|
|
915
|
-
//
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
txReceipt = new TxReceipt(txHash, TxStatus.PENDING, '');
|
|
920
|
-
}
|
|
1001
|
+
// Check the tx pool status first. If the tx is known to the pool (pending or mined), we'll use that
|
|
1002
|
+
// as a fallback if we don't find a settled receipt in the archiver.
|
|
1003
|
+
const txPoolStatus = await this.p2pClient.getTxStatus(txHash);
|
|
1004
|
+
const isKnownToPool = txPoolStatus === 'pending' || txPoolStatus === 'mined';
|
|
1005
|
+
// Then get the actual tx from the archiver, which tracks every tx in a mined block.
|
|
921
1006
|
const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
|
|
1007
|
+
let receipt;
|
|
922
1008
|
if (settledTxReceipt) {
|
|
923
|
-
|
|
1009
|
+
receipt = settledTxReceipt;
|
|
1010
|
+
} else if (isKnownToPool) {
|
|
1011
|
+
// If the tx is in the pool but not in the archiver, it's pending.
|
|
1012
|
+
// This handles race conditions between archiver and p2p, where the archiver
|
|
1013
|
+
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
1014
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
1015
|
+
} else {
|
|
1016
|
+
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
1017
|
+
receipt = new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
|
|
924
1018
|
}
|
|
925
|
-
|
|
1019
|
+
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
1020
|
+
return receipt;
|
|
926
1021
|
}
|
|
927
1022
|
getTxEffect(txHash) {
|
|
928
1023
|
return this.blockSource.getTxEffect(txHash);
|
|
@@ -936,6 +1031,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
936
1031
|
await tryStop(this.slasherClient);
|
|
937
1032
|
await tryStop(this.proofVerifier);
|
|
938
1033
|
await tryStop(this.sequencer);
|
|
1034
|
+
await tryStop(this.proverNode);
|
|
939
1035
|
await tryStop(this.p2pClient);
|
|
940
1036
|
await tryStop(this.worldStateSynchronizer);
|
|
941
1037
|
await tryStop(this.blockSource);
|
|
@@ -974,8 +1070,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
974
1070
|
*/ async getTxsByHash(txHashes) {
|
|
975
1071
|
return compactArray(await Promise.all(txHashes.map((txHash)=>this.getTxByHash(txHash))));
|
|
976
1072
|
}
|
|
977
|
-
async findLeavesIndexes(
|
|
978
|
-
const committedDb = await this.#getWorldState(
|
|
1073
|
+
async findLeavesIndexes(referenceBlock, treeId, leafValues) {
|
|
1074
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
979
1075
|
const maybeIndices = await committedDb.findLeafIndices(treeId, leafValues.map((x)=>x.toBuffer()));
|
|
980
1076
|
// We filter out undefined values
|
|
981
1077
|
const indices = maybeIndices.filter((x)=>x !== undefined);
|
|
@@ -1018,35 +1114,27 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1018
1114
|
}
|
|
1019
1115
|
return {
|
|
1020
1116
|
l2BlockNumber: BlockNumber(Number(blockNumber)),
|
|
1021
|
-
l2BlockHash:
|
|
1117
|
+
l2BlockHash: new BlockHash(blockHash),
|
|
1022
1118
|
data: index
|
|
1023
1119
|
};
|
|
1024
1120
|
});
|
|
1025
1121
|
}
|
|
1026
|
-
async
|
|
1027
|
-
const committedDb = await this.#getWorldState(
|
|
1028
|
-
return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
|
|
1029
|
-
}
|
|
1030
|
-
async getNoteHashSiblingPath(block, leafIndex) {
|
|
1031
|
-
const committedDb = await this.#getWorldState(block);
|
|
1032
|
-
return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
|
|
1033
|
-
}
|
|
1034
|
-
async getArchiveMembershipWitness(block, archive) {
|
|
1035
|
-
const committedDb = await this.#getWorldState(block);
|
|
1122
|
+
async getBlockHashMembershipWitness(referenceBlock, blockHash) {
|
|
1123
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1036
1124
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
|
|
1037
|
-
|
|
1125
|
+
blockHash
|
|
1038
1126
|
]);
|
|
1039
1127
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1040
1128
|
}
|
|
1041
|
-
async getNoteHashMembershipWitness(
|
|
1042
|
-
const committedDb = await this.#getWorldState(
|
|
1129
|
+
async getNoteHashMembershipWitness(referenceBlock, noteHash) {
|
|
1130
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1043
1131
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.NOTE_HASH_TREE, [
|
|
1044
1132
|
noteHash
|
|
1045
1133
|
]);
|
|
1046
1134
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1047
1135
|
}
|
|
1048
|
-
async getL1ToL2MessageMembershipWitness(
|
|
1049
|
-
const db = await this.#getWorldState(
|
|
1136
|
+
async getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message) {
|
|
1137
|
+
const db = await this.#getWorldState(referenceBlock);
|
|
1050
1138
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [
|
|
1051
1139
|
l1ToL2Message
|
|
1052
1140
|
]);
|
|
@@ -1076,12 +1164,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1076
1164
|
* @param epoch - The epoch at which to get the data.
|
|
1077
1165
|
* @returns The L2 to L1 messages (empty array if the epoch is not found).
|
|
1078
1166
|
*/ async getL2ToL1Messages(epoch) {
|
|
1079
|
-
// Assumes `
|
|
1080
|
-
const
|
|
1167
|
+
// Assumes `getCheckpointedBlocksForEpoch` returns blocks in ascending order of block number.
|
|
1168
|
+
const checkpointedBlocks = await this.blockSource.getCheckpointedBlocksForEpoch(epoch);
|
|
1081
1169
|
const blocksInCheckpoints = [];
|
|
1082
1170
|
let previousSlotNumber = SlotNumber.ZERO;
|
|
1083
1171
|
let checkpointIndex = -1;
|
|
1084
|
-
for (const
|
|
1172
|
+
for (const checkpointedBlock of checkpointedBlocks){
|
|
1173
|
+
const block = checkpointedBlock.block;
|
|
1085
1174
|
const slotNumber = block.header.globalVariables.slotNumber;
|
|
1086
1175
|
if (slotNumber !== previousSlotNumber) {
|
|
1087
1176
|
checkpointIndex++;
|
|
@@ -1092,16 +1181,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1092
1181
|
}
|
|
1093
1182
|
return blocksInCheckpoints.map((blocks)=>blocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
|
|
1094
1183
|
}
|
|
1095
|
-
async
|
|
1096
|
-
const
|
|
1097
|
-
return committedDb.getSiblingPath(MerkleTreeId.ARCHIVE, leafIndex);
|
|
1098
|
-
}
|
|
1099
|
-
async getPublicDataSiblingPath(block, leafIndex) {
|
|
1100
|
-
const committedDb = await this.#getWorldState(block);
|
|
1101
|
-
return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
|
|
1102
|
-
}
|
|
1103
|
-
async getNullifierMembershipWitness(block, nullifier) {
|
|
1104
|
-
const db = await this.#getWorldState(block);
|
|
1184
|
+
async getNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1185
|
+
const db = await this.#getWorldState(referenceBlock);
|
|
1105
1186
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [
|
|
1106
1187
|
nullifier.toBuffer()
|
|
1107
1188
|
]);
|
|
@@ -1117,7 +1198,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1117
1198
|
}
|
|
1118
1199
|
/**
|
|
1119
1200
|
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1120
|
-
* @param
|
|
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).
|
|
1121
1203
|
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1122
1204
|
* @returns The low nullifier membership witness (if found).
|
|
1123
1205
|
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
@@ -1128,8 +1210,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1128
1210
|
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1129
1211
|
* index of the nullifier itself when it already exists in the tree.
|
|
1130
1212
|
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1131
|
-
*/ async getLowNullifierMembershipWitness(
|
|
1132
|
-
const committedDb = await this.#getWorldState(
|
|
1213
|
+
*/ async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1214
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1133
1215
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1134
1216
|
if (!findResult) {
|
|
1135
1217
|
return undefined;
|
|
@@ -1142,8 +1224,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1142
1224
|
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
1143
1225
|
return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath);
|
|
1144
1226
|
}
|
|
1145
|
-
async getPublicDataWitness(
|
|
1146
|
-
const committedDb = await this.#getWorldState(
|
|
1227
|
+
async getPublicDataWitness(referenceBlock, leafSlot) {
|
|
1228
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1147
1229
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1148
1230
|
if (!lowLeafResult) {
|
|
1149
1231
|
return undefined;
|
|
@@ -1153,8 +1235,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1153
1235
|
return new PublicDataWitness(lowLeafResult.index, preimage, path);
|
|
1154
1236
|
}
|
|
1155
1237
|
}
|
|
1156
|
-
async getPublicStorageAt(
|
|
1157
|
-
const committedDb = await this.#getWorldState(
|
|
1238
|
+
async getPublicStorageAt(referenceBlock, contract, slot) {
|
|
1239
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1158
1240
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1159
1241
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1160
1242
|
if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
|
|
@@ -1164,14 +1246,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1164
1246
|
return preimage.leaf.value;
|
|
1165
1247
|
}
|
|
1166
1248
|
async getBlockHeader(block = 'latest') {
|
|
1167
|
-
if (
|
|
1249
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1168
1250
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1169
1251
|
if (block.equals(initialBlockHash)) {
|
|
1170
1252
|
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1171
1253
|
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1172
1254
|
}
|
|
1173
|
-
|
|
1174
|
-
return this.blockSource.getBlockHeaderByHash(blockHashFr);
|
|
1255
|
+
return this.blockSource.getBlockHeaderByHash(block);
|
|
1175
1256
|
} else {
|
|
1176
1257
|
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1177
1258
|
const blockNumber = block === 'latest' ? await this.getBlockNumber() : block;
|
|
@@ -1188,6 +1269,12 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1188
1269
|
*/ async getBlockHeaderByArchive(archive) {
|
|
1189
1270
|
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1190
1271
|
}
|
|
1272
|
+
getBlockData(number) {
|
|
1273
|
+
return this.blockSource.getBlockData(number);
|
|
1274
|
+
}
|
|
1275
|
+
getBlockDataByArchive(archive) {
|
|
1276
|
+
return this.blockSource.getBlockDataByArchive(archive);
|
|
1277
|
+
}
|
|
1191
1278
|
/**
|
|
1192
1279
|
* Simulates the public part of a transaction with the current state.
|
|
1193
1280
|
* @param tx - The transaction to simulate.
|
|
@@ -1200,17 +1287,20 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1200
1287
|
throw new BadRequestError(`Transaction total gas limit ${txGasLimit + teardownGasLimit} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${this.config.rpcSimulatePublicMaxGasLimit} for simulation`);
|
|
1201
1288
|
}
|
|
1202
1289
|
const txHash = tx.getTxHash();
|
|
1203
|
-
const
|
|
1290
|
+
const latestBlockNumber = await this.blockSource.getBlockNumber();
|
|
1291
|
+
const blockNumber = BlockNumber.add(latestBlockNumber, 1);
|
|
1204
1292
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
1205
1293
|
const coinbase = EthAddress.ZERO;
|
|
1206
1294
|
const feeRecipient = AztecAddress.ZERO;
|
|
1207
1295
|
const newGlobalVariables = await this.globalVariableBuilder.buildGlobalVariables(blockNumber, coinbase, feeRecipient);
|
|
1208
|
-
const publicProcessorFactory = new PublicProcessorFactory(this.contractDataSource, new DateProvider(), this.telemetry);
|
|
1296
|
+
const publicProcessorFactory = new PublicProcessorFactory(this.contractDataSource, new DateProvider(), this.telemetry, this.log.getBindings());
|
|
1209
1297
|
this.log.verbose(`Simulating public calls for tx ${txHash}`, {
|
|
1210
1298
|
globalVariables: newGlobalVariables.toInspect(),
|
|
1211
1299
|
txHash,
|
|
1212
1300
|
blockNumber
|
|
1213
1301
|
});
|
|
1302
|
+
// Ensure world-state has caught up with the latest block we loaded from the archiver
|
|
1303
|
+
await this.worldStateSynchronizer.syncImmediate(latestBlockNumber);
|
|
1214
1304
|
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
1215
1305
|
try {
|
|
1216
1306
|
const config = PublicSimulatorConfig.from({
|
|
@@ -1225,7 +1315,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1225
1315
|
});
|
|
1226
1316
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1227
1317
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1228
|
-
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([
|
|
1318
|
+
const [processedTxs, failedTxs, _usedTxs, returns, _blobFields, debugLogs] = await processor.process([
|
|
1229
1319
|
tx
|
|
1230
1320
|
]);
|
|
1231
1321
|
// REFACTOR: Consider returning the error rather than throwing
|
|
@@ -1236,7 +1326,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1236
1326
|
throw failedTxs[0].error;
|
|
1237
1327
|
}
|
|
1238
1328
|
const [processedTx] = processedTxs;
|
|
1239
|
-
return new PublicSimulationOutput(processedTx.revertReason, processedTx.globalVariables, processedTx.txEffect, returns, processedTx.gasUsed);
|
|
1329
|
+
return new PublicSimulationOutput(processedTx.revertReason, processedTx.globalVariables, processedTx.txEffect, returns, processedTx.gasUsed, debugLogs);
|
|
1240
1330
|
} finally{
|
|
1241
1331
|
await merkleTreeFork.close();
|
|
1242
1332
|
}
|
|
@@ -1244,10 +1334,10 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1244
1334
|
async isValidTx(tx, { isSimulation, skipFeeEnforcement } = {}) {
|
|
1245
1335
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1246
1336
|
const verifier = isSimulation ? undefined : this.proofVerifier;
|
|
1247
|
-
// We accept transactions if they are not expired by the next slot (checked based on the
|
|
1337
|
+
// We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
|
|
1248
1338
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1249
1339
|
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
1250
|
-
const validator =
|
|
1340
|
+
const validator = createTxValidatorForAcceptingTxsOverRPC(db, this.contractDataSource, verifier, {
|
|
1251
1341
|
timestamp: nextSlotTimestamp,
|
|
1252
1342
|
blockNumber,
|
|
1253
1343
|
l1ChainId: this.l1ChainId,
|
|
@@ -1256,7 +1346,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1256
1346
|
gasFees: await this.getCurrentMinFees(),
|
|
1257
1347
|
skipFeeEnforcement,
|
|
1258
1348
|
txsPermitted: !this.config.disableTransactions
|
|
1259
|
-
});
|
|
1349
|
+
}, this.log.getBindings());
|
|
1260
1350
|
return await validator.validateTx(tx);
|
|
1261
1351
|
}
|
|
1262
1352
|
getConfig() {
|
|
@@ -1398,9 +1488,76 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1398
1488
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1399
1489
|
}
|
|
1400
1490
|
}
|
|
1491
|
+
async reloadKeystore() {
|
|
1492
|
+
if (!this.config.keyStoreDirectory?.length) {
|
|
1493
|
+
throw new BadRequestError('Cannot reload keystore: node is not using a file-based keystore. ' + 'Set KEY_STORE_DIRECTORY to use file-based keystores.');
|
|
1494
|
+
}
|
|
1495
|
+
if (!this.validatorClient) {
|
|
1496
|
+
throw new BadRequestError('Cannot reload keystore: validator is not enabled.');
|
|
1497
|
+
}
|
|
1498
|
+
this.log.info('Reloading keystore from disk');
|
|
1499
|
+
// Re-read and validate keystore files
|
|
1500
|
+
const keyStores = loadKeystores(this.config.keyStoreDirectory);
|
|
1501
|
+
const newManager = new KeystoreManager(mergeKeystores(keyStores));
|
|
1502
|
+
await newManager.validateSigners();
|
|
1503
|
+
ValidatorClient.validateKeyStoreConfiguration(newManager, this.log);
|
|
1504
|
+
// Validate that every validator's publisher keys overlap with the L1 signers
|
|
1505
|
+
// that were initialized at startup. Publishers cannot be hot-reloaded, so a
|
|
1506
|
+
// validator with a publisher key that doesn't match any existing L1 signer
|
|
1507
|
+
// would silently fail on every proposer slot.
|
|
1508
|
+
if (this.keyStoreManager && this.sequencer) {
|
|
1509
|
+
const oldAdapter = NodeKeystoreAdapter.fromKeyStoreManager(this.keyStoreManager);
|
|
1510
|
+
const availablePublishers = new Set(oldAdapter.getAttesterAddresses().flatMap((a)=>oldAdapter.getPublisherAddresses(a).map((p)=>p.toString().toLowerCase())));
|
|
1511
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
1512
|
+
for (const attester of newAdapter.getAttesterAddresses()){
|
|
1513
|
+
const pubs = newAdapter.getPublisherAddresses(attester);
|
|
1514
|
+
if (pubs.length > 0 && !pubs.some((p)=>availablePublishers.has(p.toString().toLowerCase()))) {
|
|
1515
|
+
throw new BadRequestError(`Cannot reload keystore: validator ${attester} has publisher keys ` + `[${pubs.map((p)=>p.toString()).join(', ')}] but none match the L1 signers initialized at startup ` + `[${[
|
|
1516
|
+
...availablePublishers
|
|
1517
|
+
].join(', ')}]. Publishers cannot be hot-reloaded — ` + `use an existing publisher key or restart the node.`);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
// Build adapters for old and new keystores to compute diff
|
|
1522
|
+
const newAdapter = NodeKeystoreAdapter.fromKeyStoreManager(newManager);
|
|
1523
|
+
const newAddresses = newAdapter.getAttesterAddresses();
|
|
1524
|
+
const oldAddresses = this.keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(this.keyStoreManager).getAttesterAddresses() : [];
|
|
1525
|
+
const oldSet = new Set(oldAddresses.map((a)=>a.toString()));
|
|
1526
|
+
const newSet = new Set(newAddresses.map((a)=>a.toString()));
|
|
1527
|
+
const added = newAddresses.filter((a)=>!oldSet.has(a.toString()));
|
|
1528
|
+
const removed = oldAddresses.filter((a)=>!newSet.has(a.toString()));
|
|
1529
|
+
if (added.length > 0) {
|
|
1530
|
+
this.log.info(`Keystore reload: adding attester keys: ${added.map((a)=>a.toString()).join(', ')}`);
|
|
1531
|
+
}
|
|
1532
|
+
if (removed.length > 0) {
|
|
1533
|
+
this.log.info(`Keystore reload: removing attester keys: ${removed.map((a)=>a.toString()).join(', ')}`);
|
|
1534
|
+
}
|
|
1535
|
+
if (added.length === 0 && removed.length === 0) {
|
|
1536
|
+
this.log.info('Keystore reload: attester keys unchanged');
|
|
1537
|
+
}
|
|
1538
|
+
// Update the validator client (coinbase, feeRecipient, attester keys)
|
|
1539
|
+
this.validatorClient.reloadKeystore(newManager);
|
|
1540
|
+
// Update the publisher factory's keystore so newly-added validators
|
|
1541
|
+
// can be matched to existing publisher keys when proposing blocks.
|
|
1542
|
+
if (this.sequencer) {
|
|
1543
|
+
this.sequencer.updatePublisherNodeKeyStore(newAdapter);
|
|
1544
|
+
}
|
|
1545
|
+
// Update slasher's "don't-slash-self" list with new validator addresses
|
|
1546
|
+
if (this.slasherClient && !this.config.slashSelfAllowed) {
|
|
1547
|
+
const slashValidatorsNever = unique([
|
|
1548
|
+
...this.config.slashValidatorsNever ?? [],
|
|
1549
|
+
...newAddresses
|
|
1550
|
+
].map((a)=>a.toString())).map(EthAddress.fromString);
|
|
1551
|
+
this.slasherClient.updateConfig({
|
|
1552
|
+
slashValidatorsNever
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
this.keyStoreManager = newManager;
|
|
1556
|
+
this.log.info('Keystore reloaded: coinbase, feeRecipient, and attester keys updated');
|
|
1557
|
+
}
|
|
1401
1558
|
#getInitialHeaderHash() {
|
|
1402
1559
|
if (!this.initialHeaderHashPromise) {
|
|
1403
|
-
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash()
|
|
1560
|
+
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
1404
1561
|
}
|
|
1405
1562
|
return this.initialHeaderHashPromise;
|
|
1406
1563
|
}
|
|
@@ -1420,14 +1577,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1420
1577
|
this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
|
|
1421
1578
|
return this.worldStateSynchronizer.getCommitted();
|
|
1422
1579
|
}
|
|
1423
|
-
if (
|
|
1580
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1424
1581
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1425
1582
|
if (block.equals(initialBlockHash)) {
|
|
1426
1583
|
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1427
1584
|
return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
|
|
1428
1585
|
}
|
|
1429
|
-
const
|
|
1430
|
-
const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
|
|
1586
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1431
1587
|
if (!header) {
|
|
1432
1588
|
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.`);
|
|
1433
1589
|
}
|