@aztec/slasher 4.0.0-nightly.20250907 → 4.0.0-nightly.20260107

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.
Files changed (65) hide show
  1. package/README.md +60 -11
  2. package/dest/config.d.ts +1 -1
  3. package/dest/config.d.ts.map +1 -1
  4. package/dest/config.js +8 -2
  5. package/dest/empire_slasher_client.d.ts +8 -6
  6. package/dest/empire_slasher_client.d.ts.map +1 -1
  7. package/dest/empire_slasher_client.js +11 -5
  8. package/dest/factory/create_facade.d.ts +3 -2
  9. package/dest/factory/create_facade.d.ts.map +1 -1
  10. package/dest/factory/create_implementation.d.ts +3 -3
  11. package/dest/factory/create_implementation.d.ts.map +1 -1
  12. package/dest/factory/create_implementation.js +6 -4
  13. package/dest/factory/get_settings.d.ts +2 -2
  14. package/dest/factory/get_settings.d.ts.map +1 -1
  15. package/dest/factory/index.d.ts +1 -1
  16. package/dest/index.d.ts +1 -1
  17. package/dest/null_slasher_client.d.ts +3 -2
  18. package/dest/null_slasher_client.d.ts.map +1 -1
  19. package/dest/slash_offenses_collector.d.ts +1 -1
  20. package/dest/slash_offenses_collector.d.ts.map +1 -1
  21. package/dest/slash_offenses_collector.js +1 -2
  22. package/dest/slash_round_monitor.d.ts +5 -4
  23. package/dest/slash_round_monitor.d.ts.map +1 -1
  24. package/dest/slasher_client_facade.d.ts +4 -3
  25. package/dest/slasher_client_facade.d.ts.map +1 -1
  26. package/dest/slasher_client_facade.js +1 -0
  27. package/dest/slasher_client_interface.d.ts +3 -2
  28. package/dest/slasher_client_interface.d.ts.map +1 -1
  29. package/dest/stores/offenses_store.d.ts +1 -1
  30. package/dest/stores/offenses_store.d.ts.map +1 -1
  31. package/dest/stores/offenses_store.js +1 -1
  32. package/dest/stores/payloads_store.d.ts +2 -2
  33. package/dest/stores/payloads_store.d.ts.map +1 -1
  34. package/dest/stores/schema_version.d.ts +1 -1
  35. package/dest/tally_slasher_client.d.ts +14 -8
  36. package/dest/tally_slasher_client.d.ts.map +1 -1
  37. package/dest/tally_slasher_client.js +57 -12
  38. package/dest/test/dummy_watcher.d.ts +11 -0
  39. package/dest/test/dummy_watcher.d.ts.map +1 -0
  40. package/dest/test/dummy_watcher.js +14 -0
  41. package/dest/watcher.d.ts +3 -1
  42. package/dest/watcher.d.ts.map +1 -1
  43. package/dest/watchers/attestations_block_watcher.d.ts +6 -3
  44. package/dest/watchers/attestations_block_watcher.d.ts.map +1 -1
  45. package/dest/watchers/attestations_block_watcher.js +37 -22
  46. package/dest/watchers/epoch_prune_watcher.d.ts +8 -7
  47. package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -1
  48. package/dest/watchers/epoch_prune_watcher.js +48 -37
  49. package/package.json +13 -12
  50. package/src/config.ts +8 -2
  51. package/src/empire_slasher_client.ts +15 -8
  52. package/src/factory/create_facade.ts +2 -1
  53. package/src/factory/create_implementation.ts +6 -1
  54. package/src/factory/get_settings.ts +1 -1
  55. package/src/null_slasher_client.ts +2 -1
  56. package/src/slash_offenses_collector.ts +1 -2
  57. package/src/slash_round_monitor.ts +3 -2
  58. package/src/slasher_client_facade.ts +4 -2
  59. package/src/slasher_client_interface.ts +2 -1
  60. package/src/stores/offenses_store.ts +1 -1
  61. package/src/tally_slasher_client.ts +80 -17
  62. package/src/test/dummy_watcher.ts +21 -0
  63. package/src/watcher.ts +4 -1
  64. package/src/watchers/attestations_block_watcher.ts +44 -26
  65. package/src/watchers/epoch_prune_watcher.ts +67 -55
@@ -0,0 +1,21 @@
1
+ import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
2
+
3
+ import EventEmitter from 'node:events';
4
+
5
+ import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
6
+
7
+ export class DummyWatcher extends (EventEmitter as new () => WatcherEmitter) implements Watcher {
8
+ public updateConfig(_config: Partial<SlasherConfig>) {}
9
+
10
+ public start() {
11
+ return Promise.resolve();
12
+ }
13
+
14
+ public stop() {
15
+ return Promise.resolve();
16
+ }
17
+
18
+ public triggerSlash(args: WantToSlashArgs[]) {
19
+ this.emit(WANT_TO_SLASH_EVENT, args);
20
+ }
21
+ }
package/src/watcher.ts CHANGED
@@ -2,13 +2,15 @@ import { EthAddress } from '@aztec/foundation/eth-address';
2
2
  import type { TypedEventEmitter } from '@aztec/foundation/types';
3
3
  import { OffenseType } from '@aztec/stdlib/slashing';
4
4
 
5
+ import type { SlasherConfig } from './config.js';
6
+
5
7
  export const WANT_TO_SLASH_EVENT = 'want-to-slash' as const;
6
8
 
7
9
  export interface WantToSlashArgs {
8
10
  validator: EthAddress;
9
11
  amount: bigint;
10
12
  offenseType: OffenseType;
11
- epochOrSlot: bigint; // Epoch number for epoch-based offenses, block number for block-based
13
+ epochOrSlot: bigint; // Epoch number for epoch-based offenses, slot number for slot-based
12
14
  }
13
15
 
14
16
  // Event map for specific, known events of a watcher
@@ -21,4 +23,5 @@ export type WatcherEmitter = TypedEventEmitter<WatcherEventMap>;
21
23
  export type Watcher = WatcherEmitter & {
22
24
  start?: () => Promise<void>;
23
25
  stop?: () => Promise<void>;
26
+ updateConfig: (config: Partial<SlasherConfig>) => void;
24
27
  };
@@ -1,10 +1,12 @@
1
1
  import { EpochCache } from '@aztec/epoch-cache';
2
+ import { SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { merge, pick } from '@aztec/foundation/collection';
2
4
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
5
  import {
4
6
  type InvalidBlockDetectedEvent,
7
+ type L2BlockInfo,
5
8
  type L2BlockSourceEventEmitter,
6
9
  L2BlockSourceEvents,
7
- PublishedL2Block,
8
10
  type ValidateBlockNegativeResult,
9
11
  } from '@aztec/stdlib/block';
10
12
  import { OffenseType } from '@aztec/stdlib/slashing';
@@ -14,6 +16,13 @@ import EventEmitter from 'node:events';
14
16
  import type { SlasherConfig } from '../config.js';
15
17
  import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
16
18
 
19
+ const AttestationsBlockWatcherConfigKeys = [
20
+ 'slashAttestDescendantOfInvalidPenalty',
21
+ 'slashProposeInvalidAttestationsPenalty',
22
+ ] as const;
23
+
24
+ type AttestationsBlockWatcherConfig = Pick<SlasherConfig, (typeof AttestationsBlockWatcherConfigKeys)[number]>;
25
+
17
26
  /**
18
27
  * This watcher is responsible for detecting invalid blocks and creating slashing arguments for offenders.
19
28
  * An invalid block is one that doesn't have enough attestations or has incorrect attestations.
@@ -29,13 +38,14 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
29
38
  // All invalid archive roots seen
30
39
  private invalidArchiveRoots: Set<string> = new Set();
31
40
 
41
+ private config: AttestationsBlockWatcherConfig;
42
+
32
43
  private boundHandleInvalidBlock = (event: InvalidBlockDetectedEvent) => {
33
44
  try {
34
45
  this.handleInvalidBlock(event);
35
46
  } catch (err) {
36
47
  this.log.error('Error handling invalid block', err, {
37
- ...event.validationResult.block.block.toBlockInfo(),
38
- ...event.validationResult.block.l1,
48
+ ...event.validationResult,
39
49
  reason: event.validationResult.reason,
40
50
  });
41
51
  }
@@ -44,13 +54,16 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
44
54
  constructor(
45
55
  private l2BlockSource: L2BlockSourceEventEmitter,
46
56
  private epochCache: EpochCache,
47
- private config: Pick<
48
- SlasherConfig,
49
- 'slashAttestDescendantOfInvalidPenalty' | 'slashProposeInvalidAttestationsPenalty'
50
- >,
57
+ config: AttestationsBlockWatcherConfig,
51
58
  ) {
52
59
  super();
53
- this.log.info('InvalidBlockWatcher initialized');
60
+ this.config = pick(config, ...AttestationsBlockWatcherConfigKeys);
61
+ this.log.info('AttestationsBlockWatcher initialized');
62
+ }
63
+
64
+ public updateConfig(newConfig: Partial<AttestationsBlockWatcherConfig>) {
65
+ this.config = merge(this.config, pick(newConfig, ...AttestationsBlockWatcherConfigKeys));
66
+ this.log.verbose('AttestationsBlockWatcher config updated', this.config);
54
67
  }
55
68
 
56
69
  public start() {
@@ -68,16 +81,16 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
68
81
 
69
82
  private handleInvalidBlock(event: InvalidBlockDetectedEvent): void {
70
83
  const { validationResult } = event;
71
- const block = validationResult.block.block;
84
+ const block = validationResult.block;
72
85
 
73
86
  // Check if we already have processed this block, archiver may emit the same event multiple times
74
- if (this.invalidArchiveRoots.has(block.archive.root.toString())) {
75
- this.log.trace(`Already processed invalid block ${block.number}`);
87
+ if (this.invalidArchiveRoots.has(block.archive.toString())) {
88
+ this.log.trace(`Already processed invalid block ${block.blockNumber}`);
76
89
  return;
77
90
  }
78
91
 
79
- this.log.verbose(`Detected invalid block ${block.number}`, {
80
- ...block.toBlockInfo(),
92
+ this.log.verbose(`Detected invalid block ${block.blockNumber}`, {
93
+ ...block,
81
94
  reason: validationResult.valid === false ? validationResult.reason : 'unknown',
82
95
  });
83
96
 
@@ -94,11 +107,11 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
94
107
  private slashAttestorsOnAncestorInvalid(validationResult: ValidateBlockNegativeResult) {
95
108
  const block = validationResult.block;
96
109
 
97
- const parentArchive = block.block.header.lastArchive.root.toString();
98
- if (this.invalidArchiveRoots.has(block.block.header.lastArchive.root.toString())) {
99
- const attestors = validationResult.attestations.map(a => a.getSender());
100
- this.log.info(`Want to slash attestors of block ${block.block.number} built on invalid block`, {
101
- ...block.block.toBlockInfo(),
110
+ const parentArchive = block.lastArchive.toString();
111
+ if (this.invalidArchiveRoots.has(parentArchive)) {
112
+ const attestors = validationResult.attestors;
113
+ this.log.info(`Want to slash attestors of block ${block.blockNumber} built on invalid block`, {
114
+ ...block,
102
115
  ...attestors,
103
116
  parentArchive,
104
117
  });
@@ -109,7 +122,7 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
109
122
  validator: attestor,
110
123
  amount: this.config.slashAttestDescendantOfInvalidPenalty,
111
124
  offenseType: OffenseType.ATTESTED_DESCENDANT_OF_INVALID,
112
- epochOrSlot: block.block.slot,
125
+ epochOrSlot: BigInt(SlotNumber(block.slotNumber)),
113
126
  })),
114
127
  );
115
128
  }
@@ -117,9 +130,14 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
117
130
 
118
131
  private slashProposer(validationResult: ValidateBlockNegativeResult) {
119
132
  const { reason, block } = validationResult;
120
- const blockNumber = block.block.number;
121
- const slot = block.block.header.getSlot();
122
- const proposer = this.epochCache.getProposerFromEpochCommittee(validationResult, slot);
133
+ const blockNumber = block.blockNumber;
134
+ const slot = block.slotNumber;
135
+ const epochCommitteeInfo = {
136
+ committee: validationResult.committee,
137
+ seed: validationResult.seed,
138
+ epoch: validationResult.epoch,
139
+ };
140
+ const proposer = this.epochCache.getProposerFromEpochCommittee(epochCommitteeInfo, slot);
123
141
 
124
142
  if (!proposer) {
125
143
  this.log.warn(`No proposer found for block ${blockNumber} at slot ${slot}`);
@@ -132,11 +150,11 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
132
150
  validator: proposer,
133
151
  amount,
134
152
  offenseType: offense,
135
- epochOrSlot: block.block.slot,
153
+ epochOrSlot: BigInt(slot),
136
154
  };
137
155
 
138
156
  this.log.info(`Want to slash proposer of block ${blockNumber} due to ${reason}`, {
139
- ...block.block.toBlockInfo(),
157
+ ...block,
140
158
  ...args,
141
159
  });
142
160
 
@@ -156,8 +174,8 @@ export class AttestationsBlockWatcher extends (EventEmitter as new () => Watcher
156
174
  }
157
175
  }
158
176
 
159
- private addInvalidBlock(block: PublishedL2Block) {
160
- this.invalidArchiveRoots.add(block.block.archive.root.toString());
177
+ private addInvalidBlock(block: L2BlockInfo) {
178
+ this.invalidArchiveRoots.add(block.archive.toString());
161
179
 
162
180
  // Prune old entries if we exceed the maximum
163
181
  if (this.invalidArchiveRoots.size > this.maxInvalidBlocks) {
@@ -1,5 +1,6 @@
1
- import type { Tx } from '@aztec/aztec.js';
2
1
  import { EpochCache } from '@aztec/epoch-cache';
2
+ import { BlockNumber, CheckpointNumber, EpochNumber } from '@aztec/foundation/branded-types';
3
+ import { merge, pick } from '@aztec/foundation/collection';
3
4
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
5
  import {
5
6
  EthAddress,
@@ -8,9 +9,15 @@ import {
8
9
  type L2BlockSourceEventEmitter,
9
10
  L2BlockSourceEvents,
10
11
  } from '@aztec/stdlib/block';
11
- import type { IFullNodeBlockBuilder, ITxProvider, MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
12
+ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
13
+ import type {
14
+ IFullNodeBlockBuilder,
15
+ ITxProvider,
16
+ MerkleTreeWriteOperations,
17
+ SlasherConfig,
18
+ } from '@aztec/stdlib/interfaces/server';
12
19
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
13
- import { OffenseType } from '@aztec/stdlib/slashing';
20
+ import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
14
21
  import {
15
22
  ReExFailedTxsError,
16
23
  ReExStateMismatchError,
@@ -22,10 +29,9 @@ import EventEmitter from 'node:events';
22
29
 
23
30
  import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher, type WatcherEmitter } from '../watcher.js';
24
31
 
25
- type EpochPruneWatcherPenalties = {
26
- slashPrunePenalty: bigint;
27
- slashDataWithholdingPenalty: bigint;
28
- };
32
+ const EpochPruneWatcherPenaltiesConfigKeys = ['slashPrunePenalty', 'slashDataWithholdingPenalty'] as const;
33
+
34
+ type EpochPruneWatcherPenalties = Pick<SlasherConfig, (typeof EpochPruneWatcherPenaltiesConfigKeys)[number]>;
29
35
 
30
36
  /**
31
37
  * This watcher is responsible for detecting chain prunes and creating slashing arguments for the committee.
@@ -39,15 +45,18 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
39
45
  // Store bound function reference for proper listener removal
40
46
  private boundHandlePruneL2Blocks = this.handlePruneL2Blocks.bind(this);
41
47
 
48
+ private penalties: EpochPruneWatcherPenalties;
49
+
42
50
  constructor(
43
51
  private l2BlockSource: L2BlockSourceEventEmitter,
44
52
  private l1ToL2MessageSource: L1ToL2MessageSource,
45
53
  private epochCache: EpochCache,
46
54
  private txProvider: Pick<ITxProvider, 'getAvailableTxs'>,
47
55
  private blockBuilder: IFullNodeBlockBuilder,
48
- private penalties: EpochPruneWatcherPenalties,
56
+ penalties: EpochPruneWatcherPenalties,
49
57
  ) {
50
58
  super();
59
+ this.penalties = pick(penalties, ...EpochPruneWatcherPenaltiesConfigKeys);
51
60
  this.log.verbose(
52
61
  `EpochPruneWatcher initialized with penalties: valid epoch pruned=${penalties.slashPrunePenalty} data withholding=${penalties.slashDataWithholdingPenalty}`,
53
62
  );
@@ -63,56 +72,58 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
63
72
  return Promise.resolve();
64
73
  }
65
74
 
75
+ public updateConfig(config: Partial<SlasherConfig>): void {
76
+ this.penalties = merge(this.penalties, pick(config, ...EpochPruneWatcherPenaltiesConfigKeys));
77
+ this.log.verbose('EpochPruneWatcher config updated', this.penalties);
78
+ }
79
+
66
80
  private handlePruneL2Blocks(event: L2BlockPruneEvent): void {
67
81
  const { blocks, epochNumber } = event;
68
- this.log.info(`Detected chain prune. Validating epoch ${epochNumber}`);
69
-
70
- this.validateBlocks(blocks)
71
- .then(async () => {
72
- this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
73
- const validators = await this.getValidatorsForEpoch(epochNumber);
74
- // need to specify return type to be able to return offense as undefined later on
75
- const result: { validators: EthAddress[]; offense: OffenseType | undefined } = {
76
- validators,
77
- offense: OffenseType.VALID_EPOCH_PRUNED,
78
- };
79
- return result;
80
- })
81
- .catch(async error => {
82
- if (error instanceof TransactionsNotAvailableError) {
83
- this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, error);
84
- const validators = await this.getValidatorsForEpoch(epochNumber);
85
- return {
86
- validators,
87
- offense: OffenseType.DATA_WITHHOLDING,
88
- };
89
- } else {
90
- this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
91
- return {
92
- validators: [],
93
- offense: undefined,
94
- };
95
- }
96
- })
97
- .then(({ validators, offense }) => {
98
- if (validators.length === 0 || offense === undefined) {
99
- return;
100
- }
101
- const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
102
- this.log.info(`Slash for epoch ${epochNumber} created`, args);
103
- this.emit(WANT_TO_SLASH_EVENT, args);
104
- })
105
- .catch(error => {
106
- // This can happen if we fail to get the validators for the epoch.
107
- this.log.error('Error while creating slash for epoch', error);
108
- });
82
+ void this.processPruneL2Blocks(blocks, epochNumber).catch(err =>
83
+ this.log.error('Error processing pruned L2 blocks', err, { epochNumber }),
84
+ );
85
+ }
86
+
87
+ private async emitSlashForEpoch(offense: OffenseType, epochNumber: EpochNumber): Promise<void> {
88
+ const validators = await this.getValidatorsForEpoch(epochNumber);
89
+ if (validators.length === 0) {
90
+ this.log.warn(`No validators found for epoch ${epochNumber} (cannot slash for ${getOffenseTypeName(offense)})`);
91
+ return;
92
+ }
93
+ const args = this.validatorsToSlashingArgs(validators, offense, epochNumber);
94
+ this.log.verbose(`Created slash for ${getOffenseTypeName(offense)} at epoch ${epochNumber}`, args);
95
+ this.emit(WANT_TO_SLASH_EVENT, args);
96
+ }
97
+
98
+ private async processPruneL2Blocks(blocks: L2Block[], epochNumber: EpochNumber): Promise<void> {
99
+ try {
100
+ const l1Constants = this.epochCache.getL1Constants();
101
+ const epochBlocks = blocks.filter(b => getEpochAtSlot(b.slot, l1Constants) === epochNumber);
102
+ this.log.info(
103
+ `Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`,
104
+ { blocks: epochBlocks.map(b => b.toBlockInfo()) },
105
+ );
106
+
107
+ await this.validateBlocks(epochBlocks);
108
+ this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
109
+ await this.emitSlashForEpoch(OffenseType.VALID_EPOCH_PRUNED, epochNumber);
110
+ } catch (error) {
111
+ if (error instanceof TransactionsNotAvailableError) {
112
+ this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
113
+ message: error.message,
114
+ });
115
+ await this.emitSlashForEpoch(OffenseType.DATA_WITHHOLDING, epochNumber);
116
+ } else {
117
+ this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
118
+ }
119
+ }
109
120
  }
110
121
 
111
122
  public async validateBlocks(blocks: L2Block[]): Promise<void> {
112
123
  if (blocks.length === 0) {
113
124
  return;
114
125
  }
115
- const fork = await this.blockBuilder.getFork(blocks[0].header.globalVariables.blockNumber - 1);
126
+ const fork = await this.blockBuilder.getFork(BlockNumber(blocks[0].header.globalVariables.blockNumber - 1));
116
127
  try {
117
128
  for (const block of blocks) {
118
129
  await this.validateBlock(block, fork);
@@ -134,9 +145,10 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
134
145
  throw new TransactionsNotAvailableError(missingTxs);
135
146
  }
136
147
 
137
- const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockFromL1.number);
148
+ const checkpointNumber = CheckpointNumber.fromBlockNumber(blockFromL1.number);
149
+ const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
138
150
  const { block, failedTxs, numTxs } = await this.blockBuilder.buildBlock(
139
- txs as Tx[],
151
+ txs,
140
152
  l1ToL2Messages,
141
153
  blockFromL1.header.globalVariables,
142
154
  {},
@@ -154,7 +166,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
154
166
  }
155
167
  }
156
168
 
157
- private async getValidatorsForEpoch(epochNumber: bigint): Promise<EthAddress[]> {
169
+ private async getValidatorsForEpoch(epochNumber: EpochNumber): Promise<EthAddress[]> {
158
170
  const { committee } = await this.epochCache.getCommitteeForEpoch(epochNumber);
159
171
  if (!committee) {
160
172
  this.log.trace(`No committee found for epoch ${epochNumber}`);
@@ -166,7 +178,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
166
178
  private validatorsToSlashingArgs(
167
179
  validators: EthAddress[],
168
180
  offenseType: OffenseType,
169
- epochOrSlot: bigint,
181
+ epochOrSlot: EpochNumber,
170
182
  ): WantToSlashArgs[] {
171
183
  const penalty =
172
184
  offenseType === OffenseType.DATA_WITHHOLDING
@@ -176,7 +188,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
176
188
  validator: v,
177
189
  amount: penalty,
178
190
  offenseType,
179
- epochOrSlot,
191
+ epochOrSlot: BigInt(epochOrSlot),
180
192
  }));
181
193
  }
182
194
  }