diff --git a/.gitmodules b/.gitmodules index 5c52bf9..888d42d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "lib/forge-std"] path = lib/forge-std - url = https://github.com/bowd/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/lib/forge-std b/lib/forge-std index a0d8747..e4aef94 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit a0d8747cebf72307178e6db508a22c1b0cea1f1b +Subproject commit e4aef94c1768803a16fe19f7ce8b65defd027cfd diff --git a/remappings.txt b/remappings.txt index 845bd0a..d48d006 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,2 +1,2 @@ ds-test/=lib/forge-std/lib/ds-test/src/ -forge-std/=lib/forge-std/src/ +forge-std-8/=lib/forge-std/src/ diff --git a/script/Helper.s.sol b/script/Helper.s.sol index 7ddaf47..b3d2003 100644 --- a/script/Helper.s.sol +++ b/script/Helper.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.5.13; +pragma solidity >=0.5.13 <0.8.20; -import "forge-std/Script.sol"; +import "forge-std-8/Script.sol"; import "../src/PrecompileHandler.sol"; contract Helper is Script, PrecompileHandler { diff --git a/src/PrecompileHandler.sol b/src/PrecompileHandler.sol index 6343722..ef07038 100644 --- a/src/PrecompileHandler.sol +++ b/src/PrecompileHandler.sol @@ -1,15 +1,15 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.5.13; +pragma solidity >=0.5.13 <0.8.20; -import "forge-std/Vm.sol"; -import "forge-std/console2.sol"; +import "forge-std-8/Vm.sol"; +import "forge-std-8/console2.sol"; import "./Precompiles.sol"; contract PrecompileHandler is Precompiles { address constant private VM_ADDRESS = address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))); - Vm public constant vm = Vm(VM_ADDRESS); + Vm public constant _vm = Vm(VM_ADDRESS); bytes4 constant TRANSFER_SIG = bytes4(keccak256("transfer(address,address,uint256)")); bytes4 constant EPOCH_SIZE_SIG = bytes4(keccak256("epochSize()")); @@ -30,41 +30,41 @@ contract PrecompileHandler is Precompiles { Mock private revertMock = Mock(true, _empty, true); constructor() public { - vm.etch(TRANSFER, proxyTo(TRANSFER_SIG)); - vm.label(TRANSFER, "TRANSFER"); + _vm.etch(TRANSFER, proxyTo(TRANSFER_SIG)); + _vm.label(TRANSFER, "TRANSFER"); - vm.etch(EPOCH_SIZE, proxyTo(EPOCH_SIZE_SIG)); - vm.label(EPOCH_SIZE, "EPOCH_SIZE"); + _vm.etch(EPOCH_SIZE, proxyTo(EPOCH_SIZE_SIG)); + _vm.label(EPOCH_SIZE, "EPOCH_SIZE"); bytes memory catchAllProxy = proxyTo(CATCHALL_SIG); - vm.etch(FRACTION_MUL, catchAllProxy); - vm.label(FRACTION_MUL, "FRACTION_MUL"); + _vm.etch(FRACTION_MUL, catchAllProxy); + _vm.label(FRACTION_MUL, "FRACTION_MUL"); - vm.etch(PROOF_OF_POSSESSION, catchAllProxy); - vm.label(PROOF_OF_POSSESSION, "PROOF_OF_POSSESSION"); + _vm.etch(PROOF_OF_POSSESSION, catchAllProxy); + _vm.label(PROOF_OF_POSSESSION, "PROOF_OF_POSSESSION"); - vm.etch(GET_VALIDATOR, catchAllProxy); - vm.label(GET_VALIDATOR, "GET_VALIDATOR"); + _vm.etch(GET_VALIDATOR, catchAllProxy); + _vm.label(GET_VALIDATOR, "GET_VALIDATOR"); - vm.etch(NUMBER_VALIDATORS, catchAllProxy); - vm.label(NUMBER_VALIDATORS, "NUMBER_VALIDATORS"); + _vm.etch(NUMBER_VALIDATORS, catchAllProxy); + _vm.label(NUMBER_VALIDATORS, "NUMBER_VALIDATORS"); - vm.etch(BLOCK_NUMBER_FROM_HEADER, catchAllProxy); - vm.label(BLOCK_NUMBER_FROM_HEADER, "BLOCK_NUMBER_FROM_HEADER"); + _vm.etch(BLOCK_NUMBER_FROM_HEADER, catchAllProxy); + _vm.label(BLOCK_NUMBER_FROM_HEADER, "BLOCK_NUMBER_FROM_HEADER"); - vm.etch(HASH_HEADER, catchAllProxy); - vm.label(HASH_HEADER, "HASH_HEADER"); + _vm.etch(HASH_HEADER, catchAllProxy); + _vm.label(HASH_HEADER, "HASH_HEADER"); - vm.etch(GET_PARENT_SEAL_BITMAP, catchAllProxy); - vm.label(GET_PARENT_SEAL_BITMAP, "GET_PARENT_SEAL_BITMAP"); + _vm.etch(GET_PARENT_SEAL_BITMAP, catchAllProxy); + _vm.label(GET_PARENT_SEAL_BITMAP, "GET_PARENT_SEAL_BITMAP"); - vm.etch(GET_VERIFIED_SEAL_BITMAP, catchAllProxy); - vm.label(GET_VERIFIED_SEAL_BITMAP, "GET_VERIFIED_SEAL_BITMAP"); + _vm.etch(GET_VERIFIED_SEAL_BITMAP, catchAllProxy); + _vm.label(GET_VERIFIED_SEAL_BITMAP, "GET_VERIFIED_SEAL_BITMAP"); } function transfer(address from, address to, uint256 amount) public returns (bool) { - vm.deal(from, from.balance - amount); - vm.deal(to, to.balance + amount); + _vm.deal(from, from.balance - amount); + _vm.deal(to, to.balance + amount); return true; } diff --git a/src/Precompiles.sol b/src/Precompiles.sol index d28d1aa..6f324a6 100644 --- a/src/Precompiles.sol +++ b/src/Precompiles.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.5.13; +pragma solidity >=0.5.13 <0.8.20; contract Precompiles { address public constant TRANSFER = address(0xff - 2); diff --git a/src/Test.sol b/src/Test.sol index 83a87ce..1582158 100644 --- a/src/Test.sol +++ b/src/Test.sol @@ -1,56 +1,101 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.5.13; +pragma solidity >=0.5.13 <0.8.20; -import {Test as ForgeTest} from "forge-std/Test.sol"; +import { Test as ForgeTest } from "forge-std-8/Test.sol"; import "./Precompiles.sol"; import "./PrecompileHandler.sol"; contract Test is ForgeTest, Precompiles { - PrecompileHandler public ph; - address currentPrank; + PrecompileHandler public ph; + address currentPrank; - event log_named_array(string key, address[] val); - event log_named_array(string key, bytes32[] val); - event log_array(string key, address[] val); - event log_array(string key, bytes32[] val); + event log_named_array(string key, bytes32[] val); + event log_array(string key, address[] val); + event log_array(string key, bytes32[] val); - constructor() ForgeTest() public { - ph = new PrecompileHandler(); - } + constructor() public ForgeTest() { + ph = new PrecompileHandler(); + } - /* Utility functions */ + /* Utility functions */ - function changePrank(address who) internal { - // Record current prank so helper functions can revert - // if they need to prank - currentPrank = who; - super.changePrank(who); - } + function changePrank(address who) internal virtual override { + // Record current prank so helper functions can revert + // if they need to prank + currentPrank = who; + super.changePrank(who); + } - function actor(string memory name) public returns (address) { - uint256 pk = uint256(keccak256(bytes(name))); - address addr = vm.addr(pk); - vm.label(addr, name); - return addr; - } + function actor(string memory name) public returns (address) { + uint256 pk = uint256(keccak256(bytes(name))); + address addr = vm.addr(pk); + vm.label(addr, name); + return addr; + } - /* Extra assertions, extends forge-std/Test.sol */ + function actorWithPK(string memory name) public returns (address, uint256) { + uint256 pk = uint256(keccak256(bytes(name))); + address addr = vm.addr(pk); + vm.label(addr, name); + return (addr, pk); + } - function assertEq(address[] memory a, address[] memory b) internal { - if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { - emit log("Error: a == b not satisfied [address[]]"); - emit log_named_array(" Expected", b); - emit log_named_array(" Actual", a); - fail(); - } + /* Extra assertions, extends forge-std/Test.sol */ + + function assertEq(address[] memory a, address[] memory b) internal virtual override { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [address[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); } + } - function assertEq(bytes32[] memory a, bytes32[] memory b) internal { - if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { - emit log("Error: a == b not satisfied [bytes32[]]"); - emit log_named_array(" Expected", b); - emit log_named_array(" Actual", a); - fail(); - } + function assertEq( + address[] memory a, + address[] memory b, + string memory err + ) internal virtual override { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); } -} \ No newline at end of file + } + + function assertEq(bytes32[] memory a, bytes32[] memory b) internal { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log("Error: a == b not satisfied [bytes32[]]"); + emit log_named_array(" Expected", b); + emit log_named_array(" Actual", a); + fail(); + } + } + + function deployCodeTo(string memory what, address where) internal virtual override { + deployCodeTo(what, "", 0, where); + } + + function deployCodeTo( + string memory what, + bytes memory args, + address where + ) internal virtual override { + deployCodeTo(what, args, 0, where); + } + + function deployCodeTo( + string memory what, + bytes memory args, + uint256 value, + address where + ) internal virtual override { + bytes memory creationCode = vm.getCode(what); + vm.etch(where, abi.encodePacked(creationCode, args)); + (bool success, bytes memory runtimeBytecode) = where.call{ value: value }(""); + require( + success, + "StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode." + ); + vm.etch(where, runtimeBytecode); + } +} diff --git a/src/test/Precompiles.t.sol b/src/test/Precompiles.t.sol index af71e73..445050a 100644 --- a/src/test/Precompiles.t.sol +++ b/src/test/Precompiles.t.sol @@ -1,104 +1,153 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.5.13; +pragma solidity >=0.5.13 <0.8.20; import "../Test.sol"; import "./UsingPrecompiles.sol"; contract PrecompilesTest is Test, UsingPrecompiles { - function testTransfer() public { - address caller = vm.addr(0x23); - address dest = vm.addr(0x24); - vm.deal(caller, 10000); - bool success; - - (success, ) = TRANSFER.call.value(0).gas(gasleft())(abi.encode(caller, dest, 500)); - require(success, "CELO transfer failed"); - - assert(dest.balance == 500); - assert(caller.balance == 9500); - } - - function testEpochSize() public { - assert(getEpochSize() == 17280); - ph.setEpochSize(1000); - assert(getEpochSize() == 1000); - } - - function testFractionMul() public { - ph.mockReturn( - FRACTION_MUL, - keccak256(abi.encodePacked(uint256(10), uint256(5), uint256(12), uint256(4), uint256(4), uint256(10))), - abi.encode(uint256(100), uint256(12)) - ); - - (uint256 rNum, uint256 rDen) = fractionMulExp(10, 5, 12, 4, 4, 10); - assert(rNum == 100); - assert(rDen == 12); - } - - function testProofOfPossession() public { - address sender = address(0x999); - bytes memory blsKey = abi.encodePacked("ExampleKey"); - bytes memory blsPop = abi.encodePacked("ExampleKeyPop"); - - ph.mockSuccess(PROOF_OF_POSSESSION, keccak256(abi.encodePacked(sender, blsKey, blsPop))); - assert(checkProofOfPossession(sender, blsKey, blsPop)); - - ph.mockRevert(PROOF_OF_POSSESSION, keccak256(abi.encodePacked(sender, blsKey, blsPop))); - assert(checkProofOfPossession(sender, blsKey, blsPop)); - } - - function testGetValidator() public { - uint256 index = 10; - address validator = address(0x989899); - ph.mockReturn(GET_VALIDATOR, keccak256(abi.encodePacked(index, block.number)), abi.encode(validator)); - assert(validatorSignerAddressFromCurrentSet(index) == validator); - } - - function testGetValidatorAtBlock() public { - uint256 index = 10; - uint256 blockNumber = 1000; - address validator = address(0x989899); - ph.mockReturn(GET_VALIDATOR, keccak256(abi.encodePacked(index, blockNumber)), abi.encode(validator)); - assert(validatorSignerAddressFromSet(index, blockNumber) == validator); - } - - function testNumberValidatorsInSet() public { - ph.mockReturn(NUMBER_VALIDATORS, keccak256(abi.encodePacked(block.number)), abi.encode(uint256(12))); - assert(numberValidatorsInCurrentSet() == 12); - } - - function testNumberValidatorsInSetAtBlock() public { - uint256 blockNumber = 1000; - ph.mockReturn(NUMBER_VALIDATORS, keccak256(abi.encodePacked(blockNumber)), abi.encode(uint256(12))); - assert(numberValidatorsInSet(blockNumber) == 12); - } - - function testBlockNumberFromHeader() public { - uint256 blockNumber = 1000; - bytes memory header = abi.encodePacked("MockHeader"); - ph.mockReturn(BLOCK_NUMBER_FROM_HEADER, keccak256(abi.encodePacked(header)), abi.encode(blockNumber)); - assert(getBlockNumberFromHeader(header) == blockNumber); - } - - function testHashHeader() public { - bytes memory header = abi.encodePacked("MockHeader"); - bytes32 headerHash = keccak256(header); - ph.mockReturn(HASH_HEADER, keccak256(abi.encodePacked(header)), abi.encode(headerHash)); - assert(hashHeader(header) == headerHash); - } - - function testGetParentSealBitmap() public { - uint256 blockNumber = 1000; - bytes32 bitmap = bytes32(uint256(0x12345679)); - ph.mockReturn(GET_PARENT_SEAL_BITMAP, keccak256(abi.encodePacked(blockNumber)), abi.encode(bitmap)); - assert(getParentSealBitmap(blockNumber) == bitmap); - } - - function testGetVerifiedSealBitmapFromHeader() public { - bytes memory header = abi.encodePacked("MockHeader"); - bytes32 bitmap = bytes32(uint256(0x12345679)); - ph.mockReturn(GET_VERIFIED_SEAL_BITMAP, keccak256(abi.encodePacked(header)), abi.encode(bitmap)); - assert(getVerifiedSealBitmapFromHeader(header) == bitmap); - } -} \ No newline at end of file + function testTransfer() public { + address caller = vm.addr(0x23); + address dest = vm.addr(0x24); + vm.deal(caller, 10000); + bool success; + + (success, ) = TRANSFER.call{value: 0, gas: gasleft()}( + abi.encode(caller, dest, 500) + ); + require(success, "CELO transfer failed"); + + assert(dest.balance == 500); + assert(caller.balance == 9500); + } + + function testEpochSize() public { + assert(getEpochSize() == 17280); + ph.setEpochSize(1000); + assert(getEpochSize() == 1000); + } + + function testFractionMul() public { + ph.mockReturn( + FRACTION_MUL, + keccak256( + abi.encodePacked( + uint256(10), + uint256(5), + uint256(12), + uint256(4), + uint256(4), + uint256(10) + ) + ), + abi.encode(uint256(100), uint256(12)) + ); + + (uint256 rNum, uint256 rDen) = fractionMulExp(10, 5, 12, 4, 4, 10); + assert(rNum == 100); + assert(rDen == 12); + } + + function testProofOfPossession() public { + address sender = address(0x999); + bytes memory blsKey = abi.encodePacked("ExampleKey"); + bytes memory blsPop = abi.encodePacked("ExampleKeyPop"); + + ph.mockSuccess( + PROOF_OF_POSSESSION, + keccak256(abi.encodePacked(sender, blsKey, blsPop)) + ); + assert(checkProofOfPossession(sender, blsKey, blsPop)); + + ph.mockRevert( + PROOF_OF_POSSESSION, + keccak256(abi.encodePacked(sender, blsKey, blsPop)) + ); + assert(checkProofOfPossession(sender, blsKey, blsPop)); + } + + function testGetValidator() public { + uint256 index = 10; + address validator = address(0x989899); + ph.mockReturn( + GET_VALIDATOR, + keccak256(abi.encodePacked(index, block.number)), + abi.encode(validator) + ); + assert(validatorSignerAddressFromCurrentSet(index) == validator); + } + + function testGetValidatorAtBlock() public { + uint256 index = 10; + uint256 blockNumber = 1000; + address validator = address(0x989899); + ph.mockReturn( + GET_VALIDATOR, + keccak256(abi.encodePacked(index, blockNumber)), + abi.encode(validator) + ); + assert(validatorSignerAddressFromSet(index, blockNumber) == validator); + } + + function testNumberValidatorsInSet() public { + ph.mockReturn( + NUMBER_VALIDATORS, + keccak256(abi.encodePacked(block.number)), + abi.encode(uint256(12)) + ); + assert(numberValidatorsInCurrentSet() == 12); + } + + function testNumberValidatorsInSetAtBlock() public { + uint256 blockNumber = 1000; + ph.mockReturn( + NUMBER_VALIDATORS, + keccak256(abi.encodePacked(blockNumber)), + abi.encode(uint256(12)) + ); + assert(numberValidatorsInSet(blockNumber) == 12); + } + + function testBlockNumberFromHeader() public { + uint256 blockNumber = 1000; + bytes memory header = abi.encodePacked("MockHeader"); + ph.mockReturn( + BLOCK_NUMBER_FROM_HEADER, + keccak256(abi.encodePacked(header)), + abi.encode(blockNumber) + ); + assert(getBlockNumberFromHeader(header) == blockNumber); + } + + function testHashHeader() public { + bytes memory header = abi.encodePacked("MockHeader"); + bytes32 headerHash = keccak256(header); + ph.mockReturn( + HASH_HEADER, + keccak256(abi.encodePacked(header)), + abi.encode(headerHash) + ); + assert(hashHeader(header) == headerHash); + } + + function testGetParentSealBitmap() public { + uint256 blockNumber = 1000; + bytes32 bitmap = bytes32(uint256(0x12345679)); + ph.mockReturn( + GET_PARENT_SEAL_BITMAP, + keccak256(abi.encodePacked(blockNumber)), + abi.encode(bitmap) + ); + assert(getParentSealBitmap(blockNumber) == bitmap); + } + + function testGetVerifiedSealBitmapFromHeader() public { + bytes memory header = abi.encodePacked("MockHeader"); + bytes32 bitmap = bytes32(uint256(0x12345679)); + ph.mockReturn( + GET_VERIFIED_SEAL_BITMAP, + keccak256(abi.encodePacked(header)), + abi.encode(bitmap) + ); + assert(getVerifiedSealBitmapFromHeader(header) == bitmap); + } +} diff --git a/src/test/SafeMath.sol b/src/test/SafeMath.sol deleted file mode 100644 index e7091fb..0000000 --- a/src/test/SafeMath.sol +++ /dev/null @@ -1,156 +0,0 @@ -pragma solidity ^0.5.0; - -/** - * @dev Wrappers over Solidity's arithmetic operations with added overflow - * checks. - * - * Arithmetic operations in Solidity wrap on overflow. This can easily result - * in bugs, because programmers usually assume that an overflow raises an - * error, which is the standard behavior in high level programming languages. - * `SafeMath` restores this intuition by reverting the transaction when an - * operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - */ -library SafeMath { - /** - * @dev Returns the addition of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `+` operator. - * - * Requirements: - * - Addition cannot overflow. - */ - function add(uint256 a, uint256 b) internal pure returns (uint256) { - uint256 c = a + b; - require(c >= a, "SafeMath: addition overflow"); - - return c; - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - Subtraction cannot overflow. - */ - function sub(uint256 a, uint256 b) internal pure returns (uint256) { - return sub(a, b, "SafeMath: subtraction overflow"); - } - - /** - * @dev Returns the subtraction of two unsigned integers, reverting with custom message on - * overflow (when the result is negative). - * - * Counterpart to Solidity's `-` operator. - * - * Requirements: - * - Subtraction cannot overflow. - * - * _Available since v2.4.0._ - */ - function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - require(b <= a, errorMessage); - uint256 c = a - b; - - return c; - } - - /** - * @dev Returns the multiplication of two unsigned integers, reverting on - * overflow. - * - * Counterpart to Solidity's `*` operator. - * - * Requirements: - * - Multiplication cannot overflow. - */ - function mul(uint256 a, uint256 b) internal pure returns (uint256) { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) { - return 0; - } - - uint256 c = a * b; - require(c / a == b, "SafeMath: multiplication overflow"); - - return c; - } - - /** - * @dev Returns the integer division of two unsigned integers. Reverts on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - */ - function div(uint256 a, uint256 b) internal pure returns (uint256) { - return div(a, b, "SafeMath: division by zero"); - } - - /** - * @dev Returns the integer division of two unsigned integers. Reverts with custom message on - * division by zero. The result is rounded towards zero. - * - * Counterpart to Solidity's `/` operator. Note: this function uses a - * `revert` opcode (which leaves remaining gas untouched) while Solidity - * uses an invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - * - * _Available since v2.4.0._ - */ - function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - // Solidity only automatically asserts when dividing by 0 - require(b > 0, errorMessage); - uint256 c = a / b; - // assert(a == b * c + a % b); // There is no case in which this doesn't hold - - return c; - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * Reverts when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - */ - function mod(uint256 a, uint256 b) internal pure returns (uint256) { - return mod(a, b, "SafeMath: modulo by zero"); - } - - /** - * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), - * Reverts with custom message when dividing by zero. - * - * Counterpart to Solidity's `%` operator. This function uses a `revert` - * opcode (which leaves remaining gas untouched) while Solidity uses an - * invalid opcode to revert (consuming all remaining gas). - * - * Requirements: - * - The divisor cannot be zero. - * - * _Available since v2.4.0._ - */ - function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { - require(b != 0, errorMessage); - return a % b; - } -} diff --git a/src/test/UsingPrecompiles.sol b/src/test/UsingPrecompiles.sol index 44be8fe..d6fcd4b 100644 --- a/src/test/UsingPrecompiles.sol +++ b/src/test/UsingPrecompiles.sol @@ -1,269 +1,321 @@ -pragma solidity ^0.5.13; +pragma solidity >=0.5.13 <0.8.20; + -import "./SafeMath.sol"; +contract UsingPrecompiles { -contract UsingPrecompiles { - using SafeMath for uint256; + address constant _TRANSFER = address(0xff - 2); + address constant _FRACTION_MUL = address(0xff - 3); + address constant _PROOF_OF_POSSESSION = address(0xff - 4); + address constant _GET_VALIDATOR = address(0xff - 5); + address constant _NUMBER_VALIDATORS = address(0xff - 6); + address constant _EPOCH_SIZE = address(0xff - 7); + address constant _BLOCK_NUMBER_FROM_HEADER = address(0xff - 8); + 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); - address constant TRANSFER = address(0xff - 2); - address constant FRACTION_MUL = address(0xff - 3); - address constant PROOF_OF_POSSESSION = address(0xff - 4); - address constant GET_VALIDATOR = address(0xff - 5); - address constant NUMBER_VALIDATORS = address(0xff - 6); - address constant EPOCH_SIZE = address(0xff - 7); - address constant BLOCK_NUMBER_FROM_HEADER = address(0xff - 8); - 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); - - /** - * @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/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 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/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. - */ - function getEpochSize() public view returns (uint256) { - bytes memory out; - bool success; - (success, out) = EPOCH_SIZE.staticcall(abi.encodePacked()); - require(success, "error calling getEpochSize precompile"); - return getUint256FromBytes(out, 0); - } + /** + * @notice Returns the current epoch size in blocks. + * @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()); + require(success, "error calling getEpochSize precompile"); + return getUint256FromBytes(out, 0); + } - /** - * @notice Returns the epoch number at a block. - * @param blockNumber Block number where epoch number is calculated. - * @return Epoch number. - */ - function getEpochNumberOfBlock(uint256 blockNumber) public view returns (uint256) { - return epochNumberOfBlock(blockNumber, getEpochSize()); - } + /** + * @notice Returns the epoch number at a block. + * @param blockNumber Block number where epoch number is calculated. + * @return Epoch number. + */ + function getEpochNumberOfBlock( + uint256 blockNumber + ) public view returns (uint256) { + return epochNumberOfBlock(blockNumber, getEpochSize()); + } - /** - * @notice Returns the epoch number at a block. - * @return Current epoch number. - */ - function getEpochNumber() public view returns (uint256) { - return getEpochNumberOfBlock(block.number); - } + /** + * @notice Returns the epoch number at a block. + * @return Current epoch number. + */ + function getEpochNumber() public view returns (uint256) { + return getEpochNumberOfBlock(block.number); + } - /** - * @notice Returns the epoch number at a block. - * @param blockNumber Block number where epoch number is calculated. - * @param epochSize The epoch size in blocks. - * @return Epoch number. - */ - function epochNumberOfBlock(uint256 blockNumber, uint256 epochSize) - internal - pure - returns (uint256) - { - // Follows GetEpochNumber from celo-blockchain/blob/master/consensus/istanbul/utils.go - uint256 epochNumber = blockNumber / epochSize; - if (blockNumber % epochSize == 0) { - return epochNumber; - } else { - return epochNumber.add(1); + /** + * @notice Returns the epoch number at a block. + * @param blockNumber Block number where epoch number is calculated. + * @param epochSize The epoch size in blocks. + * @return Epoch number. + */ + function epochNumberOfBlock( + uint256 blockNumber, + uint256 epochSize + ) internal pure returns (uint256) { + // Follows GetEpochNumber from celo-blockchain/blob/master/consensus/istanbul/utils.go + uint256 epochNumber = blockNumber / epochSize; + if (blockNumber % epochSize == 0) { + return epochNumber; + } else { + return epochNumber++; + } } - } - /** - * @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(getUint256FromBytes(out, 0)); - } + /** + * @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(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. - */ - function numberValidatorsInCurrentSet() public view returns (uint256) { - bytes memory out; - bool success; - (success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(uint256(block.number))); - require(success, "error calling numberValidatorsInCurrentSet precompile"); - return getUint256FromBytes(out, 0); - } + /** + * @notice Gets the size of the current elected validator set. + * @return Size of the current elected validator set. + */ + function numberValidatorsInCurrentSet() public view returns (uint256) { + bytes memory out; + bool success; + (success, out) = _NUMBER_VALIDATORS.staticcall( + abi.encodePacked(uint256(block.number)) + ); + require( + success, + "error calling numberValidatorsInCurrentSet precompile" + ); + return getUint256FromBytes(out, 0); + } - /** - * @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 numberValidatorsInSet(uint256 blockNumber) public view returns (uint256) { - bytes memory out; - bool success; - (success, out) = NUMBER_VALIDATORS.staticcall(abi.encodePacked(blockNumber)); - require(success, "error calling numberValidatorsInSet precompile"); - return getUint256FromBytes(out, 0); - } + /** + * @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 numberValidatorsInSet( + uint256 blockNumber + ) public view returns (uint256) { + bytes memory out; + bool success; + (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 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 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 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 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 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 Converts bytes to uint256. - * @param bs byte[] data - * @param start offset into byte data to convert - * @return uint256 data - */ - function getUint256FromBytes(bytes memory bs, uint256 start) internal pure returns (uint256) { - return uint256(getBytes32FromBytes(bs, start)); - } + /** + * @notice Converts bytes to uint256. + * @param bs byte[] data + * @param start offset into byte data to convert + * @return uint256 data + */ + function getUint256FromBytes( + bytes memory bs, + uint256 start + ) internal pure returns (uint256) { + return uint256(getBytes32FromBytes(bs, start)); + } - /** - * @notice Converts bytes to bytes32. - * @param bs byte[] data - * @param start offset into byte data to convert - * @return bytes32 data - */ - function getBytes32FromBytes(bytes memory bs, uint256 start) internal pure returns (bytes32) { - require(bs.length >= start.add(32), "slicing out of range"); - bytes32 x; - assembly { - x := mload(add(bs, add(start, 32))) + /** + * @notice Converts bytes to bytes32. + * @param bs byte[] data + * @param start offset into byte data to convert + * @return bytes32 data + */ + function getBytes32FromBytes( + bytes memory bs, + uint256 start + ) internal pure returns (bytes32) { + require(bs.length >= start + 32, "slicing out of range"); + bytes32 x; + assembly { + x := mload(add(bs, add(start, 32))) + } + return x; } - return x; - } - /** - * @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 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) * 2 + 2) / 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); - } + /** + * @notice Computes byzantine quorum from current validator set size + * @return Byzantine quorum of validators. + */ + function minQuorumSizeInCurrentSet() public view returns (uint256) { + return minQuorumSize(block.number); + } }