-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from lightclient/add-2935
Add EIP-2935 system contract impl
- Loading branch information
Showing
6 changed files
with
272 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule forge-std
updated
22 files
+193 −0 | CONTRIBUTING.md | |
+17 −1 | README.md | |
+1 −1 | package.json | |
+12 −1 | scripts/vm.py | |
+4 −0 | src/StdChains.sol | |
+1 −1 | src/StdCheats.sol | |
+104 −0 | src/StdJson.sol | |
+104 −0 | src/StdToml.sol | |
+216 −19 | src/Vm.sol | |
+471 −463 | src/console.sol | |
+2 −2 | src/interfaces/IERC4626.sol | |
+1 −1 | test/StdAssertions.t.sol | |
+14 −12 | test/StdChains.t.sol | |
+10 −10 | test/StdCheats.t.sol | |
+12 −12 | test/StdError.t.sol | |
+1 −1 | test/StdJson.t.sol | |
+4 −14 | test/StdMath.t.sol | |
+5 −5 | test/StdStorage.t.sol | |
+1 −1 | test/StdStyle.t.sol | |
+1 −1 | test/StdToml.t.sol | |
+12 −12 | test/StdUtils.t.sol | |
+9 −6 | test/Vm.t.sol |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
;; Copy and return code. | ||
push @.end - @.start | ||
dup1 | ||
push @.start | ||
push0 | ||
codecopy | ||
push0 | ||
return | ||
|
||
.start: | ||
#assemble "main.eas" | ||
.end: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
;; ┏┓┏┓┏┓┏━ | ||
;; ┏┛┗┫ ┫┗┓┏┓┏┏┳┓ | ||
;; ┗━┗┛┗┛┗┛┗┻┛┛┗┗ | ||
;; | ||
;; This is an implementation of EIP-2935's predeploy contract. | ||
;; | ||
;; The contract implements a ring buffer to create bounded execution block | ||
;; hash lookup. | ||
|
||
;; ---------------------------------------------------------------------------- | ||
;; MACROS --------------------------------------------------------------------- | ||
;; ---------------------------------------------------------------------------- | ||
|
||
;; BUFLEN returns the HISTORY_BUFFER_LENGTH as defined in the EIP. | ||
#define BUFLEN 8191 | ||
|
||
;; SYSADDR is the address which calls the contract to submit a new root. | ||
#define SYSADDR 0xfffffffffffffffffffffffffffffffffffffffe | ||
|
||
;; do_revert sets up and then executes a revert(0,0) operation. | ||
#define %do_revert() { | ||
push0 ;; [0] | ||
push0 ;; [0, 0] | ||
revert ;; [] | ||
} | ||
|
||
;; ---------------------------------------------------------------------------- | ||
;; MACROS END ----------------------------------------------------------------- | ||
;; ---------------------------------------------------------------------------- | ||
|
||
.start: | ||
;; Protect the submit routine by verifying the caller is equal to | ||
;; sysaddr(). | ||
caller ;; [caller] | ||
push20 SYSADDR ;; [sysaddr, caller] | ||
eq ;; [sysaddr == caller] | ||
jumpi @submit ;; [] | ||
|
||
;; Fallthrough if addresses don't match -- this means the caller intends | ||
;; to read a root. | ||
|
||
;; Verify input is 32 bytes long. | ||
push1 32 ;; [32] | ||
calldatasize ;; [calldatasize, 32] | ||
eq ;; [calldatasize == 32] | ||
|
||
;; Jump to continue if length-check passed, otherwise revert. | ||
jumpi @load ;; [] | ||
%do_revert() ;; [] | ||
|
||
load: | ||
;; Check if input is requesting a block hash greater than current block | ||
;; number. | ||
push 0 ;; [0] | ||
calldataload ;; [input] | ||
push 1 ;; [1, input] | ||
number ;; [number, 1, input] | ||
sub ;; [number-1, input] | ||
dup2 ;; [input, number-1, input] | ||
gt ;; [input > number-1, input] | ||
jumpi @throw ;; [input] | ||
|
||
;; Check if the input is requesting a block hash before the earliest available | ||
;; hash currently. Since we've verfied that input <= number - 1, it's safe to | ||
;; check the following: | ||
;; number - 1 - input <= BUFLEN, which also equals: number - input < BUFLEN | ||
dup1 ;; [input, input] | ||
number ;; [number, input, input] | ||
sub ;; [number - input, input] | ||
push BUFLEN+1 ;; [buflen, number - input, input] | ||
lt ;; [buflen < number - input, input] | ||
jumpi @throw ;; [input] | ||
|
||
;; Load the hash. | ||
push BUFLEN ;; [buflen, input] | ||
swap1 ;; [input, buflen] | ||
mod ;; [input % buflen] | ||
sload ;; [hash] | ||
|
||
;; Load into memory and return. | ||
push 0 ;; [0, hash] | ||
mstore ;; [] | ||
push 32 ;; [32] | ||
push 0 ;; [0, 32] | ||
return ;; [] | ||
|
||
throw: | ||
;; Reverts current execution with no return data. | ||
pop | ||
%do_revert() | ||
|
||
submit: | ||
push 0 ;; [0] | ||
calldataload ;; [in] | ||
push BUFLEN ;; [buflen, in] | ||
push 1 ;; [1, buflen, in] | ||
number ;; [number, 1, buflen, in] | ||
sub ;; [number-1, buflen, in] | ||
mod ;; [number-1 % buflen, in] | ||
sstore | ||
|
||
stop |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "forge-std/Test.sol"; | ||
import "../src/Contract.sol"; | ||
|
||
address constant addr = 0x000000000000000000000000000000000000aaaa; | ||
address constant sysaddr = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE; | ||
uint256 constant buflen = 8191; | ||
bytes32 constant hash = hex"88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6"; | ||
|
||
function lastBlockNumber() view returns (bytes32) { | ||
return bytes32(uint256(block.number)-1); | ||
} | ||
|
||
function hash_idx() view returns (bytes32) { | ||
return bytes32(uint256(lastBlockNumber()) % buflen); | ||
} | ||
|
||
contract ContractTest is Test { | ||
address unit; | ||
|
||
function setUp() public { | ||
vm.etch(addr, hex"@bytecode@"); | ||
unit = addr; | ||
} | ||
|
||
// testRead verifies the contract returns the expected execution hash. | ||
function testExecRead() public { | ||
// Store hash at expected indexes. | ||
vm.store(unit, hash_idx(), hash); | ||
|
||
// Read hash associated with current timestamp. | ||
(bool ret, bytes memory data) = unit.call(bytes.concat(lastBlockNumber())); | ||
assertTrue(ret); | ||
assertEq(data, bytes.concat(hash)); | ||
} | ||
|
||
function testReadBadCalldataSize() public { | ||
// Store hash at expected indexes. | ||
vm.store(unit, hash_idx(), hash); | ||
|
||
// Call with 0 byte arguement. | ||
(bool ret, bytes memory data) = unit.call(hex""); | ||
assertFalse(ret); | ||
assertEq(data, hex""); | ||
|
||
// Call with 31 byte arguement. | ||
(ret, data) = unit.call(hex"00000000000000000000000000000000000000000000000000000000001337"); | ||
assertFalse(ret); | ||
assertEq(data, hex""); | ||
|
||
// Call with 33 byte arguement. | ||
(ret, data) = unit.call(hex"000000000000000000000000000000000000000000000000000000000000001337"); | ||
assertFalse(ret); | ||
assertEq(data, hex""); | ||
} | ||
|
||
function testReadBadBlockNumbers() public { | ||
// Set reasonable block number. | ||
vm.roll(21053500); | ||
uint256 number = block.number-1; | ||
|
||
// Store hash at expected indexes. | ||
vm.store(unit, hash_idx(), hash); | ||
|
||
// Wrap around buflen once forward. | ||
(bool ret, bytes memory data) = unit.call(bytes.concat(bytes32(number+buflen))); | ||
assertFalse(ret); | ||
assertEq(data, hex""); | ||
|
||
// Wrap around buflen once backward. | ||
(ret, data) = unit.call(bytes.concat(bytes32(number-buflen-1))); | ||
assertFalse(ret); | ||
assertEq(data, hex""); | ||
|
||
// Block number zero should fail. | ||
(ret, data) = unit.call(bytes.concat(bytes32(0))); | ||
assertFalse(ret); | ||
assertEq(data, hex""); | ||
} | ||
|
||
// testUpdate verifies the set functionality of the contract. | ||
function testUpdate() public { | ||
// Simulate pre-block call to set hash. | ||
vm.prank(sysaddr); | ||
(bool ret, bytes memory data) = unit.call(bytes.concat(hash)); | ||
assertTrue(ret); | ||
assertEq(data, hex""); | ||
|
||
// Verify hash. | ||
bytes32 got = vm.load(unit, hash_idx()); | ||
assertEq(got, hash); | ||
} | ||
|
||
// testRingBuffers verifies the integrity of the ring buffer is maintained | ||
// as the write indexes loop back to the start and begin overwriting | ||
// values. | ||
function testRingBuffers() public { | ||
// Set reasonable block number. | ||
vm.roll(21053500); | ||
|
||
for (uint256 i = 0; i < 10000; i += 1) { | ||
bytes32 pbbr = bytes32(i*1337); | ||
|
||
// Simulate pre-block call to set hash. | ||
vm.prank(sysaddr); | ||
(bool ret, bytes memory data) = unit.call(bytes.concat(pbbr)); | ||
assertTrue(ret); | ||
assertEq(data, hex""); | ||
|
||
// Call contract as normal account to get exeuction hash associated | ||
// with current timestamp. | ||
(ret, data) = unit.call(bytes.concat(lastBlockNumber())); | ||
assertTrue(ret); | ||
assertEq(data, bytes.concat(pbbr)); | ||
|
||
// Skip forward 1 block. | ||
vm.roll(block.number+1); | ||
} | ||
} | ||
|
||
|
||
// testHistoricalReads verifies that it is possible to read all previously | ||
// saved values in the beacon hash contract. | ||
function testHistoricalReads() public { | ||
uint256 start = 1; | ||
vm.roll(start); | ||
|
||
// Saturate storage with fake hashs. | ||
for (uint256 i = 0; i < buflen; i += 1) { | ||
bytes32 pbbr = bytes32(i*1337); | ||
vm.prank(sysaddr); | ||
(bool ret, bytes memory data) = unit.call(bytes.concat(pbbr)); | ||
assertTrue(ret); | ||
assertEq(data, hex""); | ||
vm.roll(block.number+1); | ||
} | ||
|
||
// Attempt to read all values in same block context. | ||
for (uint256 i = 0; i < buflen; i += 1) { | ||
bytes32 num = bytes32(uint256(start+i-1)); | ||
(bool ret, bytes memory got) = unit.call(bytes.concat(num)); | ||
assertTrue(ret); | ||
assertEq(got, bytes.concat(bytes32(i*1337))); | ||
} | ||
} | ||
} |