@aztec/aztec-node 3.0.0-canary.a9708bd → 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.
@@ -9,35 +9,40 @@ 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 { RegistryContract, RollupContract, createEthereumChain, getPublicClient } from '@aztec/ethereum';
13
- import { createL1TxUtilsWithBlobsFromEthSigner } from '@aztec/ethereum/l1-tx-utils-with-blobs';
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';
14
16
  import { compactArray, pick } from '@aztec/foundation/collection';
17
+ import { Fr } from '@aztec/foundation/curves/bn254';
15
18
  import { EthAddress } from '@aztec/foundation/eth-address';
16
- import { Fr } from '@aztec/foundation/fields';
17
19
  import { BadRequestError } from '@aztec/foundation/json-rpc';
18
20
  import { createLogger } from '@aztec/foundation/log';
19
- import { SerialQueue } from '@aztec/foundation/queue';
20
21
  import { count } from '@aztec/foundation/string';
21
22
  import { DateProvider, Timer } from '@aztec/foundation/timer';
22
23
  import { MembershipWitness } from '@aztec/foundation/trees';
23
24
  import { KeystoreManager, loadKeystores, mergeKeystores } from '@aztec/node-keystore';
24
25
  import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
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';
39
+ import { InboxLeaf } from '@aztec/stdlib/messaging';
35
40
  import { P2PClientType } from '@aztec/stdlib/p2p';
36
41
  import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
37
42
  import { PublicSimulationOutput, TxReceipt, TxStatus } from '@aztec/stdlib/tx';
38
43
  import { getPackageVersion } from '@aztec/stdlib/update-checker';
39
44
  import { Attributes, getTelemetryClient, trackSpan } from '@aztec/telemetry-client';
40
- import { NodeKeystoreAdapter, ValidatorClient, createValidatorClient } from '@aztec/validator-client';
45
+ import { NodeKeystoreAdapter, ValidatorClient, createBlockProposalHandler, createValidatorClient } from '@aztec/validator-client';
41
46
  import { createWorldStateSynchronizer } from '@aztec/world-state';
42
47
  import { createPublicClient, fallback, http } from 'viem';
43
48
  import { createSentinel } from '../sentinel/factory.js';
@@ -68,8 +73,6 @@ import { NodeMetrics } from './node_metrics.js';
68
73
  metrics;
69
74
  // Prevent two snapshot operations to happen simultaneously
70
75
  isUploadingSnapshot;
71
- // Serial queue to ensure that we only send one tx at a time
72
- txQueue;
73
76
  tracer;
74
77
  constructor(config, p2pClient, blockSource, logsSource, contractDataSource, l1ToL2MessageSource, worldStateSynchronizer, sequencer, slasherClient, validatorsSentinel, epochPruneWatcher, l1ChainId, version, globalVariableBuilder, epochCache, packageVersion, proofVerifier, telemetry = getTelemetryClient(), log = createLogger('node')){
75
78
  this.config = config;
@@ -92,10 +95,8 @@ import { NodeMetrics } from './node_metrics.js';
92
95
  this.telemetry = telemetry;
93
96
  this.log = log;
94
97
  this.isUploadingSnapshot = false;
95
- this.txQueue = new SerialQueue();
96
98
  this.metrics = new NodeMetrics(telemetry, 'AztecNodeService');
97
99
  this.tracer = telemetry.getTracer('AztecNodeService');
98
- this.txQueue.start();
99
100
  this.log.info(`Aztec Node version: ${this.packageVersion}`);
100
101
  this.log.info(`Aztec Node started on chain 0x${l1ChainId.toString(16)}`, config.l1Contracts);
101
102
  }
@@ -134,6 +135,7 @@ import { NodeMetrics } from './node_metrics.js';
134
135
  keyStoreManager = new KeystoreManager(keyStore);
135
136
  }
136
137
  }
138
+ await keyStoreManager?.validateSigners();
137
139
  // If we are a validator, verify our configuration before doing too much more.
138
140
  if (!config.disableValidator) {
139
141
  if (keyStoreManager === undefined) {
@@ -142,7 +144,7 @@ import { NodeMetrics } from './node_metrics.js';
142
144
  if (!keyStoreProvided) {
143
145
  log.warn('KEY STORE CREATED FROM ENVIRONMENT, IT IS RECOMMENDED TO USE A FILE-BASED KEY STORE IN PRODUCTION ENVIRONMENTS');
144
146
  }
145
- ValidatorClient.validateKeyStoreConfiguration(keyStoreManager);
147
+ ValidatorClient.validateKeyStoreConfiguration(keyStoreManager, log);
146
148
  }
147
149
  // validate that the actual chain id matches that specified in configuration
148
150
  if (config.l1ChainId !== ethereumChain.chainInfo.id) {
@@ -180,49 +182,27 @@ import { NodeMetrics } from './node_metrics.js';
180
182
  telemetry,
181
183
  dateProvider
182
184
  }, {
183
- blockUntilSync: true
185
+ blockUntilSync: !config.skipArchiverInitialSync
184
186
  });
185
187
  // now create the merkle trees and the world state synchronizer
186
188
  const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.prefilledPublicData, telemetry);
187
- 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);
188
190
  if (!config.realProofs) {
189
191
  log.warn(`Aztec node is accepting fake proofs`);
190
192
  }
191
193
  const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
192
194
  // create the tx pool and the p2p client, which will need the l2 block source
193
195
  const p2pClient = await createP2PClient(P2PClientType.Full, config, archiver, proofVerifier, worldStateSynchronizer, epochCache, packageVersion, dateProvider, telemetry, deps.p2pClientDeps);
194
- // Start world state and wait for it to sync to the archiver.
195
- await worldStateSynchronizer.start();
196
- // Start p2p. Note that it depends on world state to be running.
197
- await p2pClient.start();
196
+ // We should really not be modifying the config object
198
197
  config.txPublicSetupAllowList = config.txPublicSetupAllowList ?? await getDefaultAllowedSetupFunctions();
199
198
  const blockBuilder = new BlockBuilder({
200
199
  ...config,
201
200
  l1GenesisTime,
202
201
  slotDuration: Number(slotDuration)
203
202
  }, worldStateSynchronizer, archiver, dateProvider, telemetry);
203
+ // We'll accumulate sentinel watchers here
204
204
  const watchers = [];
205
- const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
206
- if (validatorsSentinel) {
207
- // we can run a sentinel without trying to slash.
208
- await validatorsSentinel.start();
209
- if (config.slashInactivityPenalty > 0n) {
210
- watchers.push(validatorsSentinel);
211
- }
212
- }
213
- let epochPruneWatcher;
214
- if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
215
- epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), blockBuilder, config);
216
- await epochPruneWatcher.start();
217
- watchers.push(epochPruneWatcher);
218
- }
219
- // We assume we want to slash for invalid attestations unless all max penalties are set to 0
220
- let attestationsBlockWatcher;
221
- if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
222
- attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
223
- await attestationsBlockWatcher.start();
224
- watchers.push(attestationsBlockWatcher);
225
- }
205
+ // Create validator client if required
226
206
  const validatorClient = createValidatorClient(config, {
227
207
  p2pClient,
228
208
  telemetry,
@@ -233,10 +213,56 @@ import { NodeMetrics } from './node_metrics.js';
233
213
  l1ToL2MessageSource: archiver,
234
214
  keyStoreManager
235
215
  });
216
+ // If we have a validator client, register it as a source of offenses for the slasher,
217
+ // and have it register callbacks on the p2p client *before* we start it, otherwise messages
218
+ // like attestations or auths will fail.
236
219
  if (validatorClient) {
237
220
  watchers.push(validatorClient);
221
+ if (!options.dontStartSequencer) {
222
+ await validatorClient.registerHandlers();
223
+ }
224
+ }
225
+ // If there's no validator client but alwaysReexecuteBlockProposals is enabled,
226
+ // create a BlockProposalHandler to reexecute block proposals for monitoring
227
+ if (!validatorClient && config.alwaysReexecuteBlockProposals) {
228
+ log.info('Setting up block proposal reexecution for monitoring');
229
+ createBlockProposalHandler(config, {
230
+ blockBuilder,
231
+ epochCache,
232
+ blockSource: archiver,
233
+ l1ToL2MessageSource: archiver,
234
+ p2pClient,
235
+ dateProvider,
236
+ telemetry
237
+ }).registerForReexecution(p2pClient);
238
238
  }
239
- log.verbose(`All Aztec Node subsystems synced`);
239
+ // Start world state and wait for it to sync to the archiver.
240
+ await worldStateSynchronizer.start();
241
+ // Start p2p. Note that it depends on world state to be running.
242
+ await p2pClient.start();
243
+ const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
244
+ if (validatorsSentinel && config.slashInactivityPenalty > 0n) {
245
+ watchers.push(validatorsSentinel);
246
+ }
247
+ let epochPruneWatcher;
248
+ if (config.slashPrunePenalty > 0n || config.slashDataWithholdingPenalty > 0n) {
249
+ epochPruneWatcher = new EpochPruneWatcher(archiver, archiver, epochCache, p2pClient.getTxProvider(), blockBuilder, config);
250
+ watchers.push(epochPruneWatcher);
251
+ }
252
+ // We assume we want to slash for invalid attestations unless all max penalties are set to 0
253
+ let attestationsBlockWatcher;
254
+ if (config.slashProposeInvalidAttestationsPenalty > 0n || config.slashAttestDescendantOfInvalidPenalty > 0n) {
255
+ attestationsBlockWatcher = new AttestationsBlockWatcher(archiver, epochCache, config);
256
+ watchers.push(attestationsBlockWatcher);
257
+ }
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));
240
266
  // Validator enabled, create/start relevant service
241
267
  let sequencer;
242
268
  let slasherClient;
@@ -246,12 +272,23 @@ import { NodeMetrics } from './node_metrics.js';
246
272
  const validatorAddresses = keyStoreManager ? NodeKeystoreAdapter.fromKeyStoreManager(keyStoreManager).getAddresses() : [];
247
273
  slasherClient = await createSlasher(config, config.l1Contracts, getPublicClient(config), watchers, dateProvider, epochCache, validatorAddresses, undefined);
248
274
  await slasherClient.start();
249
- const l1TxUtils = keyStoreManager.createAllValidatorPublisherSigners().map((signer)=>{
250
- return createL1TxUtilsWithBlobsFromEthSigner(publicClient, signer, log, dateProvider, config);
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(), {
283
+ ...config,
284
+ scope: 'sequencer'
285
+ }, {
286
+ telemetry,
287
+ logger: log.createChild('l1-tx-utils'),
288
+ dateProvider
251
289
  });
290
+ // Create and start the sequencer client
252
291
  sequencer = await SequencerClient.new(config, {
253
- // if deps were provided, they should override the defaults,
254
- // or things that we created in this function
255
292
  ...deps,
256
293
  epochCache,
257
294
  l1TxUtils,
@@ -271,6 +308,8 @@ import { NodeMetrics } from './node_metrics.js';
271
308
  if (!options.dontStartSequencer && sequencer) {
272
309
  await sequencer.start();
273
310
  log.verbose(`Sequencer started`);
311
+ } else if (sequencer) {
312
+ log.warn(`Sequencer created but not started`);
274
313
  }
275
314
  return new AztecNodeService(config, p2pClient, archiver, archiver, archiver, archiver, worldStateSynchronizer, sequencer, slasherClient, validatorsSentinel, epochPruneWatcher, ethereumChain.chainInfo.id, config.rollupVersion, new GlobalVariableBuilder(config), epochCache, packageVersion, proofVerifier, telemetry, log);
276
315
  }
@@ -298,6 +337,9 @@ import { NodeMetrics } from './node_metrics.js';
298
337
  getEncodedEnr() {
299
338
  return Promise.resolve(this.p2pClient.getEnr()?.encodeTxt());
300
339
  }
340
+ async getAllowedPublicSetup() {
341
+ return this.config.txPublicSetupAllowList ?? await getDefaultAllowedSetupFunctions();
342
+ }
301
343
  /**
302
344
  * Method to determine if the node is ready to accept transactions.
303
345
  * @returns - Flag indicating the readiness for tx submission.
@@ -328,7 +370,24 @@ import { NodeMetrics } from './node_metrics.js';
328
370
  * @param number - The block number being requested.
329
371
  * @returns The requested block.
330
372
  */ async getBlock(number) {
331
- return await this.blockSource.getBlock(number);
373
+ const blockNumber = number === 'latest' ? await this.getBlockNumber() : number;
374
+ return await this.blockSource.getBlock(blockNumber);
375
+ }
376
+ /**
377
+ * Get a block specified by its hash.
378
+ * @param blockHash - The block hash being requested.
379
+ * @returns The requested block.
380
+ */ async getBlockByHash(blockHash) {
381
+ const publishedBlock = await this.blockSource.getPublishedBlockByHash(blockHash);
382
+ return publishedBlock?.block;
383
+ }
384
+ /**
385
+ * Get a block specified by its archive root.
386
+ * @param archive - The archive root being requested.
387
+ * @returns The requested block.
388
+ */ async getBlockByArchive(archive) {
389
+ const publishedBlock = await this.blockSource.getPublishedBlockByArchive(archive);
390
+ return publishedBlock?.block;
332
391
  }
333
392
  /**
334
393
  * Method to request blocks. Will attempt to return all requested blocks but will return only those available.
@@ -347,6 +406,15 @@ import { NodeMetrics } from './node_metrics.js';
347
406
  */ async getCurrentBaseFees() {
348
407
  return await this.globalVariableBuilder.getCurrentBaseFees();
349
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
+ }
350
418
  /**
351
419
  * Method to fetch the latest block number synchronized by the node.
352
420
  * @returns The block number.
@@ -381,14 +449,6 @@ import { NodeMetrics } from './node_metrics.js';
381
449
  return this.contractDataSource.getContract(address);
382
450
  }
383
451
  /**
384
- * Retrieves all private logs from up to `limit` blocks, starting from the block number `from`.
385
- * @param from - The block number from which to begin retrieving logs.
386
- * @param limit - The maximum number of blocks to retrieve logs from.
387
- * @returns An array of private logs from the specified range of blocks.
388
- */ getPrivateLogs(from, limit) {
389
- return this.logsSource.getPrivateLogs(from, limit);
390
- }
391
- /**
392
452
  * Gets all logs that match any of the received tags (i.e. logs with their first field equal to a tag).
393
453
  * @param tags - The tags to filter the logs by.
394
454
  * @param logsPerTag - The maximum number of logs to return for each tag. By default no limit is set
@@ -415,7 +475,7 @@ import { NodeMetrics } from './node_metrics.js';
415
475
  * Method to submit a transaction to the p2p pool.
416
476
  * @param tx - The transaction to be submitted.
417
477
  */ async sendTx(tx) {
418
- await this.txQueue.put(()=>this.#sendTx(tx));
478
+ await this.#sendTx(tx);
419
479
  }
420
480
  async #sendTx(tx) {
421
481
  const timer = new Timer();
@@ -456,7 +516,6 @@ import { NodeMetrics } from './node_metrics.js';
456
516
  * Method to stop the aztec node.
457
517
  */ async stop() {
458
518
  this.log.info(`Stopping Aztec Node`);
459
- await this.txQueue.end();
460
519
  await tryStop(this.validatorsSentinel);
461
520
  await tryStop(this.epochPruneWatcher);
462
521
  await tryStop(this.slasherClient);
@@ -520,7 +579,7 @@ import { NodeMetrics } from './node_metrics.js';
520
579
  // Now we obtain the block hashes from the archive tree by calling await `committedDb.getLeafValue(treeId, index)`
521
580
  // (note that block number corresponds to the leaf index in the archive tree).
522
581
  const blockHashes = await Promise.all(uniqueBlockNumbers.map((blockNumber)=>{
523
- return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, blockNumber);
582
+ return committedDb.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
524
583
  }));
525
584
  // If any of the block hashes are undefined, we throw an error.
526
585
  for(let i = 0; i < uniqueBlockNumbers.length; i++){
@@ -528,7 +587,7 @@ import { NodeMetrics } from './node_metrics.js';
528
587
  throw new Error(`Block hash is undefined for block number ${uniqueBlockNumbers[i]}`);
529
588
  }
530
589
  }
531
- // Create InBlock objects by combining indices, blockNumbers and blockHashes and return them.
590
+ // Create DataInBlock objects by combining indices, blockNumbers and blockHashes and return them.
532
591
  return maybeIndices.map((index, i)=>{
533
592
  if (index === undefined) {
534
593
  return undefined;
@@ -543,7 +602,7 @@ import { NodeMetrics } from './node_metrics.js';
543
602
  return undefined;
544
603
  }
545
604
  return {
546
- l2BlockNumber: Number(blockNumber),
605
+ l2BlockNumber: BlockNumber(Number(blockNumber)),
547
606
  l2BlockHash: L2BlockHash.fromField(blockHash),
548
607
  data: index
549
608
  };
@@ -600,12 +659,17 @@ import { NodeMetrics } from './node_metrics.js';
600
659
  witness.path
601
660
  ];
602
661
  }
662
+ async getL1ToL2MessageBlock(l1ToL2Message) {
663
+ const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
664
+ return messageIndex ? BlockNumber.fromCheckpointNumber(InboxLeaf.checkpointNumberFromIndex(messageIndex)) : undefined;
665
+ }
603
666
  /**
604
667
  * Returns whether an L1 to L2 message is synced by archiver and if it's ready to be included in a block.
605
668
  * @param l1ToL2Message - The L1 to L2 message to check.
606
669
  * @returns Whether the message is synced and ready to be included in a block.
607
670
  */ async isL1ToL2MessageSynced(l1ToL2Message) {
608
- return await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message) !== undefined;
671
+ const messageIndex = await this.l1ToL2MessageSource.getL1ToL2MessageIndex(l1ToL2Message);
672
+ return messageIndex !== undefined;
609
673
  }
610
674
  /**
611
675
  * Returns all the L2 to L1 messages in a block.
@@ -715,7 +779,21 @@ import { NodeMetrics } from './node_metrics.js';
715
779
  * Returns the currently committed block header, or the initial header if no blocks have been produced.
716
780
  * @returns The current committed block header.
717
781
  */ async getBlockHeader(blockNumber = 'latest') {
718
- return blockNumber === 0 || blockNumber === 'latest' && await this.blockSource.getBlockNumber() === 0 ? this.worldStateSynchronizer.getCommitted().getInitialHeader() : this.blockSource.getBlockHeader(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);
783
+ }
784
+ /**
785
+ * Get a block header specified by its hash.
786
+ * @param blockHash - The block hash being requested.
787
+ * @returns The requested block header.
788
+ */ async getBlockHeaderByHash(blockHash) {
789
+ return await this.blockSource.getBlockHeaderByHash(blockHash);
790
+ }
791
+ /**
792
+ * Get a block header specified by its archive root.
793
+ * @param archive - The archive root being requested.
794
+ * @returns The requested block header.
795
+ */ async getBlockHeaderByArchive(archive) {
796
+ return await this.blockSource.getBlockHeaderByArchive(archive);
719
797
  }
720
798
  /**
721
799
  * Simulates the public part of a transaction with the current state.
@@ -729,7 +807,7 @@ import { NodeMetrics } from './node_metrics.js';
729
807
  throw new BadRequestError(`Transaction total gas limit ${txGasLimit + teardownGasLimit} (${txGasLimit} + ${teardownGasLimit}) exceeds maximum gas limit ${this.config.rpcSimulatePublicMaxGasLimit} for simulation`);
730
808
  }
731
809
  const txHash = tx.getTxHash();
732
- const blockNumber = await this.blockSource.getBlockNumber() + 1;
810
+ const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
733
811
  // If sequencer is not initialized, we just set these values to zero for simulation.
734
812
  const coinbase = EthAddress.ZERO;
735
813
  const feeRecipient = AztecAddress.ZERO;
@@ -742,7 +820,17 @@ import { NodeMetrics } from './node_metrics.js';
742
820
  });
743
821
  const merkleTreeFork = await this.worldStateSynchronizer.fork();
744
822
  try {
745
- const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, skipFeeEnforcement, /*clientInitiatedSimulation*/ true);
823
+ const config = PublicSimulatorConfig.from({
824
+ skipFeeEnforcement,
825
+ collectDebugLogs: true,
826
+ collectHints: false,
827
+ collectCallMetadata: true,
828
+ collectStatistics: false,
829
+ collectionLimits: CollectionLimitsConfig.from({
830
+ maxDebugLogMemoryReads: this.config.rpcSimulatePublicMaxDebugLogMemoryReads
831
+ })
832
+ });
833
+ const processor = publicProcessorFactory.create(merkleTreeFork, newGlobalVariables, config);
746
834
  // REFACTOR: Consider merging ProcessReturnValues into ProcessedTx
747
835
  const [processedTxs, failedTxs, _usedTxs, returns] = await processor.process([
748
836
  tx
@@ -765,7 +853,7 @@ import { NodeMetrics } from './node_metrics.js';
765
853
  const verifier = isSimulation ? undefined : this.proofVerifier;
766
854
  // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
767
855
  const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
768
- const blockNumber = await this.blockSource.getBlockNumber() + 1;
856
+ const blockNumber = BlockNumber(await this.blockSource.getBlockNumber() + 1);
769
857
  const validator = createValidatorForAcceptingTxs(db, this.contractDataSource, verifier, {
770
858
  timestamp: nextSlotTimestamp,
771
859
  blockNumber,
@@ -788,11 +876,14 @@ import { NodeMetrics } from './node_metrics.js';
788
876
  ...this.config,
789
877
  ...config
790
878
  };
791
- this.sequencer?.updateSequencerConfig(config);
879
+ this.sequencer?.updateConfig(config);
792
880
  this.slasherClient?.updateConfig(config);
793
881
  this.validatorsSentinel?.updateConfig(config);
794
- // this.blockBuilder.updateConfig(config); // TODO: Spyros has a PR to add the builder to `this`, so we can do this
795
882
  await this.p2pClient.updateP2PConfig(config);
883
+ const archiver = this.blockSource;
884
+ if ('updateConfig' in archiver) {
885
+ archiver.updateConfig(config);
886
+ }
796
887
  if (newConfig.realProofs !== this.config.realProofs) {
797
888
  this.proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier();
798
889
  }
@@ -823,26 +914,33 @@ import { NodeMetrics } from './node_metrics.js';
823
914
  // We break support for archiver running remotely to the node
824
915
  const archiver = this.blockSource;
825
916
  if (!('backupTo' in archiver)) {
917
+ this.metrics.recordSnapshotError();
826
918
  throw new Error('Archiver implementation does not support backups. Cannot generate snapshot.');
827
919
  }
828
920
  // Test that the archiver has done an initial sync.
829
921
  if (!archiver.isInitialSyncComplete()) {
922
+ this.metrics.recordSnapshotError();
830
923
  throw new Error(`Archiver initial sync not complete. Cannot start snapshot.`);
831
924
  }
832
925
  // And it has an L2 block hash
833
926
  const l2BlockHash = await archiver.getL2Tips().then((tips)=>tips.latest.hash);
834
927
  if (!l2BlockHash) {
928
+ this.metrics.recordSnapshotError();
835
929
  throw new Error(`Archiver has no latest L2 block hash downloaded. Cannot start snapshot.`);
836
930
  }
837
931
  if (this.isUploadingSnapshot) {
932
+ this.metrics.recordSnapshotError();
838
933
  throw new Error(`Snapshot upload already in progress. Cannot start another one until complete.`);
839
934
  }
840
935
  // Do not wait for the upload to be complete to return to the caller, but flag that an operation is in progress
841
936
  this.isUploadingSnapshot = true;
937
+ const timer = new Timer();
842
938
  void uploadSnapshot(location, this.blockSource, this.worldStateSynchronizer, this.config, this.log).then(()=>{
843
939
  this.isUploadingSnapshot = false;
940
+ this.metrics.recordSnapshot(timer.ms());
844
941
  }).catch((err)=>{
845
942
  this.isUploadingSnapshot = false;
943
+ this.metrics.recordSnapshotError();
846
944
  this.log.error(`Error uploading snapshot: ${err}`);
847
945
  });
848
946
  return Promise.resolve();
@@ -915,7 +1013,7 @@ import { NodeMetrics } from './node_metrics.js';
915
1013
  if (typeof blockNumber === 'number' && blockNumber < INITIAL_L2_BLOCK_NUM - 1) {
916
1014
  throw new Error('Invalid block number to get world state for: ' + blockNumber);
917
1015
  }
918
- let blockSyncedTo = 0;
1016
+ let blockSyncedTo = BlockNumber.ZERO;
919
1017
  try {
920
1018
  // Attempt to sync the world state if necessary
921
1019
  blockSyncedTo = await this.#syncWorldState();
@@ -938,7 +1036,7 @@ import { NodeMetrics } from './node_metrics.js';
938
1036
  * @returns A promise that fulfils once the world state is synced
939
1037
  */ async #syncWorldState() {
940
1038
  const blockSourceHeight = await this.blockSource.getBlockNumber();
941
- return this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
1039
+ return await this.worldStateSynchronizer.syncImmediate(blockSourceHeight);
942
1040
  }
943
1041
  }
944
1042
  _ts_decorate([
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env -S node --no-warnings
2
2
  export {};
3
- //# sourceMappingURL=index.d.ts.map
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=index.d.ts.map
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsd0JBQXdCLENBQUMifQ==
@@ -1,7 +1,8 @@
1
1
  import { type ConfigMappingsType } from '@aztec/foundation/config';
2
2
  export type SentinelConfig = {
3
3
  sentinelHistoryLengthInEpochs: number;
4
+ sentinelHistoricProvenPerformanceLengthInEpochs: number;
4
5
  sentinelEnabled: boolean;
5
6
  };
6
7
  export declare const sentinelConfigMappings: ConfigMappingsType<SentinelConfig>;
7
- //# sourceMappingURL=config.d.ts.map
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VudGluZWwvY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLGtCQUFrQixFQUEyQyxNQUFNLDBCQUEwQixDQUFDO0FBRTVHLE1BQU0sTUFBTSxjQUFjLEdBQUc7SUFDM0IsNkJBQTZCLEVBQUUsTUFBTSxDQUFDO0lBQ3RDLCtDQUErQyxFQUFFLE1BQU0sQ0FBQztJQUN4RCxlQUFlLEVBQUUsT0FBTyxDQUFDO0NBQzFCLENBQUM7QUFFRixlQUFPLE1BQU0sc0JBQXNCLEVBQUUsa0JBQWtCLENBQUMsY0FBYyxDQTRCckUsQ0FBQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/sentinel/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAA2C,MAAM,0BAA0B,CAAC;AAE5G,MAAM,MAAM,cAAc,GAAG;IAC3B,6BAA6B,EAAE,MAAM,CAAC;IACtC,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,CAAC,cAAc,CAWrE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/sentinel/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAA2C,MAAM,0BAA0B,CAAC;AAE5G,MAAM,MAAM,cAAc,GAAG;IAC3B,6BAA6B,EAAE,MAAM,CAAC;IACtC,+CAA+C,EAAE,MAAM,CAAC;IACxD,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,kBAAkB,CAAC,cAAc,CA4BrE,CAAC"}
@@ -5,6 +5,22 @@ export const sentinelConfigMappings = {
5
5
  env: 'SENTINEL_HISTORY_LENGTH_IN_EPOCHS',
6
6
  ...numberConfigHelper(24)
7
7
  },
8
+ /**
9
+ * The number of L2 epochs kept of proven performance history for each validator.
10
+ * This value must be large enough so that we have proven performance for every validator
11
+ * for at least slashInactivityConsecutiveEpochThreshold. Assuming this value is 3,
12
+ * and the committee size is 48, and we have 10k validators, then we pick 48 out of 10k each draw.
13
+ * For any fixed element, per-draw prob = 48/10000 = 0.0048.
14
+ * After n draws, count ~ Binomial(n, 0.0048). We want P(X >= 3).
15
+ * Results (exact binomial):
16
+ * - 90% chance: n = 1108
17
+ * - 95% chance: n = 1310
18
+ * - 99% chance: n = 1749
19
+ */ sentinelHistoricProvenPerformanceLengthInEpochs: {
20
+ description: 'The number of L2 epochs kept of proven performance history for each validator.',
21
+ env: 'SENTINEL_HISTORIC_PROVEN_PERFORMANCE_LENGTH_IN_EPOCHS',
22
+ ...numberConfigHelper(2000)
23
+ },
8
24
  sentinelEnabled: {
9
25
  description: 'Whether the sentinel is enabled or not.',
10
26
  env: 'SENTINEL_ENABLED',
@@ -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=factory.d.ts.map
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3NlbnRpbmVsL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFckQsT0FBTyxLQUFLLEVBQUUsZUFBZSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFOUQsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQzVDLE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3pELE9BQU8sS0FBSyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRXJFLE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBR3pDLHdCQUFzQixjQUFjLENBQ2xDLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLFFBQVEsRUFBRSxhQUFhLEVBQ3ZCLEdBQUcsRUFBRSxTQUFTLEVBQ2QsTUFBTSxFQUFFLGNBQWMsR0FBRyxlQUFlLEdBQUcsYUFBYSxFQUN4RCxNQUFNLHlDQUFnQyxHQUNyQyxPQUFPLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQWlCL0IifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,aAAa,EACxD,MAAM,yCAAgC,GACrC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAa/B"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/sentinel/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAsB,cAAc,CAClC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,aAAa,EACvB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,cAAc,GAAG,eAAe,GAAG,aAAa,EACxD,MAAM,yCAAgC,GACrC,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAiB/B"}
@@ -8,8 +8,10 @@ export async function createSentinel(epochCache, archiver, p2p, config, logger =
8
8
  }
9
9
  const kvStore = await createStore('sentinel', SentinelStore.SCHEMA_VERSION, config, createLogger('node:sentinel:lmdb'));
10
10
  const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
11
+ const storeHistoricProvenPerformanceLength = config.sentinelHistoricProvenPerformanceLengthInEpochs;
11
12
  const sentinelStore = new SentinelStore(kvStore, {
12
- historyLength: storeHistoryLength
13
+ historyLength: storeHistoryLength,
14
+ historicProvenPerformanceLength: storeHistoricProvenPerformanceLength
13
15
  });
14
16
  return new Sentinel(epochCache, archiver, p2p, sentinelStore, config, logger);
15
17
  }
@@ -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=index.d.ts.map
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZW50aW5lbC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXpDLFlBQVksRUFDVixlQUFlLEVBQ2YsY0FBYyxFQUNkLHNCQUFzQixFQUN0QixxQkFBcUIsR0FDdEIsTUFBTSwwQkFBMEIsQ0FBQyJ9