From 96bb13bdfb69c5903155d69821dc8be08be4fb45 Mon Sep 17 00:00:00 2001 From: LHerskind <16536249+LHerskind@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:07:48 +0000 Subject: [PATCH 1/2] bug: showcase issue --- .../ValidatorSelection.t.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) 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); From 28573e121984ad4db87a5d556ccf59be2c2a117c Mon Sep 17 00:00:00 2001 From: LHerskind <16536249+LHerskind@users.noreply.github.com> Date: Thu, 27 Mar 2025 11:18:56 +0000 Subject: [PATCH 2/2] chore: fix --- l1-contracts/src/core/Rollup.sol | 35 +++++++++---------- .../core/interfaces/IValidatorSelection.sol | 2 +- l1-contracts/src/periphery/SlashPayload.sol | 8 ++++- yarn-project/ethereum/src/contracts/rollup.ts | 15 +++++--- 4 files changed, 36 insertions(+), 24 deletions(-) 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/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();