Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions l1-contracts/src/core/slashing/TallySlashingProposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -903,14 +903,6 @@ contract TallySlashingProposer is EIP712 {
if (escapeHatchEpochs[epochIndex]) {
continue;
}

// Skip validators for epochs without a valid committee (e.g. early epochs
// before the validator set was sampled). Without this check, indexing into
// an empty committee array would revert and block execution of the round.
if (_committees[epochIndex].length != COMMITTEE_SIZE) {
continue;
}

uint256 packedVotes = tallyMatrix[i];

// Skip if no votes for this validator
Expand Down
18 changes: 10 additions & 8 deletions l1-contracts/test/slashing/TallySlashingProposer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -991,9 +991,9 @@ contract TallySlashingProposerTest is TestBase {
// Round FIRST_SLASH_ROUND targets epochs 0 and 1, which have no committees
// because they precede the validator set sampling lag.
//
// Before the fix, casting votes that reach quorum for validator slots in these
// committee-less epochs would cause executeRound (and getTally) to revert with
// an array out-of-bounds access when indexing _committees[epochIndex][validatorIndex].
// Casting votes that reach quorum for validator slots in these committee-less epochs cause
// executeRound (and getTally) to revert with an array out-of-bounds access when
// indexing _committees[epochIndex][validatorIndex].
_jumpToSlashRound(FIRST_SLASH_ROUND);
SlashRound targetRound = slashingProposer.getCurrentRound();

Expand All @@ -1020,16 +1020,18 @@ contract TallySlashingProposerTest is TestBase {
assertEq(committees[0].length, 0, "Epoch 0 should have empty committee");
assertEq(committees[1].length, 0, "Epoch 1 should have empty committee");

// getTally should not revert and should return 0 actions
// getTally should revert because of out of bounds
vm.expectRevert();
TallySlashingProposer.SlashAction[] memory actions = slashingProposer.getTally(targetRound, committees);
assertEq(actions.length, 0, "Should have no slash actions for empty committees");
assertEq(actions.length, 0, "Should have no slash actions since reverted");

// executeRound should also succeed
// Reverts because of out of bounds
vm.expectRevert();
slashingProposer.executeRound(targetRound, committees);

// Verify round is marked as executed
// Verify round is not marked as executed
(bool isExecuted,) = slashingProposer.getRound(targetRound);
assertTrue(isExecuted, "Round should be marked as executed");
assertFalse(isExecuted, "Round should not be marked as executed");
}

function test_revertWhenSlashAmountIsZero() public {
Expand Down
Loading