@aztec/slasher 2.0.3 → 2.1.0-rc.2

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 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAQnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,eAAO,MAAM,oBAAoB,EAAE,aAmBlC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,CAAC,aAAa,CAoHnE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAQnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAErE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,eAAO,MAAM,oBAAoB,EAAE,aAoBlC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,kBAAkB,CAAC,aAAa,CAyHnE,CAAC"}
package/dest/config.js CHANGED
@@ -19,6 +19,7 @@ export const DefaultSlasherConfig = {
19
19
  slashOffenseExpirationRounds: 4,
20
20
  slashMaxPayloadSize: 50,
21
21
  slashGracePeriodL2Slots: 0,
22
+ slashExecuteRoundsLookBack: 4,
22
23
  slashSelfAllowed: false
23
24
  };
24
25
  export const slasherConfigMappings = {
@@ -62,7 +63,7 @@ export const slasherConfigMappings = {
62
63
  },
63
64
  slashBroadcastedInvalidBlockPenalty: {
64
65
  env: 'SLASH_INVALID_BLOCK_PENALTY',
65
- description: 'Penalty amount for slashing a validator for an invalid block.',
66
+ description: 'Penalty amount for slashing a validator for an invalid block proposed via p2p.',
66
67
  ...bigintConfigHelper(DefaultSlasherConfig.slashBroadcastedInvalidBlockPenalty)
67
68
  },
68
69
  slashInactivityTargetPercentage: {
@@ -121,6 +122,11 @@ export const slasherConfigMappings = {
121
122
  env: 'SLASH_GRACE_PERIOD_L2_SLOTS',
122
123
  ...numberConfigHelper(DefaultSlasherConfig.slashGracePeriodL2Slots)
123
124
  },
125
+ slashExecuteRoundsLookBack: {
126
+ env: 'SLASH_EXECUTE_ROUNDS_LOOK_BACK',
127
+ description: 'How many rounds to look back when searching for a round to execute.',
128
+ ...numberConfigHelper(DefaultSlasherConfig.slashExecuteRoundsLookBack)
129
+ },
124
130
  slashSelfAllowed: {
125
131
  description: 'Whether to allow slashes to own validators',
126
132
  ...booleanConfigHelper(DefaultSlasherConfig.slashSelfAllowed)
@@ -1 +1 @@
1
- {"version":3,"file":"empire_slasher_client.d.ts","sourceRoot":"","sources":["../src/empire_slasher_client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"empire_slasher_client.d.ts","sourceRoot":"","sources":["../src/empire_slasher_client.ts"],"names":[],"mappings":"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"}
@@ -311,10 +311,15 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
311
311
  toRemove.push(payload);
312
312
  continue;
313
313
  }
314
+ // Check if slashing is enabled at all
315
+ if (!await this.slasher.isSlashingEnabled()) {
316
+ this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
317
+ return undefined;
318
+ }
314
319
  // Check if the slash payload is vetoed
315
320
  const isVetoed = await this.slasher.isPayloadVetoed(payload.payload);
316
321
  if (isVetoed) {
317
- this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed, skipping execution`);
322
+ this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed (skipping execution)`);
318
323
  toRemove.push(payload);
319
324
  continue;
320
325
  }
@@ -21,7 +21,7 @@ export type TallySlasherSettings = Prettify<SlashRoundMonitorSettings & SlashOff
21
21
  /** Committee size for block proposal */
22
22
  targetCommitteeSize: number;
23
23
  }>;
24
- export type TallySlasherClientConfig = SlashOffensesCollectorConfig & Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever'>;
24
+ export type TallySlasherClientConfig = SlashOffensesCollectorConfig & Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever' | 'slashExecuteRoundsLookBack'>;
25
25
  /**
26
26
  * The Tally Slasher client is responsible for managing slashable offenses using
27
27
  * the consensus-based slashing model where proposers vote on individual validator offenses.
@@ -87,8 +87,16 @@ export declare class TallySlasherClient implements ProposerSlashActionProvider,
87
87
  * @returns The actions to take
88
88
  */
89
89
  getProposerActions(slotNumber: bigint): Promise<ProposerSlashAction[]>;
90
- /** Returns an execute slash action if there are any rounds ready to be executed */
90
+ /**
91
+ * Returns an execute slash action if there are any rounds ready to be executed.
92
+ * Returns the oldest slash action if there are multiple rounds pending execution.
93
+ */
91
94
  protected getExecuteSlashAction(slotNumber: bigint): Promise<ProposerSlashAction | undefined>;
95
+ /**
96
+ * Checks if a given round is executable and returns an execute-slash action for it if so.
97
+ * Assumes round number has already been checked against lifetime and execution delay.
98
+ */
99
+ private tryGetRoundExecuteAction;
92
100
  /** Returns a vote action based on offenses from the target round (with offset applied) */
93
101
  protected getVoteOffensesAction(slotNumber: bigint): Promise<ProposerSlashAction | undefined>;
94
102
  /** Returns the committees that were active during the timespan of a given round */
@@ -1 +1 @@
1
- {"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tally_slasher_client.d.ts","sourceRoot":"","sources":["../src/tally_slasher_client.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { EthAddress } from '@aztec/aztec.js';
2
+ import { maxBigint } from '@aztec/foundation/bigint';
2
3
  import { compactArray, partition, times } from '@aztec/foundation/collection';
3
4
  import { createLogger } from '@aztec/foundation/log';
4
5
  import { sleep } from '@aztec/foundation/sleep';
@@ -120,19 +121,53 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
120
121
  voteAction
121
122
  ]);
122
123
  }
123
- /** Returns an execute slash action if there are any rounds ready to be executed */ async getExecuteSlashAction(slotNumber) {
124
+ /**
125
+ * Returns an execute slash action if there are any rounds ready to be executed.
126
+ * Returns the oldest slash action if there are multiple rounds pending execution.
127
+ */ async getExecuteSlashAction(slotNumber) {
124
128
  const { round: currentRound } = this.roundMonitor.getRoundForSlot(slotNumber);
125
129
  const slashingExecutionDelayInRounds = BigInt(this.settings.slashingExecutionDelayInRounds);
126
130
  const executableRound = currentRound - slashingExecutionDelayInRounds - 1n;
127
- if (executableRound < 0n) {
131
+ const lookBack = BigInt(this.config.slashExecuteRoundsLookBack);
132
+ const oldestExecutableRound = maxBigint(0n, executableRound - lookBack);
133
+ // Check if slashing is enabled at all
134
+ if (!await this.slasher.isSlashingEnabled()) {
135
+ this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
128
136
  return undefined;
129
137
  }
130
- let logData = {
138
+ this.log.debug(`Checking slashing rounds ${oldestExecutableRound} to ${executableRound} to execute`, {
139
+ slotNumber,
131
140
  currentRound,
141
+ oldestExecutableRound,
132
142
  executableRound,
133
- slotNumber
143
+ slashingExecutionDelayInRounds,
144
+ lookBack,
145
+ slashingLifetimeInRounds: this.settings.slashingLifetimeInRounds
146
+ });
147
+ // Iterate over all rounds, starting from the oldest, until we find one that is executable
148
+ for(let roundToCheck = oldestExecutableRound; roundToCheck <= executableRound; roundToCheck++){
149
+ const action = await this.tryGetRoundExecuteAction(roundToCheck);
150
+ if (action) {
151
+ return action;
152
+ }
153
+ }
154
+ // And return nothing if none are found
155
+ return undefined;
156
+ }
157
+ /**
158
+ * Checks if a given round is executable and returns an execute-slash action for it if so.
159
+ * Assumes round number has already been checked against lifetime and execution delay.
160
+ */ async tryGetRoundExecuteAction(executableRound) {
161
+ let logData = {
162
+ executableRound
134
163
  };
164
+ this.log.debug(`Testing if slashing round ${executableRound} is executable`, logData);
135
165
  try {
166
+ // Check if slashing is enabled at all
167
+ if (!await this.slasher.isSlashingEnabled()) {
168
+ this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`, logData);
169
+ return undefined;
170
+ }
136
171
  const roundInfo = await this.tallySlashingProposer.getRound(executableRound);
137
172
  logData = {
138
173
  ...logData,
@@ -141,9 +176,6 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
141
176
  if (roundInfo.isExecuted) {
142
177
  this.log.verbose(`Round ${executableRound} has already been executed`, logData);
143
178
  return undefined;
144
- } else if (!roundInfo.readyToExecute) {
145
- this.log.verbose(`Round ${executableRound} is not ready to execute yet`, logData);
146
- return undefined;
147
179
  } else if (roundInfo.voteCount === 0n) {
148
180
  this.log.debug(`Round ${executableRound} received no votes`, logData);
149
181
  return undefined;
@@ -151,6 +183,7 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
151
183
  this.log.verbose(`Round ${executableRound} does not have enough votes to execute`, logData);
152
184
  return undefined;
153
185
  }
186
+ // Check if the round yields any slashing at all
154
187
  const { actions: slashActions, committees } = await this.tallySlashingProposer.getTally(executableRound);
155
188
  if (slashActions.length === 0) {
156
189
  this.log.verbose(`Round ${executableRound} does not resolve in any slashing`, logData);
@@ -184,8 +217,8 @@ import { SlashRoundMonitor } from './slash_round_monitor.js';
184
217
  };
185
218
  } catch (error) {
186
219
  this.log.error(`Error checking round to execute ${executableRound}`, error);
220
+ return undefined;
187
221
  }
188
- return undefined;
189
222
  }
190
223
  /** Returns a vote action based on offenses from the target round (with offset applied) */ async getVoteOffensesAction(slotNumber) {
191
224
  // Compute what round we are in based on the slot number and what round will be slashed
@@ -26,6 +26,8 @@ export declare class EpochPruneWatcher extends EpochPruneWatcher_base implements
26
26
  stop(): Promise<void>;
27
27
  updateConfig(config: Partial<SlasherConfig>): void;
28
28
  private handlePruneL2Blocks;
29
+ private emitSlashForEpoch;
30
+ private processPruneL2Blocks;
29
31
  validateBlocks(blocks: L2Block[]): Promise<void>;
30
32
  validateBlock(blockFromL1: L2Block, fork: MerkleTreeWriteOperations): Promise<void>;
31
33
  private getValidatorsForEpoch;
@@ -1 +1 @@
1
- {"version":3,"file":"epoch_prune_watcher.d.ts","sourceRoot":"","sources":["../../src/watchers/epoch_prune_watcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAEL,OAAO,EAEP,KAAK,yBAAyB,EAE/B,MAAM,qBAAqB,CAAC;AAC7B,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;sCAQrD,UAAU,cAAc;AANhF;;;;;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;gBAGpC,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;IAShC,KAAK;IAKL,IAAI;IAKJ,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;IAKzD,OAAO,CAAC,mBAAmB;IA+Cd,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAchD,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;YAgClF,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,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;sCAQrD,UAAU,cAAc;AANhF;;;;;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;gBAGpC,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;IAShC,KAAK;IAKL,IAAI;IAKJ,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI;IAKzD,OAAO,CAAC,mBAAmB;YAOb,iBAAiB;YAWjB,oBAAoB;IAwBrB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAchD,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC;YAgClF,qBAAqB;IASnC,OAAO,CAAC,wBAAwB;CAgBjC"}
@@ -1,7 +1,8 @@
1
1
  import { merge, pick } from '@aztec/foundation/collection';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
  import { L2BlockSourceEvents } from '@aztec/stdlib/block';
4
- import { OffenseType } from '@aztec/stdlib/slashing';
4
+ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
5
+ import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
5
6
  import { ReExFailedTxsError, ReExStateMismatchError, TransactionsNotAvailableError, ValidatorError } from '@aztec/stdlib/validators';
6
7
  import EventEmitter from 'node:events';
7
8
  import { WANT_TO_SLASH_EVENT } from '../watcher.js';
@@ -43,44 +44,40 @@ const EpochPruneWatcherPenaltiesConfigKeys = [
43
44
  }
44
45
  handlePruneL2Blocks(event) {
45
46
  const { blocks, epochNumber } = event;
46
- this.log.info(`Detected chain prune. Validating epoch ${epochNumber}`);
47
- this.validateBlocks(blocks).then(async ()=>{
47
+ void this.processPruneL2Blocks(blocks, epochNumber).catch((err)=>this.log.error('Error processing pruned L2 blocks', err, {
48
+ epochNumber
49
+ }));
50
+ }
51
+ async emitSlashForEpoch(offense, epochNumber) {
52
+ const validators = await this.getValidatorsForEpoch(epochNumber);
53
+ if (validators.length === 0) {
54
+ this.log.warn(`No validators found for epoch ${epochNumber} (cannot slash for ${getOffenseTypeName(offense)})`);
55
+ return;
56
+ }
57
+ const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
58
+ this.log.verbose(`Created slash for ${getOffenseTypeName(offense)} at epoch ${epochNumber}`, args);
59
+ this.emit(WANT_TO_SLASH_EVENT, args);
60
+ }
61
+ async processPruneL2Blocks(blocks, epochNumber) {
62
+ try {
63
+ const l1Constants = this.epochCache.getL1Constants();
64
+ const epochBlocks = blocks.filter((b)=>getEpochAtSlot(b.slot, l1Constants) === epochNumber);
65
+ this.log.info(`Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`, {
66
+ blocks: epochBlocks.map((b)=>b.toBlockInfo())
67
+ });
68
+ await this.validateBlocks(epochBlocks);
48
69
  this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
49
- const validators = await this.getValidatorsForEpoch(epochNumber);
50
- // need to specify return type to be able to return offense as undefined later on
51
- const result = {
52
- validators,
53
- offense: OffenseType.VALID_EPOCH_PRUNED
54
- };
55
- return result;
56
- }).catch(async (error)=>{
70
+ await this.emitSlashForEpoch(OffenseType.VALID_EPOCH_PRUNED, epochNumber);
71
+ } catch (error) {
57
72
  if (error instanceof TransactionsNotAvailableError) {
58
73
  this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
59
74
  message: error.message
60
75
  });
61
- const validators = await this.getValidatorsForEpoch(epochNumber);
62
- return {
63
- validators,
64
- offense: OffenseType.DATA_WITHHOLDING
65
- };
76
+ await this.emitSlashForEpoch(OffenseType.DATA_WITHHOLDING, epochNumber);
66
77
  } else {
67
78
  this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
68
- return {
69
- validators: [],
70
- offense: undefined
71
- };
72
79
  }
73
- }).then(({ validators, offense })=>{
74
- if (validators.length === 0 || offense === undefined) {
75
- return;
76
- }
77
- const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
78
- this.log.info(`Slash for epoch ${epochNumber} created`, args);
79
- this.emit(WANT_TO_SLASH_EVENT, args);
80
- }).catch((error)=>{
81
- // This can happen if we fail to get the validators for the epoch.
82
- this.log.error('Error while creating slash for epoch', error);
83
- });
80
+ }
84
81
  }
85
82
  async validateBlocks(blocks) {
86
83
  if (blocks.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/slasher",
3
- "version": "2.0.3",
3
+ "version": "2.1.0-rc.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -54,20 +54,20 @@
54
54
  ]
55
55
  },
56
56
  "dependencies": {
57
- "@aztec/epoch-cache": "2.0.3",
58
- "@aztec/ethereum": "2.0.3",
59
- "@aztec/foundation": "2.0.3",
60
- "@aztec/kv-store": "2.0.3",
61
- "@aztec/l1-artifacts": "2.0.3",
62
- "@aztec/stdlib": "2.0.3",
63
- "@aztec/telemetry-client": "2.0.3",
57
+ "@aztec/epoch-cache": "2.1.0-rc.2",
58
+ "@aztec/ethereum": "2.1.0-rc.2",
59
+ "@aztec/foundation": "2.1.0-rc.2",
60
+ "@aztec/kv-store": "2.1.0-rc.2",
61
+ "@aztec/l1-artifacts": "2.1.0-rc.2",
62
+ "@aztec/stdlib": "2.1.0-rc.2",
63
+ "@aztec/telemetry-client": "2.1.0-rc.2",
64
64
  "source-map-support": "^0.5.21",
65
65
  "tslib": "^2.4.0",
66
66
  "viem": "2.23.7",
67
67
  "zod": "^3.23.8"
68
68
  },
69
69
  "devDependencies": {
70
- "@aztec/aztec.js": "2.0.3",
70
+ "@aztec/aztec.js": "2.1.0-rc.2",
71
71
  "@jest/globals": "^30.0.0",
72
72
  "@types/jest": "^30.0.0",
73
73
  "@types/node": "^22.15.17",
package/src/config.ts CHANGED
@@ -29,6 +29,7 @@ export const DefaultSlasherConfig: SlasherConfig = {
29
29
  slashOffenseExpirationRounds: 4,
30
30
  slashMaxPayloadSize: 50,
31
31
  slashGracePeriodL2Slots: 0,
32
+ slashExecuteRoundsLookBack: 4,
32
33
  slashSelfAllowed: false,
33
34
  };
34
35
 
@@ -83,7 +84,7 @@ export const slasherConfigMappings: ConfigMappingsType<SlasherConfig> = {
83
84
  },
84
85
  slashBroadcastedInvalidBlockPenalty: {
85
86
  env: 'SLASH_INVALID_BLOCK_PENALTY',
86
- description: 'Penalty amount for slashing a validator for an invalid block.',
87
+ description: 'Penalty amount for slashing a validator for an invalid block proposed via p2p.',
87
88
  ...bigintConfigHelper(DefaultSlasherConfig.slashBroadcastedInvalidBlockPenalty),
88
89
  },
89
90
  slashInactivityTargetPercentage: {
@@ -144,6 +145,11 @@ export const slasherConfigMappings: ConfigMappingsType<SlasherConfig> = {
144
145
  env: 'SLASH_GRACE_PERIOD_L2_SLOTS',
145
146
  ...numberConfigHelper(DefaultSlasherConfig.slashGracePeriodL2Slots),
146
147
  },
148
+ slashExecuteRoundsLookBack: {
149
+ env: 'SLASH_EXECUTE_ROUNDS_LOOK_BACK',
150
+ description: 'How many rounds to look back when searching for a round to execute.',
151
+ ...numberConfigHelper(DefaultSlasherConfig.slashExecuteRoundsLookBack),
152
+ },
147
153
  slashSelfAllowed: {
148
154
  description: 'Whether to allow slashes to own validators',
149
155
  ...booleanConfigHelper(DefaultSlasherConfig.slashSelfAllowed),
@@ -404,11 +404,17 @@ export class EmpireSlasherClient implements ProposerSlashActionProvider, Slasher
404
404
  continue;
405
405
  }
406
406
 
407
+ // Check if slashing is enabled at all
408
+ if (!(await this.slasher.isSlashingEnabled())) {
409
+ this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
410
+ return undefined;
411
+ }
412
+
407
413
  // Check if the slash payload is vetoed
408
414
  const isVetoed = await this.slasher.isPayloadVetoed(payload.payload);
409
415
 
410
416
  if (isVetoed) {
411
- this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed, skipping execution`);
417
+ this.log.info(`Payload ${payload.payload} from round ${payload.round} is vetoed (skipping execution)`);
412
418
  toRemove.push(payload);
413
419
  continue;
414
420
  }
@@ -1,6 +1,7 @@
1
1
  import { EthAddress } from '@aztec/aztec.js';
2
2
  import type { EpochCache } from '@aztec/epoch-cache';
3
3
  import { RollupContract, SlasherContract, TallySlashingProposerContract } from '@aztec/ethereum/contracts';
4
+ import { maxBigint } from '@aztec/foundation/bigint';
4
5
  import { compactArray, partition, times } from '@aztec/foundation/collection';
5
6
  import { createLogger } from '@aztec/foundation/log';
6
7
  import { sleep } from '@aztec/foundation/sleep';
@@ -45,7 +46,7 @@ export type TallySlasherSettings = Prettify<
45
46
  >;
46
47
 
47
48
  export type TallySlasherClientConfig = SlashOffensesCollectorConfig &
48
- Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever'>;
49
+ Pick<SlasherConfig, 'slashValidatorsAlways' | 'slashValidatorsNever' | 'slashExecuteRoundsLookBack'>;
49
50
 
50
51
  /**
51
52
  * The Tally Slasher client is responsible for managing slashable offenses using
@@ -177,26 +178,65 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
177
178
  return compactArray<ProposerSlashAction>([executeAction, voteAction]);
178
179
  }
179
180
 
180
- /** Returns an execute slash action if there are any rounds ready to be executed */
181
+ /**
182
+ * Returns an execute slash action if there are any rounds ready to be executed.
183
+ * Returns the oldest slash action if there are multiple rounds pending execution.
184
+ */
181
185
  protected async getExecuteSlashAction(slotNumber: bigint): Promise<ProposerSlashAction | undefined> {
182
186
  const { round: currentRound } = this.roundMonitor.getRoundForSlot(slotNumber);
183
187
  const slashingExecutionDelayInRounds = BigInt(this.settings.slashingExecutionDelayInRounds);
184
188
  const executableRound = currentRound - slashingExecutionDelayInRounds - 1n;
185
- if (executableRound < 0n) {
189
+ const lookBack = BigInt(this.config.slashExecuteRoundsLookBack);
190
+ const oldestExecutableRound = maxBigint(0n, executableRound - lookBack);
191
+
192
+ // Check if slashing is enabled at all
193
+ if (!(await this.slasher.isSlashingEnabled())) {
194
+ this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`);
186
195
  return undefined;
187
196
  }
188
197
 
189
- let logData: Record<string, unknown> = { currentRound, executableRound, slotNumber };
198
+ this.log.debug(`Checking slashing rounds ${oldestExecutableRound} to ${executableRound} to execute`, {
199
+ slotNumber,
200
+ currentRound,
201
+ oldestExecutableRound,
202
+ executableRound,
203
+ slashingExecutionDelayInRounds,
204
+ lookBack,
205
+ slashingLifetimeInRounds: this.settings.slashingLifetimeInRounds,
206
+ });
207
+
208
+ // Iterate over all rounds, starting from the oldest, until we find one that is executable
209
+ for (let roundToCheck = oldestExecutableRound; roundToCheck <= executableRound; roundToCheck++) {
210
+ const action = await this.tryGetRoundExecuteAction(roundToCheck);
211
+ if (action) {
212
+ return action;
213
+ }
214
+ }
215
+
216
+ // And return nothing if none are found
217
+ return undefined;
218
+ }
219
+
220
+ /**
221
+ * Checks if a given round is executable and returns an execute-slash action for it if so.
222
+ * Assumes round number has already been checked against lifetime and execution delay.
223
+ */
224
+ private async tryGetRoundExecuteAction(executableRound: bigint): Promise<ProposerSlashAction | undefined> {
225
+ let logData: Record<string, unknown> = { executableRound };
226
+ this.log.debug(`Testing if slashing round ${executableRound} is executable`, logData);
190
227
 
191
228
  try {
229
+ // Check if slashing is enabled at all
230
+ if (!(await this.slasher.isSlashingEnabled())) {
231
+ this.log.warn(`Slashing is disabled in the Slasher contract (skipping execution)`, logData);
232
+ return undefined;
233
+ }
234
+
192
235
  const roundInfo = await this.tallySlashingProposer.getRound(executableRound);
193
236
  logData = { ...logData, roundInfo };
194
237
  if (roundInfo.isExecuted) {
195
238
  this.log.verbose(`Round ${executableRound} has already been executed`, logData);
196
239
  return undefined;
197
- } else if (!roundInfo.readyToExecute) {
198
- this.log.verbose(`Round ${executableRound} is not ready to execute yet`, logData);
199
- return undefined;
200
240
  } else if (roundInfo.voteCount === 0n) {
201
241
  this.log.debug(`Round ${executableRound} received no votes`, logData);
202
242
  return undefined;
@@ -205,6 +245,7 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
205
245
  return undefined;
206
246
  }
207
247
 
248
+ // Check if the round yields any slashing at all
208
249
  const { actions: slashActions, committees } = await this.tallySlashingProposer.getTally(executableRound);
209
250
  if (slashActions.length === 0) {
210
251
  this.log.verbose(`Round ${executableRound} does not resolve in any slashing`, logData);
@@ -239,9 +280,8 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
239
280
  return { type: 'execute-slash', round: executableRound, committees: slashedCommittees };
240
281
  } catch (error) {
241
282
  this.log.error(`Error checking round to execute ${executableRound}`, error);
283
+ return undefined;
242
284
  }
243
-
244
- return undefined;
245
285
  }
246
286
 
247
287
  /** Returns a vote action based on offenses from the target round (with offset applied) */
@@ -1,4 +1,3 @@
1
- import type { Tx } from '@aztec/aztec.js';
2
1
  import { EpochCache } from '@aztec/epoch-cache';
3
2
  import { merge, pick } from '@aztec/foundation/collection';
4
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
@@ -9,6 +8,7 @@ import {
9
8
  type L2BlockSourceEventEmitter,
10
9
  L2BlockSourceEvents,
11
10
  } from '@aztec/stdlib/block';
11
+ import { getEpochAtSlot } from '@aztec/stdlib/epoch-helpers';
12
12
  import type {
13
13
  IFullNodeBlockBuilder,
14
14
  ITxProvider,
@@ -16,7 +16,7 @@ import type {
16
16
  SlasherConfig,
17
17
  } from '@aztec/stdlib/interfaces/server';
18
18
  import type { L1ToL2MessageSource } from '@aztec/stdlib/messaging';
19
- import { OffenseType } from '@aztec/stdlib/slashing';
19
+ import { OffenseType, getOffenseTypeName } from '@aztec/stdlib/slashing';
20
20
  import {
21
21
  ReExFailedTxsError,
22
22
  ReExStateMismatchError,
@@ -78,49 +78,44 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
78
78
 
79
79
  private handlePruneL2Blocks(event: L2BlockPruneEvent): void {
80
80
  const { blocks, epochNumber } = event;
81
- this.log.info(`Detected chain prune. Validating epoch ${epochNumber}`);
82
-
83
- this.validateBlocks(blocks)
84
- .then(async () => {
85
- this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
86
- const validators = await this.getValidatorsForEpoch(epochNumber);
87
- // need to specify return type to be able to return offense as undefined later on
88
- const result: { validators: EthAddress[]; offense: OffenseType | undefined } = {
89
- validators,
90
- offense: OffenseType.VALID_EPOCH_PRUNED,
91
- };
92
- return result;
93
- })
94
- .catch(async error => {
95
- if (error instanceof TransactionsNotAvailableError) {
96
- this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
97
- message: error.message,
98
- });
99
- const validators = await this.getValidatorsForEpoch(epochNumber);
100
- return {
101
- validators,
102
- offense: OffenseType.DATA_WITHHOLDING,
103
- };
104
- } else {
105
- this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
106
- return {
107
- validators: [],
108
- offense: undefined,
109
- };
110
- }
111
- })
112
- .then(({ validators, offense }) => {
113
- if (validators.length === 0 || offense === undefined) {
114
- return;
115
- }
116
- const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
117
- this.log.info(`Slash for epoch ${epochNumber} created`, args);
118
- this.emit(WANT_TO_SLASH_EVENT, args);
119
- })
120
- .catch(error => {
121
- // This can happen if we fail to get the validators for the epoch.
122
- this.log.error('Error while creating slash for epoch', error);
123
- });
81
+ void this.processPruneL2Blocks(blocks, epochNumber).catch(err =>
82
+ this.log.error('Error processing pruned L2 blocks', err, { epochNumber }),
83
+ );
84
+ }
85
+
86
+ private async emitSlashForEpoch(offense: OffenseType, epochNumber: bigint): Promise<void> {
87
+ const validators = await this.getValidatorsForEpoch(epochNumber);
88
+ if (validators.length === 0) {
89
+ this.log.warn(`No validators found for epoch ${epochNumber} (cannot slash for ${getOffenseTypeName(offense)})`);
90
+ return;
91
+ }
92
+ const args = this.validatorsToSlashingArgs(validators, offense, BigInt(epochNumber));
93
+ this.log.verbose(`Created slash for ${getOffenseTypeName(offense)} at epoch ${epochNumber}`, args);
94
+ this.emit(WANT_TO_SLASH_EVENT, args);
95
+ }
96
+
97
+ private async processPruneL2Blocks(blocks: L2Block[], epochNumber: bigint): Promise<void> {
98
+ try {
99
+ const l1Constants = this.epochCache.getL1Constants();
100
+ const epochBlocks = blocks.filter(b => getEpochAtSlot(b.slot, l1Constants) === epochNumber);
101
+ this.log.info(
102
+ `Detected chain prune. Validating epoch ${epochNumber} with blocks ${epochBlocks[0]?.number} to ${epochBlocks[epochBlocks.length - 1]?.number}.`,
103
+ { blocks: epochBlocks.map(b => b.toBlockInfo()) },
104
+ );
105
+
106
+ await this.validateBlocks(epochBlocks);
107
+ this.log.info(`Pruned epoch ${epochNumber} was valid. Want to slash committee for not having it proven.`);
108
+ await this.emitSlashForEpoch(OffenseType.VALID_EPOCH_PRUNED, epochNumber);
109
+ } catch (error) {
110
+ if (error instanceof TransactionsNotAvailableError) {
111
+ this.log.info(`Data for pruned epoch ${epochNumber} was not available. Will want to slash.`, {
112
+ message: error.message,
113
+ });
114
+ await this.emitSlashForEpoch(OffenseType.DATA_WITHHOLDING, epochNumber);
115
+ } else {
116
+ this.log.error(`Error while validating pruned epoch ${epochNumber}. Will not want to slash.`, error);
117
+ }
118
+ }
124
119
  }
125
120
 
126
121
  public async validateBlocks(blocks: L2Block[]): Promise<void> {
@@ -151,7 +146,7 @@ export class EpochPruneWatcher extends (EventEmitter as new () => WatcherEmitter
151
146
 
152
147
  const l1ToL2Messages = await this.l1ToL2MessageSource.getL1ToL2Messages(blockFromL1.number);
153
148
  const { block, failedTxs, numTxs } = await this.blockBuilder.buildBlock(
154
- txs as Tx[],
149
+ txs,
155
150
  l1ToL2Messages,
156
151
  blockFromL1.header.globalVariables,
157
152
  {},