Skip to content

Commit 4e12b5c

Browse files
committed
fix: Do not send slashing vote if computed vote is zero
Fixes https://linear.app/aztec-labs/issue/TMNT-231/inconsistent-voting-empty-votes
1 parent df1f722 commit 4e12b5c

File tree

6 files changed

+45
-2
lines changed

6 files changed

+45
-2
lines changed

yarn-project/slasher/src/empire_slasher_client.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ describe('EmpireSlasherClient', () => {
4848
l1StartBlock: 0n,
4949
slotDuration: 4,
5050
ethereumSlotDuration: 12,
51+
slashingAmounts: undefined,
5152
};
5253

5354
const config: SlasherConfig = {

yarn-project/slasher/src/factory/create_implementation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ async function createEmpireSlasher(
9393
slotDuration: Number(slotDuration),
9494
l1StartBlock,
9595
ethereumSlotDuration: config.ethereumSlotDuration,
96+
slashingAmounts: undefined,
9697
};
9798

9899
const payloadsStore = new SlasherPayloadsStore(kvStore, {

yarn-project/slasher/src/slash_offenses_collector.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ describe('SlashOffensesCollector', () => {
2121

2222
const settings: SlashOffensesCollectorSettings = {
2323
epochDuration: 32,
24+
slashingAmounts: [100n, 200n, 300n],
2425
};
2526

2627
const config: SlasherConfig = {

yarn-project/slasher/src/slash_offenses_collector.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import type { SlasherOffensesStore } from './stores/offenses_store.js';
88
import { WANT_TO_SLASH_EVENT, type WantToSlashArgs, type Watcher } from './watcher.js';
99

1010
export type SlashOffensesCollectorConfig = Prettify<Pick<SlasherConfig, 'slashGracePeriodL2Slots'>>;
11-
export type SlashOffensesCollectorSettings = Prettify<Pick<L1RollupConstants, 'epochDuration'>>;
11+
export type SlashOffensesCollectorSettings = Prettify<
12+
Pick<L1RollupConstants, 'epochDuration'> & { slashingAmounts: [bigint, bigint, bigint] | undefined }
13+
>;
1214

1315
/**
1416
* Collects and manages slashable offenses from watchers.
@@ -76,6 +78,13 @@ export class SlashOffensesCollector {
7678
continue;
7779
}
7880

81+
if (this.settings.slashingAmounts) {
82+
const minSlash = this.settings.slashingAmounts[0];
83+
if (arg.amount < minSlash) {
84+
this.log.warn(`Offense amount ${arg.amount} is below minimum slashing amount ${minSlash}`);
85+
}
86+
}
87+
7988
this.log.info(`Adding pending offense for validator ${arg.validator}`, pendingOffense);
8089
await this.offensesStore.addPendingOffense(pendingOffense);
8190
}

yarn-project/slasher/src/tally_slasher_client.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,26 @@ describe('TallySlasherClient', () => {
271271
expect(action.committees[0]).toHaveLength(settings.targetCommitteeSize);
272272
expect(action.committees[0][0].toString()).toEqual(EthAddress.ZERO.toString());
273273
});
274+
275+
it('should not return any action when computed votes are zero', async () => {
276+
// Round 5 votes on round 3 (offset of 2)
277+
const currentRound = 5n;
278+
const currentSlot = currentRound * BigInt(roundSize);
279+
const targetRound = 3n;
280+
281+
// Add slot-based offense for the target round
282+
await offensesStore.addPendingOffense(
283+
createOffense({
284+
validator: committee[0],
285+
epochOrSlot: targetRound * BigInt(roundSize),
286+
offenseType: OffenseType.PROPOSED_INSUFFICIENT_ATTESTATIONS, // slot-based
287+
amount: 1n, // Too low to reach minimum slash unit
288+
}),
289+
);
290+
291+
const actions = await tallySlasherClient.getProposerActions(currentSlot);
292+
expect(actions).toHaveLength(0);
293+
});
274294
});
275295

276296
describe('execute-slash', () => {
@@ -574,7 +594,7 @@ describe('TallySlasherClient', () => {
574594
it('should handle from offense detection to execution', async () => {
575595
// Round 3: Offense occurs
576596
const offenseRound = 3n;
577-
const validator = EthAddress.random();
597+
const validator = committee[0];
578598
const offense: WantToSlashArgs = {
579599
validator,
580600
amount: settings.slashingAmounts[1],

yarn-project/slasher/src/tally_slasher_client.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,17 @@ export class TallySlasherClient implements ProposerSlashActionProvider, SlasherC
292292

293293
const committees = await this.collectCommitteesActiveDuringRound(slashedRound);
294294
const votes = getSlashConsensusVotesFromOffenses(offensesToSlash, committees, this.settings);
295+
if (votes.every(v => v === 0)) {
296+
this.log.warn(`Computed votes for offenses are all zero. Skipping vote.`, {
297+
slotNumber,
298+
currentRound,
299+
slashedRound,
300+
offensesToSlash,
301+
committees,
302+
});
303+
return undefined;
304+
}
305+
295306
this.log.debug(`Computed votes for slashing ${offensesToSlash.length} offenses`, {
296307
slashedRound,
297308
currentRound,

0 commit comments

Comments
 (0)