Skip to content

Commit 31331e2

Browse files
authored
chore: Fix slash veto demo flake (#16934)
The `slash_veto_demo` e2e test was failing since a view-like call to the contract to get the committees slashed for a given round reverted: ``` 21:15:59 ● veto slash › vetoes true and sets the new tally slasher 21:15:59 21:15:59 ContractFunctionExecutionError: The contract function "getSlashTargetCommittees" reverted. 21:15:59 21:15:59 Error: ValidatorSelection__InsufficientValidatorSetSize(uint256 actual, uint256 expected) 21:15:59 (0, 4) 21:15:59 21:15:59 Contract Call: 21:15:59 address: 0x9debab8762554f8b8f5dfad92e40c1c7d2b0263a 21:15:59 function: getSlashTargetCommittees(uint256 _round) 21:15:59 args: (3) 21:15:59 sender: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 21:15:59 21:15:59 Docs: https://viem.sh/docs/contract/simulateContract 21:15:59 Version: [email protected] 21:15:59 21:15:59 71 | } 21:15:59 72 | /** Returns the slash actions and payload address for a given round (zero if no slash actions) */ async getPayload(round) { 21:15:59 > 73 | const { result: committees } = await this.contract.simulate.getSlashTargetCommittees([ 21:15:59 | ^ 21:15:59 74 | round 21:15:59 75 | ]); 21:15:59 76 | const tally = await this.contract.read.getTally([ 21:15:59 21:15:59 at getContractError (../../node_modules/viem/utils/errors/getContractError.ts:78:10) 21:15:59 at simulateContract (../../node_modules/viem/actions/public/simulateContract.ts:308:11) 21:15:59 at TallySlashingProposerContract.getPayload (../../ethereum/dest/contracts/tally_slashing_proposer.js:73:40) 21:15:59 at e2e_p2p/slash_veto_demo.test.ts:247:40 21:15:59 at retryUntil (../../foundation/dest/retry/index.js:84:24) 21:15:59 at e2e_p2p/slash_veto_demo.test.ts:310:32 21:15:59 21:15:59 Cause: 21:15:59 ContractFunctionRevertedError: The contract function "getSlashTargetCommittees" reverted. 21:15:59 21:15:59 Error: ValidatorSelection__InsufficientValidatorSetSize(uint256 actual, uint256 expected) 21:15:59 (0, 4) ``` My guess is this happened because the round queried included early epochs where the committee had not yet been formed, so one of the calls to the rollup to get the committee failed, causing the entire call to fail even if other epochs in the round did have committees. This PR fixes the L1 contract so it try/catches calls to the rollup and returns whatever committees _could_ be fetched.
2 parents 7188c46 + b59e100 commit 31331e2

File tree

2 files changed

+32
-2
lines changed

2 files changed

+32
-2
lines changed

l1-contracts/src/core/slashing/TallySlashingProposer.sol

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,8 +487,9 @@ contract TallySlashingProposer is EIP712 {
487487
/**
488488
* @notice Load committees for all epochs to be potentially slashed in a round from the rollup instance
489489
* @dev This is an expensive call. It is not marked as view since `getEpochCommittee` may modify rollup state.
490+
* If `getEpochCommittee` throws (eg committee not yet formed), an empty committee is returned for that epoch.
490491
* @param _round The round number to load committees for
491-
* @return committees Array of committees, one for each epoch in the round
492+
* @return committees Array of committees, one for each epoch in the round (may contain empty arrays for early epochs)
492493
*/
493494
function getSlashTargetCommittees(SlashRound _round) external returns (address[][] memory committees) {
494495
committees = new address[][](ROUND_SIZE_IN_EPOCHS);
@@ -497,7 +498,11 @@ contract TallySlashingProposer is EIP712 {
497498
unchecked {
498499
for (uint256 epochIndex; epochIndex < ROUND_SIZE_IN_EPOCHS; ++epochIndex) {
499500
Epoch epoch = getSlashTargetEpoch(_round, epochIndex);
500-
committees[epochIndex] = rollup.getEpochCommittee(epoch);
501+
try rollup.getEpochCommittee(epoch) returns (address[] memory committee) {
502+
committees[epochIndex] = committee;
503+
} catch {
504+
committees[epochIndex] = new address[](0);
505+
}
501506
}
502507
}
503508

l1-contracts/test/slashing/TallySlashingProposer.t.sol

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,4 +864,29 @@ contract TallySlashingProposerTest is TestBase {
864864
}
865865
}
866866
}
867+
868+
function test_getSlashTargetCommitteesEarlyEpochs() public {
869+
// Test that getSlashTargetCommittees handles epochs 0 and 1 without throwing
870+
// when ValidatorSelection__InsufficientValidatorSetSize is thrown
871+
872+
// Use a very early slash round that would target epochs 0 and 1
873+
// With SLASH_OFFSET_IN_ROUNDS = 2, round 2 targets epochs starting from (2-2)*ROUND_SIZE_IN_EPOCHS = 0
874+
SlashRound earlyRound = SlashRound.wrap(SLASH_OFFSET_IN_ROUNDS);
875+
876+
// This should not revert and should return empty committees for early epochs
877+
address[][] memory committees = slashingProposer.getSlashTargetCommittees(earlyRound);
878+
879+
// Verify we get the expected number of committees
880+
assertEq(committees.length, ROUND_SIZE_IN_EPOCHS, "Should return correct number of committees");
881+
882+
// For very early rounds, we expect empty committees for epochs 0 and 1
883+
// Since ROUND_SIZE_IN_EPOCHS = 2, both epochs should be empty
884+
for (uint256 i = 0; i < ROUND_SIZE_IN_EPOCHS; i++) {
885+
Epoch targetEpoch = slashingProposer.getSlashTargetEpoch(earlyRound, i);
886+
if (Epoch.unwrap(targetEpoch) <= 1) {
887+
assertEq(committees[i].length, 0, "Committee for early epochs should be empty");
888+
}
889+
// For epochs > 1, we might get actual committees, but that depends on the test setup
890+
}
891+
}
867892
}

0 commit comments

Comments
 (0)