diff --git a/packages/protocol/contracts-0.8/common/EpochManager.sol b/packages/protocol/contracts-0.8/common/EpochManager.sol index 6302386126b..9e07b80017f 100644 --- a/packages/protocol/contracts-0.8/common/EpochManager.sol +++ b/packages/protocol/contracts-0.8/common/EpochManager.sol @@ -52,13 +52,15 @@ contract EpochManager is uint256 public firstKnownEpoch; uint256 internal currentEpochNumber; address public oracleAddress; - address[] public elected; - + address[] public electedAccounts; mapping(address => uint256) public processedGroups; EpochProcessState public epochProcessing; mapping(uint256 => Epoch) internal epochs; mapping(address => uint256) public validatorPendingPayments; + // Electeds in the L1 assumed signers can not change during the epoch + // so we keep a copy + address[] public electedSigners; /** * @notice Event emited when epochProcessing has begun. @@ -166,7 +168,9 @@ contract EpochManager is _currentEpoch.firstBlock = firstEpochBlock; _currentEpoch.startTimestamp = block.timestamp; - elected = firstElected; + electedAccounts = firstElected; + + _setElectedSigners(firstElected); } /** @@ -177,8 +181,8 @@ contract EpochManager is function startNextEpochProcess() external nonReentrant onlySystemAlreadyInitialized { require(isTimeForNextEpoch(), "Epoch is not ready to start"); require(!isOnEpochProcess(), "Epoch process is already started"); - epochProcessing.status = EpochProcessStatus.Started; + epochProcessing.status = EpochProcessStatus.Started; epochs[currentEpochNumber].rewardsBlock = block.number; // calculate rewards @@ -227,8 +231,12 @@ contract EpochManager is IValidators validators = getValidators(); IElection election = getElection(); IScoreReader scoreReader = getScoreReader(); - for (uint i = 0; i < elected.length; i++) { - address group = validators.getValidatorsGroup(elected[i]); + require( + electedAccounts.length == electedSigners.length, + "Elected accounts and signers of different lengths." + ); + for (uint i = 0; i < electedAccounts.length; i++) { + address group = validators.getValidatorsGroup(electedAccounts[i]); if (processedGroups[group] == 0) { toProcessGroups++; uint256 groupScore = scoreReader.getGroupScore(group); @@ -240,7 +248,8 @@ contract EpochManager is ); processedGroups[group] = epochRewards == 0 ? type(uint256).max : epochRewards; } - delete elected[i]; + delete electedAccounts[i]; + delete electedSigners[i]; } require(toProcessGroups == groups.length, "number of groups does not match"); @@ -264,8 +273,17 @@ contract EpochManager is epochProcessing.totalRewardsCarbonFund ); // run elections - elected = election.electValidatorAccounts(); - _epochProcessing.status = EpochProcessStatus.NotStarted; + + address[] memory _newlyElected = election.electValidatorAccounts(); + + electedAccounts = _newlyElected; + + _setElectedSigners(_newlyElected); + + EpochProcessState memory _epochProcessingEmpty; + epochProcessing = _epochProcessingEmpty; + + emit EpochProcessingEnded(currentEpochNumber - 1); } /** @@ -321,11 +339,7 @@ contract EpochManager is } /** - * @notice Returns the info of the current epoch. - * @return firstEpoch The first block of the epoch. - * @return lastBlock The first block of the epoch. - * @return startTimestamp The starting timestamp of the epoch. - * @return rewardsBlock The reward block of the epoch. + * @notice Returns the epoch info of the specified epoch, for current epoch. */ function getCurrentEpoch() external @@ -370,11 +384,60 @@ contract EpochManager is return isOnEpochProcess(); } + /** + * @return The number of elected accounts in the current set. + */ + function numberOfElectedInCurrentSet() + external + view + onlySystemAlreadyInitialized + returns (uint256) + { + return electedAccounts.length; + } + /** * @return The list of currently elected validators. */ - function getElected() external view returns (address[] memory) { - return elected; + function getElectedAccounts() + external + view + onlySystemAlreadyInitialized + returns (address[] memory) + { + return electedAccounts; + } + + /** + * @notice Returns the currently elected account at a specified index. + * @param index The index of the currently elected account. + */ + function getElectedAccountByIndex( + uint256 index + ) external view onlySystemAlreadyInitialized returns (address) { + return electedAccounts[index]; + } + + /** + * @return The list of the validator signers of elected validators. + */ + function getElectedSigners() + external + view + onlySystemAlreadyInitialized + returns (address[] memory) + { + return electedSigners; + } + + /** + * @notice Returns the currently elected signer address at a specified index. + * @param index The index of the currently elected signer. + */ + function getElectedSignerByIndex( + uint256 index + ) external view onlySystemAlreadyInitialized returns (address) { + return electedSigners[index]; } /** @@ -397,6 +460,38 @@ contract EpochManager is return epochs[epoch].lastBlock; } + /** + * @notice Returns the epoch number of a specified blockNumber. + * @param _blockNumber Block number of the epoch info is retreived. + */ + function getEpochNumberOfBlock( + uint256 _blockNumber + ) external view onlySystemAlreadyInitialized returns (uint256) { + (uint256 _epochNumber, , , , ) = _getEpochByBlockNumber(_blockNumber); + return _epochNumber; + } + + /** + * @notice Returns the epoch info of a specified blockNumber. + * @param _blockNumber Block number of the epoch info is retreived. + * @return firstEpoch The first block of the given block number. + * @return lastBlock The first block of the given block number. + * @return startTimestamp The starting timestamp of the given block number. + * @return rewardsBlock The reward block of the given block number. + */ + function getEpochByBlockNumber( + uint256 _blockNumber + ) external view onlySystemAlreadyInitialized returns (uint256, uint256, uint256, uint256) { + ( + , + uint256 _firstBlock, + uint256 _lastBlock, + uint256 _startTimestamp, + uint256 _rewardsBlock + ) = _getEpochByBlockNumber(_blockNumber); + return (_firstBlock, _lastBlock, _startTimestamp, _rewardsBlock); + } + /** * @notice Returns the storage, major, minor, and patch version of the contract. * @return Storage version of the contract. @@ -456,16 +551,16 @@ contract EpochManager is /** * @notice Returns the epoch info of a specified epoch. - * @param epochNumber Epoch number where epoch info is retreived. - * @return firstEpoch The first block of the epoch. - * @return lastBlock The first block of the epoch. - * @return startTimestamp The starting timestamp of the epoch. - * @return rewardsBlock The reward block of the epoch. + * @param epochNumber Epoch number where the epoch info is retreived. + * @return firstEpoch The first block of the given epoch. + * @return lastBlock The first block of the given epoch. + * @return startTimestamp The starting timestamp of the given epoch. + * @return rewardsBlock The reward block of the given epoch. */ function getEpochByNumber( uint256 epochNumber ) public view onlySystemAlreadyInitialized returns (uint256, uint256, uint256, uint256) { - Epoch storage _epoch = epochs[epochNumber]; + Epoch memory _epoch = epochs[epochNumber]; return (_epoch.firstBlock, _epoch.lastBlock, _epoch.startTimestamp, _epoch.rewardsBlock); } @@ -479,14 +574,14 @@ contract EpochManager is EpochProcessState storage _epochProcessing = epochProcessing; - for (uint i = 0; i < elected.length; i++) { - uint256 validatorScore = scoreReader.getValidatorScore(elected[i]); + for (uint i = 0; i < electedAccounts.length; i++) { + uint256 validatorScore = scoreReader.getValidatorScore(electedAccounts[i]); uint256 validatorReward = validators.computeEpochReward( - elected[i], + electedAccounts[i], validatorScore, _epochProcessing.perValidatorReward ); - validatorPendingPayments[elected[i]] += validatorReward; + validatorPendingPayments[electedAccounts[i]] += validatorReward; totalRewards += validatorReward; } if (totalRewards == 0) { @@ -506,4 +601,78 @@ contract EpochManager is CELOequivalent ); } + + /** + * @notice Updates the list of elected validator signers. + */ + function _setElectedSigners(address[] memory _elected) internal { + require(electedAccounts.length > 0, "Elected list length cannot be zero."); + IAccounts accounts = getAccounts(); + electedSigners = new address[](_elected.length); + for (uint i = 0; i < _elected.length; i++) { + electedSigners[i] = accounts.getValidatorSigner(_elected[i]); + } + } + + /** + * @notice Returns the epoch info of a specified blockNumber. + * @dev This function is here for backward compatibility. It is rather gas heavy and can run out of gas. + * @param _blockNumber Block number of the epoch info is retreived. + * @return firstEpoch The first block of the given block number. + * @return lastBlock The first block of the given block number. + * @return startTimestamp The starting timestamp of the given block number. + * @return rewardsBlock The reward block of the given block number. + */ + function _getEpochByBlockNumber( + uint256 _blockNumber + ) + internal + view + onlySystemAlreadyInitialized + returns (uint256, uint256, uint256, uint256, uint256) + { + require(_blockNumber <= block.number, "Invalid blockNumber. Value too high."); + + (uint256 _firstBlockOfFirstEpoch, , , ) = getEpochByNumber(firstKnownEpoch); + + require(_blockNumber >= _firstBlockOfFirstEpoch, "Invalid blockNumber. Value too low."); + + uint256 _firstBlockOfCurrentEpoch = epochs[currentEpochNumber].firstBlock; + + if (_blockNumber >= _firstBlockOfCurrentEpoch) { + ( + uint256 _firstBlock, + uint256 _lastBlock, + uint256 _startTimestamp, + uint256 _rewardsBlock + ) = getEpochByNumber(currentEpochNumber); + return (currentEpochNumber, _firstBlock, _lastBlock, _startTimestamp, _rewardsBlock); + } + + uint256 left = firstKnownEpoch; + uint256 right = currentEpochNumber - 1; + + while (left <= right) { + uint256 mid = (left + right) / 2; + uint256 _epochFirstBlock = epochs[mid].firstBlock; + uint256 _epochLastBlock = epochs[mid].lastBlock; + + if (_blockNumber >= _epochFirstBlock && _blockNumber <= _epochLastBlock) { + Epoch memory _epoch = epochs[mid]; + return ( + mid, + _epoch.firstBlock, + _epoch.lastBlock, + _epoch.startTimestamp, + _epoch.rewardsBlock + ); + } else if (_blockNumber < _epochFirstBlock) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + revert("No matching epoch found for the given block number."); + } } diff --git a/packages/protocol/contracts-0.8/common/EpochManagerEnabler.sol b/packages/protocol/contracts-0.8/common/EpochManagerEnabler.sol index 61506ec7874..8069d13a6f7 100644 --- a/packages/protocol/contracts-0.8/common/EpochManagerEnabler.sol +++ b/packages/protocol/contracts-0.8/common/EpochManagerEnabler.sol @@ -12,8 +12,8 @@ import "./interfaces/IEpochManagerEnablerInitializer.sol"; contract EpochManagerEnabler is Initializable, - UsingPrecompiles, UsingRegistry, + UsingPrecompiles, IEpochManagerEnabler, IEpochManagerEnablerInitializer { diff --git a/packages/protocol/contracts-0.8/common/PrecompilesOverride.sol b/packages/protocol/contracts-0.8/common/PrecompilesOverride.sol new file mode 100644 index 00000000000..058b947e86d --- /dev/null +++ b/packages/protocol/contracts-0.8/common/PrecompilesOverride.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity >=0.8.7 <0.8.20; + +import "../../contracts/common/interfaces/ICeloVersionedContract.sol"; +import "../../contracts-0.8/common/IsL2Check.sol"; + +import "./UsingPrecompiles.sol"; +import "./UsingRegistry.sol"; + +/** + * @title PrecompilesOverride Contract + * @notice This contract allows for a smoother transition from L1 to L2 + * by abstracting away the usingPrecompile contract, and taking care of the L1 to L2 swtiching logic. + **/ +contract PrecompilesOverride is UsingPrecompiles, UsingRegistry { + /** + * @notice Returns the epoch number at a block. + * @param blockNumber Block number where epoch number is calculated. + * @return Epoch number. + */ + function getEpochNumberOfBlock(uint256 blockNumber) public view override returns (uint256) { + if (isL2()) { + return getEpochManager().getEpochNumberOfBlock(blockNumber); + } else { + return epochNumberOfBlock(blockNumber, getEpochSize()); + } + } + + /** + * @notice Returns the epoch number at a block. + * @return Current epoch number. + */ + function getEpochNumber() public view override returns (uint256) { + return getEpochNumberOfBlock(block.number); + } + + /** + * @notice Gets a validator signer address from the current validator set. + * @param index Index of requested validator in the validator set. + * @return Address of validator signer at the requested index. + */ + function validatorSignerAddressFromCurrentSet( + uint256 index + ) public view override returns (address) { + if (isL2()) { + return getEpochManager().getElectedSignerByIndex(index); + } else { + super.validatorSignerAddressFromCurrentSet(index); + } + } + + /** + * @notice Gets a validator address from the current validator set. + * @param index Index of requested validator in the validator set. + * @return Address of validator at the requested index. + */ + + function validatorAddressFromCurrentSet(uint256 index) public view onlyL2 returns (address) { + return getEpochManager().getElectedAccountByIndex(index); + } + + /** + * @notice Gets the size of the current elected validator set. + * @return Size of the current elected validator set. + */ + function numberValidatorsInCurrentSet() public view override returns (uint256) { + if (isL2()) { + return getEpochManager().numberOfElectedInCurrentSet(); + } else { + return super.numberValidatorsInCurrentSet(); + } + } +} diff --git a/packages/protocol/contracts-0.8/common/UsingPrecompiles.sol b/packages/protocol/contracts-0.8/common/UsingPrecompiles.sol index 261c7f59d53..b575953304b 100644 --- a/packages/protocol/contracts-0.8/common/UsingPrecompiles.sol +++ b/packages/protocol/contracts-0.8/common/UsingPrecompiles.sol @@ -31,6 +31,7 @@ contract UsingPrecompiles is IsL2Check { * @param _decimals precision * @return Numerator of the computed quantity (not reduced). * @return Denominator of the computed quantity (not reduced). + * @dev This function will be deprecated in L2. */ function fractionMulExp( uint256 aNumerator, @@ -39,7 +40,7 @@ contract UsingPrecompiles is IsL2Check { uint256 bDenominator, uint256 exponent, uint256 _decimals - ) public view returns (uint256, uint256) { + ) public view onlyL1 returns (uint256, uint256) { require(aDenominator != 0 && bDenominator != 0, "a denominator is zero"); uint256 returnNumerator; uint256 returnDenominator; @@ -57,9 +58,9 @@ contract UsingPrecompiles is IsL2Check { /** * @notice Returns the current epoch size in blocks. * @return The current epoch size in blocks. + * @dev This function will be deprecated in L2. */ - function getEpochSize() public view returns (uint256) { - allowOnlyL1(); + function getEpochSize() public view onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = EPOCH_SIZE.staticcall(abi.encodePacked(true)); @@ -71,27 +72,30 @@ contract UsingPrecompiles is IsL2Check { * @notice Returns the epoch number at a block. * @param blockNumber Block number where epoch number is calculated. * @return Epoch number. + * @dev This function will be deprecated in L2. */ - function getEpochNumberOfBlock(uint256 blockNumber) public view returns (uint256) { + function getEpochNumberOfBlock(uint256 blockNumber) public view virtual onlyL1 returns (uint256) { return epochNumberOfBlock(blockNumber, getEpochSize()); } /** * @notice Returns the epoch number at a block. * @return Current epoch number. + * @dev This function will be deprecated in L2. */ - function getEpochNumber() public view returns (uint256) { + function getEpochNumber() public view virtual onlyL1 returns (uint256) { return getEpochNumberOfBlock(block.number); } /** - * @notice Gets a validator address from the current validator set. + * @notice Gets a validator signer address from the current validator set. * @param index Index of requested validator in the validator set. - * @return Address of validator at the requested index. + * @return Address of validator signer at the requested index. + * @dev This function will be deprecated in L2. */ function validatorSignerAddressFromCurrentSet( uint256 index - ) public view virtual returns (address) { + ) public view virtual onlyL1 returns (address) { bytes memory out; bool success; (success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, uint256(block.number))); @@ -100,15 +104,16 @@ contract UsingPrecompiles is IsL2Check { } /** - * @notice Gets a validator address from the validator set at the given block number. + * @notice Gets a validator signer address from the validator set at the given block number. * @param index Index of requested validator in the validator set. * @param blockNumber Block number to retrieve the validator set from. - * @return Address of validator at the requested index. + * @return Address of validator signer at the requested index. + * @dev This function will be deprecated in L2. */ function validatorSignerAddressFromSet( uint256 index, uint256 blockNumber - ) public view returns (address) { + ) public view virtual onlyL1 returns (address) { bytes memory out; bool success; (success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, blockNumber)); @@ -119,8 +124,9 @@ contract UsingPrecompiles is IsL2Check { /** * @notice Gets the size of the current elected validator set. * @return Size of the current elected validator set. + * @dev This function will be deprecated in L2. */ - function numberValidatorsInCurrentSet() public view virtual returns (uint256) { + function numberValidatorsInCurrentSet() public view virtual onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(uint256(block.number))); @@ -132,8 +138,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Gets the size of the validator set that must sign the given block number. * @param blockNumber Block number to retrieve the validator set from. * @return Size of the validator set. + * @dev This function will be deprecated in L2. */ - function numberValidatorsInSet(uint256 blockNumber) public view virtual returns (uint256) { + function numberValidatorsInSet(uint256 blockNumber) public view virtual onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(blockNumber)); @@ -149,12 +156,13 @@ contract UsingPrecompiles is IsL2Check { * @param blsPop The BLS public key proof-of-possession, which consists of a signature on the * account address. 96 bytes. * @return True upon success. + * @dev This function will be deprecated in L2. */ function checkProofOfPossession( address sender, bytes memory blsKey, bytes memory blsPop - ) public view returns (bool) { + ) public view onlyL1 returns (bool) { bool success; (success, ) = PROOF_OF_POSSESSION.staticcall(abi.encodePacked(sender, blsKey, blsPop)); return success; @@ -164,8 +172,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Parses block number out of header. * @param header RLP encoded header * @return Block number. + * @dev This function will be deprecated in L2. */ - function getBlockNumberFromHeader(bytes memory header) public view returns (uint256) { + function getBlockNumberFromHeader(bytes memory header) public view onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = BLOCK_NUMBER_FROM_HEADER.staticcall(abi.encodePacked(header)); @@ -177,8 +186,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Computes hash of header. * @param header RLP encoded header * @return Header hash. + * @dev This function will be deprecated in L2. */ - function hashHeader(bytes memory header) public view returns (bytes32) { + function hashHeader(bytes memory header) public view onlyL1 returns (bytes32) { bytes memory out; bool success; (success, out) = HASH_HEADER.staticcall(abi.encodePacked(header)); @@ -190,8 +200,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Gets the parent seal bitmap from the header at the given block number. * @param blockNumber Block number to retrieve. Must be within 4 epochs of the current number. * @return Bitmap parent seal with set bits at indices corresponding to signing validators. + * @dev This function will be deprecated in L2. */ - function getParentSealBitmap(uint256 blockNumber) public view returns (bytes32) { + function getParentSealBitmap(uint256 blockNumber) public view onlyL1 returns (bytes32) { bytes memory out; bool success; (success, out) = GET_PARENT_SEAL_BITMAP.staticcall(abi.encodePacked(blockNumber)); @@ -205,8 +216,11 @@ contract UsingPrecompiles is IsL2Check { * header. If the parent hash is not in the blockchain, verification fails. * @param header RLP encoded header * @return Bitmap parent seal with set bits at indices correspoinding to signing validators. + * @dev This function will be deprecated in L2. */ - function getVerifiedSealBitmapFromHeader(bytes memory header) public view returns (bytes32) { + function getVerifiedSealBitmapFromHeader( + bytes memory header + ) public view onlyL1 returns (bytes32) { bytes memory out; bool success; (success, out) = GET_VERIFIED_SEAL_BITMAP.staticcall(abi.encodePacked(header)); @@ -217,16 +231,18 @@ contract UsingPrecompiles is IsL2Check { /** * @notice Returns the minimum number of required signers for a given block number. * @dev Computed in celo-blockchain as int(math.Ceil(float64(2*valSet.Size()) / 3)) + * @dev This function will be deprecated in L2. */ - function minQuorumSize(uint256 blockNumber) public view returns (uint256) { + function minQuorumSize(uint256 blockNumber) public view onlyL1 returns (uint256) { return numberValidatorsInSet(blockNumber).mul(2).add(2).div(3); } /** * @notice Computes byzantine quorum from current validator set size * @return Byzantine quorum of validators. + * @dev This function will be deprecated in L2. */ - function minQuorumSizeInCurrentSet() public view returns (uint256) { + function minQuorumSizeInCurrentSet() public view onlyL1 returns (uint256) { return minQuorumSize(block.number); } diff --git a/packages/protocol/contracts-0.8/common/test/MockEpochManager.sol b/packages/protocol/contracts-0.8/common/test/MockEpochManager.sol index d110f6a2271..a42bbaaa30a 100644 --- a/packages/protocol/contracts-0.8/common/test/MockEpochManager.sol +++ b/packages/protocol/contracts-0.8/common/test/MockEpochManager.sol @@ -9,6 +9,11 @@ import "../../../contracts/common/interfaces/IEpochManager.sol"; */ contract MockEpochManager is IEpochManager { + enum EpochProcessStatus { + NotStarted, + Started + } + struct Epoch { uint256 firstBlock; uint256 lastBlock; @@ -17,16 +22,26 @@ contract MockEpochManager is IEpochManager { uint256 rewardsBlock; } + struct EpochProcessState { + EpochProcessStatus status; + uint256 perValidatorReward; // The per validator epoch reward. + uint256 totalRewardsVoter; // The total rewards to voters. + uint256 totalRewardsCommunity; // The total community reward. + uint256 totalRewardsCarbonFund; // The total carbon offsetting partner reward. + } + uint256 public epochDuration; uint256 public firstKnownEpoch; uint256 private currentEpochNumber; - address[] public elected; + address[] public electedAccounts; + address[] public electedSigners; address public epochManagerEnabler; bool systemInitialized; bool private _isTimeForNextEpoch; bool private isProcessingEpoch; + EpochProcessState public epochProcessing; mapping(uint256 => Epoch) private epochs; event SendValidatorPaymentCalled(address validator); @@ -47,7 +62,8 @@ contract MockEpochManager is IEpochManager { _currentEpoch.firstBlock = firstEpochBlock; _currentEpoch.startTimestamp = block.timestamp; - elected = firstElected; + electedAccounts = firstElected; + electedSigners = firstElected; systemInitialized = true; epochManagerEnabler = address(0); @@ -58,7 +74,16 @@ contract MockEpochManager is IEpochManager { address[] calldata groups, address[] calldata lessers, address[] calldata greaters - ) external {} + ) external { + epochs[currentEpochNumber].lastBlock = block.number - 1; + + currentEpochNumber++; + epochs[currentEpochNumber].firstBlock = block.number; + epochs[currentEpochNumber].startTimestamp = block.timestamp; + + EpochProcessState memory _epochProcessingEmpty; + epochProcessing = _epochProcessingEmpty; + } function setIsTimeForNextEpoch(bool _isTime) external { _isTimeForNextEpoch = _isTime; @@ -68,16 +93,23 @@ contract MockEpochManager is IEpochManager { } function getCurrentEpoch() external view returns (uint256, uint256, uint256, uint256) { - Epoch storage _epoch = epochs[currentEpochNumber]; - - return (_epoch.firstBlock, _epoch.lastBlock, _epoch.startTimestamp, _epoch.rewardsBlock); + return getEpochByNumber(currentEpochNumber); } function getCurrentEpochNumber() external view returns (uint256) { return currentEpochNumber; } - function getElected() external view returns (address[] memory) { - return elected; + + function numberOfElectedInCurrentSet() external view returns (uint256) { + return electedAccounts.length; + } + + function getElectedAccounts() external view returns (address[] memory) { + return electedAccounts; + } + + function getElectedAccountByIndex(uint256 index) external view returns (address) { + return electedAccounts[index]; } function getFirstBlockAtEpoch(uint256 _epoch) external view returns (uint256) { @@ -114,7 +146,76 @@ contract MockEpochManager is IEpochManager { return isProcessingEpoch; } + function getEpochByBlockNumber( + uint256 _blockNumber + ) external view returns (uint256, uint256, uint256, uint256) { + return (0, 0, 0, 0); + } + + function getEpochNumberOfBlock(uint256 _blockNumber) external view returns (uint256) { + (uint256 _epochNumber, , , , ) = _getEpochByBlockNumber(_blockNumber); + return _epochNumber; + } + + function getElectedSigners() external view returns (address[] memory) { + return electedSigners; + } + + function getElectedSignerByIndex(uint256 index) external view returns (address) { + return electedSigners[index]; + } + function sendValidatorPayment(address validator) public { emit SendValidatorPaymentCalled(validator); } + + function getEpochByNumber( + uint256 epochNumber + ) public view returns (uint256, uint256, uint256, uint256) { + Epoch storage _epoch = epochs[epochNumber]; + return (_epoch.firstBlock, _epoch.lastBlock, _epoch.startTimestamp, _epoch.rewardsBlock); + } + + function _getEpochByBlockNumber( + uint256 _blockNumber + ) internal view returns (uint256, uint256, uint256, uint256, uint256) { + require(_blockNumber <= block.number, "Invalid blockNumber. Value too high."); + (uint256 _firstBlockOfFirstEpoch, , , ) = getEpochByNumber(firstKnownEpoch); + require(_blockNumber >= _firstBlockOfFirstEpoch, "Invalid blockNumber. Value too low."); + uint256 _firstBlockOfCurrentEpoch = epochs[currentEpochNumber].firstBlock; + + if (_blockNumber >= _firstBlockOfCurrentEpoch) { + ( + uint256 _firstBlock, + uint256 _lastBlock, + uint256 _startTimestamp, + uint256 _rewardsBlock + ) = getEpochByNumber(currentEpochNumber); + return (currentEpochNumber, _firstBlock, _lastBlock, _startTimestamp, _rewardsBlock); + } + + uint256 left = firstKnownEpoch; + uint256 right = currentEpochNumber - 1; + + while (left <= right) { + uint256 mid = (left + right) / 2; + Epoch memory _epoch = epochs[mid]; + + if (_blockNumber >= _epoch.firstBlock && _blockNumber <= _epoch.lastBlock) { + return ( + mid, + _epoch.firstBlock, + _epoch.lastBlock, + _epoch.startTimestamp, + _epoch.rewardsBlock + ); + } else if (_blockNumber < _epoch.firstBlock) { + right = mid - 1; + } else { + left = mid + 1; + } + } + + revert("No matching epoch found for the given block number."); + } } diff --git a/packages/protocol/contracts-0.8/governance/Validators.sol b/packages/protocol/contracts-0.8/governance/Validators.sol index 49653c05bcc..6d03bb2021a 100644 --- a/packages/protocol/contracts-0.8/governance/Validators.sol +++ b/packages/protocol/contracts-0.8/governance/Validators.sol @@ -13,7 +13,7 @@ import "../../contracts/common/Initializable.sol"; import "../../contracts/common/FixidityLib.sol"; import "../common/linkedlists/AddressLinkedList.sol"; import "../common/UsingRegistry.sol"; -import "../common/UsingPrecompiles.sol"; +import "../common/PrecompilesOverride.sol"; import "../../contracts/common/interfaces/ICeloVersionedContract.sol"; import "../../contracts/common/libraries/ReentrancyGuard.sol"; import "../common/interfaces/IStableToken.sol"; @@ -30,7 +30,7 @@ contract Validators is ReentrancyGuard, Initializable, UsingRegistry, - UsingPrecompiles, + PrecompilesOverride, CalledByVm { using FixidityLib for FixidityLib.Fraction; @@ -845,7 +845,7 @@ contract Validators is uint256 index ) external view returns (address) { require(isValidator(account), "Not a validator"); - require(epochNumber <= _getEpochNumber(), "Epoch cannot be larger than current"); + require(epochNumber <= getEpochNumber(), "Epoch cannot be larger than current"); MembershipHistory storage history = validators[account].membershipHistory; require(index < history.tail.add(history.numEntries), "index out of bounds"); require(index >= history.tail && history.numEntries > 0, "index out of bounds"); @@ -1116,7 +1116,7 @@ contract Validators is * @return The group that `account` was a member of at the end of the last epoch. */ function getMembershipInLastEpoch(address account) public view returns (address) { - uint256 epochNumber = _getEpochNumber(); + uint256 epochNumber = getEpochNumber(); MembershipHistory storage history = validators[account].membershipHistory; uint256 head = history.numEntries == 0 ? 0 : history.tail.add(history.numEntries.sub(1)); @@ -1448,7 +1448,7 @@ contract Validators is */ function updateMembershipHistory(address account, address group) private returns (bool) { MembershipHistory storage history = validators[account].membershipHistory; - uint256 epochNumber = _getEpochNumber(); + uint256 epochNumber = getEpochNumber(); uint256 head = history.numEntries == 0 ? 0 : history.tail.add(history.numEntries.sub(1)); @@ -1533,16 +1533,4 @@ contract Validators is _sendValidatorPaymentIfNecessary(members[i]); } } - - /** - * @notice Returns the epoch number. - * @return Current epoch number. - */ - function _getEpochNumber() private view returns (uint256) { - if (isL2()) { - return getEpochManager().getCurrentEpochNumber(); - } else { - return getEpochNumber(); - } - } } diff --git a/packages/protocol/contracts-0.8/governance/test/EpochRewardsMock.sol b/packages/protocol/contracts-0.8/governance/test/EpochRewardsMock.sol index 586e44273ad..e8557b05758 100644 --- a/packages/protocol/contracts-0.8/governance/test/EpochRewardsMock.sol +++ b/packages/protocol/contracts-0.8/governance/test/EpochRewardsMock.sol @@ -19,13 +19,11 @@ contract EpochRewardsMock08 is IEpochRewards { numValidatorsInCurrentSet = value; } - // TODO: (soloseng) implement mock function updateTargetVotingYield() external {} function getRewardsMultiplier( uint256 targetGoldTotalSupplyIncrease ) external view returns (uint256) { - // return _getRewardsMultiplier(targetGoldTotalSupplyIncrease).unwrap(); return 0; } diff --git a/packages/protocol/contracts/common/PrecompilesOverride.sol b/packages/protocol/contracts/common/PrecompilesOverride.sol new file mode 100644 index 00000000000..262152a55b4 --- /dev/null +++ b/packages/protocol/contracts/common/PrecompilesOverride.sol @@ -0,0 +1,70 @@ +pragma solidity ^0.5.13; + +import "./interfaces/ICeloVersionedContract.sol"; +import "../../contracts-0.8/common/IsL2Check.sol"; +import "./UsingRegistry.sol"; + +import "./UsingPrecompiles.sol"; + +/** + * @title PrecompilesOverride Contract + * @notice This contract allows for a smoother transition from L1 to L2 + * by abstracting away the usingPrecompile contract, and taking care of the L1 to L2 switching logic. + **/ +contract PrecompilesOverride is UsingPrecompiles, UsingRegistry { + /** + * @notice Returns the epoch number at a block. + * @param blockNumber Block number where epoch number is calculated. + * @return Epoch number. + */ + function getEpochNumberOfBlock(uint256 blockNumber) public view returns (uint256) { + if (isL2()) { + return getEpochManager().getEpochNumberOfBlock(blockNumber); + } else { + return epochNumberOfBlock(blockNumber, getEpochSize()); + } + } + + /** + * @notice Returns the epoch number at a block. + * @return Current epoch number. + */ + function getEpochNumber() public view returns (uint256) { + return getEpochNumberOfBlock(block.number); + } + + /** + * @notice Gets a validator signer address from the current validator set. + * @param index Index of requested validator in the validator set. + * @return Address of validator signer at the requested index. + */ + function validatorSignerAddressFromCurrentSet(uint256 index) public view returns (address) { + if (isL2()) { + return getEpochManager().getElectedSignerByIndex(index); + } else { + super.validatorSignerAddressFromCurrentSet(index); + } + } + + /** + * @notice Gets a validator address from the current validator set. + * @param index Index of requested validator in the validator set. + * @return Address of validator at the requested index. + */ + + function validatorAddressFromCurrentSet(uint256 index) public view onlyL2 returns (address) { + return getEpochManager().getElectedAccountByIndex(index); + } + + /** + * @notice Gets the size of the current elected validator set. + * @return Size of the current elected validator set. + */ + function numberValidatorsInCurrentSet() public view returns (uint256) { + if (isL2()) { + return getEpochManager().numberOfElectedInCurrentSet(); + } else { + return super.numberValidatorsInCurrentSet(); + } + } +} diff --git a/packages/protocol/contracts/common/UsingPrecompiles.sol b/packages/protocol/contracts/common/UsingPrecompiles.sol index f20d5db08f9..62c9d72ea86 100644 --- a/packages/protocol/contracts/common/UsingPrecompiles.sol +++ b/packages/protocol/contracts/common/UsingPrecompiles.sol @@ -2,6 +2,7 @@ pragma solidity ^0.5.13; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; +import "../common/interfaces/IEpochManager.sol"; import "../../contracts-0.8/common/IsL2Check.sol"; contract UsingPrecompiles is IsL2Check { @@ -29,6 +30,7 @@ contract UsingPrecompiles is IsL2Check { * @param _decimals precision * @return Numerator of the computed quantity (not reduced). * @return Denominator of the computed quantity (not reduced). + * @dev This function will be deprecated in L2. */ function fractionMulExp( uint256 aNumerator, @@ -37,7 +39,7 @@ contract UsingPrecompiles is IsL2Check { uint256 bDenominator, uint256 exponent, uint256 _decimals - ) public view returns (uint256, uint256) { + ) public view onlyL1 returns (uint256, uint256) { require(aDenominator != 0 && bDenominator != 0, "a denominator is zero"); uint256 returnNumerator; uint256 returnDenominator; @@ -55,9 +57,9 @@ contract UsingPrecompiles is IsL2Check { /** * @notice Returns the current epoch size in blocks. * @return The current epoch size in blocks. + * @dev This function will be deprecated in L2. */ - function getEpochSize() public view returns (uint256) { - allowOnlyL1(); + function getEpochSize() public view onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = EPOCH_SIZE.staticcall(abi.encodePacked(true)); @@ -69,54 +71,61 @@ contract UsingPrecompiles is IsL2Check { * @notice Returns the epoch number at a block. * @param blockNumber Block number where epoch number is calculated. * @return Epoch number. + * @dev This function will be deprecated in L2. */ - function getEpochNumberOfBlock(uint256 blockNumber) public view returns (uint256) { + function getEpochNumberOfBlock(uint256 blockNumber) public view onlyL1 returns (uint256) { return epochNumberOfBlock(blockNumber, getEpochSize()); } /** * @notice Returns the epoch number at a block. * @return Current epoch number. + * @dev This function will be deprecated in L2. */ - function getEpochNumber() public view returns (uint256) { + function getEpochNumber() public view onlyL1 returns (uint256) { return getEpochNumberOfBlock(block.number); } /** - * @notice Gets a validator address from the current validator set. + * @notice Gets a validator signer address from the current validator set. * @param index Index of requested validator in the validator set. - * @return Address of validator at the requested index. + * @return Address of validator signer at the requested index. + * @dev This function will be deprecated in L2. */ - function validatorSignerAddressFromCurrentSet(uint256 index) public view returns (address) { + function validatorSignerAddressFromCurrentSet( + uint256 index + ) public view onlyL1 returns (address) { bytes memory out; bool success; (success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, uint256(block.number))); require(success, "error calling validatorSignerAddressFromCurrentSet precompile"); - return address(getUint256FromBytes(out, 0)); + return address(uint160(getUint256FromBytes(out, 0))); } /** - * @notice Gets a validator address from the validator set at the given block number. + * @notice Gets a validator signer address from the validator set at the given block number. * @param index Index of requested validator in the validator set. * @param blockNumber Block number to retrieve the validator set from. - * @return Address of validator at the requested index. + * @return Address of validator signer at the requested index. + * @dev This function will be deprecated in L2. */ function validatorSignerAddressFromSet( uint256 index, uint256 blockNumber - ) public view returns (address) { + ) public view onlyL1 returns (address) { bytes memory out; bool success; (success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, blockNumber)); require(success, "error calling validatorSignerAddressFromSet precompile"); - return address(getUint256FromBytes(out, 0)); + return address(uint160(getUint256FromBytes(out, 0))); } /** * @notice Gets the size of the current elected validator set. * @return Size of the current elected validator set. + * @dev This function will be deprecated in L2. */ - function numberValidatorsInCurrentSet() public view returns (uint256) { + function numberValidatorsInCurrentSet() public view onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(uint256(block.number))); @@ -128,8 +137,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Gets the size of the validator set that must sign the given block number. * @param blockNumber Block number to retrieve the validator set from. * @return Size of the validator set. + * @dev This function will be deprecated in L2. */ - function numberValidatorsInSet(uint256 blockNumber) public view returns (uint256) { + function numberValidatorsInSet(uint256 blockNumber) public view onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(blockNumber)); @@ -145,12 +155,13 @@ contract UsingPrecompiles is IsL2Check { * @param blsPop The BLS public key proof-of-possession, which consists of a signature on the * account address. 96 bytes. * @return True upon success. + * @dev This function will be deprecated in L2. */ function checkProofOfPossession( address sender, bytes memory blsKey, bytes memory blsPop - ) public view returns (bool) { + ) public view onlyL1 returns (bool) { bool success; (success, ) = PROOF_OF_POSSESSION.staticcall(abi.encodePacked(sender, blsKey, blsPop)); return success; @@ -160,8 +171,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Parses block number out of header. * @param header RLP encoded header * @return Block number. + * @dev This function will be deprecated in L2. */ - function getBlockNumberFromHeader(bytes memory header) public view returns (uint256) { + function getBlockNumberFromHeader(bytes memory header) public view onlyL1 returns (uint256) { bytes memory out; bool success; (success, out) = BLOCK_NUMBER_FROM_HEADER.staticcall(abi.encodePacked(header)); @@ -173,8 +185,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Computes hash of header. * @param header RLP encoded header * @return Header hash. + * @dev This function will be deprecated in L2. */ - function hashHeader(bytes memory header) public view returns (bytes32) { + function hashHeader(bytes memory header) public view onlyL1 returns (bytes32) { bytes memory out; bool success; (success, out) = HASH_HEADER.staticcall(abi.encodePacked(header)); @@ -186,8 +199,9 @@ contract UsingPrecompiles is IsL2Check { * @notice Gets the parent seal bitmap from the header at the given block number. * @param blockNumber Block number to retrieve. Must be within 4 epochs of the current number. * @return Bitmap parent seal with set bits at indices corresponding to signing validators. + * @dev This function will be deprecated in L2. */ - function getParentSealBitmap(uint256 blockNumber) public view returns (bytes32) { + function getParentSealBitmap(uint256 blockNumber) public view onlyL1 returns (bytes32) { bytes memory out; bool success; (success, out) = GET_PARENT_SEAL_BITMAP.staticcall(abi.encodePacked(blockNumber)); @@ -201,8 +215,11 @@ contract UsingPrecompiles is IsL2Check { * header. If the parent hash is not in the blockchain, verification fails. * @param header RLP encoded header * @return Bitmap parent seal with set bits at indices correspoinding to signing validators. + * @dev This function will be deprecated in L2. */ - function getVerifiedSealBitmapFromHeader(bytes memory header) public view returns (bytes32) { + function getVerifiedSealBitmapFromHeader( + bytes memory header + ) public view onlyL1 returns (bytes32) { bytes memory out; bool success; (success, out) = GET_VERIFIED_SEAL_BITMAP.staticcall(abi.encodePacked(header)); @@ -213,16 +230,18 @@ contract UsingPrecompiles is IsL2Check { /** * @notice Returns the minimum number of required signers for a given block number. * @dev Computed in celo-blockchain as int(math.Ceil(float64(2*valSet.Size()) / 3)) + * @dev This function will be deprecated in L2. */ - function minQuorumSize(uint256 blockNumber) public view returns (uint256) { + function minQuorumSize(uint256 blockNumber) public view onlyL1 returns (uint256) { return numberValidatorsInSet(blockNumber).mul(2).add(2).div(3); } /** * @notice Computes byzantine quorum from current validator set size * @return Byzantine quorum of validators. + * @dev This function will be deprecated in L2. */ - function minQuorumSizeInCurrentSet() public view returns (uint256) { + function minQuorumSizeInCurrentSet() public view onlyL1 returns (uint256) { return minQuorumSize(block.number); } diff --git a/packages/protocol/contracts/common/interfaces/IEpochManager.sol b/packages/protocol/contracts/common/interfaces/IEpochManager.sol index 343039256cc..ccad6d3a1c5 100644 --- a/packages/protocol/contracts/common/interfaces/IEpochManager.sol +++ b/packages/protocol/contracts/common/interfaces/IEpochManager.sol @@ -15,15 +15,25 @@ interface IEpochManager { ) external; function sendValidatorPayment(address) external; function getCurrentEpoch() external view returns (uint256, uint256, uint256, uint256); + function getEpochByNumber( + uint256 epochNumber + ) external view returns (uint256, uint256, uint256, uint256); + function getEpochByBlockNumber( + uint256 blockNumber + ) external view returns (uint256, uint256, uint256, uint256); + function getEpochNumberOfBlock(uint256) external view returns (uint256); function getCurrentEpochNumber() external view returns (uint256); - function getElected() external view returns (address[] memory); + function numberOfElectedInCurrentSet() external view returns (uint256); + function getElectedAccounts() external view returns (address[] memory); + function getElectedAccountByIndex(uint256 index) external view returns (address); + function getElectedSigners() external view returns (address[] memory); + function getElectedSignerByIndex(uint256 index) external view returns (address); function epochDuration() external view returns (uint256); function firstKnownEpoch() external view returns (uint256); function getEpochProcessingState() external view returns (uint256, uint256, uint256, uint256, uint256); - function systemAlreadyInitialized() external view returns (bool); function isBlocked() external view returns (bool); function isTimeForNextEpoch() external view returns (bool); diff --git a/packages/protocol/contracts/governance/Election.sol b/packages/protocol/contracts/governance/Election.sol index 5e1c15c4684..f95e2ead23a 100644 --- a/packages/protocol/contracts/governance/Election.sol +++ b/packages/protocol/contracts/governance/Election.sol @@ -10,12 +10,12 @@ import "../common/CalledByVm.sol"; import "../common/Initializable.sol"; import "../common/FixidityLib.sol"; import "../common/linkedlists/AddressSortedLinkedList.sol"; -import "../common/UsingPrecompiles.sol"; import "../common/UsingRegistry.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; import "../common/libraries/Heap.sol"; import "../common/libraries/ReentrancyGuard.sol"; import "../common/Blockable.sol"; +import "../common/PrecompilesOverride.sol"; contract Election is IElection, @@ -24,7 +24,7 @@ contract Election is ReentrancyGuard, Initializable, UsingRegistry, - UsingPrecompiles, + PrecompilesOverride, CalledByVm, Blockable { @@ -637,7 +637,7 @@ contract Election is */ function hasActivatablePendingVotes(address account, address group) external view returns (bool) { PendingVote storage pendingVote = votes.pending.forGroup[group].byAccount[account]; - return pendingVote.epoch < _getEpochNumber() && pendingVote.value > 0; + return pendingVote.epoch < getEpochNumber() && pendingVote.value > 0; } /** @@ -1008,7 +1008,7 @@ contract Election is function _activate(address group, address account) internal onlyWhenNotBlocked returns (bool) { PendingVote storage pendingVote = votes.pending.forGroup[group].byAccount[account]; - require(pendingVote.epoch < _getEpochNumber(), "Pending vote epoch not passed"); + require(pendingVote.epoch < getEpochNumber(), "Pending vote epoch not passed"); uint256 value = pendingVote.value; require(value > 0, "Vote value cannot be zero"); @@ -1157,7 +1157,7 @@ contract Election is PendingVote storage pendingVote = groupPending.byAccount[account]; pendingVote.value = pendingVote.value.add(value); - pendingVote.epoch = _getEpochNumber(); + pendingVote.epoch = getEpochNumber(); } /** @@ -1278,17 +1278,4 @@ contract Election is value.mul(votes.active.forGroup[group].total).div(votes.active.forGroup[group].totalUnits); } } - - /** - * @notice Returns the epoch number. - * @return Current epoch number. - */ - function _getEpochNumber() private view returns (uint256) { - // TODO remove this after L2 is fully implemented - if (isL2()) { - return getEpochManager().getCurrentEpochNumber(); - } else { - return getEpochNumber(); - } - } } diff --git a/packages/protocol/contracts/governance/EpochRewards.sol b/packages/protocol/contracts/governance/EpochRewards.sol index c55d3d74335..63d5495beba 100644 --- a/packages/protocol/contracts/governance/EpochRewards.sol +++ b/packages/protocol/contracts/governance/EpochRewards.sol @@ -8,7 +8,7 @@ import "../common/FixidityLib.sol"; import "../common/Freezable.sol"; import "../common/Initializable.sol"; import "../common/UsingRegistry.sol"; -import "../common/UsingPrecompiles.sol"; +import "../common/PrecompilesOverride.sol"; import "../common/interfaces/ICeloToken.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; @@ -20,8 +20,8 @@ contract EpochRewards is IEpochRewards, Ownable, Initializable, - UsingPrecompiles, UsingRegistry, + PrecompilesOverride, Freezable { using FixidityLib for FixidityLib.Fraction; @@ -453,9 +453,11 @@ contract EpochRewards is (uint256 numerator, uint256 denominator) = getSortedOracles().medianRate(stableTokenAddress); if (isL2()) { return - getEpochManager().getElected().length.mul(targetValidatorEpochPayment).mul(denominator).div( - numerator - ); + getEpochManager() + .numberOfElectedInCurrentSet() + .mul(targetValidatorEpochPayment) + .mul(denominator) + .div(numerator); } return numberValidatorsInCurrentSet().mul(targetValidatorEpochPayment).mul(denominator).div( diff --git a/packages/protocol/contracts/governance/Governance.sol b/packages/protocol/contracts/governance/Governance.sol index 0d6fe070e6f..e03ac17459d 100644 --- a/packages/protocol/contracts/governance/Governance.sol +++ b/packages/protocol/contracts/governance/Governance.sol @@ -13,7 +13,7 @@ import "../common/Initializable.sol"; import "../common/FixidityLib.sol"; import "../common/linkedlists/IntegerSortedLinkedList.sol"; import "../common/UsingRegistry.sol"; -import "../common/UsingPrecompiles.sol"; +import "../common/PrecompilesOverride.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; import "../common/libraries/ReentrancyGuard.sol"; @@ -27,7 +27,7 @@ contract Governance is Initializable, ReentrancyGuard, UsingRegistry, - UsingPrecompiles + PrecompilesOverride { using Proposals for Proposals.Proposal; using FixidityLib for FixidityLib.Fraction; diff --git a/packages/protocol/test-sol/devchain/e2e/common/EpochManager.t.sol b/packages/protocol/test-sol/devchain/e2e/common/EpochManager.t.sol index 077dff4c008..ba0fa13dd7f 100644 --- a/packages/protocol/test-sol/devchain/e2e/common/EpochManager.t.sol +++ b/packages/protocol/test-sol/devchain/e2e/common/EpochManager.t.sol @@ -238,7 +238,7 @@ contract E2E_EpochManager is Test, Devchain, Utils08, ECDSAHelper08 { } function getValidatorGroupsFromElected() internal returns (address[] memory) { - address[] memory elected = epochManager.getElected(); + address[] memory elected = epochManager.getElectedAccounts(); address[] memory validatorGroups = new address[](elected.length); for (uint256 i = 0; i < elected.length; i++) { (, , address group, , ) = validators.getValidator(elected[i]); @@ -303,7 +303,7 @@ contract E2E_EpochManager is Test, Devchain, Utils08, ECDSAHelper08 { } function getCurrentlyElectedGroups() internal returns (address[] memory) { - address[] memory currentlyElected = epochManager.getElected(); + address[] memory currentlyElected = epochManager.getElectedAccounts(); // clearElectedGroupsHelper(); for (uint256 i = 0; i < currentlyElected.length; i++) { @@ -484,7 +484,7 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { (lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups); uint256 currentEpoch = epochManager.getCurrentEpochNumber(); - address[] memory currentlyElected = epochManager.getElected(); + address[] memory currentlyElected = epochManager.getElectedAccounts(); for (uint256 i = 0; i < currentlyElected.length; i++) { originalyElected.add(currentlyElected[i]); } @@ -497,7 +497,7 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { assertEq(currentEpoch + 1, epochManager.getCurrentEpochNumber()); - address[] memory newlyElected = epochManager.getElected(); + address[] memory newlyElected = epochManager.getElectedAccounts(); for (uint256 i = 0; i < currentlyElected.length; i++) { assertEq(originalyElected.contains(currentlyElected[i]), true); @@ -516,7 +516,7 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { assertEq(currentEpoch + 2, epochManager.getCurrentEpochNumber()); - address[] memory newlyElected2 = epochManager.getElected(); + address[] memory newlyElected2 = epochManager.getElectedAccounts(); for (uint256 i = 0; i < currentlyElected.length; i++) { assertEq(originalyElected.contains(newlyElected2[i]), true); @@ -541,7 +541,7 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { groups.push(newValidatorGroup); validatorsArray.push(newValidator); - assertEq(epochManager.getElected().length, validators.getRegisteredValidators().length); + assertEq(epochManager.getElectedAccounts().length, validators.getRegisteredValidators().length); assertEq(groups.length, validators.getRegisteredValidatorGroups().length); timeTravel(vm, epochDuration + 1); @@ -550,7 +550,7 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { epochManager.finishNextEpochProcess(groups, lessers, greaters); assertGroupWithVotes(groupWithVotes); - assertEq(epochManager.getElected().length, validatorsArray.length); + assertEq(epochManager.getElectedAccounts().length, validatorsArray.length); // lower the number of electable validators vm.prank(election.owner()); @@ -558,6 +558,7 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { timeTravel(vm, epochDuration + 1); epochManager.startNextEpochProcess(); + (lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups); epochManager.finishNextEpochProcess(groups, lessers, greaters); assertGroupWithVotes(groupWithVotes); @@ -570,12 +571,12 @@ contract E2E_EpochManager_FinishNextEpochProcess is E2E_EpochManager { uint256 totalRewardsCarbonFund ) = epochManager.getEpochProcessingState(); - assertGt(perValidatorReward, 0, "perValidatorReward"); - assertGt(totalRewardsVoter, 0, "totalRewardsVoter"); - assertGt(totalRewardsCommunity, 0, "totalRewardsCommunity"); - assertGt(totalRewardsCarbonFund, 0, "totalRewardsCarbonFund"); + assertEq(perValidatorReward, 0, "perValidatorReward"); + assertEq(totalRewardsVoter, 0, "totalRewardsVoter"); + assertEq(totalRewardsCommunity, 0, "totalRewardsCommunity"); + assertEq(totalRewardsCarbonFund, 0, "totalRewardsCarbonFund"); - assertEq(epochManager.getElected().length, validatorsArray.length - 1); + assertEq(epochManager.getElectedAccounts().length, validatorsArray.length - 1); } function clearElectedGroupsHelper() internal { @@ -626,7 +627,7 @@ contract E2E_GasTest_Setup is E2E_EpochManager { (lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups); uint256 currentEpoch = epochManager.getCurrentEpochNumber(); - address[] memory currentlyElected = epochManager.getElected(); + address[] memory currentlyElected = epochManager.getElectedAccounts(); for (uint256 i = 0; i < currentlyElected.length; i++) { originalyElected.add(currentlyElected[i]); } @@ -639,7 +640,7 @@ contract E2E_GasTest_Setup is E2E_EpochManager { assertEq(currentEpoch + 1, epochManager.getCurrentEpochNumber()); - address[] memory newlyElected = epochManager.getElected(); + address[] memory newlyElected = epochManager.getElectedAccounts(); for (uint256 i = 0; i < currentlyElected.length; i++) { assertEq(originalyElected.contains(currentlyElected[i]), true); @@ -658,7 +659,7 @@ contract E2E_GasTest_Setup is E2E_EpochManager { assertEq(currentEpoch + 2, epochManager.getCurrentEpochNumber()); - address[] memory newlyElected2 = epochManager.getElected(); + address[] memory newlyElected2 = epochManager.getElectedAccounts(); for (uint256 i = 0; i < currentlyElected.length; i++) { assertEq(originalyElected.contains(newlyElected2[i]), true); @@ -715,7 +716,7 @@ contract E2E_GasTest1_FinishNextEpochProcess is E2E_GasTest_Setup { console.log("validator groups: 120"); console.log("validators per group: 2"); console.log("finishNextEpochProcess gas used 2: ", gasLeftBefore1 - gasLeftAfter1); - console.log("elected count2: ", epochManager.getElected().length); + console.log("elected count2: ", epochManager.getElectedAccounts().length); } } @@ -742,6 +743,6 @@ contract E2E_GasTest2_FinishNextEpochProcess is E2E_GasTest_Setup { console.log("validator groups: 60"); console.log("validators per group: 2"); console.log("finishNextEpochProcess gas used 2: ", gasLeftBefore1 - gasLeftAfter1); - console.log("elected count2: ", epochManager.getElected().length); + console.log("elected count2: ", epochManager.getElectedAccounts().length); } } diff --git a/packages/protocol/test-sol/unit/common/EpochManager.t.sol b/packages/protocol/test-sol/unit/common/EpochManager.t.sol index a2808b24e94..beb3fb1443a 100644 --- a/packages/protocol/test-sol/unit/common/EpochManager.t.sol +++ b/packages/protocol/test-sol/unit/common/EpochManager.t.sol @@ -41,6 +41,7 @@ contract EpochManagerTest is Test, TestConstants, Utils08 { address communityRewardFund; address reserveAddress; address scoreManagerAddress; + address accountsAddress; uint256 firstEpochNumber = 100; uint256 firstEpochBlock = 100; @@ -96,6 +97,7 @@ contract EpochManagerTest is Test, TestConstants, Utils08 { firstElected.push(validator2); scoreManagerAddress = actor("scoreManagerAddress"); + accountsAddress = actor("accountsAddress"); reserveAddress = actor("reserve"); @@ -105,6 +107,7 @@ contract EpochManagerTest is Test, TestConstants, Utils08 { deployCodeTo("MockRegistry.sol", abi.encode(false), REGISTRY_ADDRESS); deployCodeTo("ScoreManager.sol", abi.encode(false), scoreManagerAddress); + deployCodeTo("Accounts.sol", abi.encode(false), accountsAddress); deployCodeTo("MockValidators.sol", abi.encode(false), address(validators)); registry = IRegistry(REGISTRY_ADDRESS); @@ -179,6 +182,21 @@ contract EpochManagerTest is Test, TestConstants, Utils08 { return (groups, lessers, greaters); } + + function _travelAndProcess_N_L2Epoch(uint256 n) public { + for (uint256 i = 0; i < n; i++) { + travelEpochL2(vm); + epochManager.startNextEpochProcess(); + + ( + address[] memory groups, + address[] memory lessers, + address[] memory greaters + ) = getGroupsWithLessersAndGreaters(); + + epochManager.finishNextEpochProcess(groups, lessers, greaters); + } + } } contract EpochManagerTest_initialize is EpochManagerTest { @@ -204,13 +222,13 @@ contract EpochManagerTest_initializeSystem is EpochManagerTest { uint256 _startTimestamp, uint256 _currentRewardsBlock ) = epochManager.getCurrentEpoch(); - assertGt(epochManager.getElected().length, 0); + assertGt(epochManager.getElectedAccounts().length, 0); assertEq(epochManager.firstKnownEpoch(), firstEpochNumber); assertEq(_firstEpochBlock, firstEpochBlock); assertEq(_lastEpochBlock, 0); assertEq(_startTimestamp, block.timestamp); assertEq(_currentRewardsBlock, 0); - assertEq(epochManager.getElected(), firstElected); + assertEq(epochManager.getElectedAccounts(), firstElected); } function test_Reverts_processCannotBeStartedAgain() public virtual { @@ -547,7 +565,7 @@ contract EpochManagerTest_finishNextEpochProcess is EpochManagerTest { vm.prank(epochManagerEnabler); initializeEpochManagerSystem(); - elected = epochManager.getElected(); + elected = epochManager.getElectedAccounts(); election.setGroupEpochRewardsBasedOnScore(group, groupEpochRewards); } @@ -617,7 +635,7 @@ contract EpochManagerTest_finishNextEpochProcess is EpochManagerTest { epochManager.finishNextEpochProcess(groups, lessers, greaters); - address[] memory afterElected = epochManager.getElected(); + address[] memory afterElected = epochManager.getElectedAccounts(); for (uint256 i = 0; i < newElected.length; i++) { assertEq(newElected[i], afterElected[i]); @@ -626,21 +644,6 @@ contract EpochManagerTest_finishNextEpochProcess is EpochManagerTest { } contract EpochManagerTest_getEpochByNumber is EpochManagerTest { - function _travelAndProcess_N_L2Epoch(uint256 n) public { - for (uint256 i = 0; i < n; i++) { - travelEpochL2(vm); - epochManager.startNextEpochProcess(); - - ( - address[] memory groups, - address[] memory lessers, - address[] memory greaters - ) = getGroupsWithLessersAndGreaters(); - - epochManager.finishNextEpochProcess(groups, lessers, greaters); - } - } - function test_shouldReturnTheEpochInfoOfSpecifiedEpoch() public { uint256 numberOfEpochsToTravel = 9; @@ -703,7 +706,7 @@ contract EpochManagerTest_getEpochByNumber is EpochManagerTest { function test_ReturnsZeroForFutureEpochs() public { initializeEpochManagerSystem(); - + address[] memory _expectedElected = new address[](0); ( uint256 _firstBlock, uint256 _lastBlock, @@ -717,3 +720,101 @@ contract EpochManagerTest_getEpochByNumber is EpochManagerTest { assertEq(_rewardBlock, 0); } } + +contract EpochManagerTest_getEpochNumberOfBlock is EpochManagerTest { + function test_ShouldRetreiveTheCorrectBlockNumberOfTheEpoch() public { + initializeEpochManagerSystem(); + assertEq(epochManager.getEpochNumberOfBlock(firstEpochBlock), firstEpochNumber); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.getEpochNumberOfBlock(firstEpochBlock); + } +} + +contract EpochManagerTest_getEpochByBlockNumber is EpochManagerTest { + function test_ShouldRetreiveTheCorrectEpochInfoOfGivenBlock() public { + initializeEpochManagerSystem(); + + _travelAndProcess_N_L2Epoch(2); + + ( + uint256 _firstBlock, + uint256 _lastBlock, + uint256 _timestamp, + uint256 rewardsBlock + ) = epochManager.getEpochByBlockNumber(firstEpochBlock + (3 * L2_BLOCK_IN_EPOCH)); + assertEq(_firstBlock, firstEpochBlock + 1 + (2 * L2_BLOCK_IN_EPOCH)); + assertEq(_lastBlock, firstEpochBlock + 1 + (3 * L2_BLOCK_IN_EPOCH) - 1); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.getEpochNumberOfBlock(firstEpochBlock); + } +} + +contract EpochManagerTest_numberOfElectedInCurrentSet is EpochManagerTest { + function test_ShouldRetreiveTheNumberOfElected() public { + initializeEpochManagerSystem(); + assertEq(epochManager.numberOfElectedInCurrentSet(), 2); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.numberOfElectedInCurrentSet(); + } +} + +contract EpochManagerTest_getElectedAccounts is EpochManagerTest { + function test_ShouldRetreiveThelistOfElectedAccounts() public { + initializeEpochManagerSystem(); + assertEq(epochManager.getElectedAccounts(), firstElected); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.getElectedAccounts(); + } +} + +contract EpochManagerTest_getElectedAccountByIndex is EpochManagerTest { + function test_ShouldRetreiveThecorrectValidator() public { + initializeEpochManagerSystem(); + assertEq(epochManager.getElectedAccountByIndex(0), validator1); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.getElectedAccountByIndex(0); + } +} +contract EpochManagerTest_getElectedSigners is EpochManagerTest { + function test_ShouldRetreiveTheElectedSigners() public { + initializeEpochManagerSystem(); + address[] memory electedSigners = new address[](firstElected.length); + electedSigners[0] = accounts.getValidatorSigner(firstElected[0]); + electedSigners[1] = accounts.getValidatorSigner(firstElected[1]); + assertEq(epochManager.getElectedSigners(), electedSigners); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.getElectedSigners(); + } +} +contract EpochManagerTest_getElectedSignerByIndex is EpochManagerTest { + function test_ShouldRetreiveThecorrectElectedSigner() public { + initializeEpochManagerSystem(); + address[] memory electedSigners = new address[](firstElected.length); + + electedSigners[1] = accounts.getValidatorSigner(firstElected[1]); + assertEq(epochManager.getElectedSignerByIndex(1), electedSigners[1]); + } + + function test_Reverts_WhenL1() public { + vm.expectRevert("Epoch system not initialized"); + epochManager.getElectedSignerByIndex(1); + } +} diff --git a/packages/protocol/test-sol/unit/common/EpochManagerEnabler.t.sol b/packages/protocol/test-sol/unit/common/EpochManagerEnabler.t.sol index 36c348cb3fa..3e91fc2e121 100644 --- a/packages/protocol/test-sol/unit/common/EpochManagerEnabler.t.sol +++ b/packages/protocol/test-sol/unit/common/EpochManagerEnabler.t.sol @@ -106,7 +106,7 @@ contract EpochManagerEnablerTest_initEpochManager is EpochManagerEnablerTest { vm.prank(nonOwner); epochManagerEnabler.initEpochManager(); - assertGt(epochManager.getElected().length, 0); + assertGt(epochManager.getElectedAccounts().length, 0); assertTrue(epochManager.systemAlreadyInitialized()); } diff --git a/packages/protocol/test-sol/utils08.sol b/packages/protocol/test-sol/utils08.sol index b86a5a54af4..066d7bdce43 100644 --- a/packages/protocol/test-sol/utils08.sol +++ b/packages/protocol/test-sol/utils08.sol @@ -40,7 +40,7 @@ contract Utils08 is TestConstants { return (addr, pk); } - // This function can be also found in OpenZeppelin's library, but in a newer version than the one + // This function can be also found in OpenZeppelin's library, but in a newer version than the one we use. function compareStrings(string memory a, string memory b) public pure returns (bool) { return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } diff --git a/packages/protocol/truffle-config-parent.js b/packages/protocol/truffle-config-parent.js index 90fed115263..3cb57cfa781 100644 --- a/packages/protocol/truffle-config-parent.js +++ b/packages/protocol/truffle-config-parent.js @@ -29,7 +29,7 @@ const BAKLAVASTAGING_FROM = '0x4588ABb84e1BBEFc2BcF4b2296F785fB7AD9F285' const STAGING_FROM = '0x4e3d385ecdee402da395a3b18575b05cc5e8ff21' const CANNOLI_FROM = '0x8C174E896A85E487aa895865657b78Ea64879dC7' // validator zero -const gasLimit = 13000000 +const gasLimit = 20000000 const hostAddress = process.env.CELO_NODE_ADDRESS || '127.0.0.1' const hostPort = parseInt(process.env.CELO_NODE_PORT || '8545')