@aztec/aztec-node 0.0.1-commit.e61ad554 → 0.0.1-commit.ec5f612
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 +33 -23
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +261 -129
- 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 +348 -182
- 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,8 +838,8 @@ _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) {
|
|
@@ -782,7 +853,7 @@ _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
859
|
return await this.blockSource.getL2BlockByHash(blockHash);
|
|
@@ -867,8 +938,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
867
938
|
if (referenceBlock) {
|
|
868
939
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
869
940
|
if (!referenceBlock.equals(initialBlockHash)) {
|
|
870
|
-
const
|
|
871
|
-
const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
|
|
941
|
+
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
872
942
|
if (!header) {
|
|
873
943
|
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
874
944
|
}
|
|
@@ -880,8 +950,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
880
950
|
if (referenceBlock) {
|
|
881
951
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
882
952
|
if (!referenceBlock.equals(initialBlockHash)) {
|
|
883
|
-
const
|
|
884
|
-
const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
|
|
953
|
+
const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
|
|
885
954
|
if (!header) {
|
|
886
955
|
throw new Error(`Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`);
|
|
887
956
|
}
|
|
@@ -922,8 +991,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
922
991
|
throw new Error(`Invalid tx: ${reason}`);
|
|
923
992
|
}
|
|
924
993
|
await this.p2pClient.sendTx(tx);
|
|
925
|
-
|
|
926
|
-
this.
|
|
994
|
+
const duration = timer.ms();
|
|
995
|
+
this.metrics.receivedTx(duration, true);
|
|
996
|
+
this.log.info(`Received tx ${txHash} in ${duration}ms`, {
|
|
927
997
|
txHash
|
|
928
998
|
});
|
|
929
999
|
}
|
|
@@ -934,18 +1004,20 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
934
1004
|
const isKnownToPool = txPoolStatus === 'pending' || txPoolStatus === 'mined';
|
|
935
1005
|
// Then get the actual tx from the archiver, which tracks every tx in a mined block.
|
|
936
1006
|
const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
|
|
1007
|
+
let receipt;
|
|
937
1008
|
if (settledTxReceipt) {
|
|
938
|
-
|
|
939
|
-
return settledTxReceipt;
|
|
1009
|
+
receipt = settledTxReceipt;
|
|
940
1010
|
} else if (isKnownToPool) {
|
|
941
1011
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
942
1012
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
943
1013
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
944
|
-
|
|
1014
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
945
1015
|
} else {
|
|
946
1016
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
947
|
-
|
|
1017
|
+
receipt = new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
|
|
948
1018
|
}
|
|
1019
|
+
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
1020
|
+
return receipt;
|
|
949
1021
|
}
|
|
950
1022
|
getTxEffect(txHash) {
|
|
951
1023
|
return this.blockSource.getTxEffect(txHash);
|
|
@@ -959,6 +1031,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
959
1031
|
await tryStop(this.slasherClient);
|
|
960
1032
|
await tryStop(this.proofVerifier);
|
|
961
1033
|
await tryStop(this.sequencer);
|
|
1034
|
+
await tryStop(this.proverNode);
|
|
962
1035
|
await tryStop(this.p2pClient);
|
|
963
1036
|
await tryStop(this.worldStateSynchronizer);
|
|
964
1037
|
await tryStop(this.blockSource);
|
|
@@ -997,8 +1070,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
997
1070
|
*/ async getTxsByHash(txHashes) {
|
|
998
1071
|
return compactArray(await Promise.all(txHashes.map((txHash)=>this.getTxByHash(txHash))));
|
|
999
1072
|
}
|
|
1000
|
-
async findLeavesIndexes(
|
|
1001
|
-
const committedDb = await this.#getWorldState(
|
|
1073
|
+
async findLeavesIndexes(referenceBlock, treeId, leafValues) {
|
|
1074
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1002
1075
|
const maybeIndices = await committedDb.findLeafIndices(treeId, leafValues.map((x)=>x.toBuffer()));
|
|
1003
1076
|
// We filter out undefined values
|
|
1004
1077
|
const indices = maybeIndices.filter((x)=>x !== undefined);
|
|
@@ -1041,35 +1114,27 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1041
1114
|
}
|
|
1042
1115
|
return {
|
|
1043
1116
|
l2BlockNumber: BlockNumber(Number(blockNumber)),
|
|
1044
|
-
l2BlockHash:
|
|
1117
|
+
l2BlockHash: new BlockHash(blockHash),
|
|
1045
1118
|
data: index
|
|
1046
1119
|
};
|
|
1047
1120
|
});
|
|
1048
1121
|
}
|
|
1049
|
-
async
|
|
1050
|
-
const committedDb = await this.#getWorldState(
|
|
1051
|
-
return committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, leafIndex);
|
|
1052
|
-
}
|
|
1053
|
-
async getNoteHashSiblingPath(block, leafIndex) {
|
|
1054
|
-
const committedDb = await this.#getWorldState(block);
|
|
1055
|
-
return committedDb.getSiblingPath(MerkleTreeId.NOTE_HASH_TREE, leafIndex);
|
|
1056
|
-
}
|
|
1057
|
-
async getArchiveMembershipWitness(block, archive) {
|
|
1058
|
-
const committedDb = await this.#getWorldState(block);
|
|
1122
|
+
async getBlockHashMembershipWitness(referenceBlock, blockHash) {
|
|
1123
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1059
1124
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
|
|
1060
|
-
|
|
1125
|
+
blockHash
|
|
1061
1126
|
]);
|
|
1062
1127
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1063
1128
|
}
|
|
1064
|
-
async getNoteHashMembershipWitness(
|
|
1065
|
-
const committedDb = await this.#getWorldState(
|
|
1129
|
+
async getNoteHashMembershipWitness(referenceBlock, noteHash) {
|
|
1130
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1066
1131
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.NOTE_HASH_TREE, [
|
|
1067
1132
|
noteHash
|
|
1068
1133
|
]);
|
|
1069
1134
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1070
1135
|
}
|
|
1071
|
-
async getL1ToL2MessageMembershipWitness(
|
|
1072
|
-
const db = await this.#getWorldState(
|
|
1136
|
+
async getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message) {
|
|
1137
|
+
const db = await this.#getWorldState(referenceBlock);
|
|
1073
1138
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [
|
|
1074
1139
|
l1ToL2Message
|
|
1075
1140
|
]);
|
|
@@ -1116,16 +1181,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1116
1181
|
}
|
|
1117
1182
|
return blocksInCheckpoints.map((blocks)=>blocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
|
|
1118
1183
|
}
|
|
1119
|
-
async
|
|
1120
|
-
const
|
|
1121
|
-
return committedDb.getSiblingPath(MerkleTreeId.ARCHIVE, leafIndex);
|
|
1122
|
-
}
|
|
1123
|
-
async getPublicDataSiblingPath(block, leafIndex) {
|
|
1124
|
-
const committedDb = await this.#getWorldState(block);
|
|
1125
|
-
return committedDb.getSiblingPath(MerkleTreeId.PUBLIC_DATA_TREE, leafIndex);
|
|
1126
|
-
}
|
|
1127
|
-
async getNullifierMembershipWitness(block, nullifier) {
|
|
1128
|
-
const db = await this.#getWorldState(block);
|
|
1184
|
+
async getNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1185
|
+
const db = await this.#getWorldState(referenceBlock);
|
|
1129
1186
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [
|
|
1130
1187
|
nullifier.toBuffer()
|
|
1131
1188
|
]);
|
|
@@ -1141,7 +1198,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1141
1198
|
}
|
|
1142
1199
|
/**
|
|
1143
1200
|
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1144
|
-
* @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).
|
|
1145
1203
|
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1146
1204
|
* @returns The low nullifier membership witness (if found).
|
|
1147
1205
|
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
@@ -1152,8 +1210,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1152
1210
|
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1153
1211
|
* index of the nullifier itself when it already exists in the tree.
|
|
1154
1212
|
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1155
|
-
*/ async getLowNullifierMembershipWitness(
|
|
1156
|
-
const committedDb = await this.#getWorldState(
|
|
1213
|
+
*/ async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1214
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1157
1215
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1158
1216
|
if (!findResult) {
|
|
1159
1217
|
return undefined;
|
|
@@ -1166,8 +1224,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1166
1224
|
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
1167
1225
|
return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath);
|
|
1168
1226
|
}
|
|
1169
|
-
async getPublicDataWitness(
|
|
1170
|
-
const committedDb = await this.#getWorldState(
|
|
1227
|
+
async getPublicDataWitness(referenceBlock, leafSlot) {
|
|
1228
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1171
1229
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1172
1230
|
if (!lowLeafResult) {
|
|
1173
1231
|
return undefined;
|
|
@@ -1177,8 +1235,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1177
1235
|
return new PublicDataWitness(lowLeafResult.index, preimage, path);
|
|
1178
1236
|
}
|
|
1179
1237
|
}
|
|
1180
|
-
async getPublicStorageAt(
|
|
1181
|
-
const committedDb = await this.#getWorldState(
|
|
1238
|
+
async getPublicStorageAt(referenceBlock, contract, slot) {
|
|
1239
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1182
1240
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1183
1241
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1184
1242
|
if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
|
|
@@ -1188,14 +1246,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1188
1246
|
return preimage.leaf.value;
|
|
1189
1247
|
}
|
|
1190
1248
|
async getBlockHeader(block = 'latest') {
|
|
1191
|
-
if (
|
|
1249
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1192
1250
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1193
1251
|
if (block.equals(initialBlockHash)) {
|
|
1194
1252
|
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1195
1253
|
return this.worldStateSynchronizer.getCommitted().getInitialHeader();
|
|
1196
1254
|
}
|
|
1197
|
-
|
|
1198
|
-
return this.blockSource.getBlockHeaderByHash(blockHashFr);
|
|
1255
|
+
return this.blockSource.getBlockHeaderByHash(block);
|
|
1199
1256
|
} else {
|
|
1200
1257
|
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1201
1258
|
const blockNumber = block === 'latest' ? await this.getBlockNumber() : block;
|
|
@@ -1212,6 +1269,12 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1212
1269
|
*/ async getBlockHeaderByArchive(archive) {
|
|
1213
1270
|
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1214
1271
|
}
|
|
1272
|
+
getBlockData(number) {
|
|
1273
|
+
return this.blockSource.getBlockData(number);
|
|
1274
|
+
}
|
|
1275
|
+
getBlockDataByArchive(archive) {
|
|
1276
|
+
return this.blockSource.getBlockDataByArchive(archive);
|
|
1277
|
+
}
|
|
1215
1278
|
/**
|
|
1216
1279
|
* Simulates the public part of a transaction with the current state.
|
|
1217
1280
|
* @param tx - The transaction to simulate.
|
|
@@ -1224,17 +1287,20 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1224
1287
|
throw new BadRequestError(`Transaction total gas limit ${txGasLimit + teardownGasLimit} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${this.config.rpcSimulatePublicMaxGasLimit} for simulation`);
|
|
1225
1288
|
}
|
|
1226
1289
|
const txHash = tx.getTxHash();
|
|
1227
|
-
const
|
|
1290
|
+
const latestBlockNumber = await this.blockSource.getBlockNumber();
|
|
1291
|
+
const blockNumber = BlockNumber.add(latestBlockNumber, 1);
|
|
1228
1292
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
1229
1293
|
const coinbase = EthAddress.ZERO;
|
|
1230
1294
|
const feeRecipient = AztecAddress.ZERO;
|
|
1231
1295
|
const newGlobalVariables = await this.globalVariableBuilder.buildGlobalVariables(blockNumber, coinbase, feeRecipient);
|
|
1232
|
-
const publicProcessorFactory = new PublicProcessorFactory(this.contractDataSource, new DateProvider(), this.telemetry);
|
|
1296
|
+
const publicProcessorFactory = new PublicProcessorFactory(this.contractDataSource, new DateProvider(), this.telemetry, this.log.getBindings());
|
|
1233
1297
|
this.log.verbose(`Simulating public calls for tx ${txHash}`, {
|
|
1234
1298
|
globalVariables: newGlobalVariables.toInspect(),
|
|
1235
1299
|
txHash,
|
|
1236
1300
|
blockNumber
|
|
1237
1301
|
});
|
|
1302
|
+
// Ensure world-state has caught up with the latest block we loaded from the archiver
|
|
1303
|
+
await this.worldStateSynchronizer.syncImmediate(latestBlockNumber);
|
|
1238
1304
|
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
1239
1305
|
try {
|
|
1240
1306
|
const config = PublicSimulatorConfig.from({
|
|
@@ -1249,7 +1315,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1249
1315
|
});
|
|
1250
1316
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1251
1317
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1252
|
-
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([
|
|
1318
|
+
const [processedTxs, failedTxs, _usedTxs, returns, _blobFields, debugLogs] = await processor.process([
|
|
1253
1319
|
tx
|
|
1254
1320
|
]);
|
|
1255
1321
|
// REFACTOR: Consider returning the error rather than throwing
|
|
@@ -1260,7 +1326,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1260
1326
|
throw failedTxs[0].error;
|
|
1261
1327
|
}
|
|
1262
1328
|
const [processedTx] = processedTxs;
|
|
1263
|
-
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);
|
|
1264
1330
|
} finally{
|
|
1265
1331
|
await merkleTreeFork.close();
|
|
1266
1332
|
}
|
|
@@ -1268,10 +1334,10 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1268
1334
|
async isValidTx(tx, { isSimulation, skipFeeEnforcement } = {}) {
|
|
1269
1335
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1270
1336
|
const verifier = isSimulation ? undefined : this.proofVerifier;
|
|
1271
|
-
// 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)
|
|
1272
1338
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1273
1339
|
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
1274
|
-
const validator =
|
|
1340
|
+
const validator = createTxValidatorForAcceptingTxsOverRPC(db, this.contractDataSource, verifier, {
|
|
1275
1341
|
timestamp: nextSlotTimestamp,
|
|
1276
1342
|
blockNumber,
|
|
1277
1343
|
l1ChainId: this.l1ChainId,
|
|
@@ -1280,7 +1346,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1280
1346
|
gasFees: await this.getCurrentMinFees(),
|
|
1281
1347
|
skipFeeEnforcement,
|
|
1282
1348
|
txsPermitted: !this.config.disableTransactions
|
|
1283
|
-
});
|
|
1349
|
+
}, this.log.getBindings());
|
|
1284
1350
|
return await validator.validateTx(tx);
|
|
1285
1351
|
}
|
|
1286
1352
|
getConfig() {
|
|
@@ -1422,6 +1488,73 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1422
1488
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1423
1489
|
}
|
|
1424
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
|
+
}
|
|
1425
1558
|
#getInitialHeaderHash() {
|
|
1426
1559
|
if (!this.initialHeaderHashPromise) {
|
|
1427
1560
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|
|
@@ -1444,14 +1577,13 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1444
1577
|
this.log.debug(`Using committed db for block 'latest', world state synced upto ${blockSyncedTo}`);
|
|
1445
1578
|
return this.worldStateSynchronizer.getCommitted();
|
|
1446
1579
|
}
|
|
1447
|
-
if (
|
|
1580
|
+
if (BlockHash.isBlockHash(block)) {
|
|
1448
1581
|
const initialBlockHash = await this.#getInitialHeaderHash();
|
|
1449
1582
|
if (block.equals(initialBlockHash)) {
|
|
1450
1583
|
// Block source doesn't handle initial header so we need to handle the case separately.
|
|
1451
1584
|
return this.worldStateSynchronizer.getSnapshot(BlockNumber.ZERO);
|
|
1452
1585
|
}
|
|
1453
|
-
const
|
|
1454
|
-
const header = await this.blockSource.getBlockHeaderByHash(blockHashFr);
|
|
1586
|
+
const header = await this.blockSource.getBlockHeaderByHash(block);
|
|
1455
1587
|
if (!header) {
|
|
1456
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.`);
|
|
1457
1589
|
}
|