Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish epoch processing split #11243

Merged
merged 57 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f3c3193
added `getEpochInfoOfEpoch(uint256)` to get epoch info of specific ep…
soloseng Oct 2, 2024
6d059e9
updated natspec
soloseng Oct 2, 2024
7a4f8a4
PR feedback
soloseng Oct 3, 2024
2e30ff6
Use constant
soloseng Oct 3, 2024
fca97c1
breakup test; added more
soloseng Oct 3, 2024
ae767fe
++ registry to precompile contract
soloseng Oct 4, 2024
9e5043e
Merge branch 'soloseng/epoch-of-block' into soloseng/backward-compati…
soloseng Oct 4, 2024
dd39947
added elected to epoch info
soloseng Oct 4, 2024
ece3579
numberValidatorsInSet backwards compatibility
soloseng Oct 4, 2024
accf227
validator address & validatorSigner address from set
soloseng Oct 4, 2024
6b7cf95
support `getEpochNumber()` backwards compatibility in UsingRegistry
soloseng Oct 4, 2024
feeb9e3
using `getEpochByBlockNumber` instead of `getEpochByNumber`
soloseng Oct 4, 2024
f22201d
limit span of loop only to L2 blocks
soloseng Oct 4, 2024
69a84e6
use binary search instead of blind loop
soloseng Oct 4, 2024
fd36439
++ natspec
soloseng Oct 4, 2024
50bec5c
added getEpochNumberOfBlock() backwards compatibility support
soloseng Oct 4, 2024
6732448
++ electedSigners
soloseng Oct 8, 2024
d786a56
make usingPrecompile onlyL1 contract
soloseng Oct 8, 2024
eaeeb8b
++ precompile override contract
soloseng Oct 8, 2024
1dcd131
fix epoch manager bug and mock
soloseng Oct 8, 2024
b6ac1f2
using precompileOverride in validators contract
soloseng Oct 8, 2024
6205b5f
wording
soloseng Oct 8, 2024
d84205d
using indexed function when querying specific validator accounts or s…
soloseng Oct 8, 2024
162d850
Merge branch 'release/core-contracts/12' into soloseng/UsingRegistry-…
soloseng Oct 8, 2024
35316a0
fixed failing test
soloseng Oct 9, 2024
a0c976a
using independent mapping for elected accounts and signers
soloseng Oct 9, 2024
86f74d9
removed old elected list
soloseng Oct 10, 2024
681a858
support 0.5.13 contracts using precompiles
soloseng Oct 10, 2024
6648ad4
-- registry import
soloseng Oct 10, 2024
f0a4fc3
++ registry in PrecompilesOverride
soloseng Oct 10, 2024
ae1577f
++ more tests
soloseng Oct 10, 2024
17566dc
increase gaslimit
soloseng Oct 10, 2024
23b0aa3
moved election history update to epoch processing start.
soloseng Oct 10, 2024
f645c26
unused var
soloseng Oct 10, 2024
96e45a4
clean up
soloseng Oct 10, 2024
04d46ee
fixed e2e test
soloseng Oct 11, 2024
a4f380f
simplify code
soloseng Oct 11, 2024
de140aa
Merge branch 'release/core-contracts/12' into soloseng/UsingRegistry-…
soloseng Oct 11, 2024
a033909
Finish epoch processing split
pahor167 Oct 12, 2024
f0b3f50
FinishEpochProcessing split e2e test
pahor167 Oct 13, 2024
7501924
setToProcessGroups unit tests
pahor167 Oct 13, 2024
3051dce
process group unit tests
pahor167 Oct 13, 2024
cf21202
Merge branch 'release/core-contracts/12' into pahor/finishEpochProces…
pahor167 Oct 13, 2024
1e52b15
Merge branch 'release/core-contracts/12' into soloseng/UsingRegistry-…
martinvol Oct 14, 2024
f3b7a51
Removed support for election historical data after that it's unlikely…
soloseng Oct 14, 2024
c15fc93
PR feedback
soloseng Oct 14, 2024
78ea07f
-- spacing
soloseng Oct 14, 2024
954b346
reorder state var
soloseng Oct 14, 2024
baf0e79
stat var reorder
soloseng Oct 14, 2024
0c14aa0
small fix
soloseng Oct 14, 2024
c67c70b
delete electedSigners at end of epochprocessing
soloseng Oct 14, 2024
0fb94bc
CI fix
soloseng Oct 15, 2024
3dcb696
added comment
pahor167 Oct 15, 2024
0e4fd2e
Merge branch 'soloseng/UsingRegistry-backward-compatibility-fix' into…
pahor167 Oct 15, 2024
4de3976
prettify
pahor167 Oct 15, 2024
784d64b
Merge branch 'release/core-contracts/12' into pahor/finishEpochProces…
pahor167 Oct 15, 2024
0f743fb
Pr comments
pahor167 Oct 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 69 additions & 3 deletions packages/protocol/contracts-0.8/common/EpochManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ contract EpochManager is
mapping(uint256 => Epoch) internal epochs;
mapping(address => uint256) public validatorPendingPayments;

uint256 public toProcessGroups = 0;

/**
* @notice Event emited when epochProcessing has begun.
* @param epochNumber The epoch number that is being processed.
Expand Down Expand Up @@ -201,6 +203,69 @@ contract EpochManager is
emit EpochProcessingStarted(currentEpochNumber);
}

function setToProcessGroups() external {
pahor167 marked this conversation as resolved.
Show resolved Hide resolved
require(isOnEpochProcess(), "Epoch process is not started");
pahor167 marked this conversation as resolved.
Show resolved Hide resolved

EpochProcessState storage _epochProcessing = epochProcessing;

IValidators validators = getValidators();
IElection election = getElection();
IScoreReader scoreReader = getScoreReader();
for (uint i = 0; i < elected.length; i++) {
address group = validators.getValidatorsGroup(elected[i]);
if (processedGroups[group] == 0) {
toProcessGroups++;
uint256 groupScore = scoreReader.getGroupScore(group);
// We need to precompute epoch rewards for each group since computation depends on total active votes for all groups.
uint256 epochRewards = election.getGroupEpochRewardsBasedOnScore(
group,
_epochProcessing.totalRewardsVoter,
groupScore
);
processedGroups[group] = epochRewards == 0 ? type(uint256).max : epochRewards;
}
}
}

function processGroup(address group, address lesser, address greater) external {
pahor167 marked this conversation as resolved.
Show resolved Hide resolved
require(isOnEpochProcess(), "Epoch process is not started");
require(toProcessGroups > 0, "no more groups to process");

uint256 epochRewards = processedGroups[group];
// checks that group is actually from elected group
require(epochRewards > 0, "group not from current elected set");
IElection election = getElection();

if (epochRewards != type(uint256).max) {
election.distributeEpochRewards(group, epochRewards, lesser, greater);
}

delete processedGroups[group];
toProcessGroups--;

if (toProcessGroups == 0) {
getCeloUnreleasedTreasury().release(
registry.getAddressForOrDie(GOVERNANCE_REGISTRY_ID),
epochProcessing.totalRewardsCommunity
);
getCeloUnreleasedTreasury().release(
getEpochRewards().carbonOffsettingPartner(),
pahor167 marked this conversation as resolved.
Show resolved Hide resolved
epochProcessing.totalRewardsCarbonFund
);
// run elections
elected = election.electValidatorAccounts();
epochProcessing.status = EpochProcessStatus.NotStarted;

// finalize epoch
// last block should be the block before and timestamp from previous block
epochs[currentEpochNumber].lastBlock = block.number - 1;
// start new epoch
currentEpochNumber++;
epochs[currentEpochNumber].firstBlock = block.number;
epochs[currentEpochNumber].startTimestamp = block.timestamp;
pahor167 marked this conversation as resolved.
Show resolved Hide resolved
}
}

/**
* @notice Finishes processing an epoch and releasing funds to the beneficiaries.
* @param groups List of validator groups to be processed.
Expand All @@ -213,6 +278,7 @@ contract EpochManager is
address[] calldata greaters
) external virtual nonReentrant {
require(isOnEpochProcess(), "Epoch process is not started");
require(toProcessGroups == 0, "not all groups processed");
pahor167 marked this conversation as resolved.
Show resolved Hide resolved
// finalize epoch
// last block should be the block before and timestamp from previous block
epochs[currentEpochNumber].lastBlock = block.number - 1;
Expand All @@ -223,14 +289,14 @@ contract EpochManager is

EpochProcessState storage _epochProcessing = epochProcessing;

uint256 toProcessGroups = 0;
uint256 _toProcessGroups = 0;
IValidators validators = getValidators();
IElection election = getElection();
IScoreReader scoreReader = getScoreReader();
for (uint i = 0; i < elected.length; i++) {
address group = validators.getValidatorsGroup(elected[i]);
if (processedGroups[group] == 0) {
toProcessGroups++;
_toProcessGroups++;
uint256 groupScore = scoreReader.getGroupScore(group);
// We need to precompute epoch rewards for each group since computation depends on total active votes for all groups.
uint256 epochRewards = election.getGroupEpochRewardsBasedOnScore(
Expand All @@ -243,7 +309,7 @@ contract EpochManager is
delete elected[i];
}

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

for (uint i = 0; i < groups.length; i++) {
uint256 epochRewards = processedGroups[groups[i]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ contract MockEpochManager is IEpochManager {
address[] calldata greaters
) external {}

function setToProcessGroups() external {}
function processGroup(address group, address lesser, address greater) external {}

function setIsTimeForNextEpoch(bool _isTime) external {
_isTimeForNextEpoch = _isTime;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface IEpochManager {
address[] calldata lessers,
address[] calldata greaters
) external;
function setToProcessGroups() external;
function processGroup(address group, address lesser, address greater) external;
function sendValidatorPayment(address) external;
function getCurrentEpoch() external view returns (uint256, uint256, uint256, uint256);
function getCurrentEpochNumber() external view returns (uint256);
Expand Down
140 changes: 140 additions & 0 deletions packages/protocol/test-sol/devchain/e2e/common/EpochManager.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -745,3 +745,143 @@ contract E2E_GasTest2_FinishNextEpochProcess is E2E_GasTest_Setup {
console.log("elected count2: ", epochManager.getElected().length);
}
}

contract E2E_FinishNextEpochProcess_Split is E2E_GasTest_Setup {
using EnumerableSet for EnumerableSet.AddressSet;

function setUp() public override {
super.setUp();

activateValidators();
whenL2(vm);

vm.prank(epochManagerEnabler);
epochManager.initializeSystem(1, 1, firstElected);

validatorsArray = getValidators().getRegisteredValidators();
groups = getValidators().getRegisteredValidatorGroups();

vm.startPrank(scoreManager.owner());
scoreManager.setGroupScore(groups[0], groupScore[0]);
scoreManager.setGroupScore(groups[1], groupScore[1]);
scoreManager.setGroupScore(groups[2], groupScore[2]);

scoreManager.setValidatorScore(validatorsArray[0], validatorScore[0]);
scoreManager.setValidatorScore(validatorsArray[1], validatorScore[1]);
scoreManager.setValidatorScore(validatorsArray[2], validatorScore[2]);
scoreManager.setValidatorScore(validatorsArray[3], validatorScore[3]);
scoreManager.setValidatorScore(validatorsArray[4], validatorScore[4]);
scoreManager.setValidatorScore(validatorsArray[5], validatorScore[5]);

vm.stopPrank();

timeTravel(vm, epochDuration + 1);
epochManager.startNextEpochProcess();

address[] memory lessers;
address[] memory greaters;
address[] memory groupsEligible;
GroupWithVotes[] memory groupWithVotes;
uint256[] memory groupActiveBalances;
(lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups);

uint256 currentEpoch = epochManager.getCurrentEpochNumber();
address[] memory currentlyElected = epochManager.getElected();
for (uint256 i = 0; i < currentlyElected.length; i++) {
originalyElected.add(currentlyElected[i]);
}

// wait some time before finishing
timeTravel(vm, epochDuration / 2);
blockTravel(vm, 100);

epochManager.setToProcessGroups();
for (uint256 i = 0; i < groups.length; i++) {
epochManager.processGroup(groups[i], lessers[i], greaters[i]);
}

assertEq(currentEpoch + 1, epochManager.getCurrentEpochNumber());

address[] memory newlyElected = epochManager.getElected();

for (uint256 i = 0; i < currentlyElected.length; i++) {
assertEq(originalyElected.contains(currentlyElected[i]), true);
}

timeTravel(vm, epochDuration + 1);
epochManager.startNextEpochProcess();

// wait some time before finishing
timeTravel(vm, epochDuration / 2);
blockTravel(vm, 100);

(lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups);
epochManager.setToProcessGroups();
for (uint256 i = 0; i < groups.length; i++) {
epochManager.processGroup(groups[i], lessers[i], greaters[i]);
}
// epochManager.finishNextEpochProcess(groups, lessers, greaters);
assertGroupWithVotes(groupWithVotes);

assertEq(currentEpoch + 2, epochManager.getCurrentEpochNumber());

address[] memory newlyElected2 = epochManager.getElected();

for (uint256 i = 0; i < currentlyElected.length; i++) {
assertEq(originalyElected.contains(newlyElected2[i]), true);
}
uint256 validatorGroupCount = 60;
uint256 validatorPerGroupCount = 2;

for (uint256 i = 0; i < validatorGroupCount; i++) {
(address newValidatorGroup, address newValidator) = registerNewValidatorGroupWithValidator(
i,
validatorPerGroupCount
);
}

timeTravel(vm, epochDuration + 1);
epochManager.startNextEpochProcess();

timeTravel(vm, epochDuration / 2);
blockTravel(vm, 100);

(lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups);

epochManager.setToProcessGroups();
for (uint256 i = 0; i < groups.length; i++) {
epochManager.processGroup(groups[i], lessers[i], greaters[i]);
}

activateValidators();

timeTravel(vm, epochDuration + 1);
epochManager.startNextEpochProcess();

groups = getCurrentlyElectedGroups();

timeTravel(vm, epochDuration / 2);
blockTravel(vm, 100);
}

/**
* @notice Test the gas used by finishNextEpochProcess
This test is trying to measure gas used by finishNextEpochProcess in a real life worst case. We have 126 validators and 123 groups.
There are two main loops in the function, one for calculating rewards and the other for updating the elected validators.
FinishNextEpochProcess is called twice, first time with going from 6 -> 110 validators which consumes approx. 6M gas and the second time with going from 110 -> 110 validators which consumes approx. 19M gas.
*/
function test_shouldFinishNextEpochProcessing_GasTest_Split() public {
address[] memory lessers;
address[] memory greaters;
GroupWithVotes[] memory groupWithVotes;
(lessers, greaters, groupWithVotes) = getLessersAndGreaters(groups);
epochManager.setToProcessGroups();

for (uint256 i = 0; i < groups.length; i++) {
uint256 gasLeftBefore1 = gasleft();
epochManager.processGroup(groups[i], lessers[i], greaters[i]);
uint256 gasLeftAfter1 = gasleft();
console.log("processGroup gas used: ", gasLeftBefore1 - gasLeftAfter1);
}
}
}
Loading
Loading