Skip to content

Commit

Permalink
E2E EpochManager test + Epoch truffle migrations & Anvil L2 migration…
Browse files Browse the repository at this point in the history
… build fix (#11198)
  • Loading branch information
pahor167 authored Sep 17, 2024
1 parent 080b430 commit 5dc389f
Show file tree
Hide file tree
Showing 49 changed files with 802 additions and 185 deletions.
13 changes: 12 additions & 1 deletion packages/protocol/contractPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,24 @@ export const SOLIDITY_08_PACKAGE = {
proxiesPath: '/', // Proxies are still with 0.5 contracts
// Proxies shouldn't have to be added to a list manually
// https://github.com/celo-org/celo-monorepo/issues/10555
contracts: ['GasPriceMinimum', 'FeeCurrencyDirectory', 'CeloUnreleasedTreasure', 'Validators'],
contracts: [
'GasPriceMinimum',
'FeeCurrencyDirectory',
'CeloUnreleasedTreasure',
'Validators',
'EpochManager',
'EpochManagerEnabler',
'ScoreManager',
],
proxyContracts: [
'GasPriceMinimumProxy',
'FeeCurrencyDirectoryProxy',
'MentoFeeCurrencyAdapterV1',
'CeloUnreleasedTreasureProxy',
'ValidatorsProxy',
'EpochManagerProxy',
'EpochManagerEnablerProxy',
'ScoreManagerProxy',
],
truffleConfig: 'truffle-config0.8.js',
} satisfies ContractPackage
46 changes: 29 additions & 17 deletions packages/protocol/contracts-0.8/common/EpochManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,19 @@ contract EpochManager is
uint256 toProcessGroups;
}

struct ProcessedGroup {
bool processed;
uint256 epochRewards;
}

// the length of an epoch in seconds
uint256 public epochDuration;

uint256 public firstKnownEpoch;
uint256 private currentEpochNumber;
address[] public elected;

// TODO this should be able to get deleted easily
// maybe even having it in a stadalone contract
mapping(address => bool) public processedGroups;
mapping(address => ProcessedGroup) public processedGroups;

EpochProcessState public epochProcessing;
mapping(uint256 => Epoch) private epochs;
Expand Down Expand Up @@ -178,9 +181,9 @@ contract EpochManager is
address[] calldata lessers,
address[] calldata greaters
) external nonReentrant {
// TODO complete this function
require(isOnEpochProcess(), "Epoch process is not started");
// finalize epoch
// last block should be the block before and timestamp from previous block
epochs[currentEpochNumber].lastBlock = block.number - 1;
// start new epoch
currentEpochNumber++;
Expand All @@ -189,27 +192,36 @@ contract EpochManager is

for (uint i = 0; i < elected.length; i++) {
address group = getValidators().getValidatorsGroup(elected[i]);
if (!processedGroups[group]) {
if (!processedGroups[group].processed) {
epochProcessing.toProcessGroups++;
processedGroups[group] = true;
uint256 groupScore = getScoreReader().getGroupScore(group);
// We need to precompute epoch rewards for each group since computation depends on total active votes for all groups.
uint256 epochRewards = getElection().getGroupEpochRewards(
group,
epochProcessing.totalRewardsVoter,
groupScore
);
processedGroups[group] = ProcessedGroup(true, epochRewards);
}
}

require(epochProcessing.toProcessGroups == groups.length, "number of groups does not match");

for (uint i = 0; i < groups.length; i++) {
// since we are adding values it makes sense to start from the end
for (uint ii = groups.length; ii > 0; ii--) {
uint256 i = ii - 1;
ProcessedGroup storage processedGroup = processedGroups[groups[i]];
// checks that group is actually from elected group
require(processedGroups[groups[i]], "group not processed");
// by doing this, we avoid processing a group twice
delete processedGroups[groups[i]];
// TODO what happens to uptime?
uint256 groupScore = getScoreReader().getGroupScore(groups[i]);
uint256 epochRewards = getElection().getGroupEpochRewards(
require(processedGroup.processed, "group not processed");
getElection().distributeEpochRewards(
groups[i],
epochProcessing.totalRewardsVoter,
groupScore
processedGroup.epochRewards,
lessers[i],
greaters[i]
);
getElection().distributeEpochRewards(groups[i], epochRewards, lessers[i], greaters[i]);

// by doing this, we avoid processing a group twice
delete processedGroups[groups[i]];
}
getCeloUnreleasedTreasure().release(
registry.getAddressForOrDie(GOVERNANCE_REGISTRY_ID),
Expand All @@ -220,7 +232,7 @@ contract EpochManager is
epochProcessing.totalRewardsCarbonFund
);
// run elections
elected = getElection().electNValidatorSigners(10, 20);
elected = getElection().electValidatorSigners();
// TODO check how to nullify stuct
epochProcessing.status = EpochProcessStatus.NotStarted;
}
Expand Down
17 changes: 15 additions & 2 deletions packages/protocol/contracts-0.8/common/EpochManagerEnabler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import "../../contracts/governance/interfaces/IEpochRewards.sol";

contract EpochManagerEnabler is Initializable, UsingPrecompiles, UsingRegistry {
uint256 public lastKnownEpochNumber;
uint256 public lastKnownFirstBlockOfEpoch;
address[] public lastKnownElectedAccounts;

/**
Expand All @@ -33,10 +34,11 @@ contract EpochManagerEnabler is Initializable, UsingPrecompiles, UsingRegistry {
*/
function initEpochManager() external onlyL2 {
require(lastKnownEpochNumber != 0, "lastKnownEpochNumber not set.");
require(lastKnownFirstBlockOfEpoch != 0, "lastKnownFirstBlockOfEpoch not set.");
require(lastKnownElectedAccounts.length > 0, "lastKnownElectedAccounts not set.");
getEpochManager().initializeSystem(
lastKnownEpochNumber,
_getFirstBlockOfEpoch(lastKnownEpochNumber),
lastKnownFirstBlockOfEpoch,
lastKnownElectedAccounts
);
}
Expand All @@ -48,8 +50,8 @@ contract EpochManagerEnabler is Initializable, UsingPrecompiles, UsingRegistry {
lastKnownEpochNumber = getEpochNumber();

uint256 numberElectedValidators = numberValidatorsInCurrentSet();

lastKnownElectedAccounts = new address[](numberElectedValidators);
lastKnownFirstBlockOfEpoch = _getFirstBlockOfEpoch(lastKnownEpochNumber);

for (uint256 i = 0; i < numberElectedValidators; i++) {
// TODO: document how much gas this takes for 110 signers
Expand All @@ -64,6 +66,17 @@ contract EpochManagerEnabler is Initializable, UsingPrecompiles, UsingRegistry {
return _getFirstBlockOfEpoch(currentEpoch);
}

/**
* @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);
}

function _getFirstBlockOfEpoch(uint256 currentEpoch) internal view returns (uint256) {
uint256 blockToCheck = block.number - 1;
uint256 blockEpochNumber = getEpochNumberOfBlock(blockToCheck);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.5.13 <0.9.0;

interface IScoreManager {
function setGroupScore(address group, uint256 score) external;
function setValidatorScore(address validator, uint256 score) external;
function getValidatorScore(address validator) external view returns (uint256);
function getGroupScore(address validator) external view returns (uint256);
function owner() external view returns (address);
}
19 changes: 8 additions & 11 deletions packages/protocol/contracts-0.8/common/test/MockCeloToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ contract MockCeloToken08 {
return _transfer(from, to, amount);
}

function _transfer(address from, address to, uint256 amount) internal returns (bool) {
if (balances[from] < amount) {
return false;
}
balances[from] -= amount;
balances[to] += amount;
return true;
}

function setBalanceOf(address a, uint256 value) external {
balances[a] = value;
}
Expand All @@ -54,7 +45,13 @@ contract MockCeloToken08 {
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function allocatedSupply() public view returns (uint256) {
return CELO_SUPPLY_CAP - L2_INITIAL_STASH_BALANCE;

function _transfer(address from, address to, uint256 amount) internal returns (bool) {
if (balances[from] < amount) {
return false;
}
balances[from] -= amount;
balances[to] += amount;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,12 @@ contract MockEpochManager is IEpochManager {
function getElected() external view returns (address[] memory) {
return elected;
}

function getEpochProcessingState()
external
view
returns (uint256, uint256, uint256, uint256, uint256)
{
return (0, 0, 0, 0, 0);
}
}
7 changes: 6 additions & 1 deletion packages/protocol/contracts-0.8/governance/Validators.sol
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ contract Validators is
_;
}

modifier onlyEpochManager() {
require(msg.sender == address(getEpochManager()), "Only epoch manager can call");
_;
}

/**
* @notice Sets initialized == true on implementation contracts
* @param test Set to true to skip implementation initialization
Expand Down Expand Up @@ -514,7 +519,7 @@ contract Validators is
}

/**
* @notice Adds the first member to a group's list of members and marks it eligible for election.
* @notice Adds the first member to a group's list of members and marks the group eligible for election.
* @param validator The validator to add to the group
* @param lesser The address of the group that has received fewer votes than this group.
* @param greater The address of the group that has received more votes than this group.
Expand Down
21 changes: 0 additions & 21 deletions packages/protocol/contracts-0.8/governance/test/ValidatorsMock.sol

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ interface IEpochManager {
function getElected() external view returns (address[] memory);
function epochManagerEnabler() external view returns (address);
function epochDuration() external view returns (uint256);
function firstKnownEpoch() external view returns (uint256);
function getEpochProcessingState()
external
view
returns (uint256, uint256, uint256, uint256, uint256);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.5.13;

import "../Proxy.sol";

/* solhint-disable-next-line no-empty-blocks */
contract EpochManagerEnablerProxy is Proxy {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.5.13;

import "../Proxy.sol";

/* solhint-disable-next-line no-empty-blocks */
contract EpochManagerProxy is Proxy {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pragma solidity ^0.5.13;

import "../Proxy.sol";

/* solhint-disable-next-line no-empty-blocks */
contract ScoreManagerProxy is Proxy {}
2 changes: 1 addition & 1 deletion packages/protocol/contracts/governance/Election.sol
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ contract Election is
uint256 value,
address lesser,
address greater
) internal onlyL1 {
) internal {
if (votes.total.eligible.contains(group)) {
uint256 newVoteTotal = votes.total.eligible.getValue(group).add(value);
votes.total.eligible.update(group, newVoteTotal, lesser, greater);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import "openzeppelin-solidity/contracts/math/SafeMath.sol";

import "../../../contracts-0.8/common/IsL2Check.sol";

// Mocks Validators, compatible with 0.5
// For forge tests, can be avoided with calls to deployCodeTo

/**
* @title Holds a list of addresses of validators
*/
Expand Down
1 change: 1 addition & 0 deletions packages/protocol/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fs_permissions = [
]

[profile.devchain] # Special profile for the tests that require an anvil devchain
test = 'test-sol/devchain'
match_path = "**/test-sol/devchain/**"
no_match_path = "{**/test/BLS12Passthrough.sol,**/test/RandomTest.sol}"

Expand Down
Loading

0 comments on commit 5dc389f

Please sign in to comment.