@aztec/slasher 0.0.1-commit.d3ec352c → 0.0.1-commit.e310a4c8

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 (50) hide show
  1. package/dest/config.d.ts +1 -1
  2. package/dest/config.d.ts.map +1 -1
  3. package/dest/config.js +16 -16
  4. package/dest/empire_slasher_client.d.ts +2 -2
  5. package/dest/empire_slasher_client.d.ts.map +1 -1
  6. package/dest/empire_slasher_client.js +0 -8
  7. package/dest/factory/create_facade.d.ts +3 -2
  8. package/dest/factory/create_facade.d.ts.map +1 -1
  9. package/dest/factory/create_facade.js +1 -1
  10. package/dest/factory/create_implementation.d.ts +2 -2
  11. package/dest/factory/create_implementation.d.ts.map +1 -1
  12. package/dest/factory/get_settings.d.ts +2 -2
  13. package/dest/factory/get_settings.d.ts.map +1 -1
  14. package/dest/generated/slasher-defaults.d.ts +19 -0
  15. package/dest/generated/slasher-defaults.d.ts.map +1 -0
  16. package/dest/generated/slasher-defaults.js +19 -0
  17. package/dest/slash_offenses_collector.d.ts +1 -1
  18. package/dest/slash_offenses_collector.d.ts.map +1 -1
  19. package/dest/slash_offenses_collector.js +5 -1
  20. package/dest/slasher_client_facade.d.ts +2 -2
  21. package/dest/slasher_client_facade.d.ts.map +1 -1
  22. package/dest/stores/offenses_store.d.ts +1 -1
  23. package/dest/stores/offenses_store.d.ts.map +1 -1
  24. package/dest/stores/offenses_store.js +4 -2
  25. package/dest/stores/payloads_store.d.ts +1 -1
  26. package/dest/stores/payloads_store.d.ts.map +1 -1
  27. package/dest/stores/payloads_store.js +6 -3
  28. package/dest/tally_slasher_client.d.ts +1 -1
  29. package/dest/tally_slasher_client.d.ts.map +1 -1
  30. package/dest/tally_slasher_client.js +10 -5
  31. package/dest/watchers/attestations_block_watcher.d.ts +7 -6
  32. package/dest/watchers/attestations_block_watcher.d.ts.map +1 -1
  33. package/dest/watchers/attestations_block_watcher.js +40 -34
  34. package/dest/watchers/epoch_prune_watcher.d.ts +7 -6
  35. package/dest/watchers/epoch_prune_watcher.d.ts.map +1 -1
  36. package/dest/watchers/epoch_prune_watcher.js +34 -13
  37. package/package.json +14 -12
  38. package/src/config.ts +17 -16
  39. package/src/empire_slasher_client.ts +1 -9
  40. package/src/factory/create_facade.ts +3 -2
  41. package/src/factory/create_implementation.ts +1 -1
  42. package/src/factory/get_settings.ts +1 -1
  43. package/src/generated/slasher-defaults.ts +21 -0
  44. package/src/slash_offenses_collector.ts +5 -1
  45. package/src/slasher_client_facade.ts +1 -1
  46. package/src/stores/offenses_store.ts +4 -2
  47. package/src/stores/payloads_store.ts +7 -4
  48. package/src/tally_slasher_client.ts +10 -5
  49. package/src/watchers/attestations_block_watcher.ts +57 -44
  50. package/src/watchers/epoch_prune_watcher.ts +45 -18
@@ -18,18 +18,18 @@ const AttestationsBlockWatcherConfigKeys = [
18
18
  l2BlockSource;
19
19
  epochCache;
20
20
  log;
21
- // Only keep track of the last N invalid blocks
22
- maxInvalidBlocks;
21
+ // Only keep track of the last N invalid checkpoints
22
+ maxInvalidCheckpoints;
23
23
  // All invalid archive roots seen
24
24
  invalidArchiveRoots;
25
25
  config;
26
- boundHandleInvalidBlock;
26
+ boundHandleInvalidCheckpoint;
27
27
  constructor(l2BlockSource, epochCache, config){
28
- super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.log = createLogger('attestations-block-watcher'), this.maxInvalidBlocks = 100, this.invalidArchiveRoots = new Set(), this.boundHandleInvalidBlock = (event)=>{
28
+ super(), this.l2BlockSource = l2BlockSource, this.epochCache = epochCache, this.log = createLogger('attestations-block-watcher'), this.maxInvalidCheckpoints = 100, this.invalidArchiveRoots = new Set(), this.boundHandleInvalidCheckpoint = (event)=>{
29
29
  try {
30
- this.handleInvalidBlock(event);
30
+ this.handleInvalidCheckpoint(event);
31
31
  } catch (err) {
32
- this.log.error('Error handling invalid block', err, {
32
+ this.log.error('Error handling invalid checkpoint', err, {
33
33
  ...event.validationResult,
34
34
  reason: event.validationResult.reason
35
35
  });
@@ -43,39 +43,39 @@ const AttestationsBlockWatcherConfigKeys = [
43
43
  this.log.verbose('AttestationsBlockWatcher config updated', this.config);
44
44
  }
45
45
  start() {
46
- this.l2BlockSource.on(L2BlockSourceEvents.InvalidAttestationsBlockDetected, this.boundHandleInvalidBlock);
46
+ this.l2BlockSource.events.on(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, this.boundHandleInvalidCheckpoint);
47
47
  return Promise.resolve();
48
48
  }
49
49
  stop() {
50
- this.l2BlockSource.removeListener(L2BlockSourceEvents.InvalidAttestationsBlockDetected, this.boundHandleInvalidBlock);
50
+ this.l2BlockSource.events.removeListener(L2BlockSourceEvents.InvalidAttestationsCheckpointDetected, this.boundHandleInvalidCheckpoint);
51
51
  return Promise.resolve();
52
52
  }
53
- handleInvalidBlock(event) {
53
+ /** Event handler for invalid checkpoints as reported by the archiver. Public for testing purposes. */ handleInvalidCheckpoint(event) {
54
54
  const { validationResult } = event;
55
- const block = validationResult.block;
56
- // Check if we already have processed this block, archiver may emit the same event multiple times
57
- if (this.invalidArchiveRoots.has(block.archive.toString())) {
58
- this.log.trace(`Already processed invalid block ${block.blockNumber}`);
55
+ const checkpoint = validationResult.checkpoint;
56
+ // Check if we already have processed this checkpoint, archiver may emit the same event multiple times
57
+ if (this.invalidArchiveRoots.has(checkpoint.archive.toString())) {
58
+ this.log.trace(`Already processed invalid checkpoint ${checkpoint.checkpointNumber}`);
59
59
  return;
60
60
  }
61
- this.log.verbose(`Detected invalid block ${block.blockNumber}`, {
62
- ...block,
61
+ this.log.verbose(`Detected invalid checkpoint ${checkpoint.checkpointNumber}`, {
62
+ ...checkpoint,
63
63
  reason: validationResult.valid === false ? validationResult.reason : 'unknown'
64
64
  });
65
- // Store the invalid block
66
- this.addInvalidBlock(event.validationResult.block);
67
- // Slash the proposer of the invalid block
65
+ // Store the invalid checkpoint
66
+ this.addInvalidCheckpoint(event.validationResult.checkpoint);
67
+ // Slash the proposer of the invalid checkpoint
68
68
  this.slashProposer(event.validationResult);
69
- // Check if the parent of this block is invalid as well, if so, we will slash its attestors as well
69
+ // Check if the parent of this checkpoint is invalid as well, if so, we will slash its attestors as well
70
70
  this.slashAttestorsOnAncestorInvalid(event.validationResult);
71
71
  }
72
72
  slashAttestorsOnAncestorInvalid(validationResult) {
73
- const block = validationResult.block;
74
- const parentArchive = block.lastArchive.toString();
73
+ const checkpoint = validationResult.checkpoint;
74
+ const parentArchive = checkpoint.lastArchive.toString();
75
75
  if (this.invalidArchiveRoots.has(parentArchive)) {
76
76
  const attestors = validationResult.attestors;
77
- this.log.info(`Want to slash attestors of block ${block.blockNumber} built on invalid block`, {
78
- ...block,
77
+ this.log.info(`Want to slash attestors of checkpoint ${checkpoint.checkpointNumber} built on invalid checkpoint`, {
78
+ ...checkpoint,
79
79
  ...attestors,
80
80
  parentArchive
81
81
  });
@@ -83,17 +83,23 @@ const AttestationsBlockWatcherConfigKeys = [
83
83
  validator: attestor,
84
84
  amount: this.config.slashAttestDescendantOfInvalidPenalty,
85
85
  offenseType: OffenseType.ATTESTED_DESCENDANT_OF_INVALID,
86
- epochOrSlot: BigInt(SlotNumber(block.slotNumber))
86
+ epochOrSlot: BigInt(SlotNumber(checkpoint.slotNumber))
87
87
  })));
88
88
  }
89
89
  }
90
90
  slashProposer(validationResult) {
91
- const { reason, block } = validationResult;
92
- const blockNumber = block.blockNumber;
93
- const slot = block.slotNumber;
94
- const proposer = this.epochCache.getProposerFromEpochCommittee(validationResult, slot);
91
+ const { reason, checkpoint } = validationResult;
92
+ const checkpointNumber = checkpoint.checkpointNumber;
93
+ const slot = checkpoint.slotNumber;
94
+ const epochCommitteeInfo = {
95
+ committee: validationResult.committee,
96
+ seed: validationResult.seed,
97
+ epoch: validationResult.epoch,
98
+ isEscapeHatchOpen: false
99
+ };
100
+ const proposer = this.epochCache.getProposerFromEpochCommittee(epochCommitteeInfo, slot);
95
101
  if (!proposer) {
96
- this.log.warn(`No proposer found for block ${blockNumber} at slot ${slot}`);
102
+ this.log.warn(`No proposer found for checkpoint ${checkpointNumber} at slot ${slot}`);
97
103
  return;
98
104
  }
99
105
  const offense = this.getOffenseFromInvalidationReason(reason);
@@ -104,8 +110,8 @@ const AttestationsBlockWatcherConfigKeys = [
104
110
  offenseType: offense,
105
111
  epochOrSlot: BigInt(slot)
106
112
  };
107
- this.log.info(`Want to slash proposer of block ${blockNumber} due to ${reason}`, {
108
- ...block,
113
+ this.log.info(`Want to slash proposer of checkpoint ${checkpointNumber} due to ${reason}`, {
114
+ ...checkpoint,
109
115
  ...args
110
116
  });
111
117
  this.emit(WANT_TO_SLASH_EVENT, [
@@ -125,10 +131,10 @@ const AttestationsBlockWatcherConfigKeys = [
125
131
  }
126
132
  }
127
133
  }
128
- addInvalidBlock(block) {
129
- this.invalidArchiveRoots.add(block.archive.toString());
134
+ addInvalidCheckpoint(checkpoint) {
135
+ this.invalidArchiveRoots.add(checkpoint.archive.toString());
130
136
  // Prune old entries if we exceed the maximum
131
- if (this.invalidArchiveRoots.size > this.maxInvalidBlocks) {
137
+ if (this.invalidArchiveRoots.size > this.maxInvalidCheckpoints) {
132
138
  const oldestKey = this.invalidArchiveRoots.keys().next().value;
133
139
  this.invalidArchiveRoots.delete(oldestKey);
134
140
  }
@@ -1,7 +1,8 @@
1
1
  import { EpochCache } from '@aztec/epoch-cache';
2
+ import type { Fr } from '@aztec/foundation/curves/bn254';
2
3
  import { L2Block, type L2BlockSourceEventEmitter } from '@aztec/stdlib/block';
3
- import type { IFullNodeBlockBuilder, ITxProvider, MerkleTreeWriteOperations, SlasherConfig } from '@aztec/stdlib/interfaces/server';
4
- import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
4
+ import type { ICheckpointsBuilder, ITxProvider, MerkleTreeWriteOperations, SlasherConfig } from '@aztec/stdlib/interfaces/server';
5
+ import { type L1ToL2MessageSource } from '@aztec/stdlib/messaging';
5
6
  import { type Watcher, type WatcherEmitter } from '../watcher.js';
6
7
  declare const EpochPruneWatcherPenaltiesConfigKeys: readonly ["slashPrunePenalty", "slashDataWithholdingPenalty"];
7
8
  type EpochPruneWatcherPenalties = Pick<SlasherConfig, (typeof EpochPruneWatcherPenaltiesConfigKeys)[number]>;
@@ -17,11 +18,11 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
17
18
  private l1ToL2MessageSource;
18
19
  private epochCache;
19
20
  private txProvider;
20
- private blockBuilder;
21
+ private checkpointsBuilder;
21
22
  private log;
22
23
  private boundHandlePruneL2Blocks;
23
24
  private penalties;
24
- constructor(l2BlockSource: L2BlockSourceEventEmitter, l1ToL2MessageSource: L1ToL2MessageSource, epochCache: EpochCache, txProvider: Pick<ITxProvider, 'getAvailableTxs'>, blockBuilder: IFullNodeBlockBuilder, penalties: EpochPruneWatcherPenalties);
25
+ constructor(l2BlockSource: L2BlockSourceEventEmitter, l1ToL2MessageSource: L1ToL2MessageSource, epochCache: EpochCache, txProvider: Pick<ITxProvider, 'getAvailableTxs'>, checkpointsBuilder: ICheckpointsBuilder, penalties: EpochPruneWatcherPenalties);
25
26
  start(): Promise<void>;
26
27
  stop(): Promise<void>;
27
28
  updateConfig(config: Partial<SlasherConfig>): void;
@@ -29,9 +30,9 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
29
30
  private emitSlashForEpoch;
30
31
  private processPruneL2Blocks;
31
32
  validateBlocks(blocks: L2Block[]): Promise<void>;
32
- validateBlock(blockFromL1: L2Block, fork: MerkleTreeWriteOperations): Promise<void>;
33
+ validateBlock(blockFromL1: L2Block, previousCheckpointOutHashes: Fr[], fork: MerkleTreeWriteOperations): Promise<void>;
33
34
  private getValidatorsForEpoch;
34
35
  private validatorsToSlashingArgs;
35
36
  }
36
37
  export {};
37
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfcHJ1bmVfd2F0Y2hlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dhdGNoZXJzL2Vwb2NoX3BydW5lX3dhdGNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBSWhELE9BQU8sRUFFTCxPQUFPLEVBRVAsS0FBSyx5QkFBeUIsRUFFL0IsTUFBTSxxQkFBcUIsQ0FBQztBQUU3QixPQUFPLEtBQUssRUFDVixxQkFBcUIsRUFDckIsV0FBVyxFQUNYLHlCQUF5QixFQUN6QixhQUFhLEVBQ2QsTUFBTSxpQ0FBaUMsQ0FBQztBQUN6QyxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBV25FLE9BQU8sRUFBNkMsS0FBSyxPQUFPLEVBQUUsS0FBSyxjQUFjLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFN0csUUFBQSxNQUFNLG9DQUFvQywrREFBZ0UsQ0FBQztBQUUzRyxLQUFLLDBCQUEwQixHQUFHLElBQUksQ0FBQyxhQUFhLEVBQUUsQ0FBQyxPQUFPLG9DQUFvQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQzs7QUFFN0c7Ozs7O0dBS0c7QUFDSCxxQkFBYSxpQkFBa0IsU0FBUSxzQkFBMkMsWUFBVyxPQUFPO0lBU2hHLE9BQU8sQ0FBQyxhQUFhO0lBQ3JCLE9BQU8sQ0FBQyxtQkFBbUI7SUFDM0IsT0FBTyxDQUFDLFVBQVU7SUFDbEIsT0FBTyxDQUFDLFVBQVU7SUFDbEIsT0FBTyxDQUFDLFlBQVk7SUFadEIsT0FBTyxDQUFDLEdBQUcsQ0FBK0M7SUFHMUQsT0FBTyxDQUFDLHdCQUF3QixDQUF1QztJQUV2RSxPQUFPLENBQUMsU0FBUyxDQUE2QjtJQUU5QyxZQUNVLGFBQWEsRUFBRSx5QkFBeUIsRUFDeEMsbUJBQW1CLEVBQUUsbUJBQW1CLEVBQ3hDLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLFVBQVUsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLGlCQUFpQixDQUFDLEVBQ2hELFlBQVksRUFBRSxxQkFBcUIsRUFDM0MsU0FBUyxFQUFFLDBCQUEwQixFQU90QztJQUVNLEtBQUssa0JBR1g7SUFFTSxJQUFJLGtCQUdWO0lBRU0sWUFBWSxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsYUFBYSxDQUFDLEdBQUcsSUFBSSxDQUd4RDtJQUVELE9BQU8sQ0FBQyxtQkFBbUI7WUFPYixpQkFBaUI7WUFXakIsb0JBQW9CO0lBd0JyQixjQUFjLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FZNUQ7SUFFWSxhQUFhLENBQUMsV0FBVyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUseUJBQXlCLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQStCL0Y7WUFFYSxxQkFBcUI7SUFTbkMsT0FBTyxDQUFDLHdCQUF3QjtDQWdCakMifQ==
38
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXBvY2hfcHJ1bmVfd2F0Y2hlci5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3dhdGNoZXJzL2Vwb2NoX3BydW5lX3dhdGNoZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBR2hELE9BQU8sS0FBSyxFQUFFLEVBQUUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRXpELE9BQU8sRUFFTCxPQUFPLEVBQ1AsS0FBSyx5QkFBeUIsRUFHL0IsTUFBTSxxQkFBcUIsQ0FBQztBQUU3QixPQUFPLEtBQUssRUFDVixtQkFBbUIsRUFDbkIsV0FBVyxFQUNYLHlCQUF5QixFQUN6QixhQUFhLEVBQ2QsTUFBTSxpQ0FBaUMsQ0FBQztBQUN6QyxPQUFPLEVBQUUsS0FBSyxtQkFBbUIsRUFBNEIsTUFBTSx5QkFBeUIsQ0FBQztBQVk3RixPQUFPLEVBQTZDLEtBQUssT0FBTyxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRTdHLFFBQUEsTUFBTSxvQ0FBb0MsK0RBQWdFLENBQUM7QUFFM0csS0FBSywwQkFBMEIsR0FBRyxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUMsT0FBTyxvQ0FBb0MsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7O0FBRTdHOzs7OztHQUtHO0FBQ0gscUJBQWEsaUJBQWtCLFNBQVEsc0JBQTJDLFlBQVcsT0FBTztJQVNoRyxPQUFPLENBQUMsYUFBYTtJQUNyQixPQUFPLENBQUMsbUJBQW1CO0lBQzNCLE9BQU8sQ0FBQyxVQUFVO0lBQ2xCLE9BQU8sQ0FBQyxVQUFVO0lBQ2xCLE9BQU8sQ0FBQyxrQkFBa0I7SUFaNUIsT0FBTyxDQUFDLEdBQUcsQ0FBK0M7SUFHMUQsT0FBTyxDQUFDLHdCQUF3QixDQUF1QztJQUV2RSxPQUFPLENBQUMsU0FBUyxDQUE2QjtJQUU5QyxZQUNVLGFBQWEsRUFBRSx5QkFBeUIsRUFDeEMsbUJBQW1CLEVBQUUsbUJBQW1CLEVBQ3hDLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLFVBQVUsRUFBRSxJQUFJLENBQUMsV0FBVyxFQUFFLGlCQUFpQixDQUFDLEVBQ2hELGtCQUFrQixFQUFFLG1CQUFtQixFQUMvQyxTQUFTLEVBQUUsMEJBQTBCLEVBT3RDO0lBRU0sS0FBSyxrQkFHWDtJQUVNLElBQUksa0JBR1Y7SUFFTSxZQUFZLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsR0FBRyxJQUFJLENBR3hEO0lBRUQsT0FBTyxDQUFDLG1CQUFtQjtZQU9iLGlCQUFpQjtZQVdqQixvQkFBb0I7SUF3QnJCLGNBQWMsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQWtCNUQ7SUFFWSxhQUFhLENBQ3hCLFdBQVcsRUFBRSxPQUFPLEVBQ3BCLDJCQUEyQixFQUFFLEVBQUUsRUFBRSxFQUNqQyxJQUFJLEVBQUUseUJBQXlCLEdBQzlCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0E4Q2Y7WUFFYSxxQkFBcUI7SUFTbkMsT0FBTyxDQUFDLHdCQUF3QjtDQWdCakMifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIhD,OAAO,EAEL,OAAO,EAEP,KAAK,yBAAyB,EAE/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,qBAAqB,EACrB,WAAW,EACX,yBAAyB,EACzB,aAAa,EACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAWnE,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,oCAAoC,+DAAgE,CAAC;AAE3G,KAAK,0BAA0B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,oCAAoC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;AAE7G;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,sBAA2C,YAAW,OAAO;IAShG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,YAAY;IAZtB,OAAO,CAAC,GAAG,CAA+C;IAG1D,OAAO,CAAC,wBAAwB,CAAuC;IAEvE,OAAO,CAAC,SAAS,CAA6B;IAE9C,YACU,aAAa,EAAE,yBAAyB,EACxC,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAChD,YAAY,EAAE,qBAAqB,EAC3C,SAAS,EAAE,0BAA0B,EAOtC;IAEM,KAAK,kBAGX;IAEM,IAAI,kBAGV;IAEM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAGxD;IAED,OAAO,CAAC,mBAAmB;YAOb,iBAAiB;YAWjB,oBAAoB;IAwBrB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAY5D;IAEY,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B/F;YAEa,qBAAqB;IASnC,OAAO,CAAC,wBAAwB;CAgBjC"}
1
+ {"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AAEzD,OAAO,EAEL,OAAO,EACP,KAAK,yBAAyB,EAG/B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EACV,mBAAmB,EACnB,WAAW,EACX,yBAAyB,EACzB,aAAa,EACd,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,mBAAmB,EAA4B,MAAM,yBAAyB,CAAC;AAY7F,OAAO,EAA6C,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7G,QAAA,MAAM,oCAAoC,+DAAgE,CAAC;AAE3G,KAAK,0BAA0B,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,oCAAoC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;;AAE7G;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,sBAA2C,YAAW,OAAO;IAShG,OAAO,CAAC,aAAa;IACrB,OAAO,CAAC,mBAAmB;IAC3B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,kBAAkB;IAZ5B,OAAO,CAAC,GAAG,CAA+C;IAG1D,OAAO,CAAC,wBAAwB,CAAuC;IAEvE,OAAO,CAAC,SAAS,CAA6B;IAE9C,YACU,aAAa,EAAE,yBAAyB,EACxC,mBAAmB,EAAE,mBAAmB,EACxC,UAAU,EAAE,UAAU,EACtB,UAAU,EAAE,IAAI,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAChD,kBAAkB,EAAE,mBAAmB,EAC/C,SAAS,EAAE,0BAA0B,EAOtC;IAEM,KAAK,kBAGX;IAEM,IAAI,kBAGV;IAEM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAGxD;IAED,OAAO,CAAC,mBAAmB;YAOb,iBAAiB;YAWjB,oBAAoB;IAwBrB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5D;IAEY,aAAa,CACxB,WAAW,EAAE,OAAO,EACpB,2BAA2B,EAAE,EAAE,EAAE,EACjC,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA8Cf;YAEa,qBAAqB;IASnC,OAAO,CAAC,wBAAwB;CAgBjC"}
@@ -1,8 +1,9 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, CheckpointNumber } from '@aztec/foundation/branded-types';
2
2
  import { merge, pick } from '@aztec/foundation/collection';
3
3
  import { createLogger } from '@aztec/foundation/log';
4
4
  import { L2BlockSourceEvents } from '@aztec/stdlib/block';
5
5
  import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
6
+ import { computeCheckpointOutHash } from '@aztec/stdlib/messaging';
6
7
  import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
7
8
  import { ReExFailedTxsError, ReExStateMismatchError, TransactionsNotAvailableError, ValidatorError } from '@aztec/stdlib/validators';
8
9
  import EventEmitter from 'node:events';
@@ -21,22 +22,22 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
21
22
  l1ToL2MessageSource;
22
23
  epochCache;
23
24
  txProvider;
24
- blockBuilder;
25
+ checkpointsBuilder;
25
26
  log;
26
27
  // Store bound function reference for proper listener removal
27
28
  boundHandlePruneL2Blocks;
28
29
  penalties;
29
- constructor(l2BlockSource, l1ToL2MessageSource, epochCache, txProvider, blockBuilder, penalties){
30
- super(), this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.epochCache = epochCache, this.txProvider = txProvider, this.blockBuilder = blockBuilder, this.log = createLogger('epoch-prune-watcher'), this.boundHandlePruneL2Blocks = this.handlePruneL2Blocks.bind(this);
30
+ constructor(l2BlockSource, l1ToL2MessageSource, epochCache, txProvider, checkpointsBuilder, penalties){
31
+ super(), this.l2BlockSource = l2BlockSource, this.l1ToL2MessageSource = l1ToL2MessageSource, this.epochCache = epochCache, this.txProvider = txProvider, this.checkpointsBuilder = checkpointsBuilder, this.log = createLogger('epoch-prune-watcher'), this.boundHandlePruneL2Blocks = this.handlePruneL2Blocks.bind(this);
31
32
  this.penalties = pick(penalties, ...EpochPruneWatcherPenaltiesConfigKeys);
32
33
  this.log.verbose(`EpochPruneWatcher initialized with penalties: valid epoch pruned=${penalties.slashPrunePenalty} data withholding=${penalties.slashDataWithholdingPenalty}`);
33
34
  }
34
35
  start() {
35
- this.l2BlockSource.on(L2BlockSourceEvents.L2PruneDetected, this.boundHandlePruneL2Blocks);
36
+ this.l2BlockSource.events.on(L2BlockSourceEvents.L2PruneUnproven, this.boundHandlePruneL2Blocks);
36
37
  return Promise.resolve();
37
38
  }
38
39
  stop() {
39
- this.l2BlockSource.removeListener(L2BlockSourceEvents.L2PruneDetected, this.boundHandlePruneL2Blocks);
40
+ this.l2BlockSource.events.removeListener(L2BlockSourceEvents.L2PruneUnproven, this.boundHandlePruneL2Blocks);
40
41
  return Promise.resolve();
41
42
  }
42
43
  updateConfig(config) {
@@ -62,7 +63,7 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
62
63
  async processPruneL2Blocks(blocks, epochNumber) {
63
64
  try {
64
65
  const l1Constants = this.epochCache.getL1Constants();
65
- const epochBlocks = blocks.filter((b)=>getEpochAtSlot(b.slot, l1Constants) === epochNumber);
66
+ const epochBlocks = blocks.filter((b)=>getEpochAtSlot(b.header.getSlot(), l1Constants) === epochNumber);
66
67
  this.log.info(`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`, {
67
68
  blocks: epochBlocks.map((b)=>b.toBlockInfo())
68
69
  });
@@ -84,16 +85,25 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
84
85
  if (blocks.length === 0) {
85
86
  return;
86
87
  }
87
- const fork = await this.blockBuilder.getFork(BlockNumber(blocks[0].header.globalVariables.blockNumber - 1));
88
+ let previousCheckpointOutHashes = [];
89
+ const fork = await this.checkpointsBuilder.getFork(BlockNumber(blocks[0].header.globalVariables.blockNumber - 1));
88
90
  try {
89
91
  for (const block of blocks){
90
- await this.validateBlock(block, fork);
92
+ await this.validateBlock(block, previousCheckpointOutHashes, fork);
93
+ // TODO(mbps): This assumes one block per checkpoint, which is only true for now.
94
+ const checkpointOutHash = computeCheckpointOutHash([
95
+ block.body.txEffects.map((tx)=>tx.l2ToL1Msgs)
96
+ ]);
97
+ previousCheckpointOutHashes = [
98
+ ...previousCheckpointOutHashes,
99
+ checkpointOutHash
100
+ ];
91
101
  }
92
102
  } finally{
93
103
  await fork.close();
94
104
  }
95
105
  }
96
- async validateBlock(blockFromL1, fork) {
106
+ async validateBlock(blockFromL1, previousCheckpointOutHashes, fork) {
97
107
  this.log.debug(`Validating pruned block ${blockFromL1.header.globalVariables.blockNumber}`);
98
108
  const txHashes = blockFromL1.body.txEffects.map((txEffect)=>txEffect.txHash);
99
109
  // We load txs from the mempool directly, since the TxCollector running in the background has already been
@@ -103,9 +113,20 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
103
113
  if (missingTxs && missingTxs.length > 0) {
104
114
  throw new TransactionsNotAvailableError(missingTxs);
105
115
  }
106
- const blockNumber = blockFromL1.number;
107
- const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockNumber);
108
- const { block, failedTxs, numTxs } = await this.blockBuilder.buildBlock(txs, l1ToL2Messages, blockFromL1.header.globalVariables, {}, fork);
116
+ const checkpointNumber = CheckpointNumber.fromBlockNumber(blockFromL1.number);
117
+ const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(checkpointNumber);
118
+ const gv = blockFromL1.header.globalVariables;
119
+ const constants = {
120
+ chainId: gv.chainId,
121
+ version: gv.version,
122
+ slotNumber: gv.slotNumber,
123
+ coinbase: gv.coinbase,
124
+ feeRecipient: gv.feeRecipient,
125
+ gasFees: gv.gasFees
126
+ };
127
+ // Use checkpoint builder to validate the block
128
+ const checkpointBuilder = await this.checkpointsBuilder.startCheckpoint(checkpointNumber, constants, l1ToL2Messages, previousCheckpointOutHashes, fork, this.log.getBindings());
129
+ const { block, failedTxs, numTxs } = await checkpointBuilder.buildBlock(txs, gv.blockNumber, gv.timestamp, {});
109
130
  if (numTxs !== txs.length) {
110
131
  // This should be detected by state mismatch, but this makes it easier to debug.
111
132
  throw new ValidatorError(`Built block with ${numTxs} txs, expected ${txs.length}`);
package/package.json CHANGED
@@ -1,20 +1,22 @@
1
1
  {
2
2
  "name": "@aztec/slasher",
3
- "version": "0.0.1-commit.d3ec352c",
3
+ "version": "0.0.1-commit.e310a4c8",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
7
7
  "./config": "./dest/config.js"
8
8
  },
9
9
  "inherits": [
10
- "../package.common.json"
10
+ "../package.common.json",
11
+ "./package.local.json"
11
12
  ],
12
13
  "scripts": {
13
14
  "build": "yarn clean && ../scripts/tsc.sh",
14
15
  "build:dev": "../scripts/tsc.sh --watch",
15
16
  "clean": "rm -rf ./dest .tsbuildinfo",
16
17
  "bb": "node --no-warnings ./dest/bb/index.js",
17
- "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
18
+ "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}",
19
+ "generate": "./scripts/generate.sh"
18
20
  },
19
21
  "jest": {
20
22
  "moduleNameMapper": {
@@ -54,25 +56,25 @@
54
56
  ]
55
57
  },
56
58
  "dependencies": {
57
- "@aztec/epoch-cache": "0.0.1-commit.d3ec352c",
58
- "@aztec/ethereum": "0.0.1-commit.d3ec352c",
59
- "@aztec/foundation": "0.0.1-commit.d3ec352c",
60
- "@aztec/kv-store": "0.0.1-commit.d3ec352c",
61
- "@aztec/l1-artifacts": "0.0.1-commit.d3ec352c",
62
- "@aztec/stdlib": "0.0.1-commit.d3ec352c",
63
- "@aztec/telemetry-client": "0.0.1-commit.d3ec352c",
59
+ "@aztec/epoch-cache": "0.0.1-commit.e310a4c8",
60
+ "@aztec/ethereum": "0.0.1-commit.e310a4c8",
61
+ "@aztec/foundation": "0.0.1-commit.e310a4c8",
62
+ "@aztec/kv-store": "0.0.1-commit.e310a4c8",
63
+ "@aztec/l1-artifacts": "0.0.1-commit.e310a4c8",
64
+ "@aztec/stdlib": "0.0.1-commit.e310a4c8",
65
+ "@aztec/telemetry-client": "0.0.1-commit.e310a4c8",
64
66
  "source-map-support": "^0.5.21",
65
67
  "tslib": "^2.4.0",
66
68
  "viem": "npm:@aztec/viem@2.38.2",
67
69
  "zod": "^3.23.8"
68
70
  },
69
71
  "devDependencies": {
70
- "@aztec/aztec.js": "0.0.1-commit.d3ec352c",
72
+ "@aztec/aztec.js": "0.0.1-commit.e310a4c8",
71
73
  "@jest/globals": "^30.0.0",
72
74
  "@types/jest": "^30.0.0",
73
75
  "@types/node": "^22.15.17",
74
76
  "@types/source-map-support": "^0.5.10",
75
- "@typescript/native-preview": "7.0.0-dev.20251126.1",
77
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
76
78
  "jest": "^30.0.0",
77
79
  "jest-mock-extended": "^4.0.0",
78
80
  "ts-node": "^10.9.1",
package/src/config.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { DefaultL1ContractsConfig } from '@aztec/ethereum';
2
1
  import type { ConfigMappingsType } from '@aztec/foundation/config';
3
2
  import {
4
3
  bigintConfigHelper,
@@ -9,27 +8,29 @@ import {
9
8
  import { EthAddress } from '@aztec/foundation/eth-address';
10
9
  import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
11
10
 
11
+ import { slasherDefaultEnv } from './generated/slasher-defaults.js';
12
+
12
13
  export type { SlasherConfig };
13
14
 
14
15
  export const DefaultSlasherConfig: SlasherConfig = {
15
16
  slashOverridePayload: undefined,
16
- slashMinPenaltyPercentage: 0.5, // 50% of penalty
17
- slashMaxPenaltyPercentage: 2.0, //2x of penalty
17
+ slashMinPenaltyPercentage: slasherDefaultEnv.SLASH_MIN_PENALTY_PERCENTAGE,
18
+ slashMaxPenaltyPercentage: slasherDefaultEnv.SLASH_MAX_PENALTY_PERCENTAGE,
18
19
  slashValidatorsAlways: [], // Empty by default
19
20
  slashValidatorsNever: [], // Empty by default
20
- slashPrunePenalty: DefaultL1ContractsConfig.slashAmountSmall,
21
- slashDataWithholdingPenalty: DefaultL1ContractsConfig.slashAmountSmall,
22
- slashInactivityTargetPercentage: 0.9,
23
- slashInactivityConsecutiveEpochThreshold: 1, // Default to 1 for backward compatibility
24
- slashBroadcastedInvalidBlockPenalty: DefaultL1ContractsConfig.slashAmountSmall,
25
- slashInactivityPenalty: DefaultL1ContractsConfig.slashAmountSmall,
26
- slashProposeInvalidAttestationsPenalty: DefaultL1ContractsConfig.slashAmountSmall,
27
- slashAttestDescendantOfInvalidPenalty: DefaultL1ContractsConfig.slashAmountSmall,
28
- slashUnknownPenalty: DefaultL1ContractsConfig.slashAmountSmall,
29
- slashOffenseExpirationRounds: 4,
30
- slashMaxPayloadSize: 50,
31
- slashGracePeriodL2Slots: 0,
32
- slashExecuteRoundsLookBack: 4,
21
+ slashPrunePenalty: BigInt(slasherDefaultEnv.SLASH_PRUNE_PENALTY),
22
+ slashDataWithholdingPenalty: BigInt(slasherDefaultEnv.SLASH_DATA_WITHHOLDING_PENALTY),
23
+ slashInactivityTargetPercentage: slasherDefaultEnv.SLASH_INACTIVITY_TARGET_PERCENTAGE,
24
+ slashInactivityConsecutiveEpochThreshold: slasherDefaultEnv.SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD,
25
+ slashBroadcastedInvalidBlockPenalty: BigInt(slasherDefaultEnv.SLASH_INVALID_BLOCK_PENALTY),
26
+ slashInactivityPenalty: BigInt(slasherDefaultEnv.SLASH_INACTIVITY_PENALTY),
27
+ slashProposeInvalidAttestationsPenalty: BigInt(slasherDefaultEnv.SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY),
28
+ slashAttestDescendantOfInvalidPenalty: BigInt(slasherDefaultEnv.SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY),
29
+ slashUnknownPenalty: BigInt(slasherDefaultEnv.SLASH_UNKNOWN_PENALTY),
30
+ slashOffenseExpirationRounds: slasherDefaultEnv.SLASH_OFFENSE_EXPIRATION_ROUNDS,
31
+ slashMaxPayloadSize: slasherDefaultEnv.SLASH_MAX_PAYLOAD_SIZE,
32
+ slashGracePeriodL2Slots: slasherDefaultEnv.SLASH_GRACE_PERIOD_L2_SLOTS,
33
+ slashExecuteRoundsLookBack: slasherDefaultEnv.SLASH_EXECUTE_ROUNDS_LOOK_BACK,
33
34
  slashSelfAllowed: false,
34
35
  };
35
36
 
@@ -1,10 +1,9 @@
1
- import { EmpireSlashingProposerContract, RollupContract, SlasherContract } from '@aztec/ethereum';
1
+ import { EmpireSlashingProposerContract, RollupContract, SlasherContract } from '@aztec/ethereum/contracts';
2
2
  import { sumBigint } from '@aztec/foundation/bigint';
3
3
  import { SlotNumber } from '@aztec/foundation/branded-types';
4
4
  import { compactArray, filterAsync, maxBy, pick } from '@aztec/foundation/collection';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
  import { createLogger } from '@aztec/foundation/log';
7
- import { sleep } from '@aztec/foundation/sleep';
8
7
  import type { DateProvider } from '@aztec/foundation/timer';
9
8
  import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
10
9
  import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
@@ -196,13 +195,6 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
196
195
  this.roundMonitor.stop();
197
196
  await this.offensesCollector.stop();
198
197
 
199
- // Viem calls eth_uninstallFilter under the hood when uninstalling event watchers, but these calls are not awaited,
200
- // meaning that any error that happens during the uninstallation will not be caught. This causes errors during jest teardowns,
201
- // where we stop anvil after all other processes are stopped, so sometimes the eth_uninstallFilter call fails because anvil
202
- // is already stopped. We add a sleep here to give the uninstallation some time to complete, but the proper fix is for
203
- // viem to await the eth_uninstallFilter calls, or to catch any errors that happen during the uninstallation.
204
- // See https://github.com/wevm/viem/issues/3714.
205
- await sleep(2000);
206
198
  this.log.info('Empire Slasher client stopped');
207
199
  }
208
200
 
@@ -1,6 +1,7 @@
1
1
  import { EpochCache } from '@aztec/epoch-cache';
2
- import type { L1ReaderConfig, ViemClient } from '@aztec/ethereum';
3
2
  import { RollupContract } from '@aztec/ethereum/contracts';
3
+ import type { L1ReaderConfig } from '@aztec/ethereum/l1-reader';
4
+ import type { ViemClient } from '@aztec/ethereum/types';
4
5
  import { unique } from '@aztec/foundation/collection';
5
6
  import { EthAddress } from '@aztec/foundation/eth-address';
6
7
  import { createLogger } from '@aztec/foundation/log';
@@ -30,7 +31,7 @@ export async function createSlasherFacade(
30
31
  throw new Error('Cannot initialize SlasherClient without a Rollup address');
31
32
  }
32
33
 
33
- const kvStore = await createStore('slasher', SCHEMA_VERSION, config, createLogger('slasher:lmdb'));
34
+ const kvStore = await createStore('slasher', SCHEMA_VERSION, config, logger.getBindings());
34
35
  const rollup = new RollupContract(l1Client, l1Contracts.rollupAddress);
35
36
 
36
37
  const slashValidatorsNever = config.slashSelfAllowed
@@ -1,10 +1,10 @@
1
1
  import { EpochCache } from '@aztec/epoch-cache';
2
- import type { ViemClient } from '@aztec/ethereum';
3
2
  import {
4
3
  EmpireSlashingProposerContract,
5
4
  RollupContract,
6
5
  TallySlashingProposerContract,
7
6
  } from '@aztec/ethereum/contracts';
7
+ import type { ViemClient } from '@aztec/ethereum/types';
8
8
  import { EthAddress } from '@aztec/foundation/eth-address';
9
9
  import { createLogger } from '@aztec/foundation/log';
10
10
  import { DateProvider } from '@aztec/foundation/timer';
@@ -1,4 +1,4 @@
1
- import type { RollupContract, TallySlashingProposerContract } from '@aztec/ethereum';
1
+ import type { RollupContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
2
2
 
3
3
  import type { TallySlasherSettings } from '../tally_slasher_client.js';
4
4
 
@@ -0,0 +1,21 @@
1
+ // Auto-generated from spartan/environments/network-defaults.yml
2
+ // Do not edit manually - run yarn generate to regenerate
3
+
4
+ /** Default slasher configuration values from network-defaults.yml */
5
+ export const slasherDefaultEnv = {
6
+ SLASH_MIN_PENALTY_PERCENTAGE: 0.5,
7
+ SLASH_MAX_PENALTY_PERCENTAGE: 2,
8
+ SLASH_OFFENSE_EXPIRATION_ROUNDS: 4,
9
+ SLASH_MAX_PAYLOAD_SIZE: 50,
10
+ SLASH_EXECUTE_ROUNDS_LOOK_BACK: 4,
11
+ SLASH_PRUNE_PENALTY: 10000000000000000000,
12
+ SLASH_DATA_WITHHOLDING_PENALTY: 10000000000000000000,
13
+ SLASH_INACTIVITY_TARGET_PERCENTAGE: 0.9,
14
+ SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD: 1,
15
+ SLASH_INACTIVITY_PENALTY: 10000000000000000000,
16
+ SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY: 10000000000000000000,
17
+ SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY: 10000000000000000000,
18
+ SLASH_UNKNOWN_PENALTY: 10000000000000000000,
19
+ SLASH_INVALID_BLOCK_PENALTY: 10000000000000000000,
20
+ SLASH_GRACE_PERIOD_L2_SLOTS: 0,
21
+ } as const;
@@ -85,7 +85,11 @@ export class SlashOffensesCollector {
85
85
  }
86
86
  }
87
87
 
88
- this.log.info(`Adding pending offense for validator ${arg.validator}`, pendingOffense);
88
+ this.log.info(`Adding pending offense for validator ${arg.validator}`, {
89
+ ...pendingOffense,
90
+ epochOrSlot: pendingOffense.epochOrSlot.toString(),
91
+ amount: pendingOffense.amount.toString(),
92
+ });
89
93
  await this.offensesStore.addPendingOffense(pendingOffense);
90
94
  }
91
95
  }
@@ -1,6 +1,6 @@
1
1
  import { EpochCache } from '@aztec/epoch-cache';
2
- import type { ViemClient } from '@aztec/ethereum';
3
2
  import { RollupContract } from '@aztec/ethereum/contracts';
3
+ import type { ViemClient } from '@aztec/ethereum/types';
4
4
  import type { SlotNumber } from '@aztec/foundation/branded-types';
5
5
  import { EthAddress } from '@aztec/foundation/eth-address';
6
6
  import { createLogger } from '@aztec/foundation/log';
@@ -76,9 +76,11 @@ export class SlasherOffensesStore {
76
76
  /** Adds a new offense (defaults to pending, but will be slashed if markAsSlashed had been called for it) */
77
77
  public async addPendingOffense(offense: Offense): Promise<void> {
78
78
  const key = this.getOffenseKey(offense);
79
- await this.offenses.set(key, serializeOffense(offense));
80
79
  const round = getRoundForOffense(offense, this.settings);
81
- await this.roundsOffenses.set(this.getRoundKey(round), key);
80
+ await this.kvStore.transactionAsync(async () => {
81
+ await this.offenses.set(key, serializeOffense(offense));
82
+ await this.roundsOffenses.set(this.getRoundKey(round), key);
83
+ });
82
84
  this.log.trace(`Adding pending offense ${key} for round ${round}`);
83
85
  }
84
86
 
@@ -118,10 +118,13 @@ export class SlasherPayloadsStore {
118
118
 
119
119
  public async incrementPayloadVotes(payloadAddress: EthAddress, round: bigint): Promise<bigint> {
120
120
  const key = this.getPayloadVotesKey(round, payloadAddress);
121
- const currentVotes = (await this.roundPayloadVotes.getAsync(key)) || 0n;
122
- const newVotes = currentVotes + 1n;
123
- await this.roundPayloadVotes.set(key, newVotes);
124
- return newVotes;
121
+ let newVotes: bigint;
122
+ await this.kvStore.transactionAsync(async () => {
123
+ const currentVotes = (await this.roundPayloadVotes.getAsync(key)) || 0n;
124
+ newVotes = currentVotes + 1n;
125
+ await this.roundPayloadVotes.set(key, newVotes);
126
+ });
127
+ return newVotes!;
125
128
  }
126
129
 
127
130
  public async addPayload(payload: SlashPayloadRound): Promise<void> {
@@ -5,7 +5,6 @@ import { maxBigint } from '@aztec/foundation/bigint';
5
5
  import { SlotNumber } from '@aztec/foundation/branded-types';
6
6
  import { compactArray, partition, times } from '@aztec/foundation/collection';
7
7
  import { createLogger } from '@aztec/foundation/log';
8
- import { sleep } from '@aztec/foundation/sleep';
9
8
  import type { DateProvider } from '@aztec/foundation/timer';
10
9
  import type { Prettify } from '@aztec/foundation/types';
11
10
  import type { SlasherConfig } from '@aztec/stdlib/interfaces/server';
@@ -138,8 +137,6 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
138
137
  this.roundMonitor.stop();
139
138
  await this.offensesCollector.stop();
140
139
 
141
- // Sleeping to sidestep viem issue with unwatching events
142
- await sleep(2000);
143
140
  this.log.info('Tally Slasher client stopped');
144
141
  }
145
142
 
@@ -281,8 +278,12 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
281
278
  return undefined;
282
279
  }
283
280
 
281
+ const slashActionsWithAmounts = slashActions.map(action => ({
282
+ validator: action.validator.toString(),
283
+ slashAmount: action.slashAmount.toString(),
284
+ }));
284
285
  this.log.info(`Round ${executableRound} is ready to execute with ${slashActions.length} slashes`, {
285
- slashActions,
286
+ slashActions: slashActionsWithAmounts,
286
287
  payloadAddress: payload.address.toString(),
287
288
  ...logData,
288
289
  });
@@ -348,11 +349,15 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
348
349
  return undefined;
349
350
  }
350
351
 
352
+ const offensesToSlashLog = offensesToSlash.map(offense => ({
353
+ ...offense,
354
+ amount: offense.amount.toString(),
355
+ }));
351
356
  this.log.info(`Voting to slash ${offensesToSlash.length} offenses`, {
352
357
  slotNumber,
353
358
  currentRound,
354
359
  slashedRound,
355
- offensesToSlash,
360
+ offensesToSlash: offensesToSlashLog,
356
361
  });
357
362
 
358
363
  const committees = await this.collectCommitteesActiveDuringRound(slashedRound);