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

Adds backwards compatibility in UsingPrecompile contract. #11236

Open
wants to merge 40 commits into
base: release/core-contracts/12
Choose a base branch
from

Conversation

soloseng
Copy link
Contributor

@soloseng soloseng commented Oct 4, 2024

Description

Updated UsingPrecompile contract to maintain backwards compatibility after L2 migration.

Fucntions that maintained backwards compatibility query the required information from the EpochManager contract instead of the precompiles.

  • Remove onlyL1 in validators.sol

Other changes

Was required to add the list of elected validators to the Epoch struct of the EpochManager contract to keep the historical list of elected validators at a given epoch number.

Tested

Added unit test.

Related issues

Backwards compatibility

for some select functions

@soloseng soloseng requested a review from a team as a code owner October 4, 2024 21:32
Copy link

openzeppelin-code bot commented Oct 4, 2024

Adds backwards compatibility in UsingPrecompile contract.

Generated at commit: 50bec5c8a89c6e7d9026818ef1d591cf0e891b53

🚨 Report Summary

Severity Level Results
Contracts Critical
High
Medium
Low
Note
Total
3
2
0
14
43
62
Dependencies Critical
High
Medium
Low
Note
Total
0
0
0
0
0
0

For more details view the full report in OpenZeppelin Code Inspector

@soloseng soloseng self-assigned this Oct 4, 2024
@soloseng
Copy link
Contributor Author

soloseng commented Oct 4, 2024

As of 50bec5c
I was able to add the required functions to preserve backwards compatibility to the UsingRegistry contract.

Testing is not fully implemented for registry functions, but the plan was to use EpochManager_WithMock.sol for testing needs and if possible remove MockEpochManager.sol

stCELO only uses numberValidatorsInCurrentSet ; validatorSignerAddressFromCurrentSet but I also added compatibility for the functions that perform the same query using specific blocknumber incase other contracts in the CELo ecosystem use them.

To get the epoch info based on block number, I used binary search to avoid looping through all the epoch 1x1. This was required for getEpochNumberOfBlock(); validatorSignerAddressFromSet()

We could further improve the compute cost by introducing a mapping of blocksToEpoch,

mapping(uint256 => uint256) internal blockToEpoch;

but that would be a more complex change and increase the gas cost of processing an epoch as we'd have to store this info during processing.

NOTE: Not all functions have backwards compatibility.

Copy link
Contributor

@m-chrzan m-chrzan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but there's a few, potentially very impactful, gas optimizations possible. There's no need to read the entire elected array into memory when we only need one field of the Epoch struct, or just one of the elected addresses. These should be fairly simple changes that would reduce the number of SLOADs by a factor of ~100 where relevant.

packages/protocol/contracts-0.8/common/EpochManager.sol Outdated Show resolved Hide resolved
packages/protocol/contracts-0.8/common/EpochManager.sol Outdated Show resolved Hide resolved
require(success, "error calling numberValidatorsInCurrentSet precompile");
return getUint256FromBytes(out, 0);
if (isL2()) {
return getEpochManager().getElected().length;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto, implementing this in EpochManager should save gas since the whole elected array won't be read into memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The limitation in implementing this whole function in EpochManager, is to avoid importing the UsingPrecompiles contract.

I created an EpochManager function that returns the number instead of getting the elected array and getting its length.

packages/protocol/contracts/common/UsingPrecompiles.sol Outdated Show resolved Hide resolved
packages/protocol/test-sol/utils08.sol Outdated Show resolved Hide resolved
Copy link
Contributor

@martinvol martinvol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to fix all precompiles in all contracts

@soloseng
Copy link
Contributor Author

soloseng commented Oct 8, 2024

Updated this PR based on our last sync.

  • added onlyL1 modifiers to all UsingPrecompiles functions.
  • created a new contract PrecompileOverride and it to handle the switching logic between L1 and L2. its currently being used by Validators contract but will need a 0.5.13 version for other core contracts still using 0.5.13 (e.g.: Election, Governance,EpochRewards

@soloseng
Copy link
Contributor Author

As of commit 681a858
Because we are wanting to keep a historical data of the elected validators account & signers, we now have to store the elected in a mapping. this causes an increase in gas when running the election, since we now have to write to 2 new storage slot (electedAccountsOfEpoch && electedSignersOfEpoch each time an election is run.

To minimize the gas cost of reading epoch info based on block number, the mapping of electedAccountsOfEpoch && electedSignersOfEpoch is kept separate than epochs. This allows us to retrieve the epoch info without reading the list of accounts and signers.

That being said, it would still be good to run a gas cost benchmark to know if it will be prohibitive.

usingPrecompile backwards compatibility has been added for Election, Governance, EpochRewards using a 0.5.13 version of the PrecompileOverride contract. This is because 0.5.13 does not support virtual && override function keywords.

* @return startTimestamp The starting timestamp of the given block number.
* @return rewardsBlock The reward block of the given block number.
*/
function _getEpochByBlockNumber(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is very likely to run out of gas, is it (and the functions that use it) really needed?

I also think that it has quite some edge cases, would be difficult to test them all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants