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
9 changes: 7 additions & 2 deletions l1-contracts/src/core/slashing/TallySlashingProposer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,9 @@ contract TallySlashingProposer is EIP712 {
/**
* @notice Load committees for all epochs to be potentially slashed in a round from the rollup instance
* @dev This is an expensive call. It is not marked as view since `getEpochCommittee` may modify rollup state.
* If `getEpochCommittee` throws (eg committee not yet formed), an empty committee is returned for that epoch.
* @param _round The round number to load committees for
* @return committees Array of committees, one for each epoch in the round
* @return committees Array of committees, one for each epoch in the round (may contain empty arrays for early epochs)
*/
function getSlashTargetCommittees(SlashRound _round) external returns (address[][] memory committees) {
committees = new address[][](ROUND_SIZE_IN_EPOCHS);
Expand All @@ -497,7 +498,11 @@ contract TallySlashingProposer is EIP712 {
unchecked {
for (uint256 epochIndex; epochIndex < ROUND_SIZE_IN_EPOCHS; ++epochIndex) {
Epoch epoch = getSlashTargetEpoch(_round, epochIndex);
committees[epochIndex] = rollup.getEpochCommittee(epoch);
try rollup.getEpochCommittee(epoch) returns (address[] memory committee) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💀

committees[epochIndex] = committee;
} catch {
committees[epochIndex] = new address[](0);
}
}
}

Expand Down
25 changes: 25 additions & 0 deletions l1-contracts/test/slashing/TallySlashingProposer.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -864,4 +864,29 @@ contract TallySlashingProposerTest is TestBase {
}
}
}

function test_getSlashTargetCommitteesEarlyEpochs() public {
// Test that getSlashTargetCommittees handles epochs 0 and 1 without throwing
// when ValidatorSelection__InsufficientValidatorSetSize is thrown

// Use a very early slash round that would target epochs 0 and 1
// With SLASH_OFFSET_IN_ROUNDS = 2, round 2 targets epochs starting from (2-2)*ROUND_SIZE_IN_EPOCHS = 0
SlashRound earlyRound = SlashRound.wrap(SLASH_OFFSET_IN_ROUNDS);

// This should not revert and should return empty committees for early epochs
address[][] memory committees = slashingProposer.getSlashTargetCommittees(earlyRound);

// Verify we get the expected number of committees
assertEq(committees.length, ROUND_SIZE_IN_EPOCHS, "Should return correct number of committees");

// For very early rounds, we expect empty committees for epochs 0 and 1
// Since ROUND_SIZE_IN_EPOCHS = 2, both epochs should be empty
for (uint256 i = 0; i < ROUND_SIZE_IN_EPOCHS; i++) {
Epoch targetEpoch = slashingProposer.getSlashTargetEpoch(earlyRound, i);
if (Epoch.unwrap(targetEpoch) <= 1) {
assertEq(committees[i].length, 0, "Committee for early epochs should be empty");
}
// For epochs > 1, we might get actual committees, but that depends on the test setup
}
}
}
Loading