From f9cf027cce484882dfce006f270b048a30cbc486 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 30 Aug 2024 09:30:14 +0000 Subject: [PATCH] feat: Populate epoch 0 from initial validator set --- l1-contracts/src/core/Rollup.sol | 1 + .../src/core/sequencer_selection/Leonidas.sol | 35 ++++-------------- l1-contracts/test/sparta/Sparta.t.sol | 37 +++++++++++++++---- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 8f79560ea25..93761eb3ac7 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -104,6 +104,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup { for (uint256 i = 0; i < _validators.length; i++) { _addValidator(_validators[i]); } + setupEpoch(); } /** diff --git a/l1-contracts/src/core/sequencer_selection/Leonidas.sol b/l1-contracts/src/core/sequencer_selection/Leonidas.sol index b471f2b9378..03fc9ec9bd2 100644 --- a/l1-contracts/src/core/sequencer_selection/Leonidas.sol +++ b/l1-contracts/src/core/sequencer_selection/Leonidas.sol @@ -78,11 +78,6 @@ contract Leonidas is Ownable, ILeonidas { constructor(address _ares) Ownable(_ares) { GENESIS_TIME = block.timestamp; - - // We will setup the initial epoch value - uint256 seed = _computeNextSeed(0); - epochs[0] = Epoch({committee: new address[](0), sampleSeed: type(uint256).max, nextSeed: seed}); - lastSeed = seed; } /** @@ -135,10 +130,6 @@ contract Leonidas is Ownable, ILeonidas { function getCommitteeAt(uint256 _ts) internal view returns (address[] memory) { uint256 epochNumber = getEpochAt(_ts); - if (epochNumber == 0) { - return new address[](0); - } - Epoch storage epoch = epochs[epochNumber]; if (epoch.sampleSeed != 0) { @@ -156,7 +147,7 @@ contract Leonidas is Ownable, ILeonidas { // Emulate a sampling of the validators uint256 sampleSeed = _getSampleSeed(epochNumber); - return _sampleValidators(epochNumber, sampleSeed); + return _sampleValidators(sampleSeed); } /** @@ -224,12 +215,11 @@ contract Leonidas is Ownable, ILeonidas { uint256 epochNumber = getCurrentEpoch(); Epoch storage epoch = epochs[epochNumber]; - // For epoch 0 the sampleSeed == type(uint256).max, so we will never enter this if (epoch.sampleSeed == 0) { epoch.sampleSeed = _getSampleSeed(epochNumber); epoch.nextSeed = lastSeed = _computeNextSeed(epochNumber); - epoch.committee = _sampleValidators(epochNumber, epoch.sampleSeed); + epoch.committee = _sampleValidators(epoch.sampleSeed); } } @@ -326,7 +316,7 @@ contract Leonidas is Ownable, ILeonidas { // Emulate a sampling of the validators uint256 sampleSeed = _getSampleSeed(epochNumber); - address[] memory committee = _sampleValidators(epochNumber, sampleSeed); + address[] memory committee = _sampleValidators(sampleSeed); return committee[_computeProposerIndex(epochNumber, slot, sampleSeed, committee.length)]; } @@ -415,23 +405,9 @@ contract Leonidas is Ownable, ILeonidas { * @dev Only used internally, should never be called for anything but the "next" epoch * Allowing us to always use `lastSeed`. * - * @dev The first epoch will always return an empty list - * If the validator set is empty, we return an empty list - * If the validator set is smaller than the target committee size, we return the full set - * If the validator set is larger than the target committee size, we sample the validators - * by using the seed of the previous epoch to compute an offset for the validator set and then - * we take the next `TARGET_COMMITTEE_SIZE` validators from that offset (wrapping around). - * - * @param _epoch - The epoch to sample the validators for - * * @return The validators for the given epoch */ - function _sampleValidators(uint256 _epoch, uint256 _seed) private view returns (address[] memory) { - // If we are in the first epoch, we just return an empty list - if (_epoch == 0) { - return new address[](0); - } - + function _sampleValidators(uint256 _seed) private view returns (address[] memory) { uint256 validatorSetSize = validatorSet.length(); if (validatorSetSize == 0) { return new address[](0); @@ -467,6 +443,9 @@ contract Leonidas is Ownable, ILeonidas { * @return The sample seed for the epoch */ function _getSampleSeed(uint256 _epoch) private view returns (uint256) { + if (_epoch == 0) { + return type(uint256).max; + } uint256 sampleSeed = epochs[_epoch].sampleSeed; if (sampleSeed != 0) { return sampleSeed; diff --git a/l1-contracts/test/sparta/Sparta.t.sol b/l1-contracts/test/sparta/Sparta.t.sol index 162e1c95058..e02935ef76f 100644 --- a/l1-contracts/test/sparta/Sparta.t.sol +++ b/l1-contracts/test/sparta/Sparta.t.sol @@ -59,6 +59,14 @@ contract SpartaTest is DecoderBase { vm.warp(initialTime); } + address[] memory initialValidators = new address[](_validatorCount); + for (uint256 i = 1; i < _validatorCount + 1; i++) { + uint256 privateKey = uint256(keccak256(abi.encode("validator", i))); + address validator = vm.addr(privateKey); + privateKeys[validator] = privateKey; + initialValidators[i - 1] = validator; + } + registry = new Registry(address(this)); availabilityOracle = new AvailabilityOracle(); portalERC20 = new PortalERC20(); @@ -68,7 +76,7 @@ contract SpartaTest is DecoderBase { IFeeJuicePortal(address(0)), bytes32(0), address(this), - new address[](0) + initialValidators ); inbox = Inbox(address(rollup.INBOX())); outbox = Outbox(address(rollup.OUTBOX())); @@ -78,15 +86,30 @@ contract SpartaTest is DecoderBase { merkleTestUtil = new MerkleTestUtil(); txsHelper = new TxsDecoderHelper(); - for (uint256 i = 1; i < _validatorCount + 1; i++) { - uint256 privateKey = uint256(keccak256(abi.encode("validator", i))); - address validator = vm.addr(privateKey); - privateKeys[validator] = privateKey; - rollup.addValidator(validator); - } _; } + mapping(address => bool) internal _seenValidators; + mapping(address => bool) internal _seenCommittee; + + function testInitialCommitteMatch() public setup(4) { + address[] memory validators = rollup.getValidators(); + address[] memory committee = rollup.getCurrentEpochCommittee(); + assertEq(rollup.getCurrentEpoch(), 0); + assertEq(validators.length, 4, "Invalid validator set size"); + assertEq(committee.length, 4, "invalid committee set size"); + + for (uint256 i = 0; i < validators.length; i++) { + _seenValidators[validators[i]] = true; + } + + for (uint256 i = 0; i < committee.length; i++) { + assertTrue(_seenValidators[committee[i]]); + assertFalse(_seenCommittee[committee[i]]); + _seenCommittee[committee[i]] = true; + } + } + function testProposerForNonSetupEpoch(uint8 _epochsToJump) public setup(4) { if (Constants.IS_DEV_NET == 1) { return;