From a4c608b4211794bb1651386785d2661b9679e8b7 Mon Sep 17 00:00:00 2001 From: pahor167 <47992132+pahor167@users.noreply.github.com> Date: Fri, 17 May 2024 11:41:23 +0200 Subject: [PATCH] L2 Experimental Validators (#10983) * L2 Experimentall Validators * integration tests * Release gold tests * prettify * version update * optimizing the check * removal of modifier * prettify * workflow update * workflow update2 * validator bytecode size fix * removal of tmate * hardcode gas limit * make release * rever of migrations * hardcode gas * Added logging * validators * refactor --- .../common/FeeCurrencyDirectory.sol | 22 +- .../contracts-0.8/common/IsL2Check.sol | 21 +- .../common/MentoFeeCurrencyAdapter.sol | 22 +- .../contracts-0.8/common/mocks/MockOracle.sol | 10 +- .../protocol/contracts/CompileExchange.sol | 210 +++---- .../protocol/contracts/common/Accounts.sol | 8 +- .../contracts/governance/Election.sol | 36 +- .../contracts/governance/Validators.sol | 54 +- .../governance/interfaces/IValidators.sol | 2 - .../contracts/stability/SortedOracles.sol | 40 +- packages/protocol/lib/web3-utils.ts | 21 +- .../protocol/scripts/truffle/make-release.ts | 12 +- .../protocol/test-sol/common/Accounts.t.sol | 13 + .../protocol/test-sol/common/IsL2Check.t.sol | 13 +- packages/protocol/test-sol/constants.sol | 5 +- .../governance/validators/Validators.t.sol | 288 +++++++-- .../validators/mocks/ValidatorsMockTunnel.sol | 59 ++ .../governance/voting/ReleaseGold.t.sol | 78 +-- .../voting/mocks/ReleaseGoldMockTunnel.sol | 83 +++ .../RevokeCeloAfterL2Transition.sol | 571 ++++++++++++++++++ .../protocol/test-sol/voting/Election.t.sol | 76 +++ 21 files changed, 1284 insertions(+), 360 deletions(-) create mode 100644 packages/protocol/test-sol/governance/validators/mocks/ValidatorsMockTunnel.sol create mode 100644 packages/protocol/test-sol/governance/voting/mocks/ReleaseGoldMockTunnel.sol create mode 100644 packages/protocol/test-sol/integration/RevokeCeloAfterL2Transition.sol diff --git a/packages/protocol/contracts-0.8/common/FeeCurrencyDirectory.sol b/packages/protocol/contracts-0.8/common/FeeCurrencyDirectory.sol index 9087e026667..604f51f9a12 100644 --- a/packages/protocol/contracts-0.8/common/FeeCurrencyDirectory.sol +++ b/packages/protocol/contracts-0.8/common/FeeCurrencyDirectory.sol @@ -54,6 +54,17 @@ contract FeeCurrencyDirectory is IFeeCurrencyDirectory, Initializable, Ownable { currencyList.pop(); } + /** + * @notice Returns the storage, major, minor, and patch version of the contract. + * @return Storage version of the contract. + * @return Major version of the contract. + * @return Minor version of the contract. + * @return Patch version of the contract. + */ + function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { + return (1, 1, 0, 0); + } + /** * @notice Returns the list of all currency addresses. * @return An array of addresses. @@ -83,15 +94,4 @@ contract FeeCurrencyDirectory is IFeeCurrencyDirectory, Initializable, Ownable { require(currencies[token].oracle != address(0), "Currency not in the directory"); (numerator, denominator) = IOracle(currencies[token].oracle).getExchangeRate(token); } - - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 0, 0); - } } diff --git a/packages/protocol/contracts-0.8/common/IsL2Check.sol b/packages/protocol/contracts-0.8/common/IsL2Check.sol index 31872234479..d07e5a7c3ed 100644 --- a/packages/protocol/contracts-0.8/common/IsL2Check.sol +++ b/packages/protocol/contracts-0.8/common/IsL2Check.sol @@ -1,4 +1,4 @@ -pragma solidity >=0.8.0 <0.8.20; +pragma solidity >=0.5.13 <0.8.20; /** * @title Based on predeploy returns whether this is L1 or L2. @@ -7,17 +7,22 @@ contract IsL2Check { address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; modifier onlyL1() { - if (IsL2()) { - revert("This method is not supported in L2 anymore."); - } + allowOnlyL1(); _; } - function IsL2() public view returns (bool) { - return address(proxyAdminAddress).code.length > 0; + function isL2() public view returns (bool) { + uint32 size; + address _addr = proxyAdminAddress; + assembly { + size := extcodesize(_addr) + } + return (size > 0); } - function IsL1() public view returns (bool) { - return !IsL2(); + function allowOnlyL1() internal view { + if (isL2()) { + revert("This method is not supported in L2 anymore."); + } } } diff --git a/packages/protocol/contracts-0.8/common/MentoFeeCurrencyAdapter.sol b/packages/protocol/contracts-0.8/common/MentoFeeCurrencyAdapter.sol index 8b578a547f6..47f29504417 100644 --- a/packages/protocol/contracts-0.8/common/MentoFeeCurrencyAdapter.sol +++ b/packages/protocol/contracts-0.8/common/MentoFeeCurrencyAdapter.sol @@ -61,6 +61,17 @@ contract MentoFeeCurrencyAdapter is IOracle, Initializable, Ownable { currencyList.pop(); } + /** + * @notice Returns the storage, major, minor, and patch version of the contract. + * @return Storage version of the contract. + * @return Major version of the contract. + * @return Minor version of the contract. + * @return Patch version of the contract. + */ + function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { + return (1, 1, 0, 0); + } + /** * @notice Returns the list of all currency addresses. * @return An array of addresses. @@ -93,15 +104,4 @@ contract MentoFeeCurrencyAdapter is IOracle, Initializable, Ownable { currencyConfig.currencyIdentifier ); } - - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 0, 0); - } } diff --git a/packages/protocol/contracts-0.8/common/mocks/MockOracle.sol b/packages/protocol/contracts-0.8/common/mocks/MockOracle.sol index 62efff8dc5e..37c9580a0c9 100644 --- a/packages/protocol/contracts-0.8/common/mocks/MockOracle.sol +++ b/packages/protocol/contracts-0.8/common/mocks/MockOracle.sol @@ -9,15 +9,15 @@ contract MockOracle is IOracle { uint256 lastUpdateTimestamp; address token; + function getExchangeRate(address _token) external view returns (uint256, uint256) { + require(token == _token, "Token not supported"); + return (numerator, denominator); + } + function setExchangeRate(address _token, uint256 _numerator, uint256 _denominator) public { numerator = _numerator; denominator = _denominator; lastUpdateTimestamp = block.timestamp; token = _token; } - - function getExchangeRate(address _token) external view returns (uint256, uint256) { - require(token == _token, "Token not supported"); - return (numerator, denominator); - } } diff --git a/packages/protocol/contracts/CompileExchange.sol b/packages/protocol/contracts/CompileExchange.sol index fff5e7793a7..27c4d2cef80 100644 --- a/packages/protocol/contracts/CompileExchange.sol +++ b/packages/protocol/contracts/CompileExchange.sol @@ -27,14 +27,6 @@ contract CompileExchange is using SafeMath for uint256; using FixidityLib for FixidityLib.Fraction; - event Exchanged(address indexed exchanger, uint256 sellAmount, uint256 buyAmount, bool soldGold); - event UpdateFrequencySet(uint256 updateFrequency); - event MinimumReportsSet(uint256 minimumReports); - event StableTokenSet(address indexed stable); - event SpreadSet(uint256 spread); - event ReserveFractionSet(uint256 reserveFraction); - event BucketsUpdated(uint256 goldBucket, uint256 stableBucket); - FixidityLib.Fraction public spread; // Fraction of the Reserve that is committed to the gold bucket when updating @@ -54,22 +46,19 @@ contract CompileExchange is bytes32 public stableTokenRegistryId; + event Exchanged(address indexed exchanger, uint256 sellAmount, uint256 buyAmount, bool soldGold); + event UpdateFrequencySet(uint256 updateFrequency); + event MinimumReportsSet(uint256 minimumReports); + event StableTokenSet(address indexed stable); + event SpreadSet(uint256 spread); + event ReserveFractionSet(uint256 reserveFraction); + event BucketsUpdated(uint256 goldBucket, uint256 stableBucket); + modifier updateBucketsIfNecessary() { _updateBucketsIfNecessary(); _; } - /** - * @notice Returns the storage, major, minor, and patch version of the contract. - * @return Storage version of the contract. - * @return Major version of the contract. - * @return Minor version of the contract. - * @return Patch version of the contract. - */ - function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 0, 0); - } - /** * @notice Sets initialized == true on implementation contracts * @param test Set to true to skip implementation initialization @@ -115,30 +104,6 @@ contract CompileExchange is _updateBucketsIfNecessary(); } - /** - * @notice Exchanges a specific amount of one token for an unspecified amount - * (greater than a threshold) of another. - * @param sellAmount The number of tokens to send to the exchange. - * @param minBuyAmount The minimum number of tokens for the exchange to send in return. - * @param sellGold True if the caller is sending CELO to the exchange, false otherwise. - * @return The number of tokens sent by the exchange. - * @dev The caller must first have approved `sellAmount` to the exchange. - * @dev This function can be frozen via the Freezable interface. - */ - function sell( - uint256 sellAmount, - uint256 minBuyAmount, - bool sellGold - ) public onlyWhenNotFrozen updateBucketsIfNecessary nonReentrant returns (uint256) { - (uint256 buyTokenBucket, uint256 sellTokenBucket) = _getBuyAndSellBuckets(sellGold); - uint256 buyAmount = _getBuyTokenAmount(buyTokenBucket, sellTokenBucket, sellAmount); - - require(buyAmount >= minBuyAmount, "Calculated buyAmount was less than specified minBuyAmount"); - - _exchange(sellAmount, buyAmount, sellGold); - return buyAmount; - } - /** * @dev DEPRECATED - Use `buy` or `sell`. * @notice Exchanges a specific amount of one token for an unspecified amount @@ -186,38 +151,6 @@ contract CompileExchange is return sellAmount; } - /** - * @notice Exchanges a specific amount of one token for a specific amount of another. - * @param sellAmount The number of tokens to send to the exchange. - * @param buyAmount The number of tokens for the exchange to send in return. - * @param sellGold True if the msg.sender is sending CELO to the exchange, false otherwise. - */ - function _exchange(uint256 sellAmount, uint256 buyAmount, bool sellGold) private { - IReserve reserve = IReserve(registry.getAddressForOrDie(RESERVE_REGISTRY_ID)); - - if (sellGold) { - goldBucket = goldBucket.add(sellAmount); - stableBucket = stableBucket.sub(buyAmount); - require( - getGoldToken().transferFrom(msg.sender, address(reserve), sellAmount), - "Transfer of sell token failed" - ); - require(IStableToken(stable).mint(msg.sender, buyAmount), "Mint of stable token failed"); - } else { - stableBucket = stableBucket.add(sellAmount); - goldBucket = goldBucket.sub(buyAmount); - require( - IERC20(stable).transferFrom(msg.sender, address(this), sellAmount), - "Transfer of sell token failed" - ); - IStableToken(stable).burn(sellAmount); - - require(reserve.transferExchangeGold(msg.sender, buyAmount), "Transfer of buyToken failed"); - } - - emit Exchanged(msg.sender, sellAmount, buyAmount, sellGold); - } - /** * @notice Returns the amount of buy tokens a user would get for sellAmount of the sell token. * @param sellAmount The amount of sellToken the user is selling to the exchange. @@ -242,25 +175,38 @@ contract CompileExchange is } /** - * @notice Returns the buy token and sell token bucket sizes, in order. The ratio of - * the two also represents the exchange rate between the two. - * @param sellGold `true` if gold is the sell token. - * @return buyTokenBucket - * @return sellTokenBucket + * @notice Returns the storage, major, minor, and patch version of the contract. + * @return Storage version of the contract. + * @return Major version of the contract. + * @return Minor version of the contract. + * @return Patch version of the contract. */ - function getBuyAndSellBuckets(bool sellGold) public view returns (uint256, uint256) { - uint256 currentGoldBucket = goldBucket; - uint256 currentStableBucket = stableBucket; + function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { + return (1, 1, 0, 0); + } - if (shouldUpdateBuckets()) { - (currentGoldBucket, currentStableBucket) = getUpdatedBuckets(); - } + /** + * @notice Exchanges a specific amount of one token for an unspecified amount + * (greater than a threshold) of another. + * @param sellAmount The number of tokens to send to the exchange. + * @param minBuyAmount The minimum number of tokens for the exchange to send in return. + * @param sellGold True if the caller is sending CELO to the exchange, false otherwise. + * @return The number of tokens sent by the exchange. + * @dev The caller must first have approved `sellAmount` to the exchange. + * @dev This function can be frozen via the Freezable interface. + */ + function sell( + uint256 sellAmount, + uint256 minBuyAmount, + bool sellGold + ) public onlyWhenNotFrozen updateBucketsIfNecessary nonReentrant returns (uint256) { + (uint256 buyTokenBucket, uint256 sellTokenBucket) = _getBuyAndSellBuckets(sellGold); + uint256 buyAmount = _getBuyTokenAmount(buyTokenBucket, sellTokenBucket, sellAmount); - if (sellGold) { - return (currentStableBucket, currentGoldBucket); - } else { - return (currentGoldBucket, currentStableBucket); - } + require(buyAmount >= minBuyAmount, "Calculated buyAmount was less than specified minBuyAmount"); + + _exchange(sellAmount, buyAmount, sellGold); + return buyAmount; } /** @@ -312,11 +258,79 @@ contract CompileExchange is emit ReserveFractionSet(newReserveFraction); } + /** + * @notice Returns the buy token and sell token bucket sizes, in order. The ratio of + * the two also represents the exchange rate between the two. + * @param sellGold `true` if gold is the sell token. + * @return buyTokenBucket + * @return sellTokenBucket + */ + function getBuyAndSellBuckets(bool sellGold) public view returns (uint256, uint256) { + uint256 currentGoldBucket = goldBucket; + uint256 currentStableBucket = stableBucket; + + if (shouldUpdateBuckets()) { + (currentGoldBucket, currentStableBucket) = getUpdatedBuckets(); + } + + if (sellGold) { + return (currentStableBucket, currentGoldBucket); + } else { + return (currentGoldBucket, currentStableBucket); + } + } + function _setStableToken(address newStableToken) internal { stable = newStableToken; emit StableTokenSet(newStableToken); } + /** + * @notice If conditions are met, updates the Uniswap bucket sizes to track + * the price reported by the Oracle. + */ + function _updateBucketsIfNecessary() private { + if (shouldUpdateBuckets()) { + // solhint-disable-next-line not-rely-on-time + lastBucketUpdate = now; + + (goldBucket, stableBucket) = getUpdatedBuckets(); + emit BucketsUpdated(goldBucket, stableBucket); + } + } + + /** + * @notice Exchanges a specific amount of one token for a specific amount of another. + * @param sellAmount The number of tokens to send to the exchange. + * @param buyAmount The number of tokens for the exchange to send in return. + * @param sellGold True if the msg.sender is sending CELO to the exchange, false otherwise. + */ + function _exchange(uint256 sellAmount, uint256 buyAmount, bool sellGold) private { + IReserve reserve = IReserve(registry.getAddressForOrDie(RESERVE_REGISTRY_ID)); + + if (sellGold) { + goldBucket = goldBucket.add(sellAmount); + stableBucket = stableBucket.sub(buyAmount); + require( + getGoldToken().transferFrom(msg.sender, address(reserve), sellAmount), + "Transfer of sell token failed" + ); + require(IStableToken(stable).mint(msg.sender, buyAmount), "Mint of stable token failed"); + } else { + stableBucket = stableBucket.add(sellAmount); + goldBucket = goldBucket.sub(buyAmount); + require( + IERC20(stable).transferFrom(msg.sender, address(this), sellAmount), + "Transfer of sell token failed" + ); + IStableToken(stable).burn(sellAmount); + + require(reserve.transferExchangeGold(msg.sender, buyAmount), "Transfer of buyToken failed"); + } + + emit Exchanged(msg.sender, sellAmount, buyAmount, sellGold); + } + /** * @notice Returns the buy token and sell token bucket sizes, in order. The ratio of * the two also represents the exchange rate between the two. @@ -405,20 +419,6 @@ contract CompileExchange is return reserveFraction.multiply(FixidityLib.newFixed(reserveGoldBalance)).fromFixed(); } - /** - * @notice If conditions are met, updates the Uniswap bucket sizes to track - * the price reported by the Oracle. - */ - function _updateBucketsIfNecessary() private { - if (shouldUpdateBuckets()) { - // solhint-disable-next-line not-rely-on-time - lastBucketUpdate = now; - - (goldBucket, stableBucket) = getUpdatedBuckets(); - emit BucketsUpdated(goldBucket, stableBucket); - } - } - /** * @notice Calculates the sell amount reduced by the spread. * @param sellAmount The original sell amount. diff --git a/packages/protocol/contracts/common/Accounts.sol b/packages/protocol/contracts/common/Accounts.sol index a697a3dad28..af5a6885dcf 100644 --- a/packages/protocol/contracts/common/Accounts.sol +++ b/packages/protocol/contracts/common/Accounts.sol @@ -11,6 +11,7 @@ import "../common/interfaces/ICeloVersionedContract.sol"; import "../common/Signatures.sol"; import "../common/UsingRegistry.sol"; import "../common/libraries/ReentrancyGuard.sol"; +import "../../contracts-0.8/common/IsL2Check.sol"; contract Accounts is IAccounts, @@ -18,7 +19,8 @@ contract Accounts is Ownable, ReentrancyGuard, Initializable, - UsingRegistry + UsingRegistry, + IsL2Check { using FixidityLib for FixidityLib.Fraction; using SafeMath for uint256; @@ -493,7 +495,7 @@ contract Accounts is * @return Patch version of the contract. */ function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 4, 2); + return (1, 1, 5, 0); } /** @@ -565,7 +567,7 @@ contract Accounts is * be greater than 1. * @dev Use `deletePaymentDelegation` to unset the payment delegation. */ - function setPaymentDelegation(address beneficiary, uint256 fraction) public { + function setPaymentDelegation(address beneficiary, uint256 fraction) public onlyL1 { require(isAccount(msg.sender), "Must first register address with Account.createAccount"); require(beneficiary != address(0), "Beneficiary cannot be address 0x0"); FixidityLib.Fraction memory f = FixidityLib.wrap(fraction); diff --git a/packages/protocol/contracts/governance/Election.sol b/packages/protocol/contracts/governance/Election.sol index 297f1d30392..33c05c82ca6 100644 --- a/packages/protocol/contracts/governance/Election.sol +++ b/packages/protocol/contracts/governance/Election.sol @@ -15,6 +15,7 @@ import "../common/UsingRegistry.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; import "../common/libraries/Heap.sol"; import "../common/libraries/ReentrancyGuard.sol"; +import "../../contracts-0.8/common/IsL2Check.sol"; contract Election is IElection, @@ -24,7 +25,8 @@ contract Election is Initializable, UsingRegistry, UsingPrecompiles, - CalledByVm + CalledByVm, + IsL2Check { using AddressSortedLinkedList for SortedLinkedList.List; using FixidityLib for FixidityLib.Fraction; @@ -196,7 +198,7 @@ contract Election is uint256 value, address lesser, address greater - ) external nonReentrant returns (bool) { + ) external nonReentrant onlyL1 returns (bool) { require(votes.total.eligible.contains(group), "Group not eligible"); require(0 < value, "Vote value cannot be zero"); require(canReceiveVotes(group, value), "Group cannot receive votes"); @@ -229,7 +231,7 @@ contract Election is * @return True upon success. * @dev Pending votes cannot be activated until an election has been held. */ - function activate(address group) external nonReentrant returns (bool) { + function activate(address group) external nonReentrant onlyL1 returns (bool) { address account = getAccounts().voteSignerToAccount(msg.sender); return _activate(group, account); } @@ -241,7 +243,10 @@ contract Election is * @return True upon success. * @dev Pending votes cannot be activated until an election has been held. */ - function activateForAccount(address group, address account) external nonReentrant returns (bool) { + function activateForAccount( + address group, + address account + ) external nonReentrant onlyL1 returns (bool) { return _activate(group, account); } @@ -338,7 +343,7 @@ contract Election is uint256 value, address lesser, address greater - ) external onlyVm { + ) external onlyVm onlyL1 { _distributeEpochRewards(group, value, lesser, greater); } @@ -364,7 +369,7 @@ contract Election is address group, address lesser, address greater - ) external onlyRegisteredContract(VALIDATORS_REGISTRY_ID) { + ) external onlyL1 onlyRegisteredContract(VALIDATORS_REGISTRY_ID) { uint256 value = getTotalVotesForGroup(group); votes.total.eligible.insert(group, value, lesser, greater); emit ValidatorGroupMarkedEligible(group); @@ -572,7 +577,10 @@ contract Election is * @return Whether or not `account` has activatable votes for `group`. * @dev Pending votes cannot be activated until an election has been held. */ - function hasActivatablePendingVotes(address account, address group) external view returns (bool) { + function hasActivatablePendingVotes( + address account, + address group + ) external view onlyL1 returns (bool) { PendingVote storage pendingVote = votes.pending.forGroup[group].byAccount[account]; return pendingVote.epoch < getEpochNumber() && pendingVote.value > 0; } @@ -602,7 +610,7 @@ contract Election is * @return Patch version of the contract. */ function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 1, 3, 1); + return (1, 1, 4, 0); } /** @@ -611,7 +619,7 @@ contract Election is * @param max The maximum number of validators that can be elected. * @return True upon success. */ - function setElectableValidators(uint256 min, uint256 max) public onlyOwner returns (bool) { + function setElectableValidators(uint256 min, uint256 max) public onlyOwner onlyL1 returns (bool) { require(0 < min, "Minimum electable validators cannot be zero"); require(min <= max, "Maximum electable validators cannot be smaller than minimum"); require( @@ -628,7 +636,9 @@ contract Election is * @param _maxNumGroupsVotedFor The maximum number of groups an account can vote for. * @return True upon success. */ - function setMaxNumGroupsVotedFor(uint256 _maxNumGroupsVotedFor) public onlyOwner returns (bool) { + function setMaxNumGroupsVotedFor( + uint256 _maxNumGroupsVotedFor + ) public onlyOwner onlyL1 returns (bool) { require(_maxNumGroupsVotedFor != maxNumGroupsVotedFor, "Max groups voted for not changed"); maxNumGroupsVotedFor = _maxNumGroupsVotedFor; emit MaxNumGroupsVotedForSet(_maxNumGroupsVotedFor); @@ -640,7 +650,7 @@ contract Election is * @param threshold Electability threshold as unwrapped Fraction. * @return True upon success. */ - function setElectabilityThreshold(uint256 threshold) public onlyOwner returns (bool) { + function setElectabilityThreshold(uint256 threshold) public onlyOwner onlyL1 returns (bool) { electabilityThreshold = FixidityLib.wrap(threshold); require( electabilityThreshold.lt(FixidityLib.fixed1()), @@ -671,7 +681,7 @@ contract Election is * If not run, voting power of account will not reflect rewards awarded. * @param flag The on/off flag. */ - function setAllowedToVoteOverMaxNumberOfGroups(bool flag) public { + function setAllowedToVoteOverMaxNumberOfGroups(bool flag) public onlyL1 { address account = getAccounts().voteSignerToAccount(msg.sender); IValidators validators = getValidators(); require( @@ -900,7 +910,7 @@ contract Election is uint256 value, address lesser, address greater - ) internal { + ) internal onlyL1 { if (votes.total.eligible.contains(group)) { uint256 newVoteTotal = votes.total.eligible.getValue(group).add(value); votes.total.eligible.update(group, newVoteTotal, lesser, greater); diff --git a/packages/protocol/contracts/governance/Validators.sol b/packages/protocol/contracts/governance/Validators.sol index d6fc540e488..4bce9ac487e 100644 --- a/packages/protocol/contracts/governance/Validators.sol +++ b/packages/protocol/contracts/governance/Validators.sol @@ -15,6 +15,7 @@ import "../common/UsingRegistry.sol"; import "../common/UsingPrecompiles.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; import "../common/libraries/ReentrancyGuard.sol"; +import "../../contracts-0.8/common/IsL2Check.sol"; /** * @title A contract for registering and electing Validator Groups and Validators. @@ -27,7 +28,8 @@ contract Validators is Initializable, UsingRegistry, UsingPrecompiles, - CalledByVm + CalledByVm, + IsL2Check { using FixidityLib for FixidityLib.Fraction; using AddressLinkedList for LinkedList.List; @@ -212,6 +214,7 @@ contract Validators is * @return True upon success. */ function updateValidatorScoreFromSigner(address signer, uint256 uptime) external onlyVm { + allowOnlyL1(); _updateValidatorScoreFromSigner(signer, uptime); } @@ -226,6 +229,7 @@ contract Validators is address signer, uint256 maxPayment ) external onlyVm returns (uint256) { + allowOnlyL1(); return _distributeEpochPaymentsFromSigner(signer, maxPayment); } @@ -246,6 +250,7 @@ contract Validators is bytes calldata blsPublicKey, bytes calldata blsPop ) external nonReentrant returns (bool) { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); _isRegistrationAllowed(account); require(!isValidator(account) && !isValidatorGroup(account), "Already registered"); @@ -307,6 +312,7 @@ contract Validators is * @dev De-affiliates with the previously affiliated group if present. */ function affiliate(address group) external nonReentrant returns (bool) { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(isValidator(account), "Not a validator"); require(isValidatorGroup(group), "Not a validator group"); @@ -347,6 +353,7 @@ contract Validators is bytes calldata blsPublicKey, bytes calldata blsPop ) external returns (bool) { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(isValidator(account), "Not a validator"); Validator storage validator = validators[account]; @@ -369,6 +376,7 @@ contract Validators is address signer, bytes calldata ecdsaPublicKey ) external onlyRegisteredContract(ACCOUNTS_REGISTRY_ID) returns (bool) { + allowOnlyL1(); require(isValidator(account), "Not a validator"); Validator storage validator = validators[account]; require( @@ -422,6 +430,7 @@ contract Validators is bytes calldata blsPublicKey, bytes calldata blsPop ) external onlyRegisteredContract(ACCOUNTS_REGISTRY_ID) returns (bool) { + allowOnlyL1(); require(isValidator(account), "Not a validator"); Validator storage validator = validators[account]; require( @@ -444,6 +453,7 @@ contract Validators is * @dev Fails if the account does not have sufficient weight. */ function registerValidatorGroup(uint256 commission) external nonReentrant returns (bool) { + allowOnlyL1(); require(commission <= FixidityLib.fixed1().unwrap(), "Commission can't be greater than 100%"); address account = getAccounts().validatorSignerToAccount(msg.sender); _isRegistrationAllowed(account); @@ -468,6 +478,7 @@ contract Validators is * @dev Fails if the group has zero members. */ function addMember(address validator) external nonReentrant returns (bool) { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(groups[account].members.numElements > 0, "Validator group empty"); return _addMember(account, validator, address(0), address(0)); @@ -487,6 +498,7 @@ contract Validators is address lesser, address greater ) external nonReentrant returns (bool) { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(groups[account].members.numElements == 0, "Validator group not empty"); return _addMember(account, validator, lesser, greater); @@ -519,6 +531,7 @@ contract Validators is address lesserMember, address greaterMember ) external nonReentrant returns (bool) { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(isValidatorGroup(account), "Not a group"); require(isValidator(validator), "Not a validator"); @@ -536,6 +549,7 @@ contract Validators is * payments made to its members. Must be in the range [0, 1.0]. */ function setNextCommissionUpdate(uint256 commission) external { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(isValidatorGroup(account), "Not a validator group"); ValidatorGroup storage group = groups[account]; @@ -550,6 +564,7 @@ contract Validators is * @notice Updates a validator group's commission based on the previously queued update */ function updateCommission() external { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(isValidatorGroup(account), "Not a validator group"); ValidatorGroup storage group = groups[account]; @@ -568,6 +583,7 @@ contract Validators is * @param validatorAccount The validator to deaffiliate from their affiliated validator group. */ function forceDeaffiliateIfValidator(address validatorAccount) external nonReentrant onlySlasher { + allowOnlyL1(); if (isValidator(validatorAccount)) { Validator storage validator = validators[validatorAccount]; if (validator.affiliation != address(0)) { @@ -581,6 +597,7 @@ contract Validators is * the last time the group was slashed. */ function resetSlashingMultiplier() external nonReentrant { + allowOnlyL1(); address account = getAccounts().validatorSignerToAccount(msg.sender); require(isValidatorGroup(account), "Not a validator group"); ValidatorGroup storage group = groups[account]; @@ -596,6 +613,7 @@ contract Validators is * @param account The group being slashed. */ function halveSlashingMultiplier(address account) external nonReentrant onlySlasher { + allowOnlyL1(); require(isValidatorGroup(account), "Not a validator group"); ValidatorGroup storage group = groups[account]; group.slashInfo.multiplier = FixidityLib.wrap(group.slashInfo.multiplier.unwrap().div(2)); @@ -713,19 +731,6 @@ contract Validators is return registeredValidators; } - /** - * @notice Returns the list of signers for the registered validator accounts. - * @return The list of signers for registered validator accounts. - */ - function getRegisteredValidatorSigners() external view returns (address[] memory) { - IAccounts accounts = getAccounts(); - address[] memory signers = new address[](registeredValidators.length); - for (uint256 i = 0; i < signers.length; i = i.add(1)) { - signers[i] = accounts.getValidatorSigner(registeredValidators[i]); - } - return signers; - } - /** * @notice Returns the list of registered validator group accounts. * @return The list of registered validator group addresses. @@ -750,6 +755,7 @@ contract Validators is * @param account The group to fetch slashing multiplier for. */ function getValidatorGroupSlashingMultiplier(address account) external view returns (uint256) { + allowOnlyL1(); require(isValidatorGroup(account), "Not a validator group"); ValidatorGroup storage group = groups[account]; return group.slashInfo.multiplier.unwrap(); @@ -767,6 +773,7 @@ contract Validators is uint256 epochNumber, uint256 index ) external view returns (address) { + allowOnlyL1(); require(isValidator(account), "Not a validator"); require(epochNumber <= getEpochNumber(), "Epoch cannot be larger than current"); MembershipHistory storage history = validators[account].membershipHistory; @@ -830,14 +837,6 @@ contract Validators is return sum.divide(FixidityLib.newFixed(uptimes.length)).unwrap(); } - /** - * @notice Returns the maximum number of members a group can add. - * @return The maximum number of members a group can add. - */ - function getMaxGroupSize() external view returns (uint256) { - return maxGroupSize; - } - /** * @notice Returns the block delay for a ValidatorGroup's commission udpdate. * @return The block delay for a ValidatorGroup's commission udpdate. @@ -854,14 +853,15 @@ contract Validators is * @return Patch version of the contract. */ function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 2, 0, 6); + return (1, 3, 0, 0); } /** - * @notice Updates the block delay for a ValidatorGroup's commission udpdate + * @notice Updates the block delay for a ValidatorGroup's commission update * @param delay Number of blocks to delay the update */ function setCommissionUpdateDelay(uint256 delay) public onlyOwner { + allowOnlyL1(); require(delay != commissionUpdateDelay, "commission update delay not changed"); commissionUpdateDelay = delay; emit CommissionUpdateDelaySet(delay); @@ -873,6 +873,7 @@ contract Validators is * @return True upon success. */ function setMaxGroupSize(uint256 size) public onlyOwner returns (bool) { + allowOnlyL1(); require(0 < size, "Max group size cannot be zero"); require(size != maxGroupSize, "Max group size not changed"); maxGroupSize = size; @@ -886,6 +887,7 @@ contract Validators is * @return True upon success. */ function setMembershipHistoryLength(uint256 length) public onlyOwner returns (bool) { + allowOnlyL1(); require(0 < length, "Membership history length cannot be zero"); require(length != membershipHistoryLength, "Membership history length not changed"); membershipHistoryLength = length; @@ -903,6 +905,7 @@ contract Validators is uint256 exponent, uint256 adjustmentSpeed ) public onlyOwner returns (bool) { + allowOnlyL1(); require( adjustmentSpeed <= FixidityLib.fixed1().unwrap(), "Adjustment speed cannot be larger than 1" @@ -965,6 +968,7 @@ contract Validators is * @param value New reset period for slashing multiplier. */ function setSlashingMultiplierResetPeriod(uint256 value) public nonReentrant onlyOwner { + allowOnlyL1(); slashingMultiplierResetPeriod = value; } @@ -973,6 +977,7 @@ contract Validators is * @param value New downtime grace period for calculating epoch scores. */ function setDowntimeGracePeriod(uint256 value) public nonReentrant onlyOwner { + allowOnlyL1(); downtimeGracePeriod = value; } @@ -1006,6 +1011,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) { + allowOnlyL1(); uint256 epochNumber = getEpochNumber(); MembershipHistory storage history = validators[account].membershipHistory; uint256 head = history.numEntries == 0 ? 0 : history.tail.add(history.numEntries.sub(1)); diff --git a/packages/protocol/contracts/governance/interfaces/IValidators.sol b/packages/protocol/contracts/governance/interfaces/IValidators.sol index 936ce9d56cd..6d8a0f92e28 100644 --- a/packages/protocol/contracts/governance/interfaces/IValidators.sol +++ b/packages/protocol/contracts/governance/interfaces/IValidators.sol @@ -49,7 +49,6 @@ interface IValidators { function halveSlashingMultiplier(address) external; // view functions - function getMaxGroupSize() external view returns (uint256); function getCommissionUpdateDelay() external view returns (uint256); function getValidatorScoreParameters() external view returns (uint256, uint256); function getMembershipHistory( @@ -80,7 +79,6 @@ interface IValidators { function getValidatorLockedGoldRequirements() external view returns (uint256, uint256); function getGroupLockedGoldRequirements() external view returns (uint256, uint256); function getRegisteredValidators() external view returns (address[] memory); - function getRegisteredValidatorSigners() external view returns (address[] memory); function getRegisteredValidatorGroups() external view returns (address[] memory); function isValidatorGroup(address) external view returns (bool); function isValidator(address) external view returns (bool); diff --git a/packages/protocol/contracts/stability/SortedOracles.sol b/packages/protocol/contracts/stability/SortedOracles.sol index df26a79b143..0b04204ba61 100644 --- a/packages/protocol/contracts/stability/SortedOracles.sol +++ b/packages/protocol/contracts/stability/SortedOracles.sol @@ -343,26 +343,6 @@ contract SortedOracles is ISortedOracles, ICeloVersionedContract, Ownable, Initi return (1, 1, 4, 0); } - /** - * @notice Returns the median of the currently stored rates for a specified rateFeedId. - * @dev Please note that this function respects the equivalentToken mapping, and so may - * return the median identified as an equivalent to the supplied rateFeedId. - * @param token The token for which the median value is being retrieved. - * @return uint256 The median exchange rate for rateFeedId (fixidity). - * @return uint256 denominator - */ - function medianRate(address token) public view returns (uint256, uint256) { - EquivalentToken storage equivalentToken = equivalentTokens[token]; - if (equivalentToken.token != address(0)) { - (uint256 equivalentMedianRate, uint256 denominator) = medianRateWithoutEquivalentMapping( - equivalentToken.token - ); - return (equivalentMedianRate, denominator); - } - - return medianRateWithoutEquivalentMapping(token); - } - /** * @notice Sets the report expiry parameter. * @param _reportExpirySeconds The number of seconds before a report is considered expired. @@ -384,6 +364,26 @@ contract SortedOracles is ISortedOracles, ICeloVersionedContract, Ownable, Initi emit BreakerBoxUpdated(address(newBreakerBox)); } + /** + * @notice Returns the median of the currently stored rates for a specified rateFeedId. + * @dev Please note that this function respects the equivalentToken mapping, and so may + * return the median identified as an equivalent to the supplied rateFeedId. + * @param token The token for which the median value is being retrieved. + * @return uint256 The median exchange rate for rateFeedId (fixidity). + * @return uint256 denominator + */ + function medianRate(address token) public view returns (uint256, uint256) { + EquivalentToken storage equivalentToken = equivalentTokens[token]; + if (equivalentToken.token != address(0)) { + (uint256 equivalentMedianRate, uint256 denominator) = medianRateWithoutEquivalentMapping( + equivalentToken.token + ); + return (equivalentMedianRate, denominator); + } + + return medianRateWithoutEquivalentMapping(token); + } + /** * @notice Returns the number of rates that are currently stored for a specifed rateFeedId. * @dev Does not take the equivalentTokens mapping into account. diff --git a/packages/protocol/lib/web3-utils.ts b/packages/protocol/lib/web3-utils.ts index a528cd5ed52..127c09ff6d5 100644 --- a/packages/protocol/lib/web3-utils.ts +++ b/packages/protocol/lib/web3-utils.ts @@ -214,7 +214,7 @@ export async function getDeployedProxiedContract = customArtifacts.require(contractName) - let Proxy:ProxyContract + let Proxy: ProxyContract // this wrap avoids a lot of rewrite const overloadedArtifact = ArtifactsSingleton.wrap(customArtifacts) // if global artifacts are not defined we need to handle it @@ -267,7 +267,7 @@ export function deploymentForProxiedContract { +export const makeTruffleContractForMigrationWithoutSingleton = (contractName: string, network: any, contractPath: string, web3: Web3) => { const artifact = require(`${path.join(__dirname, "..")}/build/contracts-${contractPath}/${contractName}.json`) const Contract = truffleContract({ @@ -283,15 +283,15 @@ export const makeTruffleContractForMigrationWithoutSingleton = (contractName: st networkType: "ethereum", provider: web3.currentProvider }) - Contract.configureNetwork({networkType: "ethereum", provider: web3.currentProvider}) + Contract.configureNetwork({ networkType: "ethereum", provider: web3.currentProvider }) - Contract.defaults({from: network.from, gas: network.gas}) + Contract.defaults({ from: network.from, gas: network.gas }) return Contract } -export const makeTruffleContractForMigration = (contractName: string, contractPath:ContractPackage, web3: Web3) => { +export const makeTruffleContractForMigration = (contractName: string, contractPath: ContractPackage, web3: Web3) => { const network = ArtifactsSingleton.getNetwork() const Contract = makeTruffleContractForMigrationWithoutSingleton(contractName, network, contractPath.name, web3) ArtifactsSingleton.getInstance(contractPath).addArtifact(contractName, Contract) @@ -315,8 +315,8 @@ export function deploymentForContract { console.info("\n-> Deploying", name) deployer.deploy(ContractProxy) - deployer.deploy(Contract, testingDeployment) + deployer.deploy(Contract, { gas: 5000000 }) deployer.then(async () => { const proxy: ProxyInstance = await ContractProxy.deployed() @@ -397,7 +396,7 @@ export async function transferOwnershipOfProxyAndImplementation< * Builds and returns mapping of function names to selectors. * Each function name maps to an array of selectors to account for overloading. */ -export function getFunctionSelectorsForContractProxy(contract: any, proxy: any, web3:any) { +export function getFunctionSelectorsForContractProxy(contract: any, proxy: any, web3: any) { const selectors: { [index: string]: string[] } = {} proxy.abi .concat(contract.abi) @@ -449,7 +448,7 @@ export function checkImports(baseContractName: string, derivativeContractArtifac const imports: any[] = derivativeContractArtifact.ast.nodes.filter((astNode: any) => isImport(astNode)) while (imports.length) { // BFS const importedContractName = (imports.pop().file as string).split('/').pop().split('.')[0] - if (importedContractName === baseContractName) { + if (importedContractName === baseContractName) { return true } const importedContractArtifact = artifacts instanceof BuildArtifacts ? diff --git a/packages/protocol/scripts/truffle/make-release.ts b/packages/protocol/scripts/truffle/make-release.ts index dedd3172377..af7911955b2 100644 --- a/packages/protocol/scripts/truffle/make-release.ts +++ b/packages/protocol/scripts/truffle/make-release.ts @@ -108,7 +108,7 @@ const deployImplementation = async ( from: string, requireVersion = true ) => { - const testingDeployment = false + // const testingDeployment = false if (from) { Contract.defaults({ from }) // override truffle with provided from address } @@ -118,9 +118,17 @@ const deployImplementation = async ( // without this delay it sometimes fails with ProviderError await delay(getRandomNumber(1, 1000)) + console.log('gas update in2') + console.log('dryRun', dryRun) + + const bytecodeSize = (Contract.bytecode.length - 2) / 2 + console.log('Bytecode size in bytes:', bytecodeSize) + const contract = await (dryRun ? Contract.at(celoRegistryAddress) - : Contract.new(testingDeployment)) + : Contract.new({ + gas: 5000000, // Setting the gas limit + })) // Sanity check that any contracts that are being changed set a version number. const getVersionNumberAbi = contract.abi.find( diff --git a/packages/protocol/test-sol/common/Accounts.t.sol b/packages/protocol/test-sol/common/Accounts.t.sol index a24e63ba0db..e984757e62d 100644 --- a/packages/protocol/test-sol/common/Accounts.t.sol +++ b/packages/protocol/test-sol/common/Accounts.t.sol @@ -15,6 +15,8 @@ contract AccountsTest is Test { Accounts accounts; MockValidators validators; + address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; + string constant name = "Account"; string constant metadataURL = "https://www.celo.org"; string constant otherMetadataURL = "https://clabs.co"; @@ -97,6 +99,10 @@ contract AccountsTest is Test { (caller2, caller2PK) = actorWithPK("caller2"); } + function _whenL2() public { + deployCodeTo("Registry.sol", abi.encode(false), proxyAdminAddress); + } + function getParsedSignatureOfAddress( address _address, uint256 privateKey @@ -604,6 +610,13 @@ contract AccountsTest_setPaymentDelegation is AccountsTest { assertEq(realFraction, fraction); } + function test_Revert_SetPaymentDelegation_WhenL2() public { + _whenL2(); + accounts.createAccount(); + vm.expectRevert("This method is not supported in L2 anymore."); + accounts.setPaymentDelegation(beneficiary, fraction); + } + function test_ShouldNotAllowFractionGreaterThan1() public { accounts.createAccount(); vm.expectRevert("Fraction must not be greater than 1"); diff --git a/packages/protocol/test-sol/common/IsL2Check.t.sol b/packages/protocol/test-sol/common/IsL2Check.t.sol index 59b8c7eedcc..73d9971c3d1 100644 --- a/packages/protocol/test-sol/common/IsL2Check.t.sol +++ b/packages/protocol/test-sol/common/IsL2Check.t.sol @@ -28,21 +28,12 @@ contract IsL2CheckBase is Test { contract IsL2Check_IsL2Test is IsL2CheckBase { function test_IsL2() public { - assertFalse(isL2Check.IsL2()); - } - - function test_IsL1() public { - assertTrue(isL2Check.IsL1()); + assertFalse(isL2Check.isL2()); } function test_IsL2_WhenProxyAdminSet() public { helper_WhenProxyAdminAddressIsSet(); - assertTrue(isL2Check.IsL2()); - } - - function test_IsL1_WhenProxyAdminSet() public { - helper_WhenProxyAdminAddressIsSet(); - assertFalse(isL2Check.IsL1()); + assertTrue(isL2Check.isL2()); } } diff --git a/packages/protocol/test-sol/constants.sol b/packages/protocol/test-sol/constants.sol index e013af797e4..45bb187faf5 100644 --- a/packages/protocol/test-sol/constants.sol +++ b/packages/protocol/test-sol/constants.sol @@ -3,8 +3,10 @@ pragma solidity ^0.5.13; // This contract is only required for Solidity 0.5 contract Constants { uint256 public constant FIXED1 = 1e24; - uint256 public constant HOUR = 60 * 60; + uint256 public constant MINUTE = 60; + uint256 public constant HOUR = 60 * MINUTE; uint256 public constant DAY = 24 * HOUR; + uint256 public constant MONTH = 30 * DAY; uint256 constant WEEK = 7 * DAY; uint256 public constant YEAR = 365 * DAY; uint256 public constant EPOCH_SIZE = DAY / 5; @@ -18,4 +20,5 @@ contract Constants { string constant AccountsContract = "Accounts"; string constant LockedGoldContract = "LockedGold"; string constant ValidatorsContract = "Validators"; + string constant GovernanceContract = "Governance"; } diff --git a/packages/protocol/test-sol/governance/validators/Validators.t.sol b/packages/protocol/test-sol/governance/validators/Validators.t.sol index ea38f184383..3da5b9e4eaf 100644 --- a/packages/protocol/test-sol/governance/validators/Validators.t.sol +++ b/packages/protocol/test-sol/governance/validators/Validators.t.sol @@ -15,6 +15,7 @@ import "@celo-contracts/governance/LockedGold.sol"; import "@celo-contracts/stability/test/MockStableToken.sol"; import "@celo-contracts/governance/test/MockElection.sol"; import "@celo-contracts/governance/test/MockLockedGold.sol"; +import "./mocks/ValidatorsMockTunnel.sol"; import "@celo-contracts/governance/test/ValidatorsMock.sol"; import "@test-sol/constants.sol"; @@ -22,63 +23,12 @@ import "@test-sol/utils/ECDSAHelper.sol"; import { Utils } from "@test-sol/utils.sol"; import { Test as ForgeTest } from "forge-std/Test.sol"; -contract ValidatorsMockTunnel is ForgeTest { - ValidatorsMock private tunnelValidators; - address validatorContractAddress; - - constructor(address _validatorContractAddress) public { - validatorContractAddress = _validatorContractAddress; - tunnelValidators = ValidatorsMock(validatorContractAddress); - } - - struct InitParams { - address registryAddress; - uint256 groupRequirementValue; - uint256 groupRequirementDuration; - uint256 validatorRequirementValue; - uint256 validatorRequirementDuration; - uint256 validatorScoreExponent; - uint256 validatorScoreAdjustmentSpeed; - } - - struct InitParams2 { - uint256 _membershipHistoryLength; - uint256 _slashingMultiplierResetPeriod; - uint256 _maxGroupSize; - uint256 _commissionUpdateDelay; - uint256 _downtimeGracePeriod; - } - - function MockInitialize( - address sender, - InitParams calldata params, - InitParams2 calldata params2 - ) external returns (bool, bytes memory) { - bytes memory data = abi.encodeWithSignature( - "initialize(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)", - params.registryAddress, - params.groupRequirementValue, - params.groupRequirementDuration, - params.validatorRequirementValue, - params.validatorRequirementDuration, - params.validatorScoreExponent, - params.validatorScoreAdjustmentSpeed, - params2._membershipHistoryLength, - params2._slashingMultiplierResetPeriod, - params2._maxGroupSize, - params2._commissionUpdateDelay, - params2._downtimeGracePeriod - ); - vm.prank(sender); - (bool success, ) = address(tunnelValidators).call(data); - require(success, "unsuccessful tunnel call"); - } -} - contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { using FixidityLib for FixidityLib.Fraction; using SafeMath for uint256; + address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; + Registry registry; Accounts accounts; MockStableToken stableToken; @@ -412,6 +362,10 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { FixidityLib.wrap(originalValidatorScoreParameters.exponent) ); } + + function _whenL2() public { + deployCodeTo("Registry.sol", abi.encode(false), proxyAdminAddress); + } } contract ValidatorsTest_Initialize is ValidatorsTest { @@ -481,10 +435,22 @@ contract ValidatorsTest_Initialize is ValidatorsTest { assertEq(actual, commissionUpdateDelay, "Wrong commissionUpdateDelay."); } + function test_Reverts_setCommissionUpdateDelay_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setCommissionUpdateDelay(commissionUpdateDelay); + } + function test_shouldHaveSetDowntimeGracePeriod() public { uint256 actual = validators.downtimeGracePeriod(); assertEq(actual, downtimeGracePeriod, "Wrong downtimeGracePeriod."); } + + function test_Reverts_SetDowntimeGracePeriod_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setDowntimeGracePeriod(downtimeGracePeriod); + } } contract ValidatorsTest_SetMembershipHistoryLength is ValidatorsTest { @@ -500,6 +466,12 @@ contract ValidatorsTest_SetMembershipHistoryLength is ValidatorsTest { assertEq(validators.membershipHistoryLength(), newLength); } + function test_Reverts_SetTheMembershipHistoryLength_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setMembershipHistoryLength(newLength); + } + function test_Emits_MembershipHistoryLengthSet() public { vm.expectEmit(true, true, true, true); emit MembershipHistoryLengthSet(newLength); @@ -518,9 +490,10 @@ contract ValidatorsTest_SetMaxGroupSize is ValidatorsTest { event MaxGroupSizeSet(uint256 size); - function test_ShouldSetMaxGroupSize() public { + function test_Reverts_SetMaxGroupSize_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); validators.setMaxGroupSize(newSize); - assertEq(validators.getMaxGroupSize(), newSize, "MaxGroupSize not properly set"); } function test_Emits_MaxGroupSizeSet() public { @@ -620,13 +593,19 @@ contract ValidatorsTest_SetValidatorScoreParameters is ValidatorsTest { event ValidatorScoreParametersSet(uint256 exponent, uint256 adjustmentSpeed); - function test_ShouldsetExponentAndAdjustmentSpeed() public { + function test_ShouldSetExponentAndAdjustmentSpeed() public { validators.setValidatorScoreParameters(newParams.exponent, newParams.adjustmentSpeed.unwrap()); (uint256 _exponent, uint256 _adjustmentSpeed) = validators.getValidatorScoreParameters(); assertEq(_exponent, newParams.exponent, "Incorrect Exponent"); assertEq(_adjustmentSpeed, newParams.adjustmentSpeed.unwrap(), "Incorrect AdjustmentSpeed"); } + function test_Reverts_SetExponentAndAdjustmentSpeed_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setValidatorScoreParameters(newParams.exponent, newParams.adjustmentSpeed.unwrap()); + } + function test_Emits_ValidatorScoreParametersSet() public { vm.expectEmit(true, true, true, true); emit ValidatorScoreParametersSet(newParams.exponent, newParams.adjustmentSpeed.unwrap()); @@ -688,6 +667,27 @@ contract ValidatorsTest_RegisterValidator is ValidatorsTest { assertTrue(validators.isValidator(validator)); } + function test_ShouldRevert_WhenInL2_WhenAccountHasAuthorizedValidatorSigner() public { + lockedGold.setAccountTotalLockedGold(validator, originalValidatorLockedGoldRequirements.value); + + (bytes memory _ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) = _generateEcdsaPubKeyWithSigner( + validator, + signerPk + ); + + ph.mockSuccess(ph.PROOF_OF_POSSESSION(), abi.encodePacked(validator, blsPublicKey, blsPop)); + + vm.prank(validator); + accounts.authorizeValidatorSigner(signer, v, r, s); + + _whenL2(); + + vm.expectRevert("This method is not supported in L2 anymore."); + vm.prank(validator); + validators.registerValidator(_ecdsaPubKey, blsPublicKey, blsPop); + validatorRegistrationEpochNumber = validators.getEpochNumber(); + } + function test_ShouldAddAccountToValidatorList_WhenAccountHasAuthorizedValidatorSigner() public { address[] memory ExpectedRegisteredValidators = new address[](1); ExpectedRegisteredValidators[0] = validator; @@ -994,6 +994,13 @@ contract ValidatorsTest_Affiliate_WhenGroupAndValidatorMeetLockedGoldRequirement assertEq(affiliation, group); } + function test_Reverts_WhenL2_WhenAffiliatingWithRegisteredValidatorGroup() public { + _whenL2(); + vm.prank(validator); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.affiliate(group); + } + function test_Emits_ValidatorAffiliatedEvent() public { vm.expectEmit(true, true, true, true); emit ValidatorAffiliated(validator, group); @@ -1066,6 +1073,13 @@ contract ValidatorsTest_Affiliate_WhenValidatorIsAlreadyAffiliatedWithValidatorG assertEq(affiliation, otherGroup); } + function test_ShouldRevert_WhenL2_WhenValidatorNotMemberOfThatValidatorGroup() public { + _whenL2(); + vm.prank(validator); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.affiliate(otherGroup); + } + function test_Emits_ValidatorDeaffiliatedEvent_WhenValidatorNotMemberOfThatValidatorGroup() public { @@ -1304,6 +1318,19 @@ contract ValidatorsTest_UpdateEcdsaPublicKey is ValidatorsTest { assertEq(actualEcdsaPubKey, _newEcdsaPubKey); } + function test_Reverts_SetValidatorEcdsaPubKey_WhenCalledByRegisteredAccountsContract_WhenL2() + public + { + _whenL2(); + (bytes memory _newEcdsaPubKey, , , ) = _generateEcdsaPubKeyWithSigner( + address(accounts), + signerPk + ); + vm.prank(address(accounts)); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.updateEcdsaPublicKey(validator, signer, _newEcdsaPubKey); + } + function test_Emits_ValidatorEcdsaPublicKeyUpdatedEvent_WhenCalledByRegisteredAccountsContract() public { @@ -1389,6 +1416,25 @@ contract ValidatorsTest_UpdatePublicKeys is ValidatorsTest { assertEq(actualBlsPublicKey, newBlsPublicKey); } + function test_Reverts_SetValidatorNewBlsPubKeyAndEcdsaPubKey_WhenCalledByRegisteredAccountsContract_WhenL2() + public + { + _whenL2(); + (bytes memory _newEcdsaPubKey, , , ) = _generateEcdsaPubKeyWithSigner( + address(accounts), + signerPk + ); + + ph.mockSuccess( + ph.PROOF_OF_POSSESSION(), + abi.encodePacked(validator, newBlsPublicKey, newBlsPop) + ); + + vm.prank(address(accounts)); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.updatePublicKeys(validator, signer, _newEcdsaPubKey, newBlsPublicKey, newBlsPop); + } + function test_Emits_ValidatorEcdsaPublicKeyUpdatedAndValidatorBlsPublicKeyUpdatedEvent_WhenCalledByRegisteredAccountsContract() public { @@ -1492,6 +1538,18 @@ contract ValidatorsTest_UpdateBlsPublicKey is ValidatorsTest { assertEq(actualBlsPublicKey, newBlsPublicKey); } + function test_Reverts_SetNewValidatorBlsPubKey_WhenL2() public { + _whenL2(); + ph.mockSuccess( + ph.PROOF_OF_POSSESSION(), + abi.encodePacked(validator, newBlsPublicKey, newBlsPop) + ); + + vm.prank(validator); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.updateBlsPublicKey(newBlsPublicKey, newBlsPop); + } + function test_Emits_ValidatorValidatorBlsPublicKeyUpdatedEvent() public { ph.mockSuccess( ph.PROOF_OF_POSSESSION(), @@ -1797,6 +1855,26 @@ contract ValidatorsTest_AddMember is ValidatorsTest { assertEq(members, expectedMembersList); } + function test_Reverts_AddFirstMemberToTheList_WhenL2() public { + _whenL2(); + address[] memory expectedMembersList = new address[](1); + expectedMembersList[0] = validator; + + vm.prank(group); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.addFirstMember(validator, address(0), address(0)); + } + + function test_Reverts_AddMemberToTheList_WhenL2() public { + _whenL2(); + address[] memory expectedMembersList = new address[](1); + expectedMembersList[0] = validator; + + vm.prank(group); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.addMember(validator); + } + function test_ShouldUpdateGroupSizeHistory() public { vm.prank(group); validators.addFirstMember(validator, address(0), address(0)); @@ -2070,6 +2148,17 @@ contract ValidatorsTest_ReorderMember is ValidatorsTest { assertEq(expectedMembersList.length, members.length); } + function test_Reverts_ReorderGroupMemberList_WhenL2() public { + _whenL2(); + address[] memory expectedMembersList = new address[](2); + expectedMembersList[0] = vm.addr(1); + expectedMembersList[1] = validator; + + vm.prank(group); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.reorderMember(vm.addr(1), validator, address(0)); + } + function test_Emits_ValidatorGroupMemberReorderedEvent() public { vm.expectEmit(true, true, true, true); emit ValidatorGroupMemberReordered(group, vm.addr(1)); @@ -2117,6 +2206,13 @@ contract ValidatorsTest_SetNextCommissionUpdate is ValidatorsTest { assertEq(_commission, commission.unwrap()); } + function test_Reverts_SetValidatorGroupCommission_WhenL2() public { + _whenL2(); + vm.prank(group); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setNextCommissionUpdate(newCommission); + } + function test_ShouldSetValidatorGroupNextCommission() public { vm.prank(group); validators.setNextCommissionUpdate(newCommission); @@ -2174,6 +2270,13 @@ contract ValidatorsTest_UpdateCommission is ValidatorsTest { assertEq(_commission, newCommission); } + function test_Reverts_SetValidatorGroupCommission_WhenL2() public { + _whenL2(); + vm.prank(group); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setNextCommissionUpdate(newCommission); + } + function test_Emits_ValidatorGroupCommissionUpdated() public { vm.prank(group); validators.setNextCommissionUpdate(newCommission); @@ -2669,6 +2772,19 @@ contract ValidatorsTest_GetMembershipInLastEpoch is ValidatorsTest { } } } + + function test_Reverts_getMembershipInLastEpoch_WhenL2() public { + blockTravel(ph.epochSize()); + + vm.prank(validator); + validators.affiliate(vm.addr(1)); + vm.prank(vm.addr(1)); + validators.addFirstMember(validator, address(0), address(0)); + + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.getMembershipInLastEpoch(validator); + } } contract ValidatorsTest_GetEpochSize is ValidatorsTest { @@ -2836,6 +2952,12 @@ contract ValidatorsTest_DistributeEpochPaymentsFromSigner is ValidatorsTest { validators.updateValidatorScoreFromSigner(validator, uptime.unwrap()); } + function test_Reverts_WhenL2_WhenValidatorAndGroupMeetBalanceRequirements() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.distributeEpochPaymentsFromSigner(validator, maxPayment); + } + function test_ShouldPayValidator_WhenValidatorAndGroupMeetBalanceRequirements() public { validators.distributeEpochPaymentsFromSigner(validator, maxPayment); assertEq(stableToken.balanceOf(validator), expectedValidatorPayment); @@ -3037,6 +3159,13 @@ contract ValidatorsTest_ForceDeaffiliateIfValidator is ValidatorsTest { assertEq(affiliation, address(0)); } + function test_Reverts_WhenSenderIsWhitelistedSlashingAddress_WhenL2() public { + _whenL2(); + vm.prank(paymentDelegatee); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.forceDeaffiliateIfValidator(validator); + } + function test_Reverts_WhenSenderNotApprovedAddress() public { vm.expectRevert("Only registered slasher can call"); validators.forceDeaffiliateIfValidator(validator); @@ -3117,6 +3246,29 @@ contract ValidatorsTest_GroupMembershipInEpoch is ValidatorsTest { } } + function test_Reverts_GroupMembershipInEpoch_WhenL2() public { + _whenL2(); + for (uint256 i = 0; i < epochInfoList.length; i++) { + address _group = epochInfoList[i].groupy; + + if (epochInfoList.length.sub(i) <= membershipHistoryLength) { + vm.expectRevert("This method is not supported in L2 anymore."); + validators.groupMembershipInEpoch( + validator, + epochInfoList[i].epochNumber, + uint256(1).add(i) + ); + } else { + vm.expectRevert("This method is not supported in L2 anymore."); + validators.groupMembershipInEpoch( + validator, + epochInfoList[i].epochNumber, + uint256(1).add(i) + ); + } + } + } + function test_Reverts_WhenEpochNumberAtGivenIndexIsGreaterThanProvidedEpochNumber() public { vm.expectRevert("index out of bounds"); validators.groupMembershipInEpoch( @@ -3178,6 +3330,14 @@ contract ValidatorsTest_HalveSlashingMultiplier is ValidatorsTest { } } + function test_Reverts_HalveSlashingMultiplier_WhenL2() public { + _whenL2(); + FixidityLib.Fraction memory expectedMultiplier = FixidityLib.fixed1(); + vm.prank(paymentDelegatee); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.halveSlashingMultiplier(group); + } + function test_ShouldUpdateLastSlashedTimestamp() public { (, , , , , , uint256 initialLastSlashed) = validators.getValidatorGroup(group); @@ -3225,6 +3385,15 @@ contract ValidatorsTest_ResetSlashingMultiplier is ValidatorsTest { assertEq(actualMultiplier, FixidityLib.fixed1().unwrap()); } + function test_Reverts_WhenSlashingMultiplierIsResetAfterResetPeriod_WhenL2() public { + _whenL2(); + timeTravel(slashingMultiplierResetPeriod); + + vm.prank(group); + vm.expectRevert("This method is not supported in L2 anymore."); + validators.resetSlashingMultiplier(); + } + function test_Reverts_WhenSlashingMultiplierIsResetBeforeResetPeriod() public { vm.expectRevert("`resetSlashingMultiplier` called before resetPeriod expired"); vm.prank(group); @@ -3240,4 +3409,11 @@ contract ValidatorsTest_ResetSlashingMultiplier is ValidatorsTest { (, , , , , uint256 actualMultiplier, ) = validators.getValidatorGroup(group); assertEq(actualMultiplier, FixidityLib.fixed1().unwrap()); } + + function test_Reverts_SetSlashingMultiplierResetPeriod_WhenL2() public { + _whenL2(); + uint256 newResetPeriod = 10 * DAY; + vm.expectRevert("This method is not supported in L2 anymore."); + validators.setSlashingMultiplierResetPeriod(newResetPeriod); + } } diff --git a/packages/protocol/test-sol/governance/validators/mocks/ValidatorsMockTunnel.sol b/packages/protocol/test-sol/governance/validators/mocks/ValidatorsMockTunnel.sol new file mode 100644 index 00000000000..d22b40f300d --- /dev/null +++ b/packages/protocol/test-sol/governance/validators/mocks/ValidatorsMockTunnel.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.5.13; +pragma experimental ABIEncoderV2; + +import "@celo-contracts/governance/test/ValidatorsMock.sol"; +import { Test as ForgeTest } from "forge-std/Test.sol"; + +contract ValidatorsMockTunnel is ForgeTest { + ValidatorsMock private tunnelValidators; + address validatorContractAddress; + + constructor(address _validatorContractAddress) public { + validatorContractAddress = _validatorContractAddress; + tunnelValidators = ValidatorsMock(validatorContractAddress); + } + + struct InitParams { + address registryAddress; + uint256 groupRequirementValue; + uint256 groupRequirementDuration; + uint256 validatorRequirementValue; + uint256 validatorRequirementDuration; + uint256 validatorScoreExponent; + uint256 validatorScoreAdjustmentSpeed; + } + + struct InitParams2 { + uint256 _membershipHistoryLength; + uint256 _slashingMultiplierResetPeriod; + uint256 _maxGroupSize; + uint256 _commissionUpdateDelay; + uint256 _downtimeGracePeriod; + } + + function MockInitialize( + address sender, + InitParams calldata params, + InitParams2 calldata params2 + ) external returns (bool, bytes memory) { + bytes memory data = abi.encodeWithSignature( + "initialize(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)", + params.registryAddress, + params.groupRequirementValue, + params.groupRequirementDuration, + params.validatorRequirementValue, + params.validatorRequirementDuration, + params.validatorScoreExponent, + params.validatorScoreAdjustmentSpeed, + params2._membershipHistoryLength, + params2._slashingMultiplierResetPeriod, + params2._maxGroupSize, + params2._commissionUpdateDelay, + params2._downtimeGracePeriod + ); + vm.prank(sender); + (bool success, ) = address(tunnelValidators).call(data); + require(success, "unsuccessful tunnel call"); + } +} diff --git a/packages/protocol/test-sol/governance/voting/ReleaseGold.t.sol b/packages/protocol/test-sol/governance/voting/ReleaseGold.t.sol index 8cbe99a3ace..1d60e8265e5 100644 --- a/packages/protocol/test-sol/governance/voting/ReleaseGold.t.sol +++ b/packages/protocol/test-sol/governance/voting/ReleaseGold.t.sol @@ -15,89 +15,13 @@ import "../../../contracts/common/Freezer.sol"; import "../../../contracts/common/GoldToken.sol"; import "../../../contracts/governance/LockedGold.sol"; import "../../../contracts/governance/ReleaseGold.sol"; +import "./mocks/ReleaseGoldMockTunnel.sol"; import "../../../contracts/stability/test/MockStableToken.sol"; import "../../../contracts/governance/test/MockElection.sol"; import "../../../contracts/governance/test/MockGovernance.sol"; import "../../../contracts/governance/test/MockValidators.sol"; import "@test-sol/utils/ECDSAHelper.sol"; -contract ReleaseGoldMockTunnel is ForgeTest { - ReleaseGold private releaseGoldTunnel; - address payable releaseGoldContractAddress; - - struct InitParams { - uint256 releaseStartTime; - uint256 releaseCliffTime; - uint256 numReleasePeriods; - uint256 releasePeriod; - uint256 amountReleasedPerPeriod; - bool revocable; - address payable _beneficiary; - } - - struct InitParams2 { - address _releaseOwner; - address payable _refundAddress; - bool subjectToLiquidityProvision; - uint256 initialDistributionRatio; - bool _canValidate; - bool _canVote; - address registryAddress; - } - - constructor(address _releaseGoldContractAddress) public { - releaseGoldContractAddress = address(uint160(_releaseGoldContractAddress)); - releaseGoldTunnel = ReleaseGold(releaseGoldContractAddress); - } - - function MockInitialize( - address sender, - InitParams calldata params, - InitParams2 calldata params2 - ) external returns (bool, bytes memory) { - bytes4 selector = bytes4( - keccak256( - "initialize(uint256,uint256,uint256,uint256,uint256,bool,address,address,address,bool,uint256,bool,bool,address)" - ) - ); - - bytes memory dataFirstHalf; - { - // Encode the first half of the parameters - dataFirstHalf = abi.encode( - params.releaseStartTime, - params.releaseCliffTime, - params.numReleasePeriods, - params.releasePeriod, - params.amountReleasedPerPeriod, - params.revocable, - params._beneficiary - ); - } - - bytes memory dataSecondHalf; - { - // Encode the second half of the parameters - dataSecondHalf = abi.encode( - params2._releaseOwner, - params2._refundAddress, - params2.subjectToLiquidityProvision, - params2.initialDistributionRatio, - params2._canValidate, - params2._canVote, - params2.registryAddress - ); - } - - // Concatenate the selector, first half, and second half - bytes memory data = abi.encodePacked(selector, dataFirstHalf, dataSecondHalf); - - vm.prank(sender); - (bool success, ) = address(releaseGoldTunnel).call(data); - require(success, "unsuccessful tunnel call"); - } -} - contract ReleaseGoldTest is Test, ECDSAHelper { using FixidityLib for FixidityLib.Fraction; diff --git a/packages/protocol/test-sol/governance/voting/mocks/ReleaseGoldMockTunnel.sol b/packages/protocol/test-sol/governance/voting/mocks/ReleaseGoldMockTunnel.sol new file mode 100644 index 00000000000..161b377fcb0 --- /dev/null +++ b/packages/protocol/test-sol/governance/voting/mocks/ReleaseGoldMockTunnel.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.5.13; +pragma experimental ABIEncoderV2; + +import "../../../../contracts/governance/ReleaseGold.sol"; +import { Test as ForgeTest } from "forge-std/Test.sol"; + +contract ReleaseGoldMockTunnel is ForgeTest { + ReleaseGold private releaseGoldTunnel; + address payable releaseGoldContractAddress; + + struct InitParams { + uint256 releaseStartTime; + uint256 releaseCliffTime; + uint256 numReleasePeriods; + uint256 releasePeriod; + uint256 amountReleasedPerPeriod; + bool revocable; + address payable _beneficiary; + } + + struct InitParams2 { + address _releaseOwner; + address payable _refundAddress; + bool subjectToLiquidityProvision; + uint256 initialDistributionRatio; + bool _canValidate; + bool _canVote; + address registryAddress; + } + + constructor(address _releaseGoldContractAddress) public { + releaseGoldContractAddress = address(uint160(_releaseGoldContractAddress)); + releaseGoldTunnel = ReleaseGold(releaseGoldContractAddress); + } + + function MockInitialize( + address sender, + InitParams calldata params, + InitParams2 calldata params2 + ) external returns (bool, bytes memory) { + bytes4 selector = bytes4( + keccak256( + "initialize(uint256,uint256,uint256,uint256,uint256,bool,address,address,address,bool,uint256,bool,bool,address)" + ) + ); + + bytes memory dataFirstHalf; + { + // Encode the first half of the parameters + dataFirstHalf = abi.encode( + params.releaseStartTime, + params.releaseCliffTime, + params.numReleasePeriods, + params.releasePeriod, + params.amountReleasedPerPeriod, + params.revocable, + params._beneficiary + ); + } + + bytes memory dataSecondHalf; + { + // Encode the second half of the parameters + dataSecondHalf = abi.encode( + params2._releaseOwner, + params2._refundAddress, + params2.subjectToLiquidityProvision, + params2.initialDistributionRatio, + params2._canValidate, + params2._canVote, + params2.registryAddress + ); + } + + // Concatenate the selector, first half, and second half + bytes memory data = abi.encodePacked(selector, dataFirstHalf, dataSecondHalf); + + vm.prank(sender); + (bool success, ) = address(releaseGoldTunnel).call(data); + require(success, "unsuccessful tunnel call"); + } +} diff --git a/packages/protocol/test-sol/integration/RevokeCeloAfterL2Transition.sol b/packages/protocol/test-sol/integration/RevokeCeloAfterL2Transition.sol new file mode 100644 index 00000000000..b6e1f0e9759 --- /dev/null +++ b/packages/protocol/test-sol/integration/RevokeCeloAfterL2Transition.sol @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.5.13; +pragma experimental ABIEncoderV2; + +import "celo-foundry/Test.sol"; + +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "@celo-contracts/common/FixidityLib.sol"; +import "@celo-contracts/common/Registry.sol"; +import "@celo-contracts/common/Accounts.sol"; +import "@celo-contracts/common/GoldToken.sol"; + +import "@celo-contracts/governance/Election.sol"; +import "@celo-contracts/governance/LockedGold.sol"; +import "@celo-contracts/governance/ReleaseGold.sol"; + +import "@celo-contracts/stability/test/MockStableToken.sol"; +import "@celo-contracts/governance/Election.sol"; +import "@celo-contracts/governance/Governance.sol"; + +import "@celo-contracts/governance/test/ValidatorsMock.sol"; +import "@test-sol/constants.sol"; +import "@test-sol/utils/ECDSAHelper.sol"; +import { Utils } from "@test-sol/utils.sol"; +import { Test as ForgeTest } from "forge-std/Test.sol"; +import "../governance/validators/mocks/ValidatorsMockTunnel.sol"; +import "../governance/voting/mocks/ReleaseGoldMockTunnel.sol"; + +contract RevokeCeloAfterL2Transition is Test, Constants, ECDSAHelper, Utils { + using FixidityLib for FixidityLib.Fraction; + + address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; + uint256 constant TOTAL_AMOUNT = 1 ether * 1_000_000; + + Registry registry; + Accounts accounts; + MockStableToken stableToken; + Election election; + ValidatorsMockTunnel public validatorsMockTunnel; + Validators public validators; + LockedGold lockedGold; + Governance governance; + GoldToken goldToken; + ReleaseGold releaseGold; + + address owner; + address accApprover; + + address group; + address member; + address validator; + uint256 validatorPk; + address beneficiary; + address refundAddress; + address releaseOwner; + + address authorizedValidatorSigner; + uint256 authorizedValidatorSignerPK; + address authorizedVoteSigner; + uint256 authorizedVoteSignerPK; + address authorizedValidatorSigner2; + uint256 authorizedValidatorSignerPK2; + address authorizedVoteSigner2; + uint256 authorizedVoteSignerPK2; + + bytes public constant blsPublicKey = + abi.encodePacked( + bytes32(0x0101010101010101010101010101010101010101010101010101010101010101), + bytes32(0x0202020202020202020202020202020202020202020202020202020202020202), + bytes32(0x0303030303030303030303030303030303030303030303030303030303030303) + ); + bytes public constant blsPop = + abi.encodePacked( + bytes16(0x04040404040404040404040404040404), + bytes16(0x05050505050505050505050505050505), + bytes16(0x06060606060606060606060606060606) + ); + + struct ValidatorLockedGoldRequirements { + uint256 value; + uint256 duration; + } + + struct GroupLockedGoldRequirements { + uint256 value; + uint256 duration; + } + + struct ValidatorScoreParameters { + uint256 exponent; + FixidityLib.Fraction adjustmentSpeed; + } + + uint256 constant DEPOSIT = 5; + uint256 constant VOTER_GOLD = 100; + uint256 constant REFERENDUM_STAGE_DURATION = 5 * 60; + uint256 constant CONCURRENT_PROPOSALS = 1; + uint256 constant DEQUEUE_FREQUENCY = 10 * 60; + uint256 constant QUERY_EXPIRY = 60 * 60; + uint256 constant EXECUTION_STAGE_DURATION = 1 * 60; + + uint256 unlockingPeriod; + + FixidityLib.Fraction public commission = FixidityLib.newFixedFraction(1, 100); + + ValidatorLockedGoldRequirements public originalValidatorLockedGoldRequirements; + GroupLockedGoldRequirements public originalGroupLockedGoldRequirements; + ValidatorScoreParameters public originalValidatorScoreParameters; + + uint256 expectedParticipationBaseline; + FixidityLib.Fraction baselineUpdateFactor; + FixidityLib.Fraction participationBaseline; + FixidityLib.Fraction participationFloor; + FixidityLib.Fraction baselineQuorumFactor; + + ValidatorsMockTunnel.InitParams public initParams; + ValidatorsMockTunnel.InitParams2 public initParams2; + + ReleaseGoldMockTunnel.InitParams releaseGoldInitParams; + ReleaseGoldMockTunnel.InitParams2 releaseGoldInitParams2; + + uint256 validatorRegistrationEpochNumber; + + function setUp() public { + owner = address(this); + accApprover = actor("approver"); + group = actor("group"); + member = actor("member"); + beneficiary = actor("beneficiary"); + refundAddress = actor("refundAddress"); + releaseOwner = actor("releaseOwner"); + + (validator, validatorPk) = actorWithPK("validator"); + (authorizedValidatorSigner, authorizedValidatorSignerPK) = actorWithPK( + "authorizedValidatorSigner" + ); + (authorizedVoteSigner, authorizedVoteSignerPK) = actorWithPK("authorizedVoteSigner"); + (authorizedValidatorSigner2, authorizedValidatorSignerPK2) = actorWithPK( + "authorizedValidatorSigner2" + ); + (authorizedVoteSigner2, authorizedVoteSignerPK2) = actorWithPK("authorizedVoteSigner2"); + + uint256 electableValidatorsMin = 4; + uint256 electableValidatorsMax = 6; + uint256 maxNumGroupsVotedFor = 3; + uint256 electabilityThreshold = FixidityLib.newFixedFraction(1, 100).unwrap(); + + unlockingPeriod = 3 * DAY; + + uint256 slashingMultiplierResetPeriod = 30 * DAY; + uint256 membershipHistoryLength = 5; + uint256 maxGroupSize = 5; + uint256 commissionUpdateDelay = 3; + uint256 downtimeGracePeriod = 0; + + baselineUpdateFactor = FixidityLib.newFixedFraction(1, 5); + participationBaseline = FixidityLib.newFixedFraction(5, 10); + participationFloor = FixidityLib.newFixedFraction(5, 100); + baselineQuorumFactor = FixidityLib.fixed1(); + expectedParticipationBaseline = FixidityLib + .multiply(baselineUpdateFactor, FixidityLib.fixed1()) + .add( + FixidityLib.multiply( + FixidityLib.fixed1().subtract(baselineUpdateFactor), + participationBaseline + ) + ) + .unwrap(); + + address registryAddress = 0x000000000000000000000000000000000000ce10; + deployCodeTo("Registry.sol", abi.encode(false), registryAddress); + registry = Registry(registryAddress); + + accounts = new Accounts(true); + stableToken = new MockStableToken(); + election = new Election(true); + lockedGold = new LockedGold(true); + validators = new Validators(true); + validatorsMockTunnel = new ValidatorsMockTunnel(address(validators)); + governance = new Governance(true); + goldToken = new GoldToken(true); + releaseGold = new ReleaseGold(true); + + registry.setAddressFor(AccountsContract, address(accounts)); + registry.setAddressFor(ElectionContract, address(election)); + registry.setAddressFor(StableTokenContract, address(stableToken)); + registry.setAddressFor(LockedGoldContract, address(lockedGold)); + registry.setAddressFor(ValidatorsContract, address(validators)); + registry.setAddressFor(GovernanceContract, address(governance)); + registry.setAddressFor(GoldTokenContract, address(goldToken)); + + goldToken.initialize(address(registry)); + + accounts.initialize(registryAddress); + + releaseGold = new ReleaseGold(true); + + releaseGoldInitParams = ReleaseGoldMockTunnel.InitParams({ + releaseStartTime: block.timestamp + 5 * MINUTE, + releaseCliffTime: HOUR, + numReleasePeriods: 4, + releasePeriod: 3 * MONTH, + amountReleasedPerPeriod: TOTAL_AMOUNT / 4, + revocable: false, + _beneficiary: address(uint160(beneficiary)) + }); + + releaseGoldInitParams2 = ReleaseGoldMockTunnel.InitParams2({ + _releaseOwner: releaseOwner, + _refundAddress: address(0), + subjectToLiquidityProvision: false, + initialDistributionRatio: 1000, + _canValidate: true, + _canVote: true, + registryAddress: registryAddress + }); + + ReleaseGoldMockTunnel tunnel = new ReleaseGoldMockTunnel(address(releaseGold)); + tunnel.MockInitialize(owner, releaseGoldInitParams, releaseGoldInitParams2); + + election.initialize( + registryAddress, + electableValidatorsMin, + electableValidatorsMax, + maxNumGroupsVotedFor, + electabilityThreshold + ); + + lockedGold.initialize(address(registry), unlockingPeriod); + + originalValidatorLockedGoldRequirements = ValidatorLockedGoldRequirements({ + value: 1000, + duration: 60 * DAY + }); + + originalGroupLockedGoldRequirements = GroupLockedGoldRequirements({ + value: 1000, + duration: 100 * DAY + }); + + originalValidatorScoreParameters = ValidatorScoreParameters({ + exponent: 5, + adjustmentSpeed: FixidityLib.newFixedFraction(5, 20) + }); + + initParams = ValidatorsMockTunnel.InitParams({ + registryAddress: registryAddress, + groupRequirementValue: originalGroupLockedGoldRequirements.value, + groupRequirementDuration: originalGroupLockedGoldRequirements.duration, + validatorRequirementValue: originalValidatorLockedGoldRequirements.value, + validatorRequirementDuration: originalValidatorLockedGoldRequirements.duration, + validatorScoreExponent: originalValidatorScoreParameters.exponent, + validatorScoreAdjustmentSpeed: originalValidatorScoreParameters.adjustmentSpeed.unwrap() + }); + initParams2 = ValidatorsMockTunnel.InitParams2({ + _membershipHistoryLength: membershipHistoryLength, + _slashingMultiplierResetPeriod: slashingMultiplierResetPeriod, + _maxGroupSize: maxGroupSize, + _commissionUpdateDelay: commissionUpdateDelay, + _downtimeGracePeriod: downtimeGracePeriod + }); + + validatorsMockTunnel.MockInitialize(owner, initParams, initParams2); + + governance.initialize( + address(registry), + accApprover, + CONCURRENT_PROPOSALS, + DEPOSIT, + QUERY_EXPIRY, + DEQUEUE_FREQUENCY, + REFERENDUM_STAGE_DURATION, + EXECUTION_STAGE_DURATION, + participationBaseline.unwrap(), + participationFloor.unwrap(), + baselineUpdateFactor.unwrap(), + baselineQuorumFactor.unwrap() + ); + + accounts.createAccount(); + + vm.deal(address(releaseGold), TOTAL_AMOUNT); + } + + function _whenL2() public { + deployCodeTo("Registry.sol", abi.encode(false), proxyAdminAddress); + } + + function _registerValidatorGroupHelper(address _group, uint256 numMembers) internal { + vm.startPrank(_group); + if (!accounts.isAccount(_group)) { + accounts.createAccount(); + } + vm.deal(_group, 10000e18); + lockedGold.lock.value(10000e18)(); + validators.registerValidatorGroup(commission.unwrap()); + vm.stopPrank(); + } + + function _registerValidatorGroupWithMembers(address _group, uint256 _numMembers) public { + _registerValidatorGroupHelper(_group, _numMembers); + + for (uint256 i = 0; i < _numMembers; i++) { + if (i == 0) { + _registerValidatorHelper(validator, validatorPk); + + vm.prank(validator); + validators.affiliate(group); + + vm.prank(group); + validators.addFirstMember(validator, address(0), address(0)); + } else { + uint256 _validator1Pk = i; + address _validator1 = vm.addr(_validator1Pk); + + vm.prank(_validator1); + accounts.createAccount(); + _registerValidatorHelper(_validator1, _validator1Pk); + vm.prank(_validator1); + validators.affiliate(group); + + vm.prank(group); + validators.addMember(_validator1); + } + } + } + + function _registerValidatorHelper( + address _validator, + uint256 _validatorPk + ) internal returns (bytes memory) { + if (!accounts.isAccount(_validator)) { + vm.prank(_validator); + accounts.createAccount(); + } + + vm.deal(_validator, 10000e18); + vm.prank(_validator); + lockedGold.lock.value(10000e18)(); + + bytes memory _ecdsaPubKey = _generateEcdsaPubKey(_validator, _validatorPk); + + ph.mockSuccess(ph.PROOF_OF_POSSESSION(), abi.encodePacked(_validator, blsPublicKey, blsPop)); + + vm.prank(_validator); + validators.registerValidator(_ecdsaPubKey, blsPublicKey, blsPop); + validatorRegistrationEpochNumber = validators.getEpochNumber(); + return _ecdsaPubKey; + } + + function _generateEcdsaPubKey( + address _account, + uint256 _accountPk + ) internal returns (bytes memory ecdsaPubKey) { + (uint8 v, bytes32 r, bytes32 s) = getParsedSignatureOfAddress(_account, _accountPk); + bytes32 addressHash = keccak256(abi.encodePacked(_account)); + + ecdsaPubKey = addressToPublicKey(addressHash, v, r, s); + } + + function getParsedSignatureOfAddress( + address _address, + uint256 privateKey + ) public pure returns (uint8, bytes32, bytes32) { + bytes32 addressHash = keccak256(abi.encodePacked(_address)); + bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(addressHash); + return vm.sign(privateKey, prefixedHash); + } + + function() external payable {} +} + +contract RevokeCeloAfterL2TransitionTest is RevokeCeloAfterL2Transition { + function test_revoke_celo_after_l2_transition() public { + uint256 lockedGoldValue = 1e18; + uint256 active = 12; + uint256 pending = 11; + + deal(address(this), lockedGoldValue); + lockedGold.lock.value(lockedGoldValue)(); + _registerValidatorGroupWithMembers(group, 1); + + election.vote(group, active, address(0), address(0)); + blockTravel(EPOCH_SIZE + 1); + election.activate(group); + election.vote(group, pending, address(0), address(0)); + + assertEq( + lockedGold.getAccountNonvotingLockedGold(address(this)), + lockedGoldValue - active - pending + ); + assertEq(lockedGold.getAccountTotalLockedGold(address(this)), lockedGoldValue); + assertEq(election.getPendingVotesForGroupByAccount(group, address(this)), pending); + assertEq(election.getActiveVotesForGroupByAccount(group, address(this)), active); + + _whenL2(); + + election.revokeActive(group, active, address(0), address(0), 0); + election.revokePending(group, pending, address(0), address(0), 0); + + assertEq(lockedGoldValue, lockedGold.getAccountNonvotingLockedGold(address(this))); + + lockedGold.unlock(lockedGoldValue); + timeTravel(unlockingPeriod + 1); + lockedGold.withdraw(0); + assertEq(address(this).balance, lockedGoldValue); + } + + function test_validatorCanRemoveCelo_WhenTransitionedToL2() public { + _registerValidatorGroupWithMembers(group, 1); + + _whenL2(); + + vm.startPrank(validator); + validators.deaffiliate(); + timeTravel(originalValidatorLockedGoldRequirements.duration + 1); + validators.deregisterValidator(0); + + uint256 totalLockedCelo = lockedGold.getAccountTotalLockedGold(validator); + + lockedGold.unlock(totalLockedCelo); + + timeTravel(unlockingPeriod + 1); + lockedGold.withdraw(0); + + assertEq(validator.balance, 10000e18); + + vm.stopPrank(); + } + + function test_validatorGroupCanRemoveCelo_WhenTransitionedToL2() public { + _registerValidatorGroupWithMembers(group, 1); + + _whenL2(); + + vm.prank(validator); + validators.deaffiliate(); + timeTravel(originalValidatorLockedGoldRequirements.duration + 1); + vm.prank(validator); + validators.deregisterValidator(0); + + vm.startPrank(group); + timeTravel(originalGroupLockedGoldRequirements.duration + 1); + validators.deregisterValidatorGroup(0); + + uint256 totalLockedCelo = lockedGold.getAccountTotalLockedGold(group); + lockedGold.unlock(totalLockedCelo); + + timeTravel(unlockingPeriod + 1); + lockedGold.withdraw(0); + + assertEq(group.balance, 10000e18); + + vm.stopPrank(); + } + + function _generateEcdsaPubKeyWithSigner( + address _validator, + uint256 _signerPk + ) internal returns (bytes memory ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) { + (v, r, s) = getParsedSignatureOfAddress(_validator, _signerPk); + + bytes32 addressHash = keccak256(abi.encodePacked(_validator)); + + ecdsaPubKey = addressToPublicKey(addressHash, v, r, s); + } + + function _registerValidatorWithSignerHelper( + address _validator, + uint256 signerPk + ) internal returns (bytes memory) { + (bytes memory _ecdsaPubKey, uint8 v, bytes32 r, bytes32 s) = _generateEcdsaPubKeyWithSigner( + _validator, + signerPk + ); + + ph.mockSuccess(ph.PROOF_OF_POSSESSION(), abi.encodePacked(_validator, blsPublicKey, blsPop)); + + vm.prank(_validator); + validators.registerValidator(_ecdsaPubKey, blsPublicKey, blsPop); + validatorRegistrationEpochNumber = validators.getEpochNumber(); + return _ecdsaPubKey; + } + + function test_releaseGoldOwnerHasValidator_CanRemoveCelo_WhenTransitionedToL2() public { + _registerValidatorGroupWithMembers(group, 1); + + (uint8 vValidator, bytes32 rValidator, bytes32 sValidator) = getParsedSignatureOfAddress( + address(releaseGold), + authorizedValidatorSignerPK + ); + (uint8 vVote, bytes32 rVote, bytes32 sVote) = getParsedSignatureOfAddress( + address(releaseGold), + authorizedVoteSignerPK + ); + + vm.startPrank(beneficiary); + releaseGold.createAccount(); + releaseGold.authorizeValidatorSigner( + address(uint160(authorizedValidatorSigner)), + vValidator, + rValidator, + sValidator + ); + releaseGold.authorizeVoteSigner(address(uint160(authorizedVoteSigner)), vVote, rVote, sVote); + releaseGold.lockGold(TOTAL_AMOUNT - 10 ether); + vm.stopPrank(); + + _registerValidatorWithSignerHelper(address(releaseGold), authorizedValidatorSignerPK); + vm.prank(authorizedValidatorSigner); + validators.affiliate(group); + + uint256 active = 12; + uint256 pending = 11; + + vm.startPrank(authorizedVoteSigner); + election.vote(group, active, address(0), address(0)); + blockTravel(EPOCH_SIZE + 1); + election.activate(group); + election.vote(group, pending, address(0), address(0)); + vm.stopPrank(); + + assertEq( + lockedGold.getAccountNonvotingLockedGold(address(releaseGold)), + TOTAL_AMOUNT - 10 ether - active - pending + ); + assertEq(lockedGold.getAccountTotalLockedGold(address(releaseGold)), TOTAL_AMOUNT - 10 ether); + assertEq(election.getPendingVotesForGroupByAccount(group, address(releaseGold)), pending); + assertEq(election.getActiveVotesForGroupByAccount(group, address(releaseGold)), active); + + _whenL2(); + + (uint8 vVote2, bytes32 rVote2, bytes32 sVote2) = getParsedSignatureOfAddress( + address(releaseGold), + authorizedVoteSignerPK2 + ); + + vm.startPrank(beneficiary); + releaseGold.authorizeVoteSigner( + address(uint160(authorizedVoteSigner2)), + vVote2, + rVote2, + sVote2 + ); + + vm.startPrank(authorizedVoteSigner2); + + election.revokeActive(group, active, address(0), address(0), 0); + election.revokePending(group, pending, address(0), address(0), 0); + + assertEq( + lockedGold.getAccountNonvotingLockedGold(address(releaseGold)), + TOTAL_AMOUNT - 10 ether + ); + vm.stopPrank(); + + vm.startPrank(authorizedValidatorSigner); + validators.deaffiliate(); + timeTravel(originalValidatorLockedGoldRequirements.duration + 1); + validators.deregisterValidator(1); + vm.stopPrank(); + + uint256 totalLockedCelo = lockedGold.getAccountTotalLockedGold(address(releaseGold)); + vm.startPrank(beneficiary); + releaseGold.unlockGold(totalLockedCelo); + + timeTravel(unlockingPeriod + 1); + releaseGold.withdrawLockedGold(0); + assertEq(address(releaseGold).balance, TOTAL_AMOUNT - 2 ether); + } +} diff --git a/packages/protocol/test-sol/voting/Election.t.sol b/packages/protocol/test-sol/voting/Election.t.sol index 9ff4d85d01b..a1ab398cea4 100644 --- a/packages/protocol/test-sol/voting/Election.t.sol +++ b/packages/protocol/test-sol/voting/Election.t.sol @@ -28,6 +28,8 @@ contract ElectionMock is Election(true) { contract ElectionTest is Utils, Constants { using FixidityLib for FixidityLib.Fraction; + address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; + event ElectableValidatorsSet(uint256 min, uint256 max); event MaxNumGroupsVotedForSet(uint256 maxNumGroupsVotedFor); event ElectabilityThresholdSet(uint256 electabilityThreshold); @@ -146,6 +148,10 @@ contract ElectionTest is Utils, Constants { electabilityThreshold ); } + + function _whenL2() public { + deployCodeTo("Registry.sol", abi.encode(false), proxyAdminAddress); + } } contract ElectionTest_Initialize is ElectionTest { @@ -190,6 +196,12 @@ contract Election_SetElectabilityThreshold is ElectionTest { vm.expectRevert("Electability threshold must be lower than 100%"); election.setElectabilityThreshold(FixidityLib.fixed1().unwrap() + 1); } + + function test_Revert_setElectabilityThreshold_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + election.setElectabilityThreshold(FixidityLib.fixed1().unwrap() + 1); + } } contract Election_SetElectableValidators is ElectionTest { @@ -202,6 +214,14 @@ contract Election_SetElectableValidators is ElectionTest { assertEq(max, newElectableValidatorsMax); } + function test_Reverts_shouldSetElectableValidators_WhenL2() public { + _whenL2(); + uint256 newElectableValidatorsMin = 2; + uint256 newElectableValidatorsMax = 4; + vm.expectRevert("This method is not supported in L2 anymore."); + election.setElectableValidators(newElectableValidatorsMin, newElectableValidatorsMax); + } + function test_ShouldEmitTheElectableValidatorsSetEvent() public { uint256 newElectableValidatorsMin = 2; uint256 newElectableValidatorsMax = 4; @@ -239,6 +259,13 @@ contract Election_SetMaxNumGroupsVotedFor is ElectionTest { assertEq(election.maxNumGroupsVotedFor(), newMaxNumGroupsVotedFor); } + function test_Revert_SetMaxNumGroupsVotedFor_WhenL2() public { + _whenL2(); + uint256 newMaxNumGroupsVotedFor = 4; + vm.expectRevert("This method is not supported in L2 anymore."); + election.setMaxNumGroupsVotedFor(newMaxNumGroupsVotedFor); + } + function test_ShouldEmitMaxNumGroupsVotedForSetEvent() public { uint256 newMaxNumGroupsVotedFor = 4; vm.expectEmit(true, false, false, false); @@ -264,6 +291,12 @@ contract Election_SetAllowedToVoteOverMaxNumberOfGroups is ElectionTest { assertEq(election.allowedToVoteOverMaxNumberOfGroups(address(this)), true); } + function test_Revert_SetAllowedToVoteOverMaxNumberOfGroups_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + election.setAllowedToVoteOverMaxNumberOfGroups(true); + } + function test_ShouldRevertWhenCalledByValidator() public { validators.setValidator(address(this)); vm.expectRevert("Validators cannot vote for more than max number of groups"); @@ -312,6 +345,13 @@ contract Election_MarkGroupEligible is ElectionTest { assertEq(eligibleGroups[0], group); } + function test_Revert_MarkGroupEligible_WhenL2() public { + _whenL2(); + address group = address(this); + vm.expectRevert("This method is not supported in L2 anymore."); + election.markGroupEligible(group, address(0), address(0)); + } + function test_ShouldEmitValidatorGroupMarkedEligibleEvent() public { address group = address(this); vm.expectEmit(true, false, false, false); @@ -425,6 +465,13 @@ contract Election_Vote_WhenGroupEligible is ElectionTest { assertEq(election.getPendingVotesForGroupByAccount(group, voter), value - maxNumGroupsVotedFor); } + function test_Revert_Vote_WhenL2() public { + _whenL2(); + + vm.expectRevert("This method is not supported in L2 anymore."); + election.vote(group, value - maxNumGroupsVotedFor, address(0), address(0)); + } + function test_ShouldSetTotalVotesByAccount_WhenMaxNumberOfGroupsWasNotReached() public { WhenVotedForMaxNumberOfGroups(); assertEq(election.getTotalVotesByAccount(voter), maxNumGroupsVotedFor); @@ -811,6 +858,13 @@ contract Election_Activate is ElectionTest { election.activate(group); } + function test_Revert_Activate_WhenL2() public { + WhenVoterHasPendingVotes(); + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + election.activate(group); + } + address voter2 = account2; uint256 value2 = 573; @@ -885,6 +939,12 @@ contract Election_Activate is ElectionTest { election.activateForAccount(group, voter); } + function test_Revert_ActivateForAccount_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + election.activateForAccount(group, voter); + } + function test_ShouldRevert_WhenTheVoterDoesNotHavePendingVotes() public { vm.expectRevert("Vote value cannot be zero"); election.activate(group); @@ -1887,6 +1947,13 @@ contract Election_DistributeEpochRewards is ElectionTest { assertEq(election.getActiveVotesForGroupByAccount(group, voter), voteValue + rewardValue); } + function test_Revert_DistributeEpochRewards_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + vm.prank(address(0)); + election.distributeEpochRewards(group, rewardValue, address(0), address(0)); + } + function test_ShouldIncrementAccountTotalVotesForGroup_WhenThereIsSingleGroupWithActiveVotes() public { @@ -2641,3 +2708,12 @@ contract Election_ConsistencyChecks is ElectionTest { revokeAllAndCheckInvariants(100); } } + +contract Election_HasActivatablePendingVotes is ElectionTest { + function test_Revert_hasActivatablePendingVotes_WhenL2() public { + _whenL2(); + vm.expectRevert("This method is not supported in L2 anymore."); + vm.prank(address(0)); + election.hasActivatablePendingVotes(address(0), address(0)); + } +}