@aztec/aztec-node 0.0.1-commit.6b90f3f5 → 0.0.1-commit.6c354eb4c
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/server.d.ts +29 -15
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +248 -96
- 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/server.ts +316 -115
- 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,9 +390,11 @@ 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';
|
|
@@ -402,13 +405,13 @@ 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
|
}
|
|
@@ -920,8 +991,9 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
920
991
|
throw new Error(`Invalid tx: ${reason}`);
|
|
921
992
|
}
|
|
922
993
|
await this.p2pClient.sendTx(tx);
|
|
923
|
-
|
|
924
|
-
this.
|
|
994
|
+
const duration = timer.ms();
|
|
995
|
+
this.metrics.receivedTx(duration, true);
|
|
996
|
+
this.log.info(`Received tx ${txHash} in ${duration}ms`, {
|
|
925
997
|
txHash
|
|
926
998
|
});
|
|
927
999
|
}
|
|
@@ -932,18 +1004,20 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
932
1004
|
const isKnownToPool = txPoolStatus === 'pending' || txPoolStatus === 'mined';
|
|
933
1005
|
// Then get the actual tx from the archiver, which tracks every tx in a mined block.
|
|
934
1006
|
const settledTxReceipt = await this.blockSource.getSettledTxReceipt(txHash);
|
|
1007
|
+
let receipt;
|
|
935
1008
|
if (settledTxReceipt) {
|
|
936
|
-
|
|
937
|
-
return settledTxReceipt;
|
|
1009
|
+
receipt = settledTxReceipt;
|
|
938
1010
|
} else if (isKnownToPool) {
|
|
939
1011
|
// If the tx is in the pool but not in the archiver, it's pending.
|
|
940
1012
|
// This handles race conditions between archiver and p2p, where the archiver
|
|
941
1013
|
// has pruned the block in which a tx was mined, but p2p has not caught up yet.
|
|
942
|
-
|
|
1014
|
+
receipt = new TxReceipt(txHash, TxStatus.PENDING, undefined, undefined);
|
|
943
1015
|
} else {
|
|
944
1016
|
// Otherwise, if we don't know the tx, we consider it dropped.
|
|
945
|
-
|
|
1017
|
+
receipt = new TxReceipt(txHash, TxStatus.DROPPED, undefined, 'Tx dropped by P2P node');
|
|
946
1018
|
}
|
|
1019
|
+
this.debugLogStore.decorateReceiptWithLogs(txHash.toString(), receipt);
|
|
1020
|
+
return receipt;
|
|
947
1021
|
}
|
|
948
1022
|
getTxEffect(txHash) {
|
|
949
1023
|
return this.blockSource.getTxEffect(txHash);
|
|
@@ -957,6 +1031,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
957
1031
|
await tryStop(this.slasherClient);
|
|
958
1032
|
await tryStop(this.proofVerifier);
|
|
959
1033
|
await tryStop(this.sequencer);
|
|
1034
|
+
await tryStop(this.proverNode);
|
|
960
1035
|
await tryStop(this.p2pClient);
|
|
961
1036
|
await tryStop(this.worldStateSynchronizer);
|
|
962
1037
|
await tryStop(this.blockSource);
|
|
@@ -995,8 +1070,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
995
1070
|
*/ async getTxsByHash(txHashes) {
|
|
996
1071
|
return compactArray(await Promise.all(txHashes.map((txHash)=>this.getTxByHash(txHash))));
|
|
997
1072
|
}
|
|
998
|
-
async findLeavesIndexes(
|
|
999
|
-
const committedDb = await this.#getWorldState(
|
|
1073
|
+
async findLeavesIndexes(referenceBlock, treeId, leafValues) {
|
|
1074
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1000
1075
|
const maybeIndices = await committedDb.findLeafIndices(treeId, leafValues.map((x)=>x.toBuffer()));
|
|
1001
1076
|
// We filter out undefined values
|
|
1002
1077
|
const indices = maybeIndices.filter((x)=>x !== undefined);
|
|
@@ -1044,22 +1119,22 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1044
1119
|
};
|
|
1045
1120
|
});
|
|
1046
1121
|
}
|
|
1047
|
-
async
|
|
1048
|
-
const committedDb = await this.#getWorldState(
|
|
1122
|
+
async getBlockHashMembershipWitness(referenceBlock, blockHash) {
|
|
1123
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1049
1124
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.ARCHIVE, [
|
|
1050
|
-
|
|
1125
|
+
blockHash
|
|
1051
1126
|
]);
|
|
1052
1127
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1053
1128
|
}
|
|
1054
|
-
async getNoteHashMembershipWitness(
|
|
1055
|
-
const committedDb = await this.#getWorldState(
|
|
1129
|
+
async getNoteHashMembershipWitness(referenceBlock, noteHash) {
|
|
1130
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1056
1131
|
const [pathAndIndex] = await committedDb.findSiblingPaths(MerkleTreeId.NOTE_HASH_TREE, [
|
|
1057
1132
|
noteHash
|
|
1058
1133
|
]);
|
|
1059
1134
|
return pathAndIndex === undefined ? undefined : MembershipWitness.fromSiblingPath(pathAndIndex.index, pathAndIndex.path);
|
|
1060
1135
|
}
|
|
1061
|
-
async getL1ToL2MessageMembershipWitness(
|
|
1062
|
-
const db = await this.#getWorldState(
|
|
1136
|
+
async getL1ToL2MessageMembershipWitness(referenceBlock, l1ToL2Message) {
|
|
1137
|
+
const db = await this.#getWorldState(referenceBlock);
|
|
1063
1138
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.L1_TO_L2_MESSAGE_TREE, [
|
|
1064
1139
|
l1ToL2Message
|
|
1065
1140
|
]);
|
|
@@ -1106,8 +1181,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1106
1181
|
}
|
|
1107
1182
|
return blocksInCheckpoints.map((blocks)=>blocks.map((block)=>block.body.txEffects.map((txEffect)=>txEffect.l2ToL1Msgs)));
|
|
1108
1183
|
}
|
|
1109
|
-
async getNullifierMembershipWitness(
|
|
1110
|
-
const db = await this.#getWorldState(
|
|
1184
|
+
async getNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1185
|
+
const db = await this.#getWorldState(referenceBlock);
|
|
1111
1186
|
const [witness] = await db.findSiblingPaths(MerkleTreeId.NULLIFIER_TREE, [
|
|
1112
1187
|
nullifier.toBuffer()
|
|
1113
1188
|
]);
|
|
@@ -1123,7 +1198,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1123
1198
|
}
|
|
1124
1199
|
/**
|
|
1125
1200
|
* Returns a low nullifier membership witness for a given nullifier at a given block.
|
|
1126
|
-
* @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).
|
|
1127
1203
|
* @param nullifier - Nullifier we try to find the low nullifier witness for.
|
|
1128
1204
|
* @returns The low nullifier membership witness (if found).
|
|
1129
1205
|
* @remarks Low nullifier witness can be used to perform a nullifier non-inclusion proof by leveraging the "linked
|
|
@@ -1134,8 +1210,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1134
1210
|
* the nullifier already exists in the tree. This is because the `getPreviousValueIndex` function returns the
|
|
1135
1211
|
* index of the nullifier itself when it already exists in the tree.
|
|
1136
1212
|
* TODO: This is a confusing behavior and we should eventually address that.
|
|
1137
|
-
*/ async getLowNullifierMembershipWitness(
|
|
1138
|
-
const committedDb = await this.#getWorldState(
|
|
1213
|
+
*/ async getLowNullifierMembershipWitness(referenceBlock, nullifier) {
|
|
1214
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1139
1215
|
const findResult = await committedDb.getPreviousValueIndex(MerkleTreeId.NULLIFIER_TREE, nullifier.toBigInt());
|
|
1140
1216
|
if (!findResult) {
|
|
1141
1217
|
return undefined;
|
|
@@ -1148,8 +1224,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1148
1224
|
const siblingPath = await committedDb.getSiblingPath(MerkleTreeId.NULLIFIER_TREE, BigInt(index));
|
|
1149
1225
|
return new NullifierMembershipWitness(BigInt(index), preimageData, siblingPath);
|
|
1150
1226
|
}
|
|
1151
|
-
async getPublicDataWitness(
|
|
1152
|
-
const committedDb = await this.#getWorldState(
|
|
1227
|
+
async getPublicDataWitness(referenceBlock, leafSlot) {
|
|
1228
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1153
1229
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1154
1230
|
if (!lowLeafResult) {
|
|
1155
1231
|
return undefined;
|
|
@@ -1159,8 +1235,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1159
1235
|
return new PublicDataWitness(lowLeafResult.index, preimage, path);
|
|
1160
1236
|
}
|
|
1161
1237
|
}
|
|
1162
|
-
async getPublicStorageAt(
|
|
1163
|
-
const committedDb = await this.#getWorldState(
|
|
1238
|
+
async getPublicStorageAt(referenceBlock, contract, slot) {
|
|
1239
|
+
const committedDb = await this.#getWorldState(referenceBlock);
|
|
1164
1240
|
const leafSlot = await computePublicDataTreeLeafSlot(contract, slot);
|
|
1165
1241
|
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());
|
|
1166
1242
|
if (!lowLeafResult || !lowLeafResult.alreadyPresent) {
|
|
@@ -1193,6 +1269,12 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1193
1269
|
*/ async getBlockHeaderByArchive(archive) {
|
|
1194
1270
|
return await this.blockSource.getBlockHeaderByArchive(archive);
|
|
1195
1271
|
}
|
|
1272
|
+
getBlockData(number) {
|
|
1273
|
+
return this.blockSource.getBlockData(number);
|
|
1274
|
+
}
|
|
1275
|
+
getBlockDataByArchive(archive) {
|
|
1276
|
+
return this.blockSource.getBlockDataByArchive(archive);
|
|
1277
|
+
}
|
|
1196
1278
|
/**
|
|
1197
1279
|
* Simulates the public part of a transaction with the current state.
|
|
1198
1280
|
* @param tx - The transaction to simulate.
|
|
@@ -1205,7 +1287,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1205
1287
|
throw new BadRequestError(`Transaction total gas limit ${txGasLimit + teardownGasLimit} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${this.config.rpcSimulatePublicMaxGasLimit} for simulation`);
|
|
1206
1288
|
}
|
|
1207
1289
|
const txHash = tx.getTxHash();
|
|
1208
|
-
const
|
|
1290
|
+
const latestBlockNumber = await this.blockSource.getBlockNumber();
|
|
1291
|
+
const blockNumber = BlockNumber.add(latestBlockNumber, 1);
|
|
1209
1292
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
1210
1293
|
const coinbase = EthAddress.ZERO;
|
|
1211
1294
|
const feeRecipient = AztecAddress.ZERO;
|
|
@@ -1216,6 +1299,8 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1216
1299
|
txHash,
|
|
1217
1300
|
blockNumber
|
|
1218
1301
|
});
|
|
1302
|
+
// Ensure world-state has caught up with the latest block we loaded from the archiver
|
|
1303
|
+
await this.worldStateSynchronizer.syncImmediate(latestBlockNumber);
|
|
1219
1304
|
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
1220
1305
|
try {
|
|
1221
1306
|
const config = PublicSimulatorConfig.from({
|
|
@@ -1230,7 +1315,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1230
1315
|
});
|
|
1231
1316
|
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
1232
1317
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
1233
|
-
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([
|
|
1318
|
+
const [processedTxs, failedTxs, _usedTxs, returns, _blobFields, debugLogs] = await processor.process([
|
|
1234
1319
|
tx
|
|
1235
1320
|
]);
|
|
1236
1321
|
// REFACTOR: Consider returning the error rather than throwing
|
|
@@ -1241,7 +1326,7 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1241
1326
|
throw failedTxs[0].error;
|
|
1242
1327
|
}
|
|
1243
1328
|
const [processedTx] = processedTxs;
|
|
1244
|
-
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);
|
|
1245
1330
|
} finally{
|
|
1246
1331
|
await merkleTreeFork.close();
|
|
1247
1332
|
}
|
|
@@ -1249,10 +1334,10 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1249
1334
|
async isValidTx(tx, { isSimulation, skipFeeEnforcement } = {}) {
|
|
1250
1335
|
const db = this.worldStateSynchronizer.getCommitted();
|
|
1251
1336
|
const verifier = isSimulation ? undefined : this.proofVerifier;
|
|
1252
|
-
// 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)
|
|
1253
1338
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
1254
1339
|
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
1255
|
-
const validator =
|
|
1340
|
+
const validator = createTxValidatorForAcceptingTxsOverRPC(db, this.contractDataSource, verifier, {
|
|
1256
1341
|
timestamp: nextSlotTimestamp,
|
|
1257
1342
|
blockNumber,
|
|
1258
1343
|
l1ChainId: this.l1ChainId,
|
|
@@ -1403,6 +1488,73 @@ _dec = trackSpan('AztecNodeService.simulatePublicCalls', (tx)=>({
|
|
|
1403
1488
|
return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
|
|
1404
1489
|
}
|
|
1405
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
|
+
}
|
|
1406
1558
|
#getInitialHeaderHash() {
|
|
1407
1559
|
if (!this.initialHeaderHashPromise) {
|
|
1408
1560
|
this.initialHeaderHashPromise = this.worldStateSynchronizer.getCommitted().getInitialHeader().hash();
|