diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 87902bfaad7d..19ac02f22c52 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -130,6 +130,23 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { return ValidatorSelectionLib.getCommitteeAt(StakingLib.getStorage(), getCurrentEpoch()); } + /** + * @notice Get the validator set for a given epoch + * + * @dev Consider removing this to replace with a `size` and individual getter. + * + * @param _epoch The epoch number to get the validator set for + * + * @return The validator set for the given epoch + */ + function getEpochCommittee(Epoch _epoch) + external + override(IValidatorSelection) + returns (address[] memory) + { + return ValidatorSelectionLib.getCommitteeAt(StakingLib.getStorage(), _epoch); + } + /** * @notice Get the committee for a given timestamp * @@ -394,24 +411,6 @@ contract Rollup is IStaking, IValidatorSelection, IRollup, RollupCore { OperatorInfo({proposer: StakingLib.getStorage().info[attester].proposer, attester: attester}); } - /** - * @notice Get the validator set for a given epoch - * - * @dev Consider removing this to replace with a `size` and individual getter. - * - * @param _epoch The epoch number to get the validator set for - * - * @return The validator set for the given epoch - */ - function getEpochCommittee(Epoch _epoch) - external - view - override(IValidatorSelection) - returns (address[] memory) - { - return ValidatorSelectionLib.getStorage().epochs[_epoch].committee; - } - /** * @notice Get the sample seed for a given timestamp * diff --git a/l1-contracts/src/core/interfaces/IValidatorSelection.sol b/l1-contracts/src/core/interfaces/IValidatorSelection.sol index 1ad6e55ff913..1962dfd899d7 100644 --- a/l1-contracts/src/core/interfaces/IValidatorSelection.sol +++ b/l1-contracts/src/core/interfaces/IValidatorSelection.sol @@ -36,6 +36,7 @@ interface IValidatorSelection is IValidatorSelectionCore { // Non view as uses transient storage function getCurrentEpochCommittee() external returns (address[] memory); function getCommitteeAt(Timestamp _ts) external returns (address[] memory); + function getEpochCommittee(Epoch _epoch) external returns (address[] memory); // Stable function getCurrentEpoch() external view returns (Epoch); @@ -46,7 +47,6 @@ interface IValidatorSelection is IValidatorSelectionCore { // Likely removal of these to replace with a size and indiviual getter // Get the current epoch committee - function getEpochCommittee(Epoch _epoch) external view returns (address[] memory); function getAttesters() external view returns (address[] memory); function getSampleSeedAt(Timestamp _ts) external view returns (uint256); diff --git a/l1-contracts/src/periphery/SlashPayload.sol b/l1-contracts/src/periphery/SlashPayload.sol index e9c429dc5e8b..bb3e49c10580 100644 --- a/l1-contracts/src/periphery/SlashPayload.sol +++ b/l1-contracts/src/periphery/SlashPayload.sol @@ -15,14 +15,20 @@ contract SlashPayload is IPayload { IValidatorSelection public immutable VALIDATOR_SELECTION; uint256 public immutable AMOUNT; + address[] public attesters; + constructor(Epoch _epoch, IValidatorSelection _validatorSelection, uint256 _amount) { EPOCH = _epoch; VALIDATOR_SELECTION = _validatorSelection; AMOUNT = _amount; + + address[] memory attesters_ = IValidatorSelection(VALIDATOR_SELECTION).getEpochCommittee(EPOCH); + for (uint256 i = 0; i < attesters_.length; i++) { + attesters.push(attesters_[i]); + } } function getActions() external view override(IPayload) returns (IPayload.Action[] memory) { - address[] memory attesters = IValidatorSelection(VALIDATOR_SELECTION).getEpochCommittee(EPOCH); IPayload.Action[] memory actions = new IPayload.Action[](attesters.length); for (uint256 i = 0; i < attesters.length; i++) { diff --git a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol index 9f2e8dc2fb49..0b8d77545d3f 100644 --- a/l1-contracts/test/validator-selection/ValidatorSelection.t.sol +++ b/l1-contracts/test/validator-selection/ValidatorSelection.t.sol @@ -164,6 +164,24 @@ contract ValidatorSelectionTest is DecoderBase { assertEq(expectedProposer, actualProposer, "Invalid proposer"); } + function testCommitteeForNonSetupEpoch(uint8 _epochsToJump) public setup(4) { + Epoch pre = rollup.getCurrentEpoch(); + vm.warp( + block.timestamp + + uint256(_epochsToJump) * rollup.getEpochDuration() * rollup.getSlotDuration() + ); + + Epoch post = rollup.getCurrentEpoch(); + + uint256 validatorSetSize = rollup.getAttesters().length; + uint256 targetCommitteeSize = rollup.getTargetCommitteeSize(); + uint256 expectedSize = + validatorSetSize > targetCommitteeSize ? targetCommitteeSize : validatorSetSize; + + assertEq(rollup.getEpochCommittee(pre).length, expectedSize, "Invalid committee size"); + assertEq(rollup.getEpochCommittee(post).length, expectedSize, "Invalid committee size"); + } + function testValidatorSetLargerThanCommittee(bool _insufficientSigs) public setup(100) { assertGt(rollup.getAttesters().length, rollup.getTargetCommitteeSize(), "Not enough validators"); uint256 committeeSize = rollup.getTargetCommitteeSize() * 2 / 3 + (_insufficientSigs ? 0 : 1); diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index a08cda99ca7c..f5af2bec5e1b 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -218,6 +218,17 @@ export class RollupContract { return result; } + async getEpochCommittee(epoch: bigint) { + const { result } = await this.client.simulateContract({ + address: this.address, + abi: RollupAbi, + functionName: 'getEpochCommittee', + args: [epoch], + }); + + return result; + } + getBlock(blockNumber: bigint) { return this.rollup.read.getBlock([blockNumber]); } @@ -391,10 +402,6 @@ export class RollupContract { return this.rollup.read.getAttesters(); } - getEpochCommittee(epoch: bigint) { - return this.rollup.read.getEpochCommittee([epoch]); - } - getInfo(address: Hex | EthAddress) { if (address instanceof EthAddress) { address = address.toString();