Skip to content

Commit

Permalink
Move Validators.sol to 0.8 (#11192)
Browse files Browse the repository at this point in the history
* WIP

* Validator test WIP, forge doesn't compile yet

* buildable

* most of the foundry tests working

* Validator related tests fixed

* truffle build working

* cache bump

* lint + prettify+ migrations

* Enable optimization for Solidity 0.8

* prettify

* Ci bump

* CI bump 2

* Validators to 0.8 config

* CI bump

* foundry fix

* Truffle migrations are partly fixed

* Added import for ValidatorsMock08 in EpochManager.t.sol, I think it was removed by mistake

* Added yarn.lock

* Changes to mock with full implementation

* Attempt to fix linking libraries not working with deployCodeTo https://github.com/foundry-rs/foundry/issues/4049

* truffle migrations fixed

* CI bump

* lint

* forge test fixes

* artifacts test fix

* lint

* update of foundry version

* add ProxyFactory import to tests

* library linking fix

* Foundry migrations fix

* migration tests fix

* CI bump

* Little cleanup + retrigger CI

* forgot to commit Validators.sol

* Fixed the ABI encoded

* lint

* Fix contract versions

* add Adapter to ignored contracts

* revert of ReentrancyGuard change

* lint fix

* remove adapters from check

* storage layout fix

---------

Co-authored-by: pahor167 <[email protected]>
  • Loading branch information
martinvol and pahor167 authored Aug 26, 2024
1 parent b63e487 commit 96eb887
Show file tree
Hide file tree
Showing 47 changed files with 891 additions and 436 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/celo-monorepo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defaults:

env:
# Increment these to force cache rebuilding
NODE_MODULE_CACHE_VERSION: 7
NODE_MODULE_CACHE_VERSION: 8
NODE_OPTIONS: '--max-old-space-size=4096'
TERM: dumb
GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.parallel=false -Dorg.gradle.configureondemand=true -Dorg.gradle.jvmargs="-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError"'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/protocol-devchain-anvil.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: ${{ env.SUPPORTED_FOUNDRY_VERSION }}
version: 'nightly-fa0e0c2ca3ae75895dd19173a02faf88509c0608'

- name: Install forge dependencies
run: forge install
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/protocol_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: ${{ env.SUPPORTED_FOUNDRY_VERSION }}
version: 'nightly-fa0e0c2ca3ae75895dd19173a02faf88509c0608'

- name: Install forge dependencies
run: forge install
Expand Down Expand Up @@ -153,4 +153,4 @@ jobs:
FOUNDRY_PROFILE=devchain forge test -vvv \
--match-path "test-sol/devchain/e2e/*" \
--fork-url $ANVIL_RPC_URL
--fork-url $ANVIL_RPC_URL
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@
path = packages/protocol/lib/celo-foundry
url = https://github.com/celo-org/celo-foundry
branch = celo-foundry-v0.5.13
[submodule "packages/protocol/lib/solidity-bytes-utils-8"]
path = packages/protocol/lib/solidity-bytes-utils-8
url = https://github.com/GNSPS/solidity-bytes-utils
branch = master
3 changes: 2 additions & 1 deletion packages/protocol/contractPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ 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'],
contracts: ['GasPriceMinimum', 'FeeCurrencyDirectory', 'CeloUnreleasedTreasure', 'Validators'],
proxyContracts: [
'GasPriceMinimumProxy',
'FeeCurrencyDirectoryProxy',
'MentoFeeCurrencyAdapterV1',
'CeloUnreleasedTreasureProxy',
'ValidatorsProxy',
],
truffleConfig: 'truffle-config0.8.js',
} satisfies ContractPackage
166 changes: 159 additions & 7 deletions packages/protocol/contracts-0.8/common/UsingPrecompiles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,39 @@ contract UsingPrecompiles is IsL2Check {
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
* @param aNumerator Numerator of first fraction
* @param aDenominator Denominator of first fraction
* @param bNumerator Numerator of exponentiated fraction
* @param bDenominator Denominator of exponentiated fraction
* @param exponent exponent to raise b to
* @param _decimals precision
* @return Numerator of the computed quantity (not reduced).
* @return Denominator of the computed quantity (not reduced).
*/
function fractionMulExp(
uint256 aNumerator,
uint256 aDenominator,
uint256 bNumerator,
uint256 bDenominator,
uint256 exponent,
uint256 _decimals
) public view returns (uint256, uint256) {
require(aDenominator != 0 && bDenominator != 0, "a denominator is zero");
uint256 returnNumerator;
uint256 returnDenominator;
bool success;
bytes memory out;
(success, out) = FRACTION_MUL.staticcall(
abi.encodePacked(aNumerator, aDenominator, bNumerator, bDenominator, exponent, _decimals)
);
require(success, "error calling fractionMulExp precompile");
returnNumerator = getUint256FromBytes(out, 0);
returnDenominator = getUint256FromBytes(out, 32);
return (returnNumerator, returnDenominator);
}

/**
* @notice Returns the current epoch size in blocks.
* @return The current epoch size in blocks.
Expand Down Expand Up @@ -54,6 +87,36 @@ contract UsingPrecompiles is IsL2Check {
return getEpochNumberOfBlock(block.number);
}

/**
* @notice Gets a validator address from the current validator set.
* @param index Index of requested validator in the validator set.
* @return Address of validator at the requested index.
*/
function validatorSignerAddressFromCurrentSet(uint256 index) public view returns (address) {
bytes memory out;
bool success;
(success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, uint256(block.number)));
require(success, "error calling validatorSignerAddressFromCurrentSet precompile");
return address(uint160(getUint256FromBytes(out, 0)));
}

/**
* @notice Gets a validator address from the validator set at the given block number.
* @param index Index of requested validator in the validator set.
* @param blockNumber Block number to retrieve the validator set from.
* @return Address of validator at the requested index.
*/
function validatorSignerAddressFromSet(
uint256 index,
uint256 blockNumber
) public view returns (address) {
bytes memory out;
bool success;
(success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, blockNumber));
require(success, "error calling validatorSignerAddressFromSet precompile");
return address(uint160(getUint256FromBytes(out, 0)));
}

/**
* @notice Gets the size of the current elected validator set.
* @return Size of the current elected validator set.
Expand All @@ -67,16 +130,105 @@ contract UsingPrecompiles is IsL2Check {
}

/**
* @notice Gets a validator address from the current validator set.
* @param index Index of requested validator in the validator set.
* @return Address of validator at the requested index.
* @notice Gets the size of the validator set that must sign the given block number.
* @param blockNumber Block number to retrieve the validator set from.
* @return Size of the validator set.
*/
function validatorSignerAddressFromCurrentSet(uint256 index) public view returns (address) {
function numberValidatorsInSet(uint256 blockNumber) public view returns (uint256) {
bytes memory out;
bool success;
(success, out) = GET_VALIDATOR.staticcall(abi.encodePacked(index, uint256(block.number)));
require(success, "error calling validatorSignerAddressFromCurrentSet precompile");
return address(uint160(getUint256FromBytes(out, 0)));
(success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(blockNumber));
require(success, "error calling numberValidatorsInSet precompile");
return getUint256FromBytes(out, 0);
}

/**
* @notice Checks a BLS proof of possession.
* @param sender The address signed by the BLS key to generate the proof of possession.
* @param blsKey The BLS public key that the validator is using for consensus, should pass proof
* of possession. 48 bytes.
* @param blsPop The BLS public key proof-of-possession, which consists of a signature on the
* account address. 96 bytes.
* @return True upon success.
*/
function checkProofOfPossession(
address sender,
bytes memory blsKey,
bytes memory blsPop
) public view returns (bool) {
bool success;
(success, ) = PROOF_OF_POSSESSION.staticcall(abi.encodePacked(sender, blsKey, blsPop));
return success;
}

/**
* @notice Parses block number out of header.
* @param header RLP encoded header
* @return Block number.
*/
function getBlockNumberFromHeader(bytes memory header) public view returns (uint256) {
bytes memory out;
bool success;
(success, out) = BLOCK_NUMBER_FROM_HEADER.staticcall(abi.encodePacked(header));
require(success, "error calling getBlockNumberFromHeader precompile");
return getUint256FromBytes(out, 0);
}

/**
* @notice Computes hash of header.
* @param header RLP encoded header
* @return Header hash.
*/
function hashHeader(bytes memory header) public view returns (bytes32) {
bytes memory out;
bool success;
(success, out) = HASH_HEADER.staticcall(abi.encodePacked(header));
require(success, "error calling hashHeader precompile");
return getBytes32FromBytes(out, 0);
}

/**
* @notice Gets the parent seal bitmap from the header at the given block number.
* @param blockNumber Block number to retrieve. Must be within 4 epochs of the current number.
* @return Bitmap parent seal with set bits at indices corresponding to signing validators.
*/
function getParentSealBitmap(uint256 blockNumber) public view returns (bytes32) {
bytes memory out;
bool success;
(success, out) = GET_PARENT_SEAL_BITMAP.staticcall(abi.encodePacked(blockNumber));
require(success, "error calling getParentSealBitmap precompile");
return getBytes32FromBytes(out, 0);
}

/**
* @notice Verifies the BLS signature on the header and returns the seal bitmap.
* The validator set used for verification is retrieved based on the parent hash field of the
* header. If the parent hash is not in the blockchain, verification fails.
* @param header RLP encoded header
* @return Bitmap parent seal with set bits at indices correspoinding to signing validators.
*/
function getVerifiedSealBitmapFromHeader(bytes memory header) public view returns (bytes32) {
bytes memory out;
bool success;
(success, out) = GET_VERIFIED_SEAL_BITMAP.staticcall(abi.encodePacked(header));
require(success, "error calling getVerifiedSealBitmapFromHeader precompile");
return getBytes32FromBytes(out, 0);
}

/**
* @notice Returns the minimum number of required signers for a given block number.
* @dev Computed in celo-blockchain as int(math.Ceil(float64(2*valSet.Size()) / 3))
*/
function minQuorumSize(uint256 blockNumber) public view returns (uint256) {
return numberValidatorsInSet(blockNumber).mul(2).add(2).div(3);
}

/**
* @notice Computes byzantine quorum from current validator set size
* @return Byzantine quorum of validators.
*/
function minQuorumSizeInCurrentSet() public view returns (uint256) {
return minQuorumSize(block.number);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.13 <0.9.0;

interface IPrecompiles {
function getEpochSize() external view returns (uint256);
function getEpochNumber() external view returns (uint256);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
pragma solidity >=0.8.0 <0.8.20;

import "@openzeppelin/contracts8/utils/math/SafeMath.sol";

import "./LinkedList.sol";

/**
* @title Maintains a doubly linked list keyed by address.
* @dev Following the `next` pointers will lead you to the head, rather than the tail.
*/
library AddressLinkedList {
using LinkedList for LinkedList.List;
using SafeMath for uint256;
/**
* @notice Inserts an element into a doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to insert.
* @param previousKey The key of the element that comes before the element to insert.
* @param nextKey The key of the element that comes after the element to insert.
*/
function insert(
LinkedList.List storage list,
address key,
address previousKey,
address nextKey
) public {
list.insert(toBytes(key), toBytes(previousKey), toBytes(nextKey));
}

/**
* @notice Inserts an element at the end of the doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to insert.
*/
function push(LinkedList.List storage list, address key) public {
list.insert(toBytes(key), bytes32(0), list.tail);
}

/**
* @notice Removes an element from the doubly linked list.
* @param list A storage pointer to the underlying list.
* @param key The key of the element to remove.
*/
function remove(LinkedList.List storage list, address key) public {
list.remove(toBytes(key));
}

/**
* @notice Updates an element in the list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @param previousKey The key of the element that comes before the updated element.
* @param nextKey The key of the element that comes after the updated element.
*/
function update(
LinkedList.List storage list,
address key,
address previousKey,
address nextKey
) public {
list.update(toBytes(key), toBytes(previousKey), toBytes(nextKey));
}

/**
* @notice Returns whether or not a particular key is present in the sorted list.
* @param list A storage pointer to the underlying list.
* @param key The element key.
* @return Whether or not the key is in the sorted list.
*/
function contains(LinkedList.List storage list, address key) public view returns (bool) {
return list.elements[toBytes(key)].exists;
}

/**
* @notice Returns the N greatest elements of the list.
* @param list A storage pointer to the underlying list.
* @param n The number of elements to return.
* @return The keys of the greatest elements.
* @dev Reverts if n is greater than the number of elements in the list.
*/
function headN(LinkedList.List storage list, uint256 n) public view returns (address[] memory) {
bytes32[] memory byteKeys = list.headN(n);
address[] memory keys = new address[](n);
for (uint256 i = 0; i < n; i = i.add(1)) {
keys[i] = toAddress(byteKeys[i]);
}
return keys;
}

/**
* @notice Gets all element keys from the doubly linked list.
* @param list A storage pointer to the underlying list.
* @return All element keys from head to tail.
*/
function getKeys(LinkedList.List storage list) public view returns (address[] memory) {
return headN(list, list.numElements);
}

function toBytes(address a) public pure returns (bytes32) {
return bytes32(uint256(uint160(a)) << 96);
}

function toAddress(bytes32 b) public pure returns (address) {
return address(uint160(uint256(b) >> 96));
}
}
Loading

0 comments on commit 96eb887

Please sign in to comment.