diff --git a/README.md b/README.md index 25bd8bb..ca8eca6 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ To assemble `src/main.etk` you will need to invoke `eas`: ```console $ eas src/main.etk -3373fffffffffffffffffffffffffffffffffffffffe14604457602036146024575f5ffd5b620180005f350680545f35146037575f5ffd5b6201800001545f5260205ff35b6201800042064281555f359062018000015500 +3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762016da0810690815414603c575f5ffd5b62016da001545f5260205ff35b5f5ffd5b62016da042064281555f359062016da0015500 ``` It's also possible to remove the `etk` preproccessing by doing a roundtrip -- @@ -31,7 +31,7 @@ $ disease --code 0x$(eas src/main.etk) 0: caller 1: push20 0xfffffffffffffffffffffffffffffffffffffffe 16: eq - 17: push1 0x44 + 17: push1 0x4d 19: jumpi 1a: push1 0x20 @@ -45,46 +45,57 @@ $ disease --code 0x$(eas src/main.etk) 23: revert 24: jumpdest - 25: push3 0x018000 - 29: push0 - 2a: calldataload - 2b: mod - 2c: dup1 - 2d: sload - 2e: push0 - 2f: calldataload - 30: eq - 31: push1 0x37 - 33: jumpi - - 34: push0 - 35: push0 - 36: revert - - 37: jumpdest - 38: push3 0x018000 - 3c: add - 3d: sload - 3e: push0 - 3f: mstore - 40: push1 0x20 - 42: push0 - 43: return - - 44: jumpdest - 45: push3 0x018000 - 49: timestamp - 4a: mod - 4b: timestamp - 4c: dup2 - 4d: sstore - 4e: push0 - 4f: calldataload - 50: swap1 - 51: push3 0x018000 - 55: add + 25: push0 + 26: calldataload + 27: dup1 + 28: iszero + 29: push1 0x49 + 2b: jumpi + + 2c: push3 0x016da0 + 30: dup2 + 31: mod + 32: swap1 + 33: dup2 + 34: sload + 35: eq + 36: push1 0x3c + 38: jumpi + + 39: push0 + 3a: push0 + 3b: revert + + 3c: jumpdest + 3d: push3 0x016da0 + 41: add + 42: sload + 43: push0 + 44: mstore + 45: push1 0x20 + 47: push0 + 48: return + + 49: jumpdest + 4a: push0 + 4b: push0 + 4c: revert + + 4d: jumpdest + 4e: push3 0x016da0 + 52: timestamp + 53: mod + 54: timestamp + 55: dup2 56: sstore - 57: stop + 57: push0 + 58: calldataload + 59: swap1 + 5a: push3 0x016da0 + 5e: add + 5f: sstore + 60: stop + ``` ### Control-flow Graph diff --git a/docs/cfg.png b/docs/cfg.png index cbaff78..67c2fdc 100644 Binary files a/docs/cfg.png and b/docs/cfg.png differ diff --git a/src/main.etk b/src/main.etk index 61c158e..5acde1e 100644 --- a/src/main.etk +++ b/src/main.etk @@ -29,7 +29,7 @@ # buflen returns the HISTORY_BUFFER_LENGTH as defined in the EIP. %def buflen() - 98304 + 93600 %end # sysaddr is the address which calls the contract to submit a new root. @@ -37,14 +37,8 @@ 0xfffffffffffffffffffffffffffffffffffffffe %end -# get_input loads the first word of input from calldata. -%macro get_input() - push0 # [0] - calldataload # [calldata[0..32]] -%end - -# revert sets up and then executes a revert instruction. -%macro revert() +# do_revert sets up and then executes a revert(0,0) operation. +%macro do_revert() push0 # [0] push0 # [0, 0] revert # [] @@ -74,31 +68,40 @@ start: # Jump to continue if length-check passed, otherwise revert. push1 loadtime # [loadtime_lbl, calldatasize == 32] jumpi # [] - %revert() # [] - + %do_revert() # [] loadtime: + jumpdest + + # Load input timestamp. + push0 # [0] + calldataload # [input_timestamp] + dup1 # [input_timestamp, input_timestamp] + + # Verify input timestamp is non-zero. + iszero # [input_timestamp == 0, input_timestamp] + push1 throw # [throw_lbl, input_timestamp == 0, input_timestamp] + jumpi # [input_timestamp] + # Compute the timestamp index and load from storage. - jumpdest # [] - push3 buflen() # [buflen] - %get_input() # [time, buflen] - mod # [time_index] - dup1 # [time_index, time_index] - sload # [timestamp, time_index] + push3 buflen() # [buflen, input_timestamp] + dup2 # [input_timestamp, buflen, input_timestamp] + mod # [time_index, input_timestamp] + swap1 # [input_timestamp, time_index] + dup2 # [time_index, input_timestamp, time_index] + sload # [stored_timestamp, input_timestamp, time_index] # Verify stored timestamp matches input timestamp. It's possible these # don't match if the slot has been overwritten by the ring buffer or if # the timestamp input wasn't a valid previous timestamp. - %get_input() # [input, timestamp, time_index] - eq # [input == timestamp, time_index] + eq # [stored_timestamp == input_timestamp, time_index] push1 loadroot # [loadroot_lbl, input == timestamp, time_index] jumpi # [time_index] - %revert() # [] - + %do_revert() # [] loadroot: # Extend index to get root index. - jumpdest + jumpdest # [time_index] push3 buflen() # [buflen, time_index] add # [root_index] sload # [root] @@ -112,6 +115,11 @@ loadroot: push0 # [offset, size] return # [] +throw: + # Reverts current execution with no return data. + jumpdest + %do_revert() + submit: jumpdest # [] diff --git a/test/Contract.t.sol.in b/test/Contract.t.sol.in index 6123596..91e50a2 100644 --- a/test/Contract.t.sol.in +++ b/test/Contract.t.sol.in @@ -6,7 +6,7 @@ import "../src/Contract.sol"; address constant addr = 0x000000000000000000000000000000000000000b; address constant sysaddr = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE; -uint256 constant buflen = 98304; +uint256 constant buflen = 93600; bytes32 constant root = hex"88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6"; function timestamp() view returns (bytes32) { @@ -87,6 +87,11 @@ contract ContractTest is Test { (ret, data) = unit.call(bytes.concat(bytes32(time+1))); assertFalse(ret); assertEq(data, hex""); + + // Timestamp zero should fail. + (ret, data) = unit.call(bytes.concat(bytes32(0))); + assertFalse(ret); + assertEq(data, hex""); } // testUpdate verifies the set functionality of the contract. @@ -137,7 +142,7 @@ contract ContractTest is Test { uint256 start = block.timestamp; // Saturate storage with fake roots. - for (uint256 i = 0; i < 8192; i += 1) { + for (uint256 i = 0; i < buflen / 12; i += 1) { bytes32 pbbr = bytes32(i*1337); vm.prank(sysaddr); (bool ret, bytes memory data) = unit.call(bytes.concat(pbbr)); @@ -147,7 +152,7 @@ contract ContractTest is Test { } // Attempt to read all values in same block context. - for (uint256 i = 0; i < 8192; i += 1) { + for (uint256 i = 0; i < buflen / 12; i += 1) { bytes32 time = bytes32(uint256(start+i*12)); (bool ret, bytes memory got) = unit.call(bytes.concat(time)); assertTrue(ret);