@aztec/aztec-node 0.87.7 → 1.0.0-nightly.20250604

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,22 +1,67 @@
1
+ import { EthAddress } from '@aztec/foundation/eth-address';
1
2
  import { BufferReader, numToUInt8, numToUInt32BE, serializeToBuffer } from '@aztec/foundation/serialize';
2
3
  export class SentinelStore {
3
4
  store;
4
5
  config;
5
- static SCHEMA_VERSION = 1;
6
- map;
6
+ static SCHEMA_VERSION = 2;
7
+ // a map from validator address to their ValidatorStatusHistory
8
+ historyMap;
9
+ // a map from validator address to their historical proven epoch performance
10
+ // e.g. { validator: [{ epoch: 1, missed: 1, total: 10 }, { epoch: 2, missed: 3, total: 7 }, ...] }
11
+ provenMap;
7
12
  constructor(store, config){
8
13
  this.store = store;
9
14
  this.config = config;
10
- this.map = store.openMap('sentinel-validator-status');
15
+ this.historyMap = store.openMap('sentinel-validator-status');
16
+ this.provenMap = store.openMap('sentinel-validator-proven');
11
17
  }
12
18
  getHistoryLength() {
13
19
  return this.config.historyLength;
14
20
  }
21
+ async updateProvenPerformance(epoch, performance) {
22
+ await this.store.transactionAsync(async ()=>{
23
+ for (const [who, { missed, total }] of Object.entries(performance)){
24
+ await this.pushValidatorProvenPerformanceForEpoch({
25
+ who: EthAddress.fromString(who),
26
+ missed,
27
+ total,
28
+ epoch
29
+ });
30
+ }
31
+ });
32
+ }
33
+ async getProvenPerformance(who) {
34
+ const currentPerformanceBuffer = await this.provenMap.getAsync(who.toString());
35
+ return currentPerformanceBuffer ? this.deserializePerformance(currentPerformanceBuffer) : [];
36
+ }
37
+ async pushValidatorProvenPerformanceForEpoch({ who, missed, total, epoch }) {
38
+ const currentPerformance = await this.getProvenPerformance(who);
39
+ const existingIndex = currentPerformance.findIndex((p)=>p.epoch === epoch);
40
+ if (existingIndex !== -1) {
41
+ currentPerformance[existingIndex] = {
42
+ missed,
43
+ total,
44
+ epoch
45
+ };
46
+ } else {
47
+ currentPerformance.push({
48
+ missed,
49
+ total,
50
+ epoch
51
+ });
52
+ }
53
+ // This should be sorted by epoch, but just in case.
54
+ // Since we keep the size small, this is not a big deal.
55
+ currentPerformance.sort((a, b)=>Number(a.epoch - b.epoch));
56
+ // keep the most recent `historyLength` entries.
57
+ const performanceToKeep = currentPerformance.slice(-this.config.historyLength);
58
+ await this.provenMap.set(who.toString(), this.serializePerformance(performanceToKeep));
59
+ }
15
60
  async updateValidators(slot, statuses) {
16
61
  await this.store.transactionAsync(async ()=>{
17
62
  for (const [who, status] of Object.entries(statuses)){
18
63
  if (status) {
19
- await this.pushValidatorStatusForSlot(who, slot, status);
64
+ await this.pushValidatorStatusForSlot(EthAddress.fromString(who), slot, status);
20
65
  }
21
66
  }
22
67
  });
@@ -30,19 +75,38 @@ export class SentinelStore {
30
75
  status
31
76
  }
32
77
  ].slice(-this.config.historyLength);
33
- await this.map.set(who, this.serializeHistory(newHistory));
78
+ await this.historyMap.set(who.toString(), this.serializeHistory(newHistory));
34
79
  }
35
80
  async getHistories() {
36
81
  const histories = {};
37
- for await (const [address, history] of this.map.entriesAsync()){
82
+ for await (const [address, history] of this.historyMap.entriesAsync()){
38
83
  histories[address] = this.deserializeHistory(history);
39
84
  }
40
85
  return histories;
41
86
  }
42
87
  async getHistory(address) {
43
- const data = await this.map.getAsync(address);
88
+ const data = await this.historyMap.getAsync(address.toString());
44
89
  return data && this.deserializeHistory(data);
45
90
  }
91
+ serializePerformance(performance) {
92
+ return serializeToBuffer(performance.map((p)=>[
93
+ numToUInt32BE(Number(p.epoch)),
94
+ numToUInt32BE(p.missed),
95
+ numToUInt32BE(p.total)
96
+ ]));
97
+ }
98
+ deserializePerformance(buffer) {
99
+ const reader = new BufferReader(buffer);
100
+ const performance = [];
101
+ while(!reader.isEmpty()){
102
+ performance.push({
103
+ epoch: BigInt(reader.readNumber()),
104
+ missed: reader.readNumber(),
105
+ total: reader.readNumber()
106
+ });
107
+ }
108
+ return performance;
109
+ }
46
110
  serializeHistory(history) {
47
111
  return serializeToBuffer(history.map((h)=>[
48
112
  numToUInt32BE(Number(h.slot)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/aztec-node",
3
- "version": "0.87.7",
3
+ "version": "1.0.0-nightly.20250604",
4
4
  "main": "dest/index.js",
5
5
  "type": "module",
6
6
  "exports": {
@@ -61,27 +61,28 @@
61
61
  ]
62
62
  },
63
63
  "dependencies": {
64
- "@aztec/archiver": "0.87.7",
65
- "@aztec/bb-prover": "0.87.7",
66
- "@aztec/blob-sink": "0.87.7",
67
- "@aztec/constants": "0.87.7",
68
- "@aztec/epoch-cache": "0.87.7",
69
- "@aztec/ethereum": "0.87.7",
70
- "@aztec/foundation": "0.87.7",
71
- "@aztec/kv-store": "0.87.7",
72
- "@aztec/l1-artifacts": "0.87.7",
73
- "@aztec/merkle-tree": "0.87.7",
74
- "@aztec/node-lib": "0.87.7",
75
- "@aztec/noir-protocol-circuits-types": "0.87.7",
76
- "@aztec/p2p": "0.87.7",
77
- "@aztec/protocol-contracts": "0.87.7",
78
- "@aztec/prover-client": "0.87.7",
79
- "@aztec/sequencer-client": "0.87.7",
80
- "@aztec/simulator": "0.87.7",
81
- "@aztec/stdlib": "0.87.7",
82
- "@aztec/telemetry-client": "0.87.7",
83
- "@aztec/validator-client": "0.87.7",
84
- "@aztec/world-state": "0.87.7",
64
+ "@aztec/archiver": "1.0.0-nightly.20250604",
65
+ "@aztec/bb-prover": "1.0.0-nightly.20250604",
66
+ "@aztec/blob-sink": "1.0.0-nightly.20250604",
67
+ "@aztec/constants": "1.0.0-nightly.20250604",
68
+ "@aztec/epoch-cache": "1.0.0-nightly.20250604",
69
+ "@aztec/ethereum": "1.0.0-nightly.20250604",
70
+ "@aztec/foundation": "1.0.0-nightly.20250604",
71
+ "@aztec/kv-store": "1.0.0-nightly.20250604",
72
+ "@aztec/l1-artifacts": "1.0.0-nightly.20250604",
73
+ "@aztec/merkle-tree": "1.0.0-nightly.20250604",
74
+ "@aztec/node-lib": "1.0.0-nightly.20250604",
75
+ "@aztec/noir-protocol-circuits-types": "1.0.0-nightly.20250604",
76
+ "@aztec/p2p": "1.0.0-nightly.20250604",
77
+ "@aztec/protocol-contracts": "1.0.0-nightly.20250604",
78
+ "@aztec/prover-client": "1.0.0-nightly.20250604",
79
+ "@aztec/sequencer-client": "1.0.0-nightly.20250604",
80
+ "@aztec/simulator": "1.0.0-nightly.20250604",
81
+ "@aztec/slasher": "1.0.0-nightly.20250604",
82
+ "@aztec/stdlib": "1.0.0-nightly.20250604",
83
+ "@aztec/telemetry-client": "1.0.0-nightly.20250604",
84
+ "@aztec/validator-client": "1.0.0-nightly.20250604",
85
+ "@aztec/world-state": "1.0.0-nightly.20250604",
85
86
  "koa": "^2.16.1",
86
87
  "koa-router": "^12.0.0",
87
88
  "tslib": "^2.4.0",
@@ -11,6 +11,7 @@ import { type SharedNodeConfig, sharedNodeConfigMappings } from '@aztec/node-lib
11
11
  import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config';
12
12
  import { type ProverClientUserConfig, proverClientConfigMappings } from '@aztec/prover-client/config';
13
13
  import { type SequencerClientConfig, sequencerClientConfigMappings } from '@aztec/sequencer-client/config';
14
+ import { type SlasherConfig, slasherConfigMappings } from '@aztec/slasher';
14
15
  import { type NodeRPCConfig, nodeRpcConfigMappings } from '@aztec/stdlib/config';
15
16
  import { type ValidatorClientConfig, validatorClientConfigMappings } from '@aztec/validator-client/config';
16
17
  import { type WorldStateConfig, worldStateConfigMappings } from '@aztec/world-state/config';
@@ -33,7 +34,8 @@ export type AztecNodeConfig = ArchiverConfig &
33
34
  SentinelConfig &
34
35
  SharedNodeConfig &
35
36
  GenesisStateConfig &
36
- NodeRPCConfig & {
37
+ NodeRPCConfig &
38
+ SlasherConfig & {
37
39
  /** L1 contracts addresses */
38
40
  l1Contracts: L1ContractAddresses;
39
41
  /** Whether the validator is disabled for this node */
@@ -52,6 +54,7 @@ export const aztecNodeConfigMappings: ConfigMappingsType<AztecNodeConfig> = {
52
54
  ...sharedNodeConfigMappings,
53
55
  ...genesisStateConfigMappings,
54
56
  ...nodeRpcConfigMappings,
57
+ ...slasherConfigMappings,
55
58
  l1Contracts: {
56
59
  description: 'The deployed L1 contract addresses',
57
60
  nested: l1ContractAddressesMapping,
@@ -10,7 +10,14 @@ import {
10
10
  type PUBLIC_DATA_TREE_HEIGHT,
11
11
  } from '@aztec/constants';
12
12
  import { EpochCache } from '@aztec/epoch-cache';
13
- import { type L1ContractAddresses, RegistryContract, createEthereumChain } from '@aztec/ethereum';
13
+ import {
14
+ type L1ContractAddresses,
15
+ RegistryContract,
16
+ RollupContract,
17
+ createEthereumChain,
18
+ createExtendedL1Client,
19
+ } from '@aztec/ethereum';
20
+ import { L1TxUtilsWithBlobs } from '@aztec/ethereum/l1-tx-utils-with-blobs';
14
21
  import { compactArray } from '@aztec/foundation/collection';
15
22
  import { EthAddress } from '@aztec/foundation/eth-address';
16
23
  import { Fr } from '@aztec/foundation/fields';
@@ -20,20 +27,18 @@ import { SerialQueue } from '@aztec/foundation/queue';
20
27
  import { count } from '@aztec/foundation/string';
21
28
  import { DateProvider, Timer } from '@aztec/foundation/timer';
22
29
  import { SiblingPath } from '@aztec/foundation/trees';
23
- import { openTmpStore } from '@aztec/kv-store/lmdb';
24
- import { RollupAbi } from '@aztec/l1-artifacts';
25
- import { SHA256Trunc, StandardTree, UnbalancedTree } from '@aztec/merkle-tree';
26
30
  import { trySnapshotSync, uploadSnapshot } from '@aztec/node-lib/actions';
27
31
  import { type P2P, createP2PClient, getDefaultAllowedSetupFunctions } from '@aztec/p2p';
28
32
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
29
33
  import {
34
+ BlockBuilder,
30
35
  GlobalVariableBuilder,
31
36
  SequencerClient,
32
37
  type SequencerPublisher,
33
- createSlasherClient,
34
38
  createValidatorForAcceptingTxs,
35
39
  } from '@aztec/sequencer-client';
36
40
  import { PublicProcessorFactory } from '@aztec/simulator/server';
41
+ import { EpochPruneWatcher, SlasherClient, type Watcher } from '@aztec/slasher';
37
42
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
38
43
  import type { InBlock, L2Block, L2BlockNumber, L2BlockSource, PublishedL2Block } from '@aztec/stdlib/block';
39
44
  import type {
@@ -90,7 +95,7 @@ import {
90
95
  import { createValidatorClient } from '@aztec/validator-client';
91
96
  import { createWorldStateSynchronizer } from '@aztec/world-state';
92
97
 
93
- import { createPublicClient, fallback, getContract, http } from 'viem';
98
+ import { createPublicClient, fallback, http } from 'viem';
94
99
 
95
100
  import { createSentinel } from '../sentinel/factory.js';
96
101
  import { Sentinel } from '../sentinel/sentinel.js';
@@ -181,7 +186,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
181
186
 
182
187
  const publicClient = createPublicClient({
183
188
  chain: ethereumChain.chainInfo,
184
- transport: fallback(config.l1RpcUrls.map(url => http(url))),
189
+ transport: fallback(config.l1RpcUrls.map((url: string) => http(url))),
185
190
  pollingInterval: config.viemPollingIntervalMS,
186
191
  });
187
192
 
@@ -194,17 +199,19 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
194
199
  // Overwrite the passed in vars.
195
200
  config.l1Contracts = { ...config.l1Contracts, ...l1ContractsAddresses };
196
201
 
197
- const rollup = getContract({
198
- address: l1ContractsAddresses.rollupAddress.toString(),
199
- abi: RollupAbi,
200
- client: publicClient,
201
- });
202
+ const l1Client = createExtendedL1Client(config.l1RpcUrls, config.publisherPrivateKey, ethereumChain.chainInfo);
203
+ const l1TxUtils = new L1TxUtilsWithBlobs(l1Client, log, config);
202
204
 
203
- const rollupVersionFromRollup = Number(await rollup.read.getVersion());
205
+ const rollupContract = new RollupContract(l1Client, config.l1Contracts.rollupAddress.toString());
206
+ const [l1GenesisTime, slotDuration, rollupVersionFromRollup] = await Promise.all([
207
+ rollupContract.getL1GenesisTime(),
208
+ rollupContract.getSlotDuration(),
209
+ rollupContract.getVersion(),
210
+ ] as const);
204
211
 
205
- config.rollupVersion ??= rollupVersionFromRollup;
212
+ config.rollupVersion ??= Number(rollupVersionFromRollup);
206
213
 
207
- if (config.rollupVersion !== rollupVersionFromRollup) {
214
+ if (config.rollupVersion !== Number(rollupVersionFromRollup)) {
208
215
  log.warn(
209
216
  `Registry looked up and returned a rollup with version (${config.rollupVersion}), but this does not match with version detected from the rollup directly: (${rollupVersionFromRollup}).`,
210
217
  );
@@ -247,32 +254,56 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
247
254
  // Start p2p. Note that it depends on world state to be running.
248
255
  await p2pClient.start();
249
256
 
250
- const slasherClient = createSlasherClient(config, archiver, telemetry);
251
- slasherClient.start();
257
+ const watchers: Watcher[] = [];
258
+
259
+ const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
260
+ if (validatorsSentinel) {
261
+ watchers.push(validatorsSentinel);
262
+ }
263
+ const epochPruneWatcher = new EpochPruneWatcher(archiver, epochCache, config.slashPrunePenalty);
264
+ watchers.push(epochPruneWatcher);
265
+
266
+ const slasherClient = await SlasherClient.new(config, config.l1Contracts, l1TxUtils, watchers, dateProvider);
267
+ await slasherClient.start();
268
+
269
+ const blockBuilder = new BlockBuilder(
270
+ {
271
+ l1GenesisTime,
272
+ slotDuration: Number(slotDuration),
273
+ rollupVersion: config.rollupVersion,
274
+ l1ChainId: config.l1ChainId,
275
+ },
276
+ archiver,
277
+ worldStateSynchronizer,
278
+ archiver,
279
+ dateProvider,
280
+ telemetry,
281
+ );
252
282
 
253
283
  const validatorClient = createValidatorClient(config, {
254
284
  p2pClient,
255
285
  telemetry,
256
286
  dateProvider,
257
287
  epochCache,
288
+ blockBuilder,
258
289
  blockSource: archiver,
259
290
  });
260
-
261
- const validatorsSentinel = await createSentinel(epochCache, archiver, p2pClient, config);
262
- await validatorsSentinel?.start();
263
-
264
291
  log.verbose(`All Aztec Node subsystems synced`);
265
292
 
266
293
  // now create the sequencer
267
294
  const sequencer = config.disableValidator
268
295
  ? undefined
269
296
  : await SequencerClient.new(config, {
297
+ // if deps were provided, they should override the defaults,
298
+ // or things that we created in this function
270
299
  ...deps,
300
+ epochCache,
301
+ l1TxUtils,
271
302
  validatorClient,
272
303
  p2pClient,
273
304
  worldStateSynchronizer,
274
305
  slasherClient,
275
- contractDataSource: archiver,
306
+ blockBuilder,
276
307
  l2BlockSource: archiver,
277
308
  l1ToL2MessageSource: archiver,
278
309
  telemetry,
@@ -528,7 +559,7 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
528
559
  public async stop() {
529
560
  this.log.info(`Stopping`);
530
561
  await this.txQueue.end();
531
- await this.validatorsSentinel?.stop();
562
+ // await this.validatorsSentinel?.stop(); <- The slasher client will stop this
532
563
  await this.sequencer?.stop();
533
564
  await this.p2pClient.stop();
534
565
  await this.worldStateSynchronizer.stop();
@@ -698,120 +729,13 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
698
729
  }
699
730
 
700
731
  /**
701
- * Returns the index and sibling path for a L2->L1 message in a block's message tree.
702
- * @remarks The message tree is constructed on-demand by taking all L2->L1 messages in a block
703
- * and creating a variable depth append-only tree with the messages as leaves.
704
- * The tree is constructed in two layers:
705
- * 1. Subtree - For each transaction in the block, a subtree is created containing all L2->L1 messages from that
706
- * transaction.
707
- * 2. Top tree - A tree containing the roots of all the subtrees as leaves
708
- * The final path is constructed by concatenating the path in the subtree with the path in the top tree.
709
- * When there is only one transaction in the block, the subtree itself becomes the block's L2->L1 message tree,
710
- * and no top tree is needed. The out hash is the root of the the block's L2->L1 message tree.
711
- * TODO: Handle the case where two messages in the same tx have the same hash.
712
- * @param blockNumber - Block number to get data from
713
- * @param l2ToL1Message - Message to get index/path for
714
- * @returns [index, siblingPath] for the message
732
+ * Returns all the L2 to L1 messages in a block.
733
+ * @param blockNumber - The block number at which to get the data.
734
+ * @returns The L2 to L1 messages (undefined if the block number is not found).
715
735
  */
716
- public async getL2ToL1MessageMembershipWitness(
717
- blockNumber: L2BlockNumber,
718
- l2ToL1Message: Fr,
719
- ): Promise<[bigint, SiblingPath<number>]> {
736
+ public async getL2ToL1Messages(blockNumber: L2BlockNumber): Promise<Fr[][] | undefined> {
720
737
  const block = await this.blockSource.getBlock(blockNumber === 'latest' ? await this.getBlockNumber() : blockNumber);
721
-
722
- if (block === undefined) {
723
- throw new Error('Block not found in getL2ToL1MessageMembershipWitness');
724
- }
725
-
726
- const messagesPerTx = block.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
727
-
728
- // Find index of message in subtree and index of tx in a block
729
- let messageIndexInTx = -1,
730
- txIndex = -1;
731
- {
732
- txIndex = messagesPerTx.findIndex(messages => {
733
- const idx = messages.findIndex(msg => msg.equals(l2ToL1Message));
734
- messageIndexInTx = Math.max(messageIndexInTx, idx);
735
- return idx !== -1;
736
- });
737
- }
738
-
739
- if (txIndex === -1) {
740
- throw new Error('The L2ToL1Message you are trying to prove inclusion of does not exist');
741
- }
742
-
743
- // Get the message path in subtree and message subtree height
744
- let messagePathInSubtree: SiblingPath<number>;
745
- let messageSubtreeHeight: number;
746
- {
747
- const subtreeStore = openTmpStore(true);
748
- const txMessages = messagesPerTx[txIndex];
749
- messageSubtreeHeight = txMessages.length <= 1 ? 1 : Math.ceil(Math.log2(txMessages.length));
750
- const subtree = new StandardTree(
751
- subtreeStore,
752
- new SHA256Trunc(),
753
- `subtree_${txIndex}`,
754
- messageSubtreeHeight,
755
- 0n,
756
- Fr,
757
- );
758
- subtree.appendLeaves(txMessages);
759
- messagePathInSubtree = await subtree.getSiblingPath(BigInt(messageIndexInTx), true);
760
- await subtreeStore.delete();
761
- }
762
-
763
- // If the number of txs is 1 we are dealing with a special case where the tx subtree itself is the whole block's
764
- // l2 to l1 message tree.
765
- const numTransactions = block.body.txEffects.length;
766
- if (numTransactions === 1) {
767
- return [BigInt(messageIndexInTx), messagePathInSubtree];
768
- }
769
-
770
- // Calculate roots for all tx subtrees
771
- const txSubtreeRoots = await Promise.all(
772
- messagesPerTx.map(async (messages, txIdx) => {
773
- // For a tx with no messages, we have to set an out hash of 0 to match what the circuit does.
774
- if (messages.length === 0) {
775
- return Fr.ZERO;
776
- }
777
-
778
- const txStore = openTmpStore(true);
779
- const txTreeHeight = messages.length <= 1 ? 1 : Math.ceil(Math.log2(messages.length));
780
- const txTree = new StandardTree(
781
- txStore,
782
- new SHA256Trunc(),
783
- `tx_messages_subtree_${txIdx}`,
784
- txTreeHeight,
785
- 0n,
786
- Fr,
787
- );
788
- txTree.appendLeaves(messages);
789
- const root = Fr.fromBuffer(txTree.getRoot(true));
790
- await txStore.delete();
791
- return root;
792
- }),
793
- );
794
-
795
- // Construct the top tree and compute the combined path
796
- let combinedPath: Buffer[];
797
- {
798
- const topTreeHeight = Math.ceil(Math.log2(txSubtreeRoots.length));
799
- // The root of this tree is the out_hash calculated in Noir => we truncate to match Noir's SHA
800
- const topTree = new UnbalancedTree(new SHA256Trunc(), 'top_tree', topTreeHeight, Fr);
801
- await topTree.appendLeaves(txSubtreeRoots);
802
-
803
- const txPathInTopTree = await topTree.getSiblingPath(txSubtreeRoots[txIndex].toBigInt());
804
- // Append subtree path to top tree path
805
- combinedPath = messagePathInSubtree.toBufferArray().concat(txPathInTopTree.toBufferArray());
806
- }
807
-
808
- // Append binary index of subtree path to binary index of top tree path
809
- const combinedIndex = parseInt(
810
- txIndex.toString(2).concat(messageIndexInTx.toString(2).padStart(messageSubtreeHeight, '0')),
811
- 2,
812
- );
813
-
814
- return [BigInt(combinedIndex), new SiblingPath(combinedPath.length, combinedPath)];
738
+ return block?.body.txEffects.map(txEffect => txEffect.l2ToL1Msgs);
815
739
  }
816
740
 
817
741
  /**
@@ -3,6 +3,7 @@ import { createLogger } from '@aztec/foundation/log';
3
3
  import type { DataStoreConfig } from '@aztec/kv-store/config';
4
4
  import { createStore } from '@aztec/kv-store/lmdb-v2';
5
5
  import type { P2PClient } from '@aztec/p2p';
6
+ import type { SlasherConfig } from '@aztec/slasher/config';
6
7
  import type { L2BlockSource } from '@aztec/stdlib/block';
7
8
 
8
9
  import type { SentinelConfig } from './config.js';
@@ -13,7 +14,7 @@ export async function createSentinel(
13
14
  epochCache: EpochCache,
14
15
  archiver: L2BlockSource,
15
16
  p2p: P2PClient,
16
- config: SentinelConfig & DataStoreConfig,
17
+ config: SentinelConfig & DataStoreConfig & SlasherConfig,
17
18
  logger = createLogger('node:sentinel'),
18
19
  ): Promise<Sentinel | undefined> {
19
20
  if (!config.sentinelEnabled) {
@@ -27,5 +28,5 @@ export async function createSentinel(
27
28
  );
28
29
  const storeHistoryLength = config.sentinelHistoryLengthInEpochs * epochCache.getL1Constants().epochDuration;
29
30
  const sentinelStore = new SentinelStore(kvStore, { historyLength: storeHistoryLength });
30
- return new Sentinel(epochCache, archiver, p2p, sentinelStore, logger);
31
+ return new Sentinel(epochCache, archiver, p2p, sentinelStore, config, logger);
31
32
  }