@aztec/aztec-node 3.0.0-devnet.2 → 3.0.0-devnet.2-patch.1
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 -2
- package/dest/aztec-node/config.d.ts.map +1 -1
- package/dest/aztec-node/config.js +12 -1
- package/dest/aztec-node/node_metrics.d.ts +5 -1
- package/dest/aztec-node/node_metrics.d.ts.map +1 -1
- package/dest/aztec-node/node_metrics.js +21 -0
- package/dest/aztec-node/server.d.ts +30 -36
- package/dest/aztec-node/server.d.ts.map +1 -1
- package/dest/aztec-node/server.js +64 -44
- package/dest/bin/index.d.ts +1 -1
- package/dest/index.d.ts +1 -1
- package/dest/sentinel/config.d.ts +1 -1
- package/dest/sentinel/factory.d.ts +1 -1
- package/dest/sentinel/index.d.ts +1 -1
- package/dest/sentinel/sentinel.d.ts +20 -19
- package/dest/sentinel/sentinel.d.ts.map +1 -1
- package/dest/sentinel/sentinel.js +24 -17
- package/dest/sentinel/store.d.ts +6 -5
- package/dest/sentinel/store.d.ts.map +1 -1
- package/dest/sentinel/store.js +3 -2
- package/dest/test/index.d.ts +1 -1
- package/package.json +28 -27
- package/src/aztec-node/config.ts +17 -6
- package/src/aztec-node/node_metrics.ts +28 -0
- package/src/aztec-node/server.ts +118 -93
- package/src/sentinel/sentinel.ts +48 -36
- package/src/sentinel/store.ts +11 -10
|
@@ -9,26 +9,30 @@ import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@azte
|
|
|
9
9
|
import { createBlobSinkClient } from '@aztec/blob-sink/client';
|
|
10
10
|
import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
|
|
11
11
|
import { EpochCache } from '@aztec/epoch-cache';
|
|
12
|
-
import {
|
|
12
|
+
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
13
|
+
import { getPublicClient } from '@aztec/ethereum/client';
|
|
14
|
+
import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
15
|
+
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
13
16
|
import { compactArray, pick } from '@aztec/foundation/collection';
|
|
17
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
18
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
15
|
-
import { Fr } from '@aztec/foundation/fields';
|
|
16
19
|
import { BadRequestError } from '@aztec/foundation/json-rpc';
|
|
17
20
|
import { createLogger } from '@aztec/foundation/log';
|
|
18
|
-
import { SerialQueue } from '@aztec/foundation/queue';
|
|
19
21
|
import { count } from '@aztec/foundation/string';
|
|
20
22
|
import { DateProvider, Timer } from '@aztec/foundation/timer';
|
|
21
23
|
import { MembershipWitness } from '@aztec/foundation/trees';
|
|
22
24
|
import { KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
|
|
23
25
|
import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
|
|
24
|
-
import { createL1TxUtilsWithBlobsFromEthSigner } from '@aztec/node-lib/factories';
|
|
26
|
+
import { createForwarderL1TxUtilsFromEthSigner, createL1TxUtilsWithBlobsFromEthSigner } from '@aztec/node-lib/factories';
|
|
25
27
|
import { createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
|
|
26
28
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
27
29
|
import { BlockBuilder, GlobalVariableBuilder, SequencerClient, createValidatorForAcceptingTxs } from '@aztec/sequencer-client';
|
|
28
30
|
import { PublicProcessorFactory } from '@aztec/simulator/server';
|
|
29
31
|
import { AttestationsBlockWatcher, EpochPruneWatcher, createSlasher } from '@aztec/slasher';
|
|
32
|
+
import { CollectionLimitsConfig, PublicSimulatorConfig } from '@aztec/stdlib/avm';
|
|
30
33
|
import { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
31
34
|
import { L2BlockHash } from '@aztec/stdlib/block';
|
|
35
|
+
import { GasFees } from '@aztec/stdlib/gas';
|
|
32
36
|
import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
|
|
33
37
|
import { AztecNodeAdminConfigSchema } from '@aztec/stdlib/interfaces/client';
|
|
34
38
|
import { tryStop } from '@aztec/stdlib/interfaces/server';
|
|
@@ -69,8 +73,6 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
69
73
|
metrics;
|
|
70
74
|
// Prevent two snapshot operations to happen simultaneously
|
|
71
75
|
isUploadingSnapshot;
|
|
72
|
-
// Serial queue to ensure that we only send one tx at a time
|
|
73
|
-
txQueue;
|
|
74
76
|
tracer;
|
|
75
77
|
constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion, proofVerifier, telemetry = getTelemetryClient(), log = createLogger('node')){
|
|
76
78
|
this.config = config;
|
|
@@ -93,10 +95,8 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
93
95
|
this.telemetry = telemetry;
|
|
94
96
|
this.log = log;
|
|
95
97
|
this.isUploadingSnapshot = false;
|
|
96
|
-
this.txQueue = new SerialQueue();
|
|
97
98
|
this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
|
|
98
99
|
this.tracer = telemetry.getTracer('AztecNodeService');
|
|
99
|
-
this.txQueue.start();
|
|
100
100
|
this.log.info(`Aztec Node version: ${this.packageVersion}`);
|
|
101
101
|
this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config.l1Contracts);
|
|
102
102
|
}
|
|
@@ -182,11 +182,11 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
182
182
|
telemetry,
|
|
183
183
|
dateProvider
|
|
184
184
|
}, {
|
|
185
|
-
blockUntilSync:
|
|
185
|
+
blockUntilSync: !config.skipArchiverInitialSync
|
|
186
186
|
});
|
|
187
187
|
// now create the merkle trees and the world state synchronizer
|
|
188
188
|
const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.prefilledPublicData, telemetry);
|
|
189
|
-
const circuitVerifier = config.realProofs ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier();
|
|
189
|
+
const circuitVerifier = config.realProofs || config.debugForceTxProofVerification ? await BBCircuitVerifier.new(config) : new TestCircuitVerifier(config.proverTestVerificationDelayMs);
|
|
190
190
|
if (!config.realProofs) {
|
|
191
191
|
log.warn(`Aztec node is accepting fake proofs`);
|
|
192
192
|
}
|
|
@@ -241,27 +241,28 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
241
241
|
// Start p2p. Note that it depends on world state to be running.
|
|
242
242
|
await p2pClient.start();
|
|
243
243
|
const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
|
|
244
|
-
if (validatorsSentinel) {
|
|
245
|
-
|
|
246
|
-
await validatorsSentinel.start();
|
|
247
|
-
if (config.slashInactivityPenalty > 0n) {
|
|
248
|
-
watchers.push(validatorsSentinel);
|
|
249
|
-
}
|
|
244
|
+
if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
|
|
245
|
+
watchers.push(validatorsSentinel);
|
|
250
246
|
}
|
|
251
247
|
let epochPruneWatcher;
|
|
252
248
|
if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
|
|
253
249
|
epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), blockBuilder, config);
|
|
254
|
-
await epochPruneWatcher.start();
|
|
255
250
|
watchers.push(epochPruneWatcher);
|
|
256
251
|
}
|
|
257
252
|
// We assume we want to slash for invalid attestations unless all max penalties are set to 0
|
|
258
253
|
let attestationsBlockWatcher;
|
|
259
254
|
if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
|
|
260
255
|
attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
|
|
261
|
-
await attestationsBlockWatcher.start();
|
|
262
256
|
watchers.push(attestationsBlockWatcher);
|
|
263
257
|
}
|
|
264
|
-
|
|
258
|
+
// Start p2p-related services once the archiver has completed sync
|
|
259
|
+
void archiver.waitForInitialSync().then(async ()=>{
|
|
260
|
+
await p2pClient.start();
|
|
261
|
+
await validatorsSentinel?.start();
|
|
262
|
+
await epochPruneWatcher?.start();
|
|
263
|
+
await attestationsBlockWatcher?.start();
|
|
264
|
+
log.info(`All p2p services started`);
|
|
265
|
+
}).catch((err)=>log.error('Failed to start p2p services after archiver sync', err));
|
|
265
266
|
// Validator enabled, create/start relevant service
|
|
266
267
|
let sequencer;
|
|
267
268
|
let slasherClient;
|
|
@@ -271,7 +272,14 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
271
272
|
const validatorAddresses = keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses() : [];
|
|
272
273
|
slasherClient = await createSlasher(config, config.l1Contracts, getPublicClient(config), watchers, dateProvider, epochCache, validatorAddresses, undefined);
|
|
273
274
|
await slasherClient.start();
|
|
274
|
-
const l1TxUtils = await
|
|
275
|
+
const l1TxUtils = config.publisherForwarderAddress ? await createForwarderL1TxUtilsFromEthSigner(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), config.publisherForwarderAddress, {
|
|
276
|
+
...config,
|
|
277
|
+
scope: 'sequencer'
|
|
278
|
+
}, {
|
|
279
|
+
telemetry,
|
|
280
|
+
logger: log.createChild('l1-tx-utils'),
|
|
281
|
+
dateProvider
|
|
282
|
+
}) : await createL1TxUtilsWithBlobsFromEthSigner(publicClient, keyStoreManager.createAllValidatorPublisherSigners(), {
|
|
275
283
|
...config,
|
|
276
284
|
scope: 'sequencer'
|
|
277
285
|
}, {
|
|
@@ -279,9 +287,8 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
279
287
|
logger: log.createChild('l1-tx-utils'),
|
|
280
288
|
dateProvider
|
|
281
289
|
});
|
|
290
|
+
// Create and start the sequencer client
|
|
282
291
|
sequencer = await SequencerClient.new(config, {
|
|
283
|
-
// if deps were provided, they should override the defaults,
|
|
284
|
-
// or things that we created in this function
|
|
285
292
|
...deps,
|
|
286
293
|
epochCache,
|
|
287
294
|
l1TxUtils,
|
|
@@ -399,6 +406,15 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
399
406
|
*/ async getCurrentBaseFees() {
|
|
400
407
|
return await this.globalVariableBuilder.getCurrentBaseFees();
|
|
401
408
|
}
|
|
409
|
+
async getMaxPriorityFees() {
|
|
410
|
+
for await (const tx of this.p2pClient.iteratePendingTxs()){
|
|
411
|
+
return tx.getGasSettings().maxPriorityFeesPerGas;
|
|
412
|
+
}
|
|
413
|
+
return GasFees.from({
|
|
414
|
+
feePerDaGas: 0n,
|
|
415
|
+
feePerL2Gas: 0n
|
|
416
|
+
});
|
|
417
|
+
}
|
|
402
418
|
/**
|
|
403
419
|
* Method to fetch the latest block number synchronized by the node.
|
|
404
420
|
* @returns The block number.
|
|
@@ -433,14 +449,6 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
433
449
|
return this.contractDataSource.getContract(address);
|
|
434
450
|
}
|
|
435
451
|
/**
|
|
436
|
-
* Retrieves all private logs from up to `limit` blocks, starting from the block number `from`.
|
|
437
|
-
* @param from - The block number from which to begin retrieving logs.
|
|
438
|
-
* @param limit - The maximum number of blocks to retrieve logs from.
|
|
439
|
-
* @returns An array of private logs from the specified range of blocks.
|
|
440
|
-
*/ getPrivateLogs(from, limit) {
|
|
441
|
-
return this.logsSource.getPrivateLogs(from, limit);
|
|
442
|
-
}
|
|
443
|
-
/**
|
|
444
452
|
* Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
|
|
445
453
|
* @param tags - The tags to filter the logs by.
|
|
446
454
|
* @param logsPerTag - The maximum number of logs to return for each tag. By default no limit is set
|
|
@@ -467,7 +475,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
467
475
|
* Method to submit a transaction to the p2p pool.
|
|
468
476
|
* @param tx - The transaction to be submitted.
|
|
469
477
|
*/ async sendTx(tx) {
|
|
470
|
-
await this
|
|
478
|
+
await this.#sendTx(tx);
|
|
471
479
|
}
|
|
472
480
|
async #sendTx(tx) {
|
|
473
481
|
const timer = new Timer();
|
|
@@ -508,7 +516,6 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
508
516
|
* Method to stop the aztec node.
|
|
509
517
|
*/ async stop() {
|
|
510
518
|
this.log.info(`Stopping Aztec Node`);
|
|
511
|
-
await this.txQueue.end();
|
|
512
519
|
await tryStop(this.validatorsSentinel);
|
|
513
520
|
await tryStop(this.epochPruneWatcher);
|
|
514
521
|
await tryStop(this.slasherClient);
|
|
@@ -572,7 +579,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
572
579
|
// Now we obtain the block hashes from the archive tree by calling await `committedDb.getLeafValue(treeId, index)`
|
|
573
580
|
// (note that block number corresponds to the leaf index in the archive tree).
|
|
574
581
|
const blockHashes = await Promise.all(uniqueBlockNumbers.map((blockNumber)=>{
|
|
575
|
-
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, blockNumber);
|
|
582
|
+
return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
|
|
576
583
|
}));
|
|
577
584
|
// If any of the block hashes are undefined, we throw an error.
|
|
578
585
|
for(let i = 0; i < uniqueBlockNumbers.length; i++){
|
|
@@ -580,7 +587,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
580
587
|
throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
|
|
581
588
|
}
|
|
582
589
|
}
|
|
583
|
-
// Create
|
|
590
|
+
// Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
|
|
584
591
|
return maybeIndices.map((index, i)=>{
|
|
585
592
|
if (index === undefined) {
|
|
586
593
|
return undefined;
|
|
@@ -595,7 +602,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
595
602
|
return undefined;
|
|
596
603
|
}
|
|
597
604
|
return {
|
|
598
|
-
l2BlockNumber: Number(blockNumber),
|
|
605
|
+
l2BlockNumber: BlockNumber(Number(blockNumber)),
|
|
599
606
|
l2BlockHash: L2BlockHash.fromField(blockHash),
|
|
600
607
|
data: index
|
|
601
608
|
};
|
|
@@ -654,7 +661,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
654
661
|
}
|
|
655
662
|
async getL1ToL2MessageBlock(l1ToL2Message) {
|
|
656
663
|
const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
|
|
657
|
-
return messageIndex ? InboxLeaf.
|
|
664
|
+
return messageIndex ? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex)) : undefined;
|
|
658
665
|
}
|
|
659
666
|
/**
|
|
660
667
|
* Returns whether an L1 to L2 message is synced by archiver and if it's ready to be included in a block.
|
|
@@ -772,7 +779,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
772
779
|
* Returns the currently committed block header, or the initial header if no blocks have been produced.
|
|
773
780
|
* @returns The current committed block header.
|
|
774
781
|
*/ async getBlockHeader(blockNumber = 'latest') {
|
|
775
|
-
return blockNumber ===
|
|
782
|
+
return blockNumber === BlockNumber.ZERO || blockNumber === 'latest' && await this.blockSource.getBlockNumber() === BlockNumber.ZERO ? this.worldStateSynchronizer.getCommitted().getInitialHeader() : this.blockSource.getBlockHeader(blockNumber === 'latest' ? blockNumber : blockNumber);
|
|
776
783
|
}
|
|
777
784
|
/**
|
|
778
785
|
* Get a block header specified by its hash.
|
|
@@ -800,7 +807,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
800
807
|
throw new BadRequestError(`Transaction total gas limit ${txGasLimit + teardownGasLimit} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${this.config.rpcSimulatePublicMaxGasLimit} for simulation`);
|
|
801
808
|
}
|
|
802
809
|
const txHash = tx.getTxHash();
|
|
803
|
-
const blockNumber = await this.blockSource.getBlockNumber() + 1;
|
|
810
|
+
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
804
811
|
// If sequencer is not initialized, we just set these values to zero for simulation.
|
|
805
812
|
const coinbase = EthAddress.ZERO;
|
|
806
813
|
const feeRecipient = AztecAddress.ZERO;
|
|
@@ -813,11 +820,17 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
813
820
|
});
|
|
814
821
|
const merkleTreeFork = await this.worldStateSynchronizer.fork();
|
|
815
822
|
try {
|
|
816
|
-
const
|
|
823
|
+
const config = PublicSimulatorConfig.from({
|
|
817
824
|
skipFeeEnforcement,
|
|
818
|
-
|
|
819
|
-
|
|
825
|
+
collectDebugLogs: true,
|
|
826
|
+
collectHints: false,
|
|
827
|
+
collectCallMetadata: true,
|
|
828
|
+
collectStatistics: false,
|
|
829
|
+
collectionLimits: CollectionLimitsConfig.from({
|
|
830
|
+
maxDebugLogMemoryReads: this.config.rpcSimulatePublicMaxDebugLogMemoryReads
|
|
831
|
+
})
|
|
820
832
|
});
|
|
833
|
+
const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
|
|
821
834
|
// REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
|
|
822
835
|
const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([
|
|
823
836
|
tx
|
|
@@ -840,7 +853,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
840
853
|
const verifier = isSimulation ? undefined : this.proofVerifier;
|
|
841
854
|
// We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
|
|
842
855
|
const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
|
|
843
|
-
const blockNumber = await this.blockSource.getBlockNumber() + 1;
|
|
856
|
+
const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
|
|
844
857
|
const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, {
|
|
845
858
|
timestamp: nextSlotTimestamp,
|
|
846
859
|
blockNumber,
|
|
@@ -901,26 +914,33 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
901
914
|
// We break support for archiver running remotely to the node
|
|
902
915
|
const archiver = this.blockSource;
|
|
903
916
|
if (!('backupTo' in archiver)) {
|
|
917
|
+
this.metrics.recordSnapshotError();
|
|
904
918
|
throw new Error('Archiver implementation does not support backups. Cannot generate snapshot.');
|
|
905
919
|
}
|
|
906
920
|
// Test that the archiver has done an initial sync.
|
|
907
921
|
if (!archiver.isInitialSyncComplete()) {
|
|
922
|
+
this.metrics.recordSnapshotError();
|
|
908
923
|
throw new Error(`Archiver initial sync not complete. Cannot start snapshot.`);
|
|
909
924
|
}
|
|
910
925
|
// And it has an L2 block hash
|
|
911
926
|
const l2BlockHash = await archiver.getL2Tips().then((tips)=>tips.latest.hash);
|
|
912
927
|
if (!l2BlockHash) {
|
|
928
|
+
this.metrics.recordSnapshotError();
|
|
913
929
|
throw new Error(`Archiver has no latest L2 block hash downloaded. Cannot start snapshot.`);
|
|
914
930
|
}
|
|
915
931
|
if (this.isUploadingSnapshot) {
|
|
932
|
+
this.metrics.recordSnapshotError();
|
|
916
933
|
throw new Error(`Snapshot upload already in progress. Cannot start another one until complete.`);
|
|
917
934
|
}
|
|
918
935
|
// Do not wait for the upload to be complete to return to the caller, but flag that an operation is in progress
|
|
919
936
|
this.isUploadingSnapshot = true;
|
|
937
|
+
const timer = new Timer();
|
|
920
938
|
void uploadSnapshot(location, this.blockSource, this.worldStateSynchronizer, this.config, this.log).then(()=>{
|
|
921
939
|
this.isUploadingSnapshot = false;
|
|
940
|
+
this.metrics.recordSnapshot(timer.ms());
|
|
922
941
|
}).catch((err)=>{
|
|
923
942
|
this.isUploadingSnapshot = false;
|
|
943
|
+
this.metrics.recordSnapshotError();
|
|
924
944
|
this.log.error(`Error uploading snapshot: ${err}`);
|
|
925
945
|
});
|
|
926
946
|
return Promise.resolve();
|
|
@@ -993,7 +1013,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
993
1013
|
if (typeof blockNumber === 'number' && blockNumber < INITIAL_L2_BLOCK_NUM - 1) {
|
|
994
1014
|
throw new Error('Invalid block number to get world state for: ' + blockNumber);
|
|
995
1015
|
}
|
|
996
|
-
let blockSyncedTo =
|
|
1016
|
+
let blockSyncedTo = BlockNumber.ZERO;
|
|
997
1017
|
try {
|
|
998
1018
|
// Attempt to sync the world state if necessary
|
|
999
1019
|
blockSyncedTo = await this.#syncWorldState();
|
|
@@ -1016,7 +1036,7 @@ import { NodeMetrics } from './node_metrics.js';
|
|
|
1016
1036
|
* @returns A promise that fulfils once the world state is synced
|
|
1017
1037
|
*/ async #syncWorldState() {
|
|
1018
1038
|
const blockSourceHeight = await this.blockSource.getBlockNumber();
|
|
1019
|
-
return this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
1039
|
+
return await this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
|
|
1020
1040
|
}
|
|
1021
1041
|
}
|
|
1022
1042
|
_ts_decorate([
|
package/dest/bin/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --no-warnings
|
|
2
2
|
export {};
|
|
3
|
-
//# sourceMappingURL=
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9iaW4vaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
|
package/dest/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export * from './aztec-node/config.js';
|
|
2
2
|
export * from './aztec-node/server.js';
|
|
3
|
-
//# sourceMappingURL=
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsd0JBQXdCLENBQUMifQ==
|
|
@@ -5,4 +5,4 @@ export type SentinelConfig = {
|
|
|
5
5
|
sentinelEnabled: boolean;
|
|
6
6
|
};
|
|
7
7
|
export declare const sentinelConfigMappings: ConfigMappingsType<SentinelConfig>;
|
|
8
|
-
//# sourceMappingURL=
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VudGluZWwvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGtCQUFrQixFQUEyQyxNQUFNLDBCQUEwQixDQUFDO0FBRTVHLE1BQU0sTUFBTSxjQUFjLEdBQUc7SUFDM0IsNkJBQTZCLEVBQUUsTUFBTSxDQUFDO0lBQ3RDLCtDQUErQyxFQUFFLE1BQU0sQ0FBQztJQUN4RCxlQUFlLEVBQUUsT0FBTyxDQUFDO0NBQzFCLENBQUM7QUFFRixlQUFPLE1BQU0sc0JBQXNCLEVBQUUsa0JBQWtCLENBQUMsY0FBYyxDQTRCckUsQ0FBQyJ9
|
|
@@ -6,4 +6,4 @@ import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
|
|
|
6
6
|
import type { SentinelConfig } from './config.js';
|
|
7
7
|
import { Sentinel } from './sentinel.js';
|
|
8
8
|
export declare function createSentinel(epochCache: EpochCache, archiver: L2BlockSource, p2p: P2PClient, config: SentinelConfig & DataStoreConfig & SlasherConfig, logger?: import("@aztec/foundation/log").Logger): Promise<Sentinel | undefined>;
|
|
9
|
-
//# sourceMappingURL=
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlbnRpbmVsL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFckQsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFOUQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRXJFLE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBR3pDLHdCQUFzQixjQUFjLENBQ2xDLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLFFBQVEsRUFBRSxhQUFhLEVBQ3ZCLEdBQUcsRUFBRSxTQUFTLEVBQ2QsTUFBTSxFQUFFLGNBQWMsR0FBRyxlQUFlLEdBQUcsYUFBYSxFQUN4RCxNQUFNLHlDQUFnQyxHQUNyQyxPQUFPLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQWlCL0IifQ==
|
package/dest/sentinel/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { Sentinel } from './sentinel.js';
|
|
2
2
|
export type { ValidatorsStats, ValidatorStats, ValidatorStatusHistory, ValidatorStatusInSlot, } from '@aztec/stdlib/validators';
|
|
3
|
-
//# sourceMappingURL=
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZW50aW5lbC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXpDLFlBQVksRUFDVixlQUFlLEVBQ2YsY0FBYyxFQUNkLHNCQUFzQixFQUN0QixxQkFBcUIsR0FDdEIsTUFBTSwwQkFBMEIsQ0FBQyJ9
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EpochCache } from '@aztec/epoch-cache';
|
|
2
|
+
import { BlockNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
3
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
4
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
4
5
|
import { type L2TipsStore } from '@aztec/kv-store/stores';
|
|
@@ -19,10 +20,10 @@ export declare class Sentinel extends Sentinel_base implements L2BlockStreamEven
|
|
|
19
20
|
protected runningPromise: RunningPromise;
|
|
20
21
|
protected blockStream: L2BlockStream;
|
|
21
22
|
protected l2TipsStore: L2TipsStore;
|
|
22
|
-
protected initialSlot:
|
|
23
|
-
protected lastProcessedSlot:
|
|
24
|
-
protected slotNumberToBlock: Map<
|
|
25
|
-
blockNumber:
|
|
23
|
+
protected initialSlot: SlotNumber | undefined;
|
|
24
|
+
protected lastProcessedSlot: SlotNumber | undefined;
|
|
25
|
+
protected slotNumberToBlock: Map<SlotNumber, {
|
|
26
|
+
blockNumber: BlockNumber;
|
|
26
27
|
archive: string;
|
|
27
28
|
attestors: EthAddress[];
|
|
28
29
|
}>;
|
|
@@ -34,15 +35,15 @@ export declare class Sentinel extends Sentinel_base implements L2BlockStreamEven
|
|
|
34
35
|
stop(): Promise<void>;
|
|
35
36
|
handleBlockStreamEvent(event: L2BlockStreamEvent): Promise<void>;
|
|
36
37
|
protected handleChainProven(event: L2BlockStreamEvent): Promise<void>;
|
|
37
|
-
protected computeProvenPerformance(epoch:
|
|
38
|
+
protected computeProvenPerformance(epoch: EpochNumber): Promise<ValidatorsEpochPerformance>;
|
|
38
39
|
/**
|
|
39
40
|
* Checks if a validator has been inactive for the specified number of consecutive epochs for which we have data on it.
|
|
40
41
|
* @param validator The validator address to check
|
|
41
42
|
* @param currentEpoch Epochs strictly before the current one are evaluated only
|
|
42
43
|
* @param requiredConsecutiveEpochs Number of consecutive epochs required for slashing
|
|
43
44
|
*/
|
|
44
|
-
protected checkPastInactivity(validator: EthAddress, currentEpoch:
|
|
45
|
-
protected handleProvenPerformance(epoch:
|
|
45
|
+
protected checkPastInactivity(validator: EthAddress, currentEpoch: EpochNumber, requiredConsecutiveEpochs: number): Promise<boolean>;
|
|
46
|
+
protected handleProvenPerformance(epoch: EpochNumber, performance: ValidatorsEpochPerformance): Promise<void>;
|
|
46
47
|
/**
|
|
47
48
|
* Process data for two L2 slots ago.
|
|
48
49
|
* Note that we do not process historical data, since we rely on p2p data for processing,
|
|
@@ -54,38 +55,38 @@ export declare class Sentinel extends Sentinel_base implements L2BlockStreamEven
|
|
|
54
55
|
* We also don't move past the archiver last synced L2 slot, as we don't want to process data that is not yet available.
|
|
55
56
|
* Last, we check the p2p is synced with the archiver, so it has pulled all attestations from it.
|
|
56
57
|
*/
|
|
57
|
-
protected isReadyToProcess(currentSlot:
|
|
58
|
+
protected isReadyToProcess(currentSlot: SlotNumber): Promise<SlotNumber | false>;
|
|
58
59
|
/**
|
|
59
60
|
* Gathers committee and proposer data for a given slot, computes slot stats,
|
|
60
61
|
* and updates overall stats.
|
|
61
62
|
*/
|
|
62
|
-
protected processSlot(slot:
|
|
63
|
+
protected processSlot(slot: SlotNumber): Promise<void>;
|
|
63
64
|
/** Computes activity for a given slot. */
|
|
64
|
-
protected getSlotActivity(slot:
|
|
65
|
+
protected getSlotActivity(slot: SlotNumber, epoch: EpochNumber, proposer: EthAddress, committee: EthAddress[]): Promise<{
|
|
65
66
|
[k: string]: ValidatorStatusInSlot | undefined;
|
|
66
67
|
}>;
|
|
67
68
|
/** Push the status for each slot for each validator. */
|
|
68
|
-
protected updateValidators(slot:
|
|
69
|
+
protected updateValidators(slot: SlotNumber, stats: Record<`0x${string}`, ValidatorStatusInSlot | undefined>): Promise<void>;
|
|
69
70
|
/** Computes stats to be returned based on stored data. */
|
|
70
|
-
computeStats({ fromSlot, toSlot, validators
|
|
71
|
-
fromSlot?:
|
|
72
|
-
toSlot?:
|
|
71
|
+
computeStats({ fromSlot, toSlot, validators }?: {
|
|
72
|
+
fromSlot?: SlotNumber;
|
|
73
|
+
toSlot?: SlotNumber;
|
|
73
74
|
validators?: EthAddress[];
|
|
74
75
|
}): Promise<ValidatorsStats>;
|
|
75
76
|
/** Computes stats for a single validator. */
|
|
76
|
-
getValidatorStats(validatorAddress: EthAddress, fromSlot?:
|
|
77
|
-
protected computeStatsForValidator(address: `0x${string}`, allHistory: ValidatorStatusHistory, fromSlot?:
|
|
77
|
+
getValidatorStats(validatorAddress: EthAddress, fromSlot?: SlotNumber, toSlot?: SlotNumber): Promise<SingleValidatorStats | undefined>;
|
|
78
|
+
protected computeStatsForValidator(address: `0x${string}`, allHistory: ValidatorStatusHistory, fromSlot?: SlotNumber, toSlot?: SlotNumber): ValidatorStats;
|
|
78
79
|
protected computeMissed(history: ValidatorStatusHistory, computeOverPrefix: ValidatorStatusType | undefined, filter: ValidatorStatusInSlot[]): {
|
|
79
80
|
currentStreak: number;
|
|
80
81
|
rate: number | undefined;
|
|
81
82
|
count: number;
|
|
82
83
|
total: number;
|
|
83
84
|
};
|
|
84
|
-
protected computeFromSlot(slot:
|
|
85
|
+
protected computeFromSlot(slot: SlotNumber | undefined): {
|
|
85
86
|
timestamp: bigint;
|
|
86
|
-
slot:
|
|
87
|
+
slot: SlotNumber;
|
|
87
88
|
date: string;
|
|
88
89
|
} | undefined;
|
|
89
90
|
}
|
|
90
91
|
export {};
|
|
91
|
-
//# sourceMappingURL=
|
|
92
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VudGluZWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZW50aW5lbC9zZW50aW5lbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsV0FBVyxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUV2RixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFM0QsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBQ25FLE9BQU8sRUFBcUIsS0FBSyxXQUFXLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUM3RSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDNUMsT0FBTyxFQUlMLEtBQUssT0FBTyxFQUNaLEtBQUssY0FBYyxFQUNwQixNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzNELE9BQU8sRUFDTCxLQUFLLGFBQWEsRUFDbEIsYUFBYSxFQUNiLEtBQUssa0JBQWtCLEVBQ3ZCLEtBQUsseUJBQXlCLEVBRS9CLE1BQU0scUJBQXFCLENBQUM7QUFFN0IsT0FBTyxLQUFLLEVBQ1Ysb0JBQW9CLEVBQ3BCLGNBQWMsRUFDZCxzQkFBc0IsRUFDdEIscUJBQXFCLEVBQ3JCLG1CQUFtQixFQUNuQiwwQkFBMEIsRUFDMUIsZUFBZSxFQUNoQixNQUFNLDBCQUEwQixDQUFDO0FBSWxDLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxZQUFZLENBQUM7O0FBRTNDLHFCQUFhLFFBQVMsU0FBUSxhQUEyQyxZQUFXLHlCQUF5QixFQUFFLE9BQU87SUFZbEgsU0FBUyxDQUFDLFVBQVUsRUFBRSxVQUFVO0lBQ2hDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsYUFBYTtJQUNq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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../../src/sentinel/sentinel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"sentinel.d.ts","sourceRoot":"","sources":["../../src/sentinel/sentinel.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
1
2
|
import { countWhile, filterAsync, fromEntries, getEntries, mapValues } from '@aztec/foundation/collection';
|
|
2
3
|
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
4
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -19,6 +20,7 @@ export class Sentinel extends EventEmitter {
|
|
|
19
20
|
l2TipsStore;
|
|
20
21
|
initialSlot;
|
|
21
22
|
lastProcessedSlot;
|
|
23
|
+
// eslint-disable-next-line aztec-custom/no-non-primitive-in-collections
|
|
22
24
|
slotNumberToBlock;
|
|
23
25
|
constructor(epochCache, archiver, p2p, store, config, logger = createLogger('node:sentinel')){
|
|
24
26
|
super(), this.epochCache = epochCache, this.archiver = archiver, this.p2p = p2p, this.store = store, this.config = config, this.logger = logger, this.slotNumberToBlock = new Map();
|
|
@@ -38,7 +40,7 @@ export class Sentinel extends EventEmitter {
|
|
|
38
40
|
}
|
|
39
41
|
/** Loads initial slot and initializes blockstream. We will not process anything at or before the initial slot. */ async init() {
|
|
40
42
|
this.initialSlot = this.epochCache.getEpochAndSlotNow().slot;
|
|
41
|
-
const startingBlock = await this.archiver.getBlockNumber();
|
|
43
|
+
const startingBlock = BlockNumber(await this.archiver.getBlockNumber());
|
|
42
44
|
this.logger.info(`Starting validator sentinel with initial slot ${this.initialSlot} and block ${startingBlock}`);
|
|
43
45
|
this.blockStream = new L2BlockStream(this.archiver, this.l2TipsStore, this, this.logger, {
|
|
44
46
|
startingBlock
|
|
@@ -53,7 +55,7 @@ export class Sentinel extends EventEmitter {
|
|
|
53
55
|
// Store mapping from slot to archive, block number, and attestors
|
|
54
56
|
for (const block of event.blocks){
|
|
55
57
|
this.slotNumberToBlock.set(block.block.header.getSlot(), {
|
|
56
|
-
blockNumber: block.block.number,
|
|
58
|
+
blockNumber: BlockNumber(block.block.number),
|
|
57
59
|
archive: block.block.archive.root.toString(),
|
|
58
60
|
attestors: getAttestationInfoFromPublishedL2Block(block).filter((a)=>a.status === 'recovered-from-signature').map((a)=>a.address)
|
|
59
61
|
});
|
|
@@ -74,7 +76,7 @@ export class Sentinel extends EventEmitter {
|
|
|
74
76
|
if (event.type !== 'chain-proven') {
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
77
|
-
const blockNumber = event.block.number;
|
|
79
|
+
const blockNumber = BlockNumber(event.block.number);
|
|
78
80
|
const block = await this.archiver.getBlock(blockNumber);
|
|
79
81
|
if (!block) {
|
|
80
82
|
this.logger.error(`Failed to get block ${blockNumber}`, {
|
|
@@ -131,13 +133,15 @@ export class Sentinel extends EventEmitter {
|
|
|
131
133
|
}
|
|
132
134
|
// Get all historical performance for this validator
|
|
133
135
|
const allPerformance = await this.store.getProvenPerformance(validator);
|
|
136
|
+
// Sort by epoch descending to get most recent first, keep only epochs strictly before the current one, and get the first N
|
|
137
|
+
const pastEpochs = allPerformance.sort((a, b)=>Number(b.epoch - a.epoch)).filter((p)=>p.epoch < currentEpoch);
|
|
134
138
|
// If we don't have enough historical data, don't slash
|
|
135
|
-
if (
|
|
139
|
+
if (pastEpochs.length < requiredConsecutiveEpochs) {
|
|
136
140
|
this.logger.debug(`Not enough historical data for slashing ${validator} for inactivity (${allPerformance.length} epochs < ${requiredConsecutiveEpochs} required)`);
|
|
137
141
|
return false;
|
|
138
142
|
}
|
|
139
|
-
//
|
|
140
|
-
return
|
|
143
|
+
// Check that we have at least requiredConsecutiveEpochs and that all of them are above the inactivity threshold
|
|
144
|
+
return pastEpochs.slice(0, requiredConsecutiveEpochs).every((p)=>p.missed / p.total >= this.config.slashInactivityTargetPercentage);
|
|
141
145
|
}
|
|
142
146
|
async handleProvenPerformance(epoch, performance) {
|
|
143
147
|
if (this.config.slashInactivityPenalty === 0n) {
|
|
@@ -155,7 +159,7 @@ export class Sentinel extends EventEmitter {
|
|
|
155
159
|
validator: EthAddress.fromString(address),
|
|
156
160
|
amount: this.config.slashInactivityPenalty,
|
|
157
161
|
offenseType: OffenseType.INACTIVITY,
|
|
158
|
-
epochOrSlot: epoch
|
|
162
|
+
epochOrSlot: BigInt(epoch)
|
|
159
163
|
}));
|
|
160
164
|
if (criminals.length > 0) {
|
|
161
165
|
this.logger.verbose(`Identified ${criminals.length} validators to slash due to inactivity in at least ${epochThreshold} consecutive epochs`, {
|
|
@@ -190,7 +194,11 @@ export class Sentinel extends EventEmitter {
|
|
|
190
194
|
* We also don't move past the archiver last synced L2 slot, as we don't want to process data that is not yet available.
|
|
191
195
|
* Last, we check the p2p is synced with the archiver, so it has pulled all attestations from it.
|
|
192
196
|
*/ async isReadyToProcess(currentSlot) {
|
|
193
|
-
|
|
197
|
+
if (currentSlot < 2) {
|
|
198
|
+
this.logger.trace(`Current slot ${currentSlot} too early.`);
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const targetSlot = SlotNumber(currentSlot - 2);
|
|
194
202
|
if (this.lastProcessedSlot && this.lastProcessedSlot >= targetSlot) {
|
|
195
203
|
this.logger.trace(`Already processed slot ${targetSlot}`, {
|
|
196
204
|
lastProcessedSlot: this.lastProcessedSlot
|
|
@@ -206,7 +214,7 @@ export class Sentinel extends EventEmitter {
|
|
|
206
214
|
return false;
|
|
207
215
|
}
|
|
208
216
|
const archiverSlot = await this.archiver.getL2SlotNumber();
|
|
209
|
-
if (archiverSlot < targetSlot) {
|
|
217
|
+
if (archiverSlot === undefined || archiverSlot < targetSlot) {
|
|
210
218
|
this.logger.debug(`Waiting for archiver to sync with L2 slot ${targetSlot}`, {
|
|
211
219
|
archiverSlot,
|
|
212
220
|
targetSlot
|
|
@@ -314,7 +322,7 @@ export class Sentinel extends EventEmitter {
|
|
|
314
322
|
await this.store.getHistory(v)
|
|
315
323
|
]))) : await this.store.getHistories();
|
|
316
324
|
const slotNow = this.epochCache.getEpochAndSlotNow().slot;
|
|
317
|
-
fromSlot ??= (this.lastProcessedSlot ?? slotNow) -
|
|
325
|
+
fromSlot ??= SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
|
|
318
326
|
toSlot ??= this.lastProcessedSlot ?? slotNow;
|
|
319
327
|
const stats = mapValues(histories, (history, address)=>this.computeStatsForValidator(address, history ?? [], fromSlot, toSlot));
|
|
320
328
|
return {
|
|
@@ -330,25 +338,24 @@ export class Sentinel extends EventEmitter {
|
|
|
330
338
|
return undefined;
|
|
331
339
|
}
|
|
332
340
|
const slotNow = this.epochCache.getEpochAndSlotNow().slot;
|
|
333
|
-
const effectiveFromSlot = fromSlot ?? (this.lastProcessedSlot ?? slotNow) -
|
|
341
|
+
const effectiveFromSlot = fromSlot ?? SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
|
|
334
342
|
const effectiveToSlot = toSlot ?? this.lastProcessedSlot ?? slotNow;
|
|
335
343
|
const historyLength = BigInt(this.store.getHistoryLength());
|
|
336
|
-
if (effectiveToSlot - effectiveFromSlot > historyLength) {
|
|
337
|
-
throw new Error(`Slot range (${effectiveToSlot - effectiveFromSlot}) exceeds history length (${historyLength}). ` + `Requested range: ${effectiveFromSlot} to ${effectiveToSlot}.`);
|
|
344
|
+
if (BigInt(effectiveToSlot) - BigInt(effectiveFromSlot) > historyLength) {
|
|
345
|
+
throw new Error(`Slot range (${BigInt(effectiveToSlot) - BigInt(effectiveFromSlot)}) exceeds history length (${historyLength}). ` + `Requested range: ${effectiveFromSlot} to ${effectiveToSlot}.`);
|
|
338
346
|
}
|
|
339
347
|
const validator = this.computeStatsForValidator(validatorAddress.toString(), history, effectiveFromSlot, effectiveToSlot);
|
|
340
|
-
const allTimeProvenPerformance = await this.store.getProvenPerformance(validatorAddress);
|
|
341
348
|
return {
|
|
342
349
|
validator,
|
|
343
|
-
allTimeProvenPerformance,
|
|
350
|
+
allTimeProvenPerformance: await this.store.getProvenPerformance(validatorAddress),
|
|
344
351
|
lastProcessedSlot: this.lastProcessedSlot,
|
|
345
352
|
initialSlot: this.initialSlot,
|
|
346
353
|
slotWindow: this.store.getHistoryLength()
|
|
347
354
|
};
|
|
348
355
|
}
|
|
349
356
|
computeStatsForValidator(address, allHistory, fromSlot, toSlot) {
|
|
350
|
-
let history = fromSlot ? allHistory.filter((h)=>h.slot >= fromSlot) : allHistory;
|
|
351
|
-
history = toSlot ? history.filter((h)=>h.slot <= toSlot) : history;
|
|
357
|
+
let history = fromSlot ? allHistory.filter((h)=>BigInt(h.slot) >= fromSlot) : allHistory;
|
|
358
|
+
history = toSlot ? history.filter((h)=>BigInt(h.slot) <= toSlot) : history;
|
|
352
359
|
const lastProposal = history.filter((h)=>h.status === 'block-proposed' || h.status === 'block-mined').at(-1);
|
|
353
360
|
const lastAttestation = history.filter((h)=>h.status === 'attestation-sent').at(-1);
|
|
354
361
|
return {
|