@aztec/aztec-node 0.0.1-commit.8c0b8ff → 0.0.1-commit.8cb2d04d8

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.
@@ -1,5 +1,6 @@
1
1
  import { Archiver, createArchiver } from '@aztec/archiver';
2
- import { BBCircuitVerifier, QueuedIVCVerifier, TestCircuitVerifier } from '@aztec/bb-prover';
2
+ import { BBCircuitVerifier, BatchChonkVerifier, QueuedIVCVerifier } from '@aztec/bb-prover';
3
+ import { TestCircuitVerifier } from '@aztec/bb-prover/test';
3
4
  import { type BlobClientInterface, createBlobClientWithFileStores } from '@aztec/blob-client/client';
4
5
  import { Blob } from '@aztec/blob-lib';
5
6
  import { ARCHIVE_HEIGHT, type L1_TO_L2_MSG_TREE_HEIGHT, type NOTE_HASH_TREE_HEIGHT } from '@aztec/constants';
@@ -8,6 +9,7 @@ import { createEthereumChain } from '@aztec/ethereum/chain';
8
9
  import { getPublicClient, makeL1HttpTransport } from '@aztec/ethereum/client';
9
10
  import { RegistryContract, RollupContract } from '@aztec/ethereum/contracts';
10
11
  import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses';
12
+ import type { L1TxUtils } from '@aztec/ethereum/l1-tx-utils';
11
13
  import { BlockNumber, CheckpointNumber, EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
12
14
  import { chunkBy, compactArray, pick, unique } from '@aztec/foundation/collection';
13
15
  import { Fr } from '@aztec/foundation/curves/bn254';
@@ -31,7 +33,12 @@ import {
31
33
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
32
34
  import { type ProverNode, type ProverNodeDeps, createProverNode } from '@aztec/prover-node';
33
35
  import { createKeyStoreForProver } from '@aztec/prover-node/config';
34
- import { GlobalVariableBuilder, SequencerClient, type SequencerPublisher } from '@aztec/sequencer-client';
36
+ import {
37
+ FeeProviderImpl,
38
+ GlobalVariableBuilder,
39
+ SequencerClient,
40
+ type SequencerPublisher,
41
+ } from '@aztec/sequencer-client';
35
42
  import { PublicProcessorFactory } from '@aztec/simulator/server';
36
43
  import {
37
44
  AttestationsBlockWatcher,
@@ -58,7 +65,7 @@ import type {
58
65
  NodeInfo,
59
66
  ProtocolContractAddresses,
60
67
  } from '@aztec/stdlib/contract';
61
- import { GasFees } from '@aztec/stdlib/gas';
68
+ import { GasFees, type ManaUsageEstimate } from '@aztec/stdlib/gas';
62
69
  import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
63
70
  import {
64
71
  type AztecNode,
@@ -81,11 +88,12 @@ import {
81
88
  import type { DebugLogStore, LogFilter, SiloedTag, Tag, TxScopedL2Log } from '@aztec/stdlib/logs';
82
89
  import { InMemoryDebugLogStore, NullDebugLogStore } from '@aztec/stdlib/logs';
83
90
  import { InboxLeaf, type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
84
- import type { Offense, SlashPayloadRound } from '@aztec/stdlib/slashing';
85
- import type { NullifierLeafPreimage, PublicDataTreeLeaf, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
91
+ import type { Offense } from '@aztec/stdlib/slashing';
92
+ import type { NullifierLeafPreimage, PublicDataTreeLeafPreimage } from '@aztec/stdlib/trees';
86
93
  import { MerkleTreeId, NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
87
94
  import {
88
95
  type BlockHeader,
96
+ type FeeProvider,
89
97
  type GlobalVariableBuilder as GlobalVariableBuilderInterface,
90
98
  type IndexedTxEffect,
91
99
  PublicSimulationOutput,
@@ -97,6 +105,7 @@ import {
97
105
  } from '@aztec/stdlib/tx';
98
106
  import { getPackageVersion } from '@aztec/stdlib/update-checker';
99
107
  import type { SingleValidatorStats, ValidatorsStats } from '@aztec/stdlib/validators';
108
+ import type { GenesisData } from '@aztec/stdlib/world-state';
100
109
  import {
101
110
  Attributes,
102
111
  type TelemetryClient,
@@ -151,9 +160,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
151
160
  protected readonly l1ChainId: number,
152
161
  protected readonly version: number,
153
162
  protected readonly globalVariableBuilder: GlobalVariableBuilderInterface,
163
+ protected readonly feeProvider: FeeProvider,
154
164
  protected readonly epochCache: EpochCacheInterface,
155
165
  protected readonly packageVersion: string,
156
- private proofVerifier: ClientProtocolCircuitVerifier,
166
+ private peerProofVerifier: ClientProtocolCircuitVerifier,
167
+ private rpcProofVerifier: ClientProtocolCircuitVerifier,
157
168
  private telemetry: TelemetryClient = getTelemetryClient(),
158
169
  private log = createLogger('node'),
159
170
  private blobClient?: BlobClientInterface,
@@ -175,6 +186,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
175
186
  }
176
187
  }
177
188
 
189
+ /** @internal Exposed for testing — returns the RPC proof verifier. */
190
+ public getProofVerifier(): ClientProtocolCircuitVerifier {
191
+ return this.rpcProofVerifier;
192
+ }
193
+
178
194
  public async getWorldStateSyncStatus(): Promise<WorldStateSyncStatus> {
179
195
  const status = await this.worldStateSynchronizer.status();
180
196
  return status.syncSummary;
@@ -201,7 +217,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
201
217
  slashingProtectionDb?: SlashingProtectionDatabase;
202
218
  } = {},
203
219
  options: {
204
- prefilledPublicData?: PublicDataTreeLeaf[];
220
+ genesis?: GenesisData;
205
221
  dontStartSequencer?: boolean;
206
222
  dontStartProverNode?: boolean;
207
223
  } = {},
@@ -304,16 +320,18 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
304
320
  );
305
321
 
306
322
  // now create the merkle trees and the world state synchronizer
307
- const worldStateSynchronizer = await createWorldStateSynchronizer(
308
- config,
309
- archiver,
310
- options.prefilledPublicData,
311
- telemetry,
312
- );
313
- const circuitVerifier =
314
- config.realProofs || config.debugForceTxProofVerification
315
- ? await BBCircuitVerifier.new(config)
316
- : new TestCircuitVerifier(config.proverTestVerificationDelayMs);
323
+ const worldStateSynchronizer = await createWorldStateSynchronizer(config, archiver, options.genesis, telemetry);
324
+ const useRealVerifiers = config.realProofs || config.debugForceTxProofVerification;
325
+ let peerProofVerifier: ClientProtocolCircuitVerifier;
326
+ let rpcProofVerifier: ClientProtocolCircuitVerifier;
327
+ if (useRealVerifiers) {
328
+ peerProofVerifier = await BatchChonkVerifier.new(config, config.bbChonkVerifyMaxBatch, 'peer');
329
+ const rpcVerifier = await BBCircuitVerifier.new(config);
330
+ rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, config.numConcurrentIVCVerifiers);
331
+ } else {
332
+ peerProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
333
+ rpcProofVerifier = new TestCircuitVerifier(config.proverTestVerificationDelayMs);
334
+ }
317
335
 
318
336
  let debugLogStore: DebugLogStore;
319
337
  if (!config.realProofs) {
@@ -327,8 +345,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
327
345
  debugLogStore = new NullDebugLogStore();
328
346
  }
329
347
 
330
- const proofVerifier = new QueuedIVCVerifier(config, circuitVerifier);
331
-
332
348
  const proverOnly = config.enableProverNode && config.disableValidator;
333
349
  if (proverOnly) {
334
350
  log.info('Starting in prover-only mode: skipping validator, sequencer, sentinel, and slasher subsystems');
@@ -338,7 +354,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
338
354
  const p2pClient = await createP2PClient(
339
355
  config,
340
356
  archiver,
341
- proofVerifier,
357
+ peerProofVerifier,
342
358
  worldStateSynchronizer,
343
359
  epochCache,
344
360
  packageVersion,
@@ -389,6 +405,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
389
405
  // like attestations or auths will fail.
390
406
  if (validatorClient) {
391
407
  watchers.push(validatorClient);
408
+
409
+ const vc = validatorClient;
410
+ const getValidatorAddresses = () => vc.getValidatorAddresses().map(a => a.toString());
411
+ validatorClient.getProposalHandler().register(p2pClient, true, archiver, getValidatorAddresses);
412
+
392
413
  if (!options.dontStartSequencer) {
393
414
  await validatorClient.registerHandlers();
394
415
  }
@@ -398,7 +419,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
398
419
  // If there's no validator client, create a ProposalHandler to handle block and checkpoint proposals
399
420
  // for monitoring or reexecution. Reexecution (default) allows us to follow the pending chain,
400
421
  // while non-reexecution is used for validating the proposals and collecting their txs.
401
- // Checkpoint proposals are handled if the blob client can upload blobs.
422
+ // Checkpoint proposals rebuild blobs if the blob client can upload blobs.
402
423
  if (!validatorClient) {
403
424
  const reexecute = !!config.alwaysReexecuteBlockProposals;
404
425
  log.info(`Setting up proposal handler` + (reexecute ? ' with reexecution of proposals' : ''));
@@ -412,7 +433,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
412
433
  blobClient,
413
434
  dateProvider,
414
435
  telemetry,
415
- }).register(p2pClient, reexecute);
436
+ }).register(p2pClient, reexecute, archiver);
416
437
  }
417
438
 
418
439
  // Start world state and wait for it to sync to the archiver.
@@ -454,7 +475,6 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
454
475
  void archiver
455
476
  .waitForInitialSync()
456
477
  .then(async () => {
457
- await p2pClient.start();
458
478
  await validatorsSentinel?.start();
459
479
  await epochPruneWatcher?.start();
460
480
  await attestationsBlockWatcher?.start();
@@ -462,13 +482,16 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
462
482
  })
463
483
  .catch(err => log.error('Failed to start p2p services after archiver sync', err));
464
484
 
465
- const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, {
485
+ const globalVariableBuilderConfig = {
466
486
  l1Contracts: config.l1Contracts,
467
487
  ethereumSlotDuration: config.ethereumSlotDuration,
468
488
  rollupVersion: BigInt(config.rollupVersion),
469
489
  l1GenesisTime,
470
490
  slotDuration: Number(slotDuration),
471
- });
491
+ };
492
+
493
+ const globalVariableBuilder = new GlobalVariableBuilder(dateProvider, publicClient, globalVariableBuilderConfig);
494
+ const feeProvider = new FeeProviderImpl(dateProvider, publicClient, globalVariableBuilderConfig);
472
495
 
473
496
  // Validator enabled, create/start relevant service
474
497
  let sequencer: SequencerClient | undefined;
@@ -507,6 +530,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
507
530
  { telemetry, logger: log.createChild('l1-tx-utils'), dateProvider, kzg: Blob.getViemKzgInstance() },
508
531
  );
509
532
 
533
+ // Create a funder L1TxUtils from the keystore funding account (if configured)
534
+ const fundingSigner = keyStoreManager?.createFundingSigner();
535
+ let funderL1TxUtils: L1TxUtils | undefined;
536
+ if (fundingSigner) {
537
+ const [funder] = await createL1TxUtilsFromSigners(
538
+ publicClient,
539
+ [fundingSigner],
540
+ { ...config, scope: 'sequencer' },
541
+ { telemetry, logger: log.createChild('l1-tx-utils:funder'), dateProvider },
542
+ );
543
+ funderL1TxUtils = funder;
544
+ }
545
+
510
546
  // Create and start the sequencer client
511
547
  const checkpointsBuilder = new CheckpointsBuilder(
512
548
  { ...config, l1GenesisTime, slotDuration: Number(slotDuration), rollupManaLimit },
@@ -521,6 +557,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
521
557
  ...deps,
522
558
  epochCache,
523
559
  l1TxUtils,
560
+ funderL1TxUtils,
524
561
  validatorClient,
525
562
  p2pClient,
526
563
  worldStateSynchronizer,
@@ -582,9 +619,11 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
582
619
  ethereumChain.chainInfo.id,
583
620
  config.rollupVersion,
584
621
  globalVariableBuilder,
622
+ feeProvider,
585
623
  epochCache,
586
624
  packageVersion,
587
- proofVerifier,
625
+ peerProofVerifier,
626
+ rpcProofVerifier,
588
627
  telemetry,
589
628
  log,
590
629
  blobClient,
@@ -733,12 +772,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
733
772
  return this.blockSource.getCheckpointsDataForEpoch(epochNumber);
734
773
  }
735
774
 
736
- /**
737
- * Method to fetch the current min L2 fees.
738
- * @returns The current min L2 fees.
739
- */
740
775
  public async getCurrentMinFees(): Promise<GasFees> {
741
- return await this.globalVariableBuilder.getCurrentMinFees();
776
+ return await this.feeProvider.getCurrentMinFees();
777
+ }
778
+
779
+ /** Returns predicted min fees for the current slot and next N slots. */
780
+ public async getPredictedMinFees(manaUsage?: ManaUsageEstimate): Promise<GasFees[]> {
781
+ return await this.feeProvider.getPredictedMinFees(manaUsage);
742
782
  }
743
783
 
744
784
  public async getMaxPriorityFees(): Promise<GasFees> {
@@ -806,18 +846,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
806
846
  page?: number,
807
847
  referenceBlock?: BlockHash,
808
848
  ): Promise<TxScopedL2Log[][]> {
849
+ let upToBlockNumber: BlockNumber | undefined;
809
850
  if (referenceBlock) {
810
851
  const initialBlockHash = await this.#getInitialHeaderHash();
811
- if (!referenceBlock.equals(initialBlockHash)) {
852
+ if (referenceBlock.equals(initialBlockHash)) {
853
+ upToBlockNumber = BlockNumber(0);
854
+ } else {
812
855
  const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
813
856
  if (!header) {
814
857
  throw new Error(
815
858
  `Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
816
859
  );
817
860
  }
861
+ upToBlockNumber = header.globalVariables.blockNumber;
818
862
  }
819
863
  }
820
- return this.logsSource.getPrivateLogsByTags(tags, page);
864
+ return this.logsSource.getPrivateLogsByTags(tags, page, upToBlockNumber);
821
865
  }
822
866
 
823
867
  public async getPublicLogsByTagsFromContract(
@@ -826,18 +870,22 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
826
870
  page?: number,
827
871
  referenceBlock?: BlockHash,
828
872
  ): Promise<TxScopedL2Log[][]> {
873
+ let upToBlockNumber: BlockNumber | undefined;
829
874
  if (referenceBlock) {
830
875
  const initialBlockHash = await this.#getInitialHeaderHash();
831
- if (!referenceBlock.equals(initialBlockHash)) {
876
+ if (referenceBlock.equals(initialBlockHash)) {
877
+ upToBlockNumber = BlockNumber(0);
878
+ } else {
832
879
  const header = await this.blockSource.getBlockHeaderByHash(referenceBlock);
833
880
  if (!header) {
834
881
  throw new Error(
835
882
  `Block ${referenceBlock.toString()} not found in the node. This might indicate a reorg has occurred.`,
836
883
  );
837
884
  }
885
+ upToBlockNumber = header.globalVariables.blockNumber;
838
886
  }
839
887
  }
840
- return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page);
888
+ return this.logsSource.getPublicLogsByTagsFromContract(contractAddress, tags, page, upToBlockNumber);
841
889
  }
842
890
 
843
891
  /**
@@ -923,7 +971,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
923
971
  await tryStop(this.validatorsSentinel);
924
972
  await tryStop(this.epochPruneWatcher);
925
973
  await tryStop(this.slasherClient);
926
- await tryStop(this.proofVerifier);
974
+ await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
927
975
  await tryStop(this.sequencer);
928
976
  await tryStop(this.proverNode);
929
977
  await tryStop(this.p2pClient);
@@ -1013,7 +1061,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1013
1061
  );
1014
1062
 
1015
1063
  // Build a map from block number to block hash
1016
- const blockNumberToHash = new Map<BlockNumber, Fr>();
1064
+ const blockNumberToHash = new Map<BlockNumber, BlockHash>();
1017
1065
  for (let i = 0; i < uniqueBlockNumbers.length; i++) {
1018
1066
  const blockHash = blockHashes[i];
1019
1067
  if (blockHash === undefined) {
@@ -1031,13 +1079,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1031
1079
  if (blockNumber === undefined) {
1032
1080
  throw new Error(`Block number not found for leaf index ${index} in tree ${MerkleTreeId[treeId]}`);
1033
1081
  }
1034
- const blockHash = blockNumberToHash.get(blockNumber);
1035
- if (blockHash === undefined) {
1082
+ const l2BlockHash = blockNumberToHash.get(blockNumber);
1083
+ if (l2BlockHash === undefined) {
1036
1084
  throw new Error(`Block hash not found for block number ${blockNumber}`);
1037
1085
  }
1038
1086
  return {
1039
1087
  l2BlockNumber: blockNumber,
1040
- l2BlockHash: new BlockHash(blockHash),
1088
+ l2BlockHash,
1041
1089
  data: index,
1042
1090
  };
1043
1091
  });
@@ -1047,20 +1095,14 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1047
1095
  referenceBlock: BlockParameter,
1048
1096
  blockHash: BlockHash,
1049
1097
  ): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
1050
- // Block 0 (the initial block) has an empty archive, so no membership witness can exist.
1051
- if (referenceBlock === BlockNumber.ZERO) {
1052
- return undefined;
1053
- }
1054
- if (BlockHash.isBlockHash(referenceBlock)) {
1055
- const initialBlockHash = await this.#getInitialHeaderHash();
1056
- if (referenceBlock.equals(initialBlockHash)) {
1057
- return undefined;
1058
- }
1059
- }
1060
1098
  // The Noir circuit checks the archive membership proof against `anchor_block_header.last_archive.root`,
1061
1099
  // which is the archive tree root BEFORE the anchor block was added (i.e. the state after block N-1).
1062
1100
  // So we need the world state at block N-1, not block N, to produce a sibling path matching that root.
1063
1101
  const referenceBlockNumber = await this.resolveBlockNumber(referenceBlock);
1102
+ if (referenceBlockNumber === BlockNumber.ZERO) {
1103
+ // Block 0 (the initial block) has an empty archive, so no membership witness can exist.
1104
+ return undefined;
1105
+ }
1064
1106
  const committedDb = await this.getWorldState(BlockNumber(referenceBlockNumber - 1));
1065
1107
  const [pathAndIndex] = await committedDb.findSiblingPaths<MerkleTreeId.ARCHIVE>(MerkleTreeId.ARCHIVE, [blockHash]);
1066
1108
  return pathAndIndex === undefined
@@ -1323,7 +1365,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1323
1365
  { isSimulation, skipFeeEnforcement }: { isSimulation?: boolean; skipFeeEnforcement?: boolean } = {},
1324
1366
  ): Promise<TxValidationResult> {
1325
1367
  const db = this.worldStateSynchronizer.getCommitted();
1326
- const verifier = isSimulation ? undefined : this.proofVerifier;
1368
+ const verifier = isSimulation ? undefined : this.rpcProofVerifier;
1327
1369
 
1328
1370
  // We accept transactions if they are not expired by the next slot (checked based on the ExpirationTimestamp field)
1329
1371
  const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
@@ -1372,7 +1414,15 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1372
1414
  archiver.updateConfig(config);
1373
1415
  }
1374
1416
  if (newConfig.realProofs !== this.config.realProofs) {
1375
- this.proofVerifier = config.realProofs ? await BBCircuitVerifier.new(newConfig) : new TestCircuitVerifier();
1417
+ await Promise.all([tryStop(this.peerProofVerifier), tryStop(this.rpcProofVerifier)]);
1418
+ if (newConfig.realProofs) {
1419
+ this.peerProofVerifier = await BatchChonkVerifier.new(newConfig, newConfig.bbChonkVerifyMaxBatch, 'peer');
1420
+ const rpcVerifier = await BBCircuitVerifier.new(newConfig);
1421
+ this.rpcProofVerifier = new QueuedIVCVerifier(rpcVerifier, newConfig.numConcurrentIVCVerifiers);
1422
+ } else {
1423
+ this.peerProofVerifier = new TestCircuitVerifier();
1424
+ this.rpcProofVerifier = new TestCircuitVerifier();
1425
+ }
1376
1426
  }
1377
1427
 
1378
1428
  this.config = newConfig;
@@ -1447,7 +1497,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1447
1497
  return Promise.resolve();
1448
1498
  }
1449
1499
 
1450
- public async rollbackTo(targetBlock: BlockNumber, force?: boolean): Promise<void> {
1500
+ public async rollbackTo(targetBlock: BlockNumber, force?: boolean, resumeSync = true): Promise<void> {
1451
1501
  const archiver = this.blockSource as Archiver;
1452
1502
  if (!('rollbackTo' in archiver)) {
1453
1503
  throw new Error('Archiver implementation does not support rollbacks.');
@@ -1477,9 +1527,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1477
1527
  this.log.error(`Error during rollback`, err);
1478
1528
  throw err;
1479
1529
  } finally {
1480
- this.log.info(`Resuming world state and archiver sync.`);
1481
- this.worldStateSynchronizer.resumeSync();
1482
- archiver.resume();
1530
+ if (resumeSync) {
1531
+ this.log.info(`Resuming world state and archiver sync.`);
1532
+ this.worldStateSynchronizer.resumeSync();
1533
+ archiver.resume();
1534
+ } else {
1535
+ this.log.info(`Sync left paused after rollback (resumeSync=false).`);
1536
+ }
1483
1537
  }
1484
1538
  }
1485
1539
 
@@ -1496,19 +1550,12 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1496
1550
  return Promise.resolve();
1497
1551
  }
1498
1552
 
1499
- public getSlashPayloads(): Promise<SlashPayloadRound[]> {
1500
- if (!this.slasherClient) {
1501
- throw new Error(`Slasher client not enabled`);
1502
- }
1503
- return this.slasherClient.getSlashPayloads();
1504
- }
1505
-
1506
1553
  public getSlashOffenses(round: bigint | 'all' | 'current'): Promise<Offense[]> {
1507
1554
  if (!this.slasherClient) {
1508
1555
  throw new Error(`Slasher client not enabled`);
1509
1556
  }
1510
1557
  if (round === 'all') {
1511
- return this.slasherClient.getPendingOffenses();
1558
+ return this.slasherClient.getOffenses();
1512
1559
  } else {
1513
1560
  return this.slasherClient.gatherOffensesForRound(round === 'current' ? undefined : BigInt(round));
1514
1561
  }
@@ -1694,7 +1741,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, AztecNodeDeb
1694
1741
  // Double-check world-state synced to the same block hash as was requested
1695
1742
  if (BlockHash.isBlockHash(block)) {
1696
1743
  const blockHash = await snapshot.getLeafValue(MerkleTreeId.ARCHIVE, BigInt(blockNumber));
1697
- if (!blockHash || !new BlockHash(blockHash).equals(block)) {
1744
+ if (!blockHash || !block.equals(blockHash)) {
1698
1745
  throw new Error(
1699
1746
  `Block hash ${block.toString()} not found in world state at block number ${blockNumber}. If the node API has been queried with anchor block hash possibly a reorg has occurred.`,
1700
1747
  );
@@ -1,10 +1,10 @@
1
1
  import type { EpochCache } from '@aztec/epoch-cache';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
- import type { DataStoreConfig } from '@aztec/kv-store/config';
4
3
  import { createStore } from '@aztec/kv-store/lmdb-v2';
5
4
  import type { P2PClient } from '@aztec/p2p';
6
5
  import type { L2BlockSource } from '@aztec/stdlib/block';
7
6
  import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
7
+ import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
8
8
 
9
9
  import type { SentinelConfig } from './config.js';
10
10
  import { Sentinel } from './sentinel.js';
@@ -88,7 +88,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
88
88
 
89
89
  /** Loads initial slot and initializes blockstream. We will not process anything at or before the initial slot. */
90
90
  protected async init() {
91
- this.initialSlot = this.epochCache.getEpochAndSlotNow().slot;
91
+ this.initialSlot = this.epochCache.getSlotNow();
92
92
  const startingBlock = BlockNumber(await this.archiver.getBlockNumber());
93
93
  this.logger.info(`Starting validator sentinel with initial slot ${this.initialSlot} and block ${startingBlock}`);
94
94
  this.blockStream = new L2BlockStream(this.archiver, this.l2TipsStore, this, this.logger, { startingBlock });
@@ -219,7 +219,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
219
219
  // Check that we have at least requiredConsecutiveEpochs and that all of them are above the inactivity threshold
220
220
  return pastEpochs
221
221
  .slice(0, requiredConsecutiveEpochs)
222
- .every(p => p.missed / p.total >= this.config.slashInactivityTargetPercentage);
222
+ .every(p => (p.total === 0 ? false : p.missed / p.total >= this.config.slashInactivityTargetPercentage));
223
223
  }
224
224
 
225
225
  protected async handleProvenPerformance(epoch: EpochNumber, performance: ValidatorsEpochPerformance) {
@@ -228,7 +228,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
228
228
  }
229
229
 
230
230
  const inactiveValidators = getEntries(performance)
231
- .filter(([_, { missed, total }]) => missed / total >= this.config.slashInactivityTargetPercentage)
231
+ .filter(([_, { missed, total }]) => total > 0 && missed / total >= this.config.slashInactivityTargetPercentage)
232
232
  .map(([address]) => address);
233
233
 
234
234
  this.logger.debug(`Found ${inactiveValidators.length} inactive validators in epoch ${epoch}`, {
@@ -264,7 +264,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
264
264
  * and we don't have that data if we were offline during the period.
265
265
  */
266
266
  public async work() {
267
- const { slot: currentSlot } = this.epochCache.getEpochAndSlotNow();
267
+ const currentSlot = this.epochCache.getSlotNow();
268
268
  try {
269
269
  // Manually sync the block stream to ensure we have the latest data.
270
270
  // Note we never `start` the blockstream, so it loops at the same pace as we do.
@@ -436,7 +436,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
436
436
  ? fromEntries(await Promise.all(validators.map(async v => [v.toString(), await this.store.getHistory(v)])))
437
437
  : await this.store.getHistories();
438
438
 
439
- const slotNow = this.epochCache.getEpochAndSlotNow().slot;
439
+ const slotNow = this.epochCache.getSlotNow();
440
440
  fromSlot ??= SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
441
441
  toSlot ??= this.lastProcessedSlot ?? slotNow;
442
442
 
@@ -464,7 +464,7 @@ export class Sentinel extends (EventEmitter as new () => WatcherEmitter) impleme
464
464
  return undefined;
465
465
  }
466
466
 
467
- const slotNow = this.epochCache.getEpochAndSlotNow().slot;
467
+ const slotNow = this.epochCache.getSlotNow();
468
468
  const effectiveFromSlot =
469
469
  fromSlot ?? SlotNumber(Math.max((this.lastProcessedSlot ?? slotNow) - this.store.getHistoryLength(), 0));
470
470
  const effectiveToSlot = toSlot ?? this.lastProcessedSlot ?? slotNow;