diff --git a/.github/workflows/protocol_tests.yml b/.github/workflows/protocol_tests.yml index 874fa50f66b..dd683c81f2e 100644 --- a/.github/workflows/protocol_tests.yml +++ b/.github/workflows/protocol_tests.yml @@ -88,8 +88,7 @@ jobs: if: success() || failure() run: | forge test -vvv \ - --match-path "test-sol/unit/governance/voting/*" \ - --block-gas-limit 50000000 + --match-path "test-sol/unit/governance/voting/*" - name: Run unit tests stability if: success() || failure() diff --git a/packages/protocol/contracts/common/UsingPrecompiles.sol b/packages/protocol/contracts/common/UsingPrecompiles.sol index a09eea3410b..017ad69bd33 100644 --- a/packages/protocol/contracts/common/UsingPrecompiles.sol +++ b/packages/protocol/contracts/common/UsingPrecompiles.sol @@ -2,8 +2,9 @@ pragma solidity ^0.5.13; import "openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; +import "../../contracts-0.8/common/IsL2Check.sol"; -contract UsingPrecompiles { +contract UsingPrecompiles is IsL2Check { using SafeMath for uint256; address constant TRANSFER = address(0xff - 2); @@ -16,6 +17,7 @@ contract UsingPrecompiles { address constant HASH_HEADER = address(0xff - 9); address constant GET_PARENT_SEAL_BITMAP = address(0xff - 10); address constant GET_VERIFIED_SEAL_BITMAP = address(0xff - 11); + uint256 constant DAY = 86400; /** * @notice calculate a * b^x for fractions a, b to `decimals` precision @@ -55,11 +57,15 @@ contract UsingPrecompiles { * @return The current epoch size in blocks. */ function getEpochSize() public view returns (uint256) { - bytes memory out; - bool success; - (success, out) = EPOCH_SIZE.staticcall(abi.encodePacked(true)); - require(success, "error calling getEpochSize precompile"); - return getUint256FromBytes(out, 0); + if (isL2()) { + return DAY.div(5); + } else { + bytes memory out; + bool success; + (success, out) = EPOCH_SIZE.staticcall(abi.encodePacked(true)); + require(success, "error calling getEpochSize precompile"); + return getUint256FromBytes(out, 0); + } } /** diff --git a/packages/protocol/contracts/governance/BlockchainParameters.sol b/packages/protocol/contracts/governance/BlockchainParameters.sol index 8d358602344..7162d7e5ab4 100644 --- a/packages/protocol/contracts/governance/BlockchainParameters.sol +++ b/packages/protocol/contracts/governance/BlockchainParameters.sol @@ -5,12 +5,10 @@ import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; import "../common/Initializable.sol"; import "../common/UsingPrecompiles.sol"; -import "../../contracts-0.8/common/IsL2Check.sol"; - /** * @title Contract for storing blockchain parameters that can be set by governance. */ -contract BlockchainParameters is Ownable, Initializable, UsingPrecompiles, IsL2Check { +contract BlockchainParameters is Ownable, Initializable, UsingPrecompiles { using SafeMath for uint256; // obsolete diff --git a/packages/protocol/contracts/governance/Election.sol b/packages/protocol/contracts/governance/Election.sol index 33c05c82ca6..8a7633599cd 100644 --- a/packages/protocol/contracts/governance/Election.sol +++ b/packages/protocol/contracts/governance/Election.sol @@ -15,7 +15,6 @@ 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, @@ -25,8 +24,7 @@ contract Election is Initializable, UsingRegistry, UsingPrecompiles, - CalledByVm, - IsL2Check + CalledByVm { using AddressSortedLinkedList for SortedLinkedList.List; using FixidityLib for FixidityLib.Fraction; @@ -198,7 +196,7 @@ contract Election is uint256 value, address lesser, address greater - ) external nonReentrant onlyL1 returns (bool) { + ) external nonReentrant 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"); @@ -231,7 +229,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 onlyL1 returns (bool) { + function activate(address group) external nonReentrant returns (bool) { address account = getAccounts().voteSignerToAccount(msg.sender); return _activate(group, account); } @@ -243,10 +241,7 @@ 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 onlyL1 returns (bool) { + function activateForAccount(address group, address account) external nonReentrant returns (bool) { return _activate(group, account); } @@ -369,7 +364,7 @@ contract Election is address group, address lesser, address greater - ) external onlyL1 onlyRegisteredContract(VALIDATORS_REGISTRY_ID) { + ) external onlyRegisteredContract(VALIDATORS_REGISTRY_ID) { uint256 value = getTotalVotesForGroup(group); votes.total.eligible.insert(group, value, lesser, greater); emit ValidatorGroupMarkedEligible(group); @@ -544,7 +539,7 @@ contract Election is address group, uint256 totalEpochRewards, uint256[] calldata uptimes - ) external view returns (uint256) { + ) external view onlyL1 returns (uint256) { IValidators validators = getValidators(); // The group must meet the balance requirements for their voters to receive epoch rewards. if (!validators.meetsAccountLockedGoldRequirements(group) || votes.active.total <= 0) { @@ -577,10 +572,7 @@ 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 onlyL1 returns (bool) { + function hasActivatablePendingVotes(address account, address group) external view returns (bool) { PendingVote storage pendingVote = votes.pending.forGroup[group].byAccount[account]; return pendingVote.epoch < getEpochNumber() && pendingVote.value > 0; } @@ -619,7 +611,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 onlyL1 returns (bool) { + function setElectableValidators(uint256 min, uint256 max) public onlyOwner returns (bool) { require(0 < min, "Minimum electable validators cannot be zero"); require(min <= max, "Maximum electable validators cannot be smaller than minimum"); require( @@ -636,9 +628,7 @@ 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 onlyL1 returns (bool) { + function setMaxNumGroupsVotedFor(uint256 _maxNumGroupsVotedFor) public onlyOwner returns (bool) { require(_maxNumGroupsVotedFor != maxNumGroupsVotedFor, "Max groups voted for not changed"); maxNumGroupsVotedFor = _maxNumGroupsVotedFor; emit MaxNumGroupsVotedForSet(_maxNumGroupsVotedFor); @@ -650,7 +640,7 @@ contract Election is * @param threshold Electability threshold as unwrapped Fraction. * @return True upon success. */ - function setElectabilityThreshold(uint256 threshold) public onlyOwner onlyL1 returns (bool) { + function setElectabilityThreshold(uint256 threshold) public onlyOwner returns (bool) { electabilityThreshold = FixidityLib.wrap(threshold); require( electabilityThreshold.lt(FixidityLib.fixed1()), @@ -681,7 +671,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 onlyL1 { + function setAllowedToVoteOverMaxNumberOfGroups(bool flag) public { address account = getAccounts().voteSignerToAccount(msg.sender); IValidators validators = getValidators(); require( @@ -822,7 +812,7 @@ contract Election is * @notice Returns get current validator signers using the precompiles. * @return List of current validator signers. */ - function getCurrentValidatorSigners() public view returns (address[] memory) { + function getCurrentValidatorSigners() public view onlyL1 returns (address[] memory) { uint256 n = numberValidatorsInCurrentSet(); address[] memory res = new address[](n); for (uint256 i = 0; i < n; i = i.add(1)) { diff --git a/packages/protocol/contracts/governance/EpochRewards.sol b/packages/protocol/contracts/governance/EpochRewards.sol index fc9135e3f39..b7e398d8ffa 100644 --- a/packages/protocol/contracts/governance/EpochRewards.sol +++ b/packages/protocol/contracts/governance/EpochRewards.sol @@ -10,7 +10,6 @@ import "../common/Initializable.sol"; import "../common/UsingRegistry.sol"; import "../common/UsingPrecompiles.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; -import "../../contracts-0.8/common/IsL2Check.sol"; /** * @title Contract for calculating epoch rewards. @@ -22,8 +21,7 @@ contract EpochRewards is UsingPrecompiles, UsingRegistry, Freezable, - CalledByVm, - IsL2Check + CalledByVm { using FixidityLib for FixidityLib.Fraction; using SafeMath for uint256; diff --git a/packages/protocol/contracts/governance/Governance.sol b/packages/protocol/contracts/governance/Governance.sol index 7d3a50e81e8..e4860e60a7a 100644 --- a/packages/protocol/contracts/governance/Governance.sol +++ b/packages/protocol/contracts/governance/Governance.sol @@ -941,7 +941,7 @@ contract Governance is * @return Patch version of the contract. */ function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) { - return (1, 4, 1, 1); + return (1, 4, 2, 0); } /** diff --git a/packages/protocol/contracts/governance/SlasherUtil.sol b/packages/protocol/contracts/governance/SlasherUtil.sol index 066bed0dc6a..9884d15e082 100644 --- a/packages/protocol/contracts/governance/SlasherUtil.sol +++ b/packages/protocol/contracts/governance/SlasherUtil.sol @@ -7,9 +7,8 @@ import "../common/Initializable.sol"; import "../common/UsingRegistry.sol"; import "../common/UsingPrecompiles.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; -import "../../contracts-0.8/common/IsL2Check.sol"; -contract SlasherUtil is Ownable, Initializable, UsingRegistry, UsingPrecompiles, IsL2Check { +contract SlasherUtil is Ownable, Initializable, UsingRegistry, UsingPrecompiles { using SafeMath for uint256; struct SlashingIncentives { diff --git a/packages/protocol/contracts/governance/Validators.sol b/packages/protocol/contracts/governance/Validators.sol index 4bce9ac487e..2f5ce7b2af5 100644 --- a/packages/protocol/contracts/governance/Validators.sol +++ b/packages/protocol/contracts/governance/Validators.sol @@ -15,7 +15,6 @@ 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. @@ -28,8 +27,7 @@ contract Validators is Initializable, UsingRegistry, UsingPrecompiles, - CalledByVm, - IsL2Check + CalledByVm { using FixidityLib for FixidityLib.Fraction; using AddressLinkedList for LinkedList.List; @@ -531,7 +529,6 @@ 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"); @@ -560,6 +557,7 @@ contract Validators is group.nextCommissionBlock = block.number.add(commissionUpdateDelay); emit ValidatorGroupCommissionUpdateQueued(account, commission, group.nextCommissionBlock); } + /** * @notice Updates a validator group's commission based on the previously queued update */ @@ -583,7 +581,6 @@ 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)) { @@ -597,7 +594,6 @@ 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]; @@ -755,7 +751,6 @@ 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(); @@ -1032,6 +1027,7 @@ contract Validators is * @return Fixidity representation of the epoch score between 0 and 1. */ function calculateEpochScore(uint256 uptime) public view returns (uint256) { + allowOnlyL1(); require(uptime <= FixidityLib.fixed1().unwrap(), "Uptime cannot be larger than one"); uint256 numerator; uint256 denominator; diff --git a/packages/protocol/contracts/identity/Random.sol b/packages/protocol/contracts/identity/Random.sol index 81249f17540..8e67853ed1d 100644 --- a/packages/protocol/contracts/identity/Random.sol +++ b/packages/protocol/contracts/identity/Random.sol @@ -8,7 +8,6 @@ import "../common/CalledByVm.sol"; import "../common/Initializable.sol"; import "../common/UsingPrecompiles.sol"; import "../common/interfaces/ICeloVersionedContract.sol"; -import "../../contracts-0.8/common/IsL2Check.sol"; /** * @title Provides randomness for verifier selection @@ -19,8 +18,7 @@ contract Random is Ownable, Initializable, UsingPrecompiles, - CalledByVm, - IsL2Check + CalledByVm { using SafeMath for uint256; diff --git a/packages/protocol/test-sol/unit/governance/validators/Validators.t.sol b/packages/protocol/test-sol/unit/governance/validators/Validators.t.sol index 737cc83eef1..2978ec2f183 100644 --- a/packages/protocol/test-sol/unit/governance/validators/Validators.t.sol +++ b/packages/protocol/test-sol/unit/governance/validators/Validators.t.sol @@ -27,6 +27,21 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { using FixidityLib for FixidityLib.Fraction; using SafeMath for uint256; + struct ValidatorLockedGoldRequirements { + uint256 value; + uint256 duration; + } + + struct GroupLockedGoldRequirements { + uint256 value; + uint256 duration; + } + + struct ValidatorScoreParameters { + uint256 exponent; + FixidityLib.Fraction adjustmentSpeed; + } + address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; Registry registry; @@ -68,28 +83,6 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { FixidityLib.Fraction public commission = FixidityLib.newFixedFraction(1, 100); - event AccountSlashed( - address indexed slashed, - uint256 penalty, - address indexed reporter, - uint256 reward - ); - - struct ValidatorLockedGoldRequirements { - uint256 value; - uint256 duration; - } - - struct GroupLockedGoldRequirements { - uint256 value; - uint256 duration; - } - - struct ValidatorScoreParameters { - uint256 exponent; - FixidityLib.Fraction adjustmentSpeed; - } - ValidatorLockedGoldRequirements public originalValidatorLockedGoldRequirements; GroupLockedGoldRequirements public originalGroupLockedGoldRequirements; ValidatorScoreParameters public originalValidatorScoreParameters; @@ -103,6 +96,12 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { ValidatorsMockTunnel.InitParams public initParams; ValidatorsMockTunnel.InitParams2 public initParams2; + event AccountSlashed( + address indexed slashed, + uint256 penalty, + address indexed reporter, + uint256 reward + ); event MaxGroupSizeSet(uint256 size); event CommissionUpdateDelaySet(uint256 delay); event ValidatorScoreParametersSet(uint256 exponent, uint256 adjustmentSpeed); @@ -212,6 +211,38 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { accounts.createAccount(); } + function _whenL2() public { + deployCodeTo("Registry.sol", abi.encode(false), proxyAdminAddress); + } + + 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 getParsedSignatureOfAddress( address _address, uint256 privateKey @@ -296,34 +327,6 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { validators.registerValidatorGroup(commission.unwrap()); } - 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 _removeMemberAndTimeTravel( address _group, address _validator, @@ -334,6 +337,14 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { timeTravel(_duration); } + function _calculateScore(uint256 _uptime, uint256 _gracePeriod) internal view returns (uint256) { + return + _safeExponent( + _max1(_uptime.add(_gracePeriod)), + FixidityLib.wrap(originalValidatorScoreParameters.exponent) + ); + } + function _max1(uint256 num) internal pure returns (FixidityLib.Fraction memory) { return num > FixidityLib.fixed1().unwrap() ? FixidityLib.fixed1() : FixidityLib.wrap(num); } @@ -354,18 +365,6 @@ contract ValidatorsTest is Test, Constants, Utils, ECDSAHelper { } return result.unwrap(); } - - function _calculateScore(uint256 _uptime, uint256 _gracePeriod) internal view returns (uint256) { - return - _safeExponent( - _max1(_uptime.add(_gracePeriod)), - FixidityLib.wrap(originalValidatorScoreParameters.exponent) - ); - } - - function _whenL2() public { - deployCodeTo("Registry.sol", abi.encode(false), proxyAdminAddress); - } } contract ValidatorsTest_Initialize is ValidatorsTest { @@ -822,11 +821,6 @@ contract ValidatorsTest_DeregisterValidator_WhenAccountHasNeverBeenMemberOfValid timeTravel(originalValidatorLockedGoldRequirements.duration); } - function _deregisterValidator(address _validator) internal { - vm.prank(_validator); - validators.deregisterValidator(INDEX); - } - function test_ShouldMarkAccountAsNotValidator_WhenAccountHasNeverBeenMemberOfValidatorGroup() public { @@ -875,6 +869,10 @@ contract ValidatorsTest_DeregisterValidator_WhenAccountHasNeverBeenMemberOfValid vm.prank(validator); validators.deregisterValidator(INDEX + 1); } + function _deregisterValidator(address _validator) internal { + vm.prank(_validator); + validators.deregisterValidator(INDEX); + } } contract ValidatorsTest_DeregisterValidator_WhenAccountHasBeenMemberOfValidatorGroup is @@ -896,11 +894,6 @@ contract ValidatorsTest_DeregisterValidator_WhenAccountHasBeenMemberOfValidatorG validators.addFirstMember(validator, address(0), address(0)); } - function _deregisterValidator(address _validator) internal { - vm.prank(_validator); - validators.deregisterValidator(INDEX); - } - function test_ShouldMarkAccountAsNotValidator_WhenValidatorNoLongerMemberOfValidatorGroup() public { @@ -970,6 +963,11 @@ contract ValidatorsTest_DeregisterValidator_WhenAccountHasBeenMemberOfValidatorG vm.expectRevert("Has been group member recently"); _deregisterValidator(validator); } + + function _deregisterValidator(address _validator) internal { + vm.prank(_validator); + validators.deregisterValidator(INDEX); + } } contract ValidatorsTest_Affiliate_WhenGroupAndValidatorMeetLockedGoldRequirements is @@ -1828,6 +1826,8 @@ contract ValidatorsTest_AddMember is ValidatorsTest { uint256 _registrationEpoch; uint256 _additionEpoch; + uint256[] expectedSizeHistory; + function setUp() public { super.setUp(); @@ -1935,8 +1935,6 @@ contract ValidatorsTest_AddMember is ValidatorsTest { validators.addMember(otherValidator); } - uint256[] expectedSizeHistory; - function test_ShouldUpdateGroupsSizeHistoryAndBalanceRequirements_WhenAddingManyValidatorsAffiliatedWithGroup() public { @@ -2041,6 +2039,9 @@ contract ValidatorsTest_AddMember is ValidatorsTest { } contract ValidatorsTest_RemoveMember is ValidatorsTest { + uint256 _registrationEpoch; + uint256 _additionEpoch; + function setUp() public { super.setUp(); _registerValidatorGroupWithMembers(group, 1); @@ -2058,9 +2059,6 @@ contract ValidatorsTest_RemoveMember is ValidatorsTest { assertEq(members.length, expectedMembersList.length); } - uint256 _registrationEpoch; - uint256 _additionEpoch; - function test_ShouldUpdateMemberMembershipHistory() public { vm.prank(group); validators.removeMember(validator); @@ -2148,15 +2146,53 @@ contract ValidatorsTest_ReorderMember is ValidatorsTest { assertEq(expectedMembersList.length, members.length); } - function test_Reverts_ReorderGroupMemberList_WhenL2() public { + function test_Emits_ValidatorGroupMemberReorderedEvent() public { + vm.expectEmit(true, true, true, true); + emit ValidatorGroupMemberReordered(group, vm.addr(1)); + + vm.prank(group); + validators.reorderMember(vm.addr(1), validator, address(0)); + } + + function test_Reverts_WhenAccountIsNotRegisteredValidatorGroup() public { + vm.expectRevert("Not a group"); + vm.prank(vm.addr(1)); + validators.reorderMember(vm.addr(1), validator, address(0)); + } + + function test_Reverts_WhenMemberNotRegisteredValidator() public { + vm.expectRevert("Not a validator"); + vm.prank(group); + validators.reorderMember(nonValidator, validator, address(0)); + } + + function test_Reverts_WhenValidatorNotMemberOfValidatorGroup() public { + vm.prank(vm.addr(1)); + validators.deaffiliate(); + + vm.expectRevert("Not a member of the group"); + vm.prank(group); + validators.reorderMember(vm.addr(1), validator, address(0)); + } +} +contract ValidatorsTest_ReorderMember_L2 is ValidatorsTest { + function setUp() public { + super.setUp(); + _registerValidatorGroupWithMembers(group, 2); _whenL2(); + } + + function test_ShouldReorderGroupMemberList() public { address[] memory expectedMembersList = new address[](2); expectedMembersList[0] = vm.addr(1); expectedMembersList[1] = validator; vm.prank(group); - vm.expectRevert("This method is no longer supported in L2."); validators.reorderMember(vm.addr(1), validator, address(0)); + (address[] memory members, , , , , , ) = validators.getValidatorGroup(group); + + assertEq(expectedMembersList, members); + assertEq(expectedMembersList.length, members.length); } function test_Emits_ValidatorGroupMemberReorderedEvent() public { @@ -2410,6 +2446,13 @@ contract ValidatorsTest_CalculateEpochScore is ValidatorsTest { vm.expectRevert("Uptime cannot be larger than one"); validators.calculateEpochScore(uptime.unwrap()); } + + function test_Reverts_WhenL2() public { + _whenL2(); + + vm.expectRevert("This method is no longer supported in L2."); + validators.calculateEpochScore(1); + } } contract ValidatorsTest_CalculateGroupEpochScore is ValidatorsTest { @@ -2555,6 +2598,19 @@ contract ValidatorsTest_CalculateGroupEpochScore is ValidatorsTest { vm.expectRevert("Uptime cannot be larger than one"); validators.calculateGroupEpochScore(unwrapedUptimes); } + + function test_Reverts_WhenL2() public { + _whenL2(); + FixidityLib.Fraction[] memory uptimes = new FixidityLib.Fraction[](5); + uptimes[0] = FixidityLib.newFixedFraction(9, 10); + uptimes[1] = FixidityLib.newFixedFraction(9, 10); + uptimes[3] = FixidityLib.newFixedFraction(9, 10); + uptimes[4] = FixidityLib.newFixedFraction(9, 10); + + (uint256[] memory unwrapedUptimes, ) = _computeGroupUptimeCalculation(uptimes); + vm.expectRevert("This method is no longer supported in L2."); + validators.calculateGroupEpochScore(unwrapedUptimes); + } } contract ValidatorsTest_UpdateValidatorScoreFromSigner is ValidatorsTest { @@ -2638,6 +2694,12 @@ contract ValidatorsTest_UpdateValidatorScoreFromSigner is ValidatorsTest { } contract ValidatorsTest_UpdateMembershipHistory is ValidatorsTest { + address[] public expectedMembershipHistoryGroups; + uint256[] public expectedMembershipHistoryEpochs; + + address[] public actualMembershipHistoryGroups; + uint256[] public actualMembershipHistoryEpochs; + function setUp() public { super.setUp(); _registerValidatorHelper(validator, validatorPk); @@ -2648,12 +2710,6 @@ contract ValidatorsTest_UpdateMembershipHistory is ValidatorsTest { } } - address[] public expectedMembershipHistoryGroups; - uint256[] public expectedMembershipHistoryEpochs; - - address[] public actualMembershipHistoryGroups; - uint256[] public actualMembershipHistoryEpochs; - function test_ShouldOverwritePreviousEntry_WhenChangingGroupsInSameEpoch() public { uint256 numTest = 10; @@ -3159,11 +3215,29 @@ contract ValidatorsTest_ForceDeaffiliateIfValidator is ValidatorsTest { assertEq(affiliation, address(0)); } - function test_Reverts_WhenSenderIsWhitelistedSlashingAddress_WhenL2() public { + function test_Reverts_WhenSenderNotApprovedAddress() public { + vm.expectRevert("Only registered slasher can call"); + validators.forceDeaffiliateIfValidator(validator); + } +} +contract ValidatorsTest_ForceDeaffiliateIfValidator_L2 is ValidatorsTest { + function setUp() public { + super.setUp(); + + _registerValidatorHelper(validator, validatorPk); + _registerValidatorGroupHelper(group, 1); + + vm.prank(validator); + validators.affiliate(group); _whenL2(); + lockedGold.addSlasherTest(paymentDelegatee); + } + + function test_ShouldSucceed_WhenSenderIsWhitelistedSlashingAddress() public { vm.prank(paymentDelegatee); - vm.expectRevert("This method is no longer supported in L2."); validators.forceDeaffiliateIfValidator(validator); + (, , address affiliation, , ) = validators.getValidator(validator); + assertEq(affiliation, address(0)); } function test_Reverts_WhenSenderNotApprovedAddress() public { @@ -3173,17 +3247,17 @@ contract ValidatorsTest_ForceDeaffiliateIfValidator is ValidatorsTest { } contract ValidatorsTest_GroupMembershipInEpoch is ValidatorsTest { + struct EpochInfo { + uint256 epochNumber; + address groupy; + } + uint256 totalEpochs = 24; uint256 gapSize = 3; uint256 contractIndex; EpochInfo[] public epochInfoList; - struct EpochInfo { - uint256 epochNumber; - address groupy; - } - function setUp() public { super.setUp(); @@ -3385,13 +3459,16 @@ contract ValidatorsTest_ResetSlashingMultiplier is ValidatorsTest { assertEq(actualMultiplier, FixidityLib.fixed1().unwrap()); } - function test_Reverts_WhenSlashingMultiplierIsResetAfterResetPeriod_WhenL2() public { + function test_ShouldReturnToDefault_WhenSlashingMultiplierIsResetAfterResetPeriod_WhenL2() + public + { _whenL2(); timeTravel(slashingMultiplierResetPeriod); vm.prank(group); - vm.expectRevert("This method is no longer supported in L2."); validators.resetSlashingMultiplier(); + (, , , , , uint256 actualMultiplier, ) = validators.getValidatorGroup(group); + assertEq(actualMultiplier, FixidityLib.fixed1().unwrap()); } function test_Reverts_WhenSlashingMultiplierIsResetBeforeResetPeriod() public { diff --git a/packages/protocol/test-sol/unit/voting/Election.t.sol b/packages/protocol/test-sol/unit/governance/voting/Election.t.sol similarity index 79% rename from packages/protocol/test-sol/unit/voting/Election.t.sol rename to packages/protocol/test-sol/unit/governance/voting/Election.t.sol index 86fe2a80076..4000d1a1b4d 100644 --- a/packages/protocol/test-sol/unit/voting/Election.t.sol +++ b/packages/protocol/test-sol/unit/governance/voting/Election.t.sol @@ -30,32 +30,6 @@ contract ElectionTest is Utils, Constants { address constant proxyAdminAddress = 0x4200000000000000000000000000000000000018; - event ElectableValidatorsSet(uint256 min, uint256 max); - event MaxNumGroupsVotedForSet(uint256 maxNumGroupsVotedFor); - event ElectabilityThresholdSet(uint256 electabilityThreshold); - event AllowedToVoteOverMaxNumberOfGroups(address indexed account, bool flag); - event ValidatorGroupMarkedEligible(address indexed group); - event ValidatorGroupMarkedIneligible(address indexed group); - event ValidatorGroupVoteCast(address indexed account, address indexed group, uint256 value); - event ValidatorGroupVoteActivated( - address indexed account, - address indexed group, - uint256 value, - uint256 units - ); - event ValidatorGroupPendingVoteRevoked( - address indexed account, - address indexed group, - uint256 value - ); - event ValidatorGroupActiveVoteRevoked( - address indexed account, - address indexed group, - uint256 value, - uint256 units - ); - event EpochRewardsDistributedToVoters(address indexed group, uint256 value); - Accounts accounts; ElectionMock election; Freezer freezer; @@ -85,6 +59,32 @@ contract ElectionTest is Utils, Constants { address[] accountsArray; + event ElectableValidatorsSet(uint256 min, uint256 max); + event MaxNumGroupsVotedForSet(uint256 maxNumGroupsVotedFor); + event ElectabilityThresholdSet(uint256 electabilityThreshold); + event AllowedToVoteOverMaxNumberOfGroups(address indexed account, bool flag); + event ValidatorGroupMarkedEligible(address indexed group); + event ValidatorGroupMarkedIneligible(address indexed group); + event ValidatorGroupVoteCast(address indexed account, address indexed group, uint256 value); + event ValidatorGroupVoteActivated( + address indexed account, + address indexed group, + uint256 value, + uint256 units + ); + event ValidatorGroupPendingVoteRevoked( + address indexed account, + address indexed group, + uint256 value + ); + event ValidatorGroupActiveVoteRevoked( + address indexed account, + address indexed group, + uint256 value, + uint256 units + ); + event EpochRewardsDistributedToVoters(address indexed group, uint256 value); + function createAccount(address account) public { vm.prank(account); accounts.createAccount(); @@ -197,10 +197,19 @@ contract ElectionTest_SetElectabilityThreshold is ElectionTest { vm.expectRevert("Electability threshold must be lower than 100%"); election.setElectabilityThreshold(FixidityLib.fixed1().unwrap() + 1); } +} - function test_Revert_setElectabilityThreshold_WhenL2() public { +contract ElectionTest_SetElectabilityThreshold_L2 is ElectionTest { + function test_shouldSetElectabilityThreshold() public { _whenL2(); - vm.expectRevert("This method is no longer supported in L2."); + uint256 newElectabilityThreshold = FixidityLib.newFixedFraction(1, 200).unwrap(); + election.setElectabilityThreshold(newElectabilityThreshold); + assertEq(election.electabilityThreshold(), newElectabilityThreshold); + } + + function test_ShouldRevertWhenThresholdLargerThan100Percent() public { + _whenL2(); + vm.expectRevert("Electability threshold must be lower than 100%"); election.setElectabilityThreshold(FixidityLib.fixed1().unwrap() + 1); } } @@ -215,12 +224,48 @@ contract ElectionTest_SetElectableValidators is ElectionTest { assertEq(max, newElectableValidatorsMax); } - function test_Reverts_shouldSetElectableValidators_WhenL2() public { + function test_ShouldEmitTheElectableValidatorsSetEvent() public { + uint256 newElectableValidatorsMin = 2; + uint256 newElectableValidatorsMax = 4; + vm.expectEmit(true, false, false, false); + emit ElectableValidatorsSet(newElectableValidatorsMin, newElectableValidatorsMax); + election.setElectableValidators(newElectableValidatorsMin, newElectableValidatorsMax); + } + + function test_ShouldRevertWhenMinElectableValidatorsIsZero() public { + vm.expectRevert("Minimum electable validators cannot be zero"); + election.setElectableValidators(0, electableValidatorsMax); + } + + function test_ShouldRevertWhenTHeminIsGreaterThanMax() public { + vm.expectRevert("Maximum electable validators cannot be smaller than minimum"); + election.setElectableValidators(electableValidatorsMax, electableValidatorsMin); + } + + function test_ShouldRevertWhenValuesAreUnchanged() public { + vm.expectRevert("Electable validators not changed"); + election.setElectableValidators(electableValidatorsMin, electableValidatorsMax); + } + + function test_ShouldRevertWhenCalledByNonOwner() public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(nonOwner); + election.setElectableValidators(1, 2); + } +} + +contract ElectionTest_SetElectableValidators_L2 is ElectionTest { + function setUp() public { + super.setUp(); _whenL2(); + } + function test_shouldSetElectableValidators() public { uint256 newElectableValidatorsMin = 2; uint256 newElectableValidatorsMax = 4; - vm.expectRevert("This method is no longer supported in L2."); election.setElectableValidators(newElectableValidatorsMin, newElectableValidatorsMax); + (uint256 min, uint256 max) = election.getElectableValidators(); + assertEq(min, newElectableValidatorsMin); + assertEq(max, newElectableValidatorsMax); } function test_ShouldEmitTheElectableValidatorsSetEvent() public { @@ -260,11 +305,35 @@ contract ElectionTest_SetMaxNumGroupsVotedFor is ElectionTest { assertEq(election.maxNumGroupsVotedFor(), newMaxNumGroupsVotedFor); } - function test_Revert_SetMaxNumGroupsVotedFor_WhenL2() public { + function test_ShouldEmitMaxNumGroupsVotedForSetEvent() public { + uint256 newMaxNumGroupsVotedFor = 4; + vm.expectEmit(true, false, false, false); + emit MaxNumGroupsVotedForSet(newMaxNumGroupsVotedFor); + election.setMaxNumGroupsVotedFor(newMaxNumGroupsVotedFor); + } + + function test_ShouldRevertWhenCalledByNonOwner() public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(nonOwner); + election.setMaxNumGroupsVotedFor(1); + } + + function test_ShouldRevert_WhenMaxNumGroupsVotedForIsUnchanged() public { + vm.expectRevert("Max groups voted for not changed"); + election.setMaxNumGroupsVotedFor(maxNumGroupsVotedFor); + } +} + +contract ElectionTest_SetMaxNumGroupsVotedFor_L2 is ElectionTest { + function setUp() public { + super.setUp(); _whenL2(); + } + + function test_shouldSetMaxNumGroupsVotedFor() public { uint256 newMaxNumGroupsVotedFor = 4; - vm.expectRevert("This method is no longer supported in L2."); election.setMaxNumGroupsVotedFor(newMaxNumGroupsVotedFor); + assertEq(election.maxNumGroupsVotedFor(), newMaxNumGroupsVotedFor); } function test_ShouldEmitMaxNumGroupsVotedForSetEvent() public { @@ -292,10 +361,48 @@ contract ElectionTest_SetAllowedToVoteOverMaxNumberOfGroups is ElectionTest { assertEq(election.allowedToVoteOverMaxNumberOfGroups(address(this)), true); } - function test_Revert_SetAllowedToVoteOverMaxNumberOfGroups_WhenL2() public { + function test_ShouldRevertWhenCalledByValidator() public { + validators.setValidator(address(this)); + vm.expectRevert("Validators cannot vote for more than max number of groups"); + election.setAllowedToVoteOverMaxNumberOfGroups(true); + } + + function test_ShouldRevertWhenCalledByValidatorGroup() public { + validators.setValidatorGroup(address(this)); + vm.expectRevert("Validator groups cannot vote for more than max number of groups"); + election.setAllowedToVoteOverMaxNumberOfGroups(true); + } + + function test_ShouldEmitAllowedToVoteOverMaxNumberOfGroupsEvent() public { + vm.expectEmit(true, false, false, false); + emit AllowedToVoteOverMaxNumberOfGroups(address(this), true); + election.setAllowedToVoteOverMaxNumberOfGroups(true); + } + + function test_ShouldSwitchAllowedToVoteOverMaxNumberOfGroupsOff_WhenTurnedOn() public { + election.setAllowedToVoteOverMaxNumberOfGroups(true); + assertEq(election.allowedToVoteOverMaxNumberOfGroups(address(this)), true); + election.setAllowedToVoteOverMaxNumberOfGroups(false); + assertEq(election.allowedToVoteOverMaxNumberOfGroups(address(this)), false); + } + + function test_ShouldEmitAllowedToVoteOverMaxNumberOfGroupsEvent_WhenTurnedOn() public { + election.setAllowedToVoteOverMaxNumberOfGroups(true); + vm.expectEmit(true, false, false, false); + emit AllowedToVoteOverMaxNumberOfGroups(address(this), false); + election.setAllowedToVoteOverMaxNumberOfGroups(false); + } +} + +contract ElectionTest_SetAllowedToVoteOverMaxNumberOfGroups_L2 is ElectionTest { + function setUp() public { + super.setUp(); _whenL2(); - vm.expectRevert("This method is no longer supported in L2."); + } + + function test_shouldSetAllowedToVoteOverMaxNumberOfGroups() public { election.setAllowedToVoteOverMaxNumberOfGroups(true); + assertEq(election.allowedToVoteOverMaxNumberOfGroups(address(this)), true); } function test_ShouldRevertWhenCalledByValidator() public { @@ -346,11 +453,40 @@ contract ElectionTest_MarkGroupEligible is ElectionTest { assertEq(eligibleGroups[0], group); } - function test_Revert_MarkGroupEligible_WhenL2() public { + function test_ShouldEmitValidatorGroupMarkedEligibleEvent() public { + address group = address(this); + vm.expectEmit(true, false, false, false); + emit ValidatorGroupMarkedEligible(group); + election.markGroupEligible(group, address(0), address(0)); + } + + function test_ShouldRevertWhenAlreadyMarkedEligible() public { + address group = address(this); + election.markGroupEligible(group, address(0), address(0)); + vm.expectRevert("invalid key"); + election.markGroupEligible(group, address(0), address(0)); + } + + function test_ShouldRevertWhenCalledByNonValidator() public { + vm.expectRevert("only registered contract"); + vm.prank(nonOwner); + election.markGroupEligible(address(this), address(0), address(0)); + } +} + +contract ElectionTest_MarkGroupEligible_L2 is ElectionTest { + function setUp() public { + super.setUp(); _whenL2(); + registry.setAddressFor("Validators", address(address(this))); + } + + function test_shouldMarkGroupEligible() public { address group = address(this); - vm.expectRevert("This method is no longer supported in L2."); election.markGroupEligible(group, address(0), address(0)); + address[] memory eligibleGroups = election.getEligibleValidatorGroups(); + assertEq(eligibleGroups.length, 1); + assertEq(eligibleGroups[0], group); } function test_ShouldEmitValidatorGroupMarkedEligibleEvent() public { @@ -409,6 +545,7 @@ contract ElectionTest_MarkGroupInEligible is ElectionTest { election.markGroupIneligible(address(this)); } } + contract ElectionTest_Vote_WhenGroupEligible is ElectionTest { address voter = address(this); address group = account1; @@ -466,13 +603,6 @@ contract ElectionTest_Vote_WhenGroupEligible is ElectionTest { assertEq(election.getPendingVotesForGroupByAccount(group, voter), value - maxNumGroupsVotedFor); } - function test_Revert_Vote_WhenL2() public { - _whenL2(); - - vm.expectRevert("This method is no longer supported in L2."); - election.vote(group, value - maxNumGroupsVotedFor, address(0), address(0)); - } - function test_ShouldSetTotalVotesByAccount_WhenMaxNumberOfGroupsWasNotReached() public { WhenVotedForMaxNumberOfGroups(); assertEq(election.getTotalVotesByAccount(voter), maxNumGroupsVotedFor); @@ -605,7 +735,7 @@ contract ElectionTest_Vote_WhenGroupEligible is ElectionTest { } } -contract ElectionTest_Vote_WhenGroupEligible_WhenGroupCanReceiveVotes is ElectionTest { +contract ElectionTest_Vote_WhenGroupEligible_L2 is ElectionTest { address voter = address(this); address group = account1; uint256 value = 1000; @@ -616,7 +746,7 @@ contract ElectionTest_Vote_WhenGroupEligible_WhenGroupCanReceiveVotes is Electio function setUp() public { super.setUp(); - + _whenL2(); address[] memory members = new address[](1); members[0] = account9; validators.setMembers(group, members); @@ -624,45 +754,238 @@ contract ElectionTest_Vote_WhenGroupEligible_WhenGroupCanReceiveVotes is Electio registry.setAddressFor("Validators", address(this)); election.markGroupEligible(group, address(0), address(0)); registry.setAddressFor("Validators", address(validators)); + } - lockedGold.setTotalLockedGold(value); - validators.setNumRegisteredValidators(1); + function test_ShouldRevert_WhenTheVoterDoesNotHaveSufficientNonVotingBalance() public { + lockedGold.incrementNonvotingAccountBalance(voter, value - 1); + vm.expectRevert("SafeMath: subtraction overflow"); + election.vote(group, value, address(0), address(0)); } - function WhenTheVoterCanVoteForAnAdditionalGroup() public { + function WhenVotedForMaxNumberOfGroups() public returns (address newGroup) { lockedGold.incrementNonvotingAccountBalance(voter, value); - } - function WhenTheVoterHasNotAlreadyVotedForThisGroup() public { - WhenTheVoterCanVoteForAnAdditionalGroup(); - election.vote(group, value, address(0), address(0)); + for (uint256 i = 0; i < maxNumGroupsVotedFor; i++) { + address[] memory members = new address[](1); + members[0] = accountsArray[9]; + newGroup = accountsArray[i + 2]; + setupGroupAndVote(newGroup, group, members, true); + } } - function test_ShouldAddTheGroupToListOfGroupsTheAccountHasVotedFor_WhenTheVoterHasNotAlreadyVotedForThisGroup() - public - { - WhenTheVoterHasNotAlreadyVotedForThisGroup(); - address[] memory groupsVotedFor = election.getGroupsVotedForByAccount(voter); - assertEq(groupsVotedFor.length, 1); - assertEq(groupsVotedFor[0], group); - } + function test_ShouldRevert_WhenTheVoterCannotVoteForAnAdditionalGroup() public { + address newGroup = WhenVotedForMaxNumberOfGroups(); - function test_ShouldIncrementTheAccountsPendingVotesForTheGroup_WhenTheVoterHasNotAlreadyVotedForThisGroup() - public - { - WhenTheVoterHasNotAlreadyVotedForThisGroup(); - assertEq(election.getPendingVotesForGroupByAccount(group, voter), value); + vm.expectRevert("Voted for too many groups"); + election.vote(group, value - maxNumGroupsVotedFor, newGroup, address(0)); } - function test_ShouldIncrementTheAccountsTotalVotesForTheGroup_WhenTheVoterHasNotAlreadyVotedForThisGroup() + function test_ShouldAllowToVoteForAnotherGroup_WhenTheVoterIsOverMaxNumberGroupsVotedForButCanVoteForAdditionalGroup() public { - WhenTheVoterHasNotAlreadyVotedForThisGroup(); - assertEq(election.getTotalVotesForGroupByAccount(group, voter), value); + address newGroup = WhenVotedForMaxNumberOfGroups(); + election.setAllowedToVoteOverMaxNumberOfGroups(true); + + vm.expectEmit(true, true, true, true); + emit ValidatorGroupVoteCast(voter, group, value - maxNumGroupsVotedFor); + election.vote(group, value - maxNumGroupsVotedFor, newGroup, address(0)); + assertEq(election.getPendingVotesForGroupByAccount(group, voter), value - maxNumGroupsVotedFor); } - function test_ShouldIncrementTheACcountsTotalVotes_WhenTheVoterHasNotAlreadyVotedForThisGroup() - public + function test_ShouldSetTotalVotesByAccount_WhenMaxNumberOfGroupsWasNotReached() public { + WhenVotedForMaxNumberOfGroups(); + assertEq(election.getTotalVotesByAccount(voter), maxNumGroupsVotedFor); + } + + function WhenVotedForMoreThanMaxNumberOfGroups() public returns (address newGroup) { + newGroup = WhenVotedForMaxNumberOfGroups(); + election.setAllowedToVoteOverMaxNumberOfGroups(true); + election.vote(group, voterFirstGroupVote, newGroup, address(0)); + } + + function test_ShouldRevert_WhenTurningOffSetAllowedToVoteOverMaxNUmberOfGroups_WhenOverMaximumNumberOfGroupsVoted() + public + { + WhenVotedForMoreThanMaxNumberOfGroups(); + + vm.expectRevert("Too many groups voted for!"); + election.setAllowedToVoteOverMaxNumberOfGroups(false); + } + + function test_ShouldReturnOnlyLastVotedWith_WhenVotesWereNotManuallyCounted() public { + WhenVotedForMoreThanMaxNumberOfGroups(); + assertEq(election.getTotalVotesByAccount(voter), voterFirstGroupVote); + } + + function manuallyUpdateTotalVotesForAllGroups(address _voter) public { + for (uint256 i = 0; i < maxNumGroupsVotedFor; i++) { + election.updateTotalVotesByAccountForGroup(_voter, accountsArray[i + 2]); + } + election.updateTotalVotesByAccountForGroup(_voter, group); + } + + function WhenTotalVotesWereManuallyCounted() public { + WhenVotedForMoreThanMaxNumberOfGroups(); + manuallyUpdateTotalVotesForAllGroups(voter); + } + + function test_ShouldReturnTotalVotesByAccount_WhenTotalVotesAreManuallyCounted() public { + WhenTotalVotesWereManuallyCounted(); + assertEq(election.getTotalVotesByAccount(voter), value - originallyNotVotedWithAmount); + } + + function test_ShouldReturnLoweredTotalNumberOfVotes_WhenVotesRevoked_WhenTotalVotesWereManuallyCounted() + public + { + uint256 revokeDiff = 100; + uint256 revokeValue = voterFirstGroupVote - revokeDiff; + + WhenTotalVotesWereManuallyCounted(); + election.revokePending(group, revokeValue, accountsArray[4], address(0), 3); + assertEq(election.getTotalVotesByAccount(voter), maxNumGroupsVotedFor + revokeDiff); + } + + function WhenVotesAreBeingActivated() public returns (address newGroup) { + newGroup = WhenVotedForMoreThanMaxNumberOfGroups(); + blockTravel(ph.epochSize() + 1); + election.activateForAccount(group, voter); + } + + function test_ShouldIncrementTheAccountsActiveVotesForGroup_WhenVotesAreBeingActivated() public { + WhenVotesAreBeingActivated(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter), voterFirstGroupVote); + } + + function test_ShouldReturnCorrectValueWhenManuallyCounted_WhenVotesAreBeingActivated() public { + WhenVotesAreBeingActivated(); + manuallyUpdateTotalVotesForAllGroups(voter); + + assertEq(election.getTotalVotesByAccount(voter), value - originallyNotVotedWithAmount); + } + + function WhenAwardsAreDistributed() public returns (address newGroup) { + newGroup = WhenVotesAreBeingActivated(); + election.distributeEpochRewards(group, rewardValue, newGroup, address(0)); + } + + // TODO: Implement when validator L2 rewards mechanism is implemented. + function skip_test_ShouldRevokeActiveVotes_WhenAwardsAreDistributed() public { + // (more then original votes without rewards) + address newGroup = WhenAwardsAreDistributed(); + election.revokeActive(group, value, newGroup, address(0), 3); + assertEq( + election.getActiveVotesForGroupByAccount(group, voter), + rewardValue - maxNumGroupsVotedFor - originallyNotVotedWithAmount + ); + } + + // TODO: Implement when validator L2 rewards mechanism is implemented. + function skip_test_ShouldReturnCorrectValueWhenManuallyCounted_WhenMoreVotesThanActiveIsRevoked_WhenAwardsAreDistributed() + public + { + address newGroup = WhenAwardsAreDistributed(); + election.revokeActive(group, value, newGroup, address(0), 3); + manuallyUpdateTotalVotesForAllGroups(voter); + + assertEq(election.getTotalVotesByAccount(voter), rewardValue - originallyNotVotedWithAmount); + } + + // TODO: Implement when validator L2 rewards mechanism is implemented. + function skip_test_ShouldReturnTotalVotesByAccount_WhenTotalVotesAreManuallyCountedOnReward_WhenAwardsAreDistributed() + public + { + WhenAwardsAreDistributed(); + manuallyUpdateTotalVotesForAllGroups(voter); + + assertEq( + election.getTotalVotesByAccount(voter), + value + rewardValue - originallyNotVotedWithAmount + ); + } + + // TODO: Implement when validator L2 rewards mechanism is implemented. + function skip_test_ShouldIncreaseTotalVotesCountOnceVoted_WhenTotalVotesAreManuallyCountedOnReward_WhenAwardsAreDistributed() + public + { + address newGroup = WhenAwardsAreDistributed(); + manuallyUpdateTotalVotesForAllGroups(voter); + + election.vote(newGroup, originallyNotVotedWithAmount, account4, group); + + assertEq(election.getTotalVotes(), value + rewardValue); + } + + function test_ShouldRevert_WhenTheGroupCannotReceiveVotes() public { + lockedGold.setTotalLockedGold(value / 2 - 1); + address[] memory members = new address[](1); + members[0] = account9; + validators.setMembers(group, members); + validators.setNumRegisteredValidators(1); + assertEq(election.getNumVotesReceivable(group), value - 2); + + vm.expectRevert("Group cannot receive votes"); + election.vote(group, value, address(0), address(0)); + } +} + +contract ElectionTest_Vote_WhenGroupEligible_WhenGroupCanReceiveVotes is ElectionTest { + address voter = address(this); + address group = account1; + uint256 value = 1000; + + uint256 originallyNotVotedWithAmount = 1; + uint256 voterFirstGroupVote = value - maxNumGroupsVotedFor - originallyNotVotedWithAmount; + uint256 rewardValue = 1000000; + + function setUp() public { + super.setUp(); + + address[] memory members = new address[](1); + members[0] = account9; + validators.setMembers(group, members); + + registry.setAddressFor("Validators", address(this)); + election.markGroupEligible(group, address(0), address(0)); + registry.setAddressFor("Validators", address(validators)); + + lockedGold.setTotalLockedGold(value); + validators.setNumRegisteredValidators(1); + } + + function WhenTheVoterCanVoteForAnAdditionalGroup() public { + lockedGold.incrementNonvotingAccountBalance(voter, value); + } + + function WhenTheVoterHasNotAlreadyVotedForThisGroup() public { + WhenTheVoterCanVoteForAnAdditionalGroup(); + election.vote(group, value, address(0), address(0)); + } + + function test_ShouldAddTheGroupToListOfGroupsTheAccountHasVotedFor_WhenTheVoterHasNotAlreadyVotedForThisGroup() + public + { + WhenTheVoterHasNotAlreadyVotedForThisGroup(); + address[] memory groupsVotedFor = election.getGroupsVotedForByAccount(voter); + assertEq(groupsVotedFor.length, 1); + assertEq(groupsVotedFor[0], group); + } + + function test_ShouldIncrementTheAccountsPendingVotesForTheGroup_WhenTheVoterHasNotAlreadyVotedForThisGroup() + public + { + WhenTheVoterHasNotAlreadyVotedForThisGroup(); + assertEq(election.getPendingVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldIncrementTheAccountsTotalVotesForTheGroup_WhenTheVoterHasNotAlreadyVotedForThisGroup() + public + { + WhenTheVoterHasNotAlreadyVotedForThisGroup(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldIncrementTheACcountsTotalVotes_WhenTheVoterHasNotAlreadyVotedForThisGroup() + public { WhenTheVoterHasNotAlreadyVotedForThisGroup(); assertEq(election.getTotalVotesByAccount(voter), value); @@ -788,6 +1111,9 @@ contract ElectionTest_Activate is ElectionTest { address group = account1; uint256 value = 1000; + address voter2 = account2; + uint256 value2 = 573; + function setUp() public { super.setUp(); @@ -859,16 +1185,162 @@ contract ElectionTest_Activate is ElectionTest { election.activate(group); } - function test_Revert_Activate_WhenL2() public { + function WhenAnotherVoterActivatesVotes() public { + WhenEpochBoundaryHasPassed(); + lockedGold.incrementNonvotingAccountBalance(voter2, value2); + vm.prank(voter2); + election.vote(group, value2, address(0), address(0)); + blockTravel(ph.epochSize() + 1); + vm.prank(voter2); + election.activate(group); + } + + function test_ShouldNotModifyTheFirstAccountActiveVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheFirstAccountTotalVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheFirstAccountTotalVotes_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesByAccount(voter), value); + } + + function test_ShouldDecrementTheSecondAccountsPendingVotesFOrTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getPendingVotesForGroupByAccount(group, voter2), 0); + } + + function test_ShouldIncrementTheSecondAccountActiveVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter2), value2); + } + + function test_ShouldNotModifyTheSecondsAccountTotalVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter2), value2); + } + + function test_ShouldNotMOdifyTheSecondAccountTotalVotes_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesByAccount(voter2), value2); + } + + function test_ShouldNotModifyTotalVotesForGroup_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesForGroup(group), value + value2); + } + + function test_ShouldNotModifyTotalVotes_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotes(), value + value2); + } + + function test_ShouldRevert_WhenAnEpochBoundaryHadNotPassedSinceThePendingVotesWereMade() public { WhenVoterHasPendingVotes(); - _whenL2(); - vm.expectRevert("This method is no longer supported in L2."); + vm.expectRevert("Pending vote epoch not passed"); + election.activateForAccount(group, voter); + } + + function test_ShouldRevert_WhenTheVoterDoesNotHavePendingVotes() public { + vm.expectRevert("Vote value cannot be zero"); election.activate(group); } +} + +contract ElectionTest_Activate_L2 is ElectionTest { + address voter = address(this); + address group = account1; + uint256 value = 1000; address voter2 = account2; uint256 value2 = 573; + function setUp() public { + super.setUp(); + _whenL2(); + address[] memory members = new address[](1); + members[0] = account9; + validators.setMembers(group, members); + + registry.setAddressFor("Validators", address(this)); + election.markGroupEligible(group, address(0), address(0)); + registry.setAddressFor("Validators", address(validators)); + + lockedGold.setTotalLockedGold(value); + validators.setMembers(group, members); + validators.setNumRegisteredValidators(1); + lockedGold.incrementNonvotingAccountBalance(voter, value); + } + + function WhenVoterHasPendingVotes() public { + election.vote(group, value, address(0), address(0)); + } + + function WhenEpochBoundaryHasPassed() public { + WhenVoterHasPendingVotes(); + blockTravel(ph.epochSize() + 1); + election.activate(group); + } + + function test_ShouldDecrementTheAccountsPendingVotesForTheGroup_WhenEpochBoundaryHasPassed() + public + { + WhenEpochBoundaryHasPassed(); + assertEq(election.getPendingVotesForGroupByAccount(group, voter), 0); + } + + function test_ShouldIncrementTheAccountsActiveVotesForTheGroup_WhenEpochBoundaryHasPassed() + public + { + WhenEpochBoundaryHasPassed(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheAccountsTotalVotesForTheGroup_WhenEpochBoundaryHasPassed() + public + { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheAccountsTotalVotes_WhenEpochBoundaryHasPassed() public { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotesByAccount(voter), value); + } + + function test_ShouldNotModifyTotalVotesForGroup_WhenEpochBoundaryHasPassed() public { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotesForGroup(group), value); + } + + function test_ShouldNotModifyTotalVotes_WhenEpochBoundaryHasPassed() public { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotes(), value); + } + + function test_ShouldEmitValidatorGroupVoteActivatedEvent_WhenEpochBoundaryHasPassed() public { + WhenVoterHasPendingVotes(); + blockTravel(ph.epochSize() + 1); + vm.expectEmit(true, true, true, false); + emit ValidatorGroupVoteActivated(voter, group, value, value * 100000000000000000000); + election.activate(group); + } + function WhenAnotherVoterActivatesVotes() public { WhenEpochBoundaryHasPassed(); lockedGold.incrementNonvotingAccountBalance(voter2, value2); @@ -940,26 +1412,178 @@ contract ElectionTest_Activate is ElectionTest { election.activateForAccount(group, voter); } - function test_Revert_ActivateForAccount_WhenL2() public { - _whenL2(); - vm.expectRevert("This method is no longer supported in L2."); + function test_ShouldRevert_WhenTheVoterDoesNotHavePendingVotes() public { + vm.expectRevert("Vote value cannot be zero"); + election.activate(group); + } +} + +contract ElectionTest_ActivateForAccount is ElectionTest { + address voter = address(this); + address group = account1; + uint256 value = 1000; + + address voter2 = account2; + uint256 value2 = 573; + + function setUp() public { + super.setUp(); + + address[] memory members = new address[](1); + members[0] = account9; + validators.setMembers(group, members); + + registry.setAddressFor("Validators", address(this)); + election.markGroupEligible(group, address(0), address(0)); + registry.setAddressFor("Validators", address(validators)); + + lockedGold.setTotalLockedGold(value); + validators.setMembers(group, members); + validators.setNumRegisteredValidators(1); + lockedGold.incrementNonvotingAccountBalance(voter, value); + } + + function WhenVoterHasPendingVotes() public { + election.vote(group, value, address(0), address(0)); + } + + function WhenEpochBoundaryHasPassed() public { + WhenVoterHasPendingVotes(); + blockTravel(ph.epochSize() + 1); + election.activateForAccount(group, voter); + } + + function test_ShouldDecrementTheAccountsPendingVotesForTheGroup_WhenEpochBoundaryHasPassed() + public + { + WhenEpochBoundaryHasPassed(); + assertEq(election.getPendingVotesForGroupByAccount(group, voter), 0); + } + + function test_ShouldIncrementTheAccountsActiveVotesForTheGroup_WhenEpochBoundaryHasPassed() + public + { + WhenEpochBoundaryHasPassed(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheAccountsTotalVotesForTheGroup_WhenEpochBoundaryHasPassed() + public + { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheAccountsTotalVotes_WhenEpochBoundaryHasPassed() public { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotesByAccount(voter), value); + } + + function test_ShouldNotModifyTotalVotesForGroup_WhenEpochBoundaryHasPassed() public { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotesForGroup(group), value); + } + + function test_ShouldNotModifyTotalVotes_WhenEpochBoundaryHasPassed() public { + WhenEpochBoundaryHasPassed(); + assertEq(election.getTotalVotes(), value); + } + + function test_ShouldEmitValidatorGroupVoteActivatedEvent_WhenEpochBoundaryHasPassed() public { + WhenVoterHasPendingVotes(); + blockTravel(ph.epochSize() + 1); + vm.expectEmit(true, true, true, false); + emit ValidatorGroupVoteActivated(voter, group, value, value * 100000000000000000000); + election.activate(group); + } + + function WhenAnotherVoterActivatesVotes() public { + WhenEpochBoundaryHasPassed(); + lockedGold.incrementNonvotingAccountBalance(voter2, value2); + vm.prank(voter2); + election.vote(group, value2, address(0), address(0)); + blockTravel(ph.epochSize() + 1); + election.activateForAccount(group, voter2); + } + + function test_ShouldNotModifyTheFirstAccountActiveVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheFirstAccountTotalVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter), value); + } + + function test_ShouldNotModifyTheFirstAccountTotalVotes_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesByAccount(voter), value); + } + + function test_ShouldDecrementTheSecondAccountsPendingVotesFOrTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getPendingVotesForGroupByAccount(group, voter2), 0); + } + + function test_ShouldIncrementTheSecondAccountActiveVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getActiveVotesForGroupByAccount(group, voter2), value2); + } + + function test_ShouldNotModifyTheSecondsAccountTotalVotesForTheGroup_WhenAnotherVoterActivatesVotes() + public + { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesForGroupByAccount(group, voter2), value2); + } + + function test_ShouldNotMOdifyTheSecondAccountTotalVotes_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesByAccount(voter2), value2); + } + + function test_ShouldNotModifyTotalVotesForGroup_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotesForGroup(group), value + value2); + } + + function test_ShouldNotModifyTotalVotes_WhenAnotherVoterActivatesVotes() public { + WhenAnotherVoterActivatesVotes(); + assertEq(election.getTotalVotes(), value + value2); + } + + function test_ShouldRevert_WhenEpochBoundaryHasNotPassedSinceThePendingVotesWereMade() public { + WhenVoterHasPendingVotes(); + vm.expectRevert("Pending vote epoch not passed"); election.activateForAccount(group, voter); } function test_ShouldRevert_WhenTheVoterDoesNotHavePendingVotes() public { vm.expectRevert("Vote value cannot be zero"); - election.activate(group); + election.activateForAccount(group, voter); } } -contract ElectionTest_ActivateForAccount is ElectionTest { +contract ElectionTest_ActivateForAccount_L2 is ElectionTest { address voter = address(this); address group = account1; uint256 value = 1000; + address voter2 = account2; + uint256 value2 = 573; + function setUp() public { super.setUp(); - + _whenL2(); address[] memory members = new address[](1); members[0] = account9; validators.setMembers(group, members); @@ -1028,9 +1652,6 @@ contract ElectionTest_ActivateForAccount is ElectionTest { election.activate(group); } - address voter2 = account2; - uint256 value2 = 573; - function WhenAnotherVoterActivatesVotes() public { WhenEpochBoundaryHasPassed(); lockedGold.incrementNonvotingAccountBalance(voter2, value2); @@ -1538,6 +2159,11 @@ contract ElectionTest_RevokeActive is ElectionTest { } contract ElectionTest_ElectionValidatorSigners is ElectionTest { + struct MemberWithVotes { + address member; + uint256 votes; + } + address group1 = address(this); address group2 = account1; address group3 = account2; @@ -1571,11 +2197,6 @@ contract ElectionTest_ElectionValidatorSigners is ElectionTest { uint256 totalLockedGold = voter1Weight + voter2Weight + voter3Weight; - struct MemberWithVotes { - address member; - uint256 votes; - } - mapping(address => uint256) votesConsideredForElection; MemberWithVotes[] membersWithVotes; @@ -1598,37 +2219,6 @@ contract ElectionTest_ElectionValidatorSigners is ElectionTest { random.addTestRandomness(block.number + 1, hash); } - // Helper function to sort an array of uint256 - function sort(uint256[] memory data) internal pure returns (uint256[] memory) { - uint256 length = data.length; - for (uint256 i = 0; i < length; i++) { - for (uint256 j = i + 1; j < length; j++) { - if (data[i] > data[j]) { - uint256 temp = data[i]; - data[i] = data[j]; - data[j] = temp; - } - } - } - return data; - } - - function sortMembersWithVotesDesc( - MemberWithVotes[] memory data - ) internal pure returns (MemberWithVotes[] memory) { - uint256 length = data.length; - for (uint256 i = 0; i < length; i++) { - for (uint256 j = i + 1; j < length; j++) { - if (data[i].votes < data[j].votes) { - MemberWithVotes memory temp = data[i]; - data[i] = data[j]; - data[j] = temp; - } - } - } - return data; - } - function WhenThereIsALargeNumberOfGroups() public { lockedGold.setTotalLockedGold(1e25); validators.setNumRegisteredValidators(400); @@ -1791,6 +2381,37 @@ contract ElectionTest_ElectionValidatorSigners is ElectionTest { vm.expectRevert("Not enough elected validators"); election.electValidatorSigners(); } + + // Helper function to sort an array of uint256 + function sort(uint256[] memory data) internal pure returns (uint256[] memory) { + uint256 length = data.length; + for (uint256 i = 0; i < length; i++) { + for (uint256 j = i + 1; j < length; j++) { + if (data[i] > data[j]) { + uint256 temp = data[i]; + data[i] = data[j]; + data[j] = temp; + } + } + } + return data; + } + + function sortMembersWithVotesDesc( + MemberWithVotes[] memory data + ) internal pure returns (MemberWithVotes[] memory) { + uint256 length = data.length; + for (uint256 i = 0; i < length; i++) { + for (uint256 j = i + 1; j < length; j++) { + if (data[i].votes < data[j].votes) { + MemberWithVotes memory temp = data[i]; + data[i] = data[j]; + data[j] = temp; + } + } + } + return data; + } } contract ElectionTest_GetGroupEpochRewards is ElectionTest { @@ -1801,6 +2422,12 @@ contract ElectionTest_GetGroupEpochRewards is ElectionTest { uint256 voteValue2 = 1000000000; uint256 totalRewardValue = 3000000000; + uint256 expectedGroup1EpochRewards = + FixidityLib + .newFixedFraction(voteValue1, voteValue1 + voteValue2) + .multiply(FixidityLib.newFixed(totalRewardValue)) + .fromFixed(); + function setUp() public { super.setUp(); @@ -1880,12 +2507,6 @@ contract ElectionTest_GetGroupEpochRewards is ElectionTest { assertEq(election.getGroupEpochRewards(group2, totalRewardValue, uptimes), 0); } - uint256 expectedGroup1EpochRewards = - FixidityLib - .newFixedFraction(voteValue1, voteValue1 + voteValue2) - .multiply(FixidityLib.newFixed(totalRewardValue)) - .fromFixed(); - function test_ShouldReturnProportionalRewardValueForOtherGroup_WhenOneGroupDoesNotMeetLockedGoldRequirements_WhenTwoGroupsHaveActiveVotes() public { @@ -1920,6 +2541,13 @@ contract ElectionTest_DistributeEpochRewards is ElectionTest { uint256 rewardValue = 1000000; uint256 rewardValue2 = 10000000; + uint256 expectedGroupTotalActiveVotes = voteValue + voteValue2 / 2 + rewardValue; + uint256 expectedVoterActiveVotesForGroup = + FixidityLib.newFixedFraction(expectedGroupTotalActiveVotes * 2, 3).fromFixed(); + uint256 expectedVoter2ActiveVotesForGroup = + FixidityLib.newFixedFraction(expectedGroupTotalActiveVotes, 3).fromFixed(); + uint256 expectedVoter2ActiveVotesForGroup2 = voteValue / 2 + rewardValue2; + function setUp() public { super.setUp(); @@ -1977,13 +2605,6 @@ contract ElectionTest_DistributeEpochRewards is ElectionTest { assertEq(election.getTotalVotes(), voteValue + rewardValue); } - uint256 expectedGroupTotalActiveVotes = voteValue + voteValue2 / 2 + rewardValue; - uint256 expectedVoterActiveVotesForGroup = - FixidityLib.newFixedFraction(expectedGroupTotalActiveVotes * 2, 3).fromFixed(); - uint256 expectedVoter2ActiveVotesForGroup = - FixidityLib.newFixedFraction(expectedGroupTotalActiveVotes, 3).fromFixed(); - uint256 expectedVoter2ActiveVotesForGroup2 = voteValue / 2 + rewardValue2; - function WhenThereAreTwoGroupsWithActiveVotes() public { registry.setAddressFor("Validators", address(this)); election.markGroupEligible(group2, address(0), group); @@ -2087,6 +2708,15 @@ contract ElectionTest_ForceDecrementVotes is ElectionTest { uint256 slashedValue = value; uint256 remaining = value - slashedValue; + uint256 totalRemaining; + uint256 group1Remaining; + uint256 group2TotalRemaining; + uint256 group2PendingRemaining; + uint256 group2ActiveRemaining; + + uint256 group1RemainingActiveVotes; + address[] initialOrdering; + function setUp() public { super.setUp(); } @@ -2331,12 +2961,6 @@ contract ElectionTest_ForceDecrementVotes is ElectionTest { assertEq(election.getActiveVotesForGroupByAccount(group, voter), remaining); } - uint256 totalRemaining; - uint256 group1Remaining; - uint256 group2TotalRemaining; - uint256 group2PendingRemaining; - uint256 group2ActiveRemaining; - function WhenWeSlashAllOfGroup1VotesAndSomeOfGroup2__WhenWeSlash1MoreVoteThanGroup1PendingVoteTotal_WhenAccountHasVotedForMoreThanOneGroupInequally() public { @@ -2373,9 +2997,6 @@ contract ElectionTest_ForceDecrementVotes is ElectionTest { assertEq(election.getActiveVotesForGroupByAccount(group2, voter), group2ActiveRemaining); } - uint256 group1RemainingActiveVotes; - address[] initialOrdering; - function WhenSlashAffectsElectionOrder() public { WhenAccountHasVotedForMoreThanOneGroupInequally(); @@ -2491,12 +3112,6 @@ contract ElectionTest_ForceDecrementVotes is ElectionTest { } contract ElectionTest_ConsistencyChecks is ElectionTest { - address voter = address(this); - address group = account2; - uint256 rewardValue2 = 10000000; - - AccountStruct[] _accounts; - struct AccountStruct { address account; uint256 active; @@ -2511,6 +3126,12 @@ contract ElectionTest_ConsistencyChecks is ElectionTest { RevokeActive } + address voter = address(this); + address group = account2; + uint256 rewardValue2 = 10000000; + + AccountStruct[] _accounts; + function setUp() public { super.setUp(); @@ -2538,52 +3159,6 @@ contract ElectionTest_ConsistencyChecks is ElectionTest { } } - function makeRandomAction(AccountStruct storage account, uint256 salt) internal { - VoteActionType[] memory actions = new VoteActionType[](4); - uint256 actionCount = 0; - - if (account.nonVoting > 0) { - actions[actionCount++] = VoteActionType.Vote; - } - if (election.hasActivatablePendingVotes(account.account, group)) { - // Assuming this is a view function - actions[actionCount++] = VoteActionType.Activate; - } - if (account.pending > 0) { - actions[actionCount++] = VoteActionType.RevokePending; - } - if (account.active > 0) { - actions[actionCount++] = VoteActionType.RevokeActive; - } - - VoteActionType action = actions[generatePRN(0, actionCount - 1, uint256(account.account))]; - uint256 value; - - vm.startPrank(account.account); - if (action == VoteActionType.Vote) { - value = generatePRN(0, account.nonVoting, uint256(account.account) + salt); - election.vote(group, value, address(0), address(0)); - account.nonVoting -= value; - account.pending += value; - } else if (action == VoteActionType.Activate) { - value = account.pending; - election.activate(group); - account.pending -= value; - account.active += value; - } else if (action == VoteActionType.RevokePending) { - value = generatePRN(0, account.pending, uint256(account.account) + salt); - election.revokePending(group, value, address(0), address(0), 0); - account.pending -= value; - account.nonVoting += value; - } else if (action == VoteActionType.RevokeActive) { - value = generatePRN(0, account.active, uint256(account.account) + salt); - election.revokeActive(group, value, address(0), address(0), 0); - account.active -= value; - account.nonVoting += value; - } - vm.stopPrank(); - } - function checkVoterInvariants(AccountStruct memory account, uint256 delta) public { assertAlmostEqual( election.getPendingVotesForGroupByAccount(group, account.account), @@ -2708,13 +3283,84 @@ contract ElectionTest_ConsistencyChecks is ElectionTest { } revokeAllAndCheckInvariants(100); } + + function makeRandomAction(AccountStruct storage account, uint256 salt) internal { + VoteActionType[] memory actions = new VoteActionType[](4); + uint256 actionCount = 0; + + if (account.nonVoting > 0) { + actions[actionCount++] = VoteActionType.Vote; + } + if (election.hasActivatablePendingVotes(account.account, group)) { + // Assuming this is a view function + actions[actionCount++] = VoteActionType.Activate; + } + if (account.pending > 0) { + actions[actionCount++] = VoteActionType.RevokePending; + } + if (account.active > 0) { + actions[actionCount++] = VoteActionType.RevokeActive; + } + + VoteActionType action = actions[generatePRN(0, actionCount - 1, uint256(account.account))]; + uint256 value; + + vm.startPrank(account.account); + if (action == VoteActionType.Vote) { + value = generatePRN(0, account.nonVoting, uint256(account.account) + salt); + election.vote(group, value, address(0), address(0)); + account.nonVoting -= value; + account.pending += value; + } else if (action == VoteActionType.Activate) { + value = account.pending; + election.activate(group); + account.pending -= value; + account.active += value; + } else if (action == VoteActionType.RevokePending) { + value = generatePRN(0, account.pending, uint256(account.account) + salt); + election.revokePending(group, value, address(0), address(0), 0); + account.pending -= value; + account.nonVoting += value; + } else if (action == VoteActionType.RevokeActive) { + value = generatePRN(0, account.active, uint256(account.account) + salt); + election.revokeActive(group, value, address(0), address(0), 0); + account.active -= value; + account.nonVoting += value; + } + vm.stopPrank(); + } } contract ElectionTest_HasActivatablePendingVotes is ElectionTest { - function test_Revert_hasActivatablePendingVotes_WhenL2() public { + address voter = address(this); + address group = account1; + uint256 value = 1000; + + function setUp() public { + super.setUp(); + address[] memory members = new address[](1); + members[0] = account9; + validators.setMembers(group, members); + + registry.setAddressFor("Validators", address(this)); + election.markGroupEligible(group, address(0), address(0)); + registry.setAddressFor("Validators", address(validators)); + + lockedGold.setTotalLockedGold(value); + validators.setMembers(group, members); + validators.setNumRegisteredValidators(1); + + lockedGold.incrementNonvotingAccountBalance(voter, value); + election.vote(group, value, address(0), address(0)); + blockTravel(ph.epochSize() + 1); + } + function test_ReturnsTrue_WhenUserHasVoted() public { + assertTrue(election.hasActivatablePendingVotes(voter, group)); + } + + function test_ReturnsTrue_WhenUserHasVotedOnL2() public { _whenL2(); - vm.expectRevert("This method is no longer supported in L2."); - vm.prank(address(0)); - election.hasActivatablePendingVotes(address(0), address(0)); + + assertTrue(election.hasActivatablePendingVotes(voter, group)); } }