From 8317c8c19bd25cc2802c2a65ead835bf4c7a6ac6 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 15:36:52 +0530 Subject: [PATCH 01/15] extend history retention to 8192 and modify blockhash to charge additional sload gas --- EIPS/eip-2935.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 26212c2c29868..96d164eb3369e 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -1,7 +1,7 @@ --- eip: 2935 -title: Save historical block hashes in state -description: store previous block hashes as storage slots of a system contract to allow for stateless execution +title: Save and serve limited historical block hashes from state +description: Store and serve previous 8192 block hashes as storage slots of a system contract to allow for stateless execution author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria) discussions-to: https://ethereum-magicians.org/t/eip-2935-save-historical-block-hashes-in-state/4565 status: Draft @@ -12,15 +12,15 @@ created: 2020-09-03 ## Abstract -Store last 256 historical block hashes in a contract, and modify the `BLOCKHASH (0x40)` opcode to read and serve from this contract storage. +Store last `8192` historical block hashes in a contract, and modify the `BLOCKHASH (0x40)` opcode to read and serve from this contract storage. ## Motivation -Currently BLOCKHASH opcode accesses history to resolve hash of the block number in EVM. However a more stateless client friendly way is to maintain and serve these hashes from state. +Currently `BLOCKHASH` opcode accesses history to resolve hash of the block number in EVM. However a more stateless client friendly way is to maintain and serve these hashes from state. -Although this is possible even in Merkle trie state, but Verkle trie state further allows bundling the BLOCKHASH witnesses (along with other witnesses) in an efficient manner making it worthwhile to have these in state. +Although this is possible even in Merkle trie state, but Verkle trie state further allows bundling the `BLOCKHASH` witnesses (along with other witnesses) in an efficient manner making it worthwhile to have these in state. -A side benefit of this approach could be that it allows building/validating proofs related to last 256 ancestors directly against the current state. +A side benefit of this approach could be that it allows building/validating proofs related to last `8192` ancestors directly against the current state. ## Specification @@ -28,7 +28,8 @@ A side benefit of this approach could be that it allows building/validating proo | - | - | | `FORK_TIMESTAMP` | TBD | | `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe`| -| `HISTORY_SERVE_WINDOW` | `256` | +| `HISTORY_SERVE_WINDOW` | `8192` | +| `BLOCKHASH_PERSIST_WINDOW` | `256` | This EIP specifies for storing last `HISTORY_SERVE_WINDOW` block hashes in a ring buffer storage of `HISTORY_SERVE_WINDOW` length. @@ -40,10 +41,10 @@ def process_block_hash_history(block: Block, state: State): if block.timestamp >= FORK_TIMESTAMP: state.insert_slot(HISTORY_STORAGE_ADDRESS, (block.number-1) % HISTORY_SERVE_WINDOW , block.parent.hash) - # If this is the first fork block, add the parent's direct 255 ancestors as well + # If this is the fork block, add the parent's direct `BLOCKHASH_PERSIST_WINDOW - 1` ancestors as well if block.parent.timestamp < FORK_TIMESTAMP: ancestor = block.parent - for i in range(HISTORY_SERVE_WINDOW - 1): + for i in range(FORK_TRANSITION_PERSIST_LENGTH - 1): # stop at genesis block if ancestor.number == 0: break @@ -52,7 +53,7 @@ def process_block_hash_history(block: Block, state: State): state.insert_slot(HISTORY_STORAGE_ADDRESS, ancestor.number % HISTORY_SERVE_WINDOW, ancestor.hash) ``` -Note that if this is the fork block, then it persists the additional requisite history that could be needed while resolving `BLOCKHASH` opcode for all of the `HISTORY_SERVE_WINDOW` ancestors (up until genesis). +Note that if this is the fork block, then it persists the additional requisite history that could be needed while resolving `BLOCKHASH` opcode for all of the `BLOCKHASH_PERSIST_WINDOW` ancestors (up until genesis). For resolving the `BLOCKHASH` opcode this fork onwards (`block.timestamp >= FORK_TIMESTAMP`), switch the logic to: @@ -70,7 +71,7 @@ Some activation scenarios: * For the fork to be activated at genesis, no history is written to the genesis state, and at the start of block `1`, genesis hash will be written as a normal operation to slot `0`. * for activation at block `1`, only genesis hash will be written at slot `0` as there is no additional history that needs to be persisted. * for activation at block `32`, block `31`'s hash will be written to slot `31` and additonal history for `0..30`'s hashes will be persisted, so all in all `0..31`'s hashes. - * for activation at block `1000`, block `744-999`'s hashes will be presisted in the slot and `BLOCKHASH` for `743` or less would resolve to `0` as only `HISTORY_SERVE_WINDOW` can be served. + * for activation at block `1000`, block `744-999`'s hashes will be presisted in the slot and `BLOCKHASH` for `743` or less would resolve to `0` as only `BLOCKHASH_PERSIST_WINDOW` are persisted. ### [EIP-158](./eip-158.md) handling @@ -82,7 +83,7 @@ This address is currently exempt from [EIP-158](./eip-158.md) cleanup in Kaustin ### Gas costs and witnesses -We propose not to modify any gas costs since clients can directly resolve `BLOCKHASH` from an in-memory maintained structure or do a direct actual `SLOAD` or even a "system" execution of the deployed contract's `get`. However, for purposes of bundling block witnesses for stateless clients (for e.g. in Verkle activated networks), client should record corresponding witness accesses and bundle in the witnesses along with the corresponding proofs. +Since now `BLOCKHASH` is served from state, the clients now **additionally** charge the corresponding warm or cold `SLOAD` costs. For verkle based networks this would imply doing and bundling similar accesses (and gas charges) to `SLOAD`. ## Rationale @@ -96,14 +97,15 @@ However after weighing pros and cons, we decided to go with just a limited ring Second concern was how to best transition the BLOCKHASH resolution logic post fork by: -1. Either waiting for `HISTORY_SERVE_WINDOW` blocks for the entire relevant history to persist -2. Storing of all last `HISTORY_SERVE_WINDOW` block hashes on the fork block. +1. Either waiting for `BLOCKHASH_PERSIST_WINDOW` blocks for the entire relevant history to persist +2. Storing of all last `BLOCKHASH_PERSIST_WINDOW` block hashes on the fork block. -We choose to go with later as it alleviates the need to detect fork activation height to transition to the new logic in backward compatible manner as the entire requisite history will be available from the first block of the fork itself. The cost of doing so is marginal considering the `HISTORY_SERVE_WINDOW` being small. +We choose to go with later as it alleviates the need to detect fork activation height to transition to the new logic in backward compatible manner as the entire `BLOCKHASH` requisite history will be available from the first block of the fork itself. +The cost of doing so is marginal considering the `BLOCKHASH_PERSIST_WINDOW` being small and clients should already have it handy for resolving upto `BLOCKHASH_PERSIST_WINDOW` depth. ## Backwards Compatibility -The behavior of `BLOCKHASH` opcode remains same and this EIP doesn't affect backward compatibility with the contracts deployed or the gas consumption costs as the resolution of the opcode is handled "directly" by the clients. +The behavior of `BLOCKHASH` opcode gets extended in backward compatible manner as the history it can serve will get extended with each block post the fork upto `HISTORY_SERVE_WINDOW`. However the gas charges will get bumped as per the additional `SLOAD` costs. ## Test Cases From bbb6d226fafc8a9e7f78014f800453f63e6ab332 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 15:40:41 +0530 Subject: [PATCH 02/15] title --- EIPS/eip-2935.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 96d164eb3369e..1cceae4aa88af 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -1,6 +1,6 @@ --- eip: 2935 -title: Save and serve limited historical block hashes from state +title: Serve historical block hashes from state description: Store and serve previous 8192 block hashes as storage slots of a system contract to allow for stateless execution author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria) discussions-to: https://ethereum-magicians.org/t/eip-2935-save-historical-block-hashes-in-state/4565 From 56250b66bb6f5c5dd8459c6f7c3823ea0cfb5e4a Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 16:19:27 +0530 Subject: [PATCH 03/15] fix cleanup --- EIPS/eip-2935.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 1cceae4aa88af..7cc3a8d486777 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -1,7 +1,7 @@ --- eip: 2935 title: Serve historical block hashes from state -description: Store and serve previous 8192 block hashes as storage slots of a system contract to allow for stateless execution +description: Store and serve last 8192 block hashes as storage slots of a system contract to allow for stateless execution author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria) discussions-to: https://ethereum-magicians.org/t/eip-2935-save-historical-block-hashes-in-state/4565 status: Draft @@ -44,7 +44,7 @@ def process_block_hash_history(block: Block, state: State): # If this is the fork block, add the parent's direct `BLOCKHASH_PERSIST_WINDOW - 1` ancestors as well if block.parent.timestamp < FORK_TIMESTAMP: ancestor = block.parent - for i in range(FORK_TRANSITION_PERSIST_LENGTH - 1): + for i in range(BLOCKHASH_PERSIST_WINDOW - 1): # stop at genesis block if ancestor.number == 0: break From fcd968dd9d8c87e2952fcd51585235194db2c355 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:53:46 +0200 Subject: [PATCH 04/15] add missing contributor names Co-authored-by: Ignacio Hagopian --- EIPS/eip-2935.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 7cc3a8d486777..072060bf74715 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -2,7 +2,7 @@ eip: 2935 title: Serve historical block hashes from state description: Store and serve last 8192 block hashes as storage slots of a system contract to allow for stateless execution -author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria) +author: Vitalik Buterin (@vbuterin), Tomasz Stanczak (@tkstanczak), Guillaume Ballet (@gballet), Gajinder Singh (@g11tech), Tanishq Jasoria (@tanishqjasoria), Ignacio Hagopian (@jsign), Jochem Brouwer (@jochem-brouwer) discussions-to: https://ethereum-magicians.org/t/eip-2935-save-historical-block-hashes-in-state/4565 status: Draft type: Standards Track From 363fb62c3e0604438e8191fb96fddc63f4105980 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 17:27:34 +0530 Subject: [PATCH 05/15] further update on discussion --- EIPS/eip-2935.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 072060bf74715..3a5e2e37696b7 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -29,7 +29,6 @@ A side benefit of this approach could be that it allows building/validating proo | `FORK_TIMESTAMP` | TBD | | `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe`| | `HISTORY_SERVE_WINDOW` | `8192` | -| `BLOCKHASH_PERSIST_WINDOW` | `256` | This EIP specifies for storing last `HISTORY_SERVE_WINDOW` block hashes in a ring buffer storage of `HISTORY_SERVE_WINDOW` length. @@ -41,10 +40,10 @@ def process_block_hash_history(block: Block, state: State): if block.timestamp >= FORK_TIMESTAMP: state.insert_slot(HISTORY_STORAGE_ADDRESS, (block.number-1) % HISTORY_SERVE_WINDOW , block.parent.hash) - # If this is the fork block, add the parent's direct `BLOCKHASH_PERSIST_WINDOW - 1` ancestors as well + # If this is the fork block, add the parent's direct `HISTORY_SERVE_WINDOW - 1` ancestors as well if block.parent.timestamp < FORK_TIMESTAMP: ancestor = block.parent - for i in range(BLOCKHASH_PERSIST_WINDOW - 1): + for i in range(HISTORY_SERVE_WINDOW - 1): # stop at genesis block if ancestor.number == 0: break @@ -53,7 +52,7 @@ def process_block_hash_history(block: Block, state: State): state.insert_slot(HISTORY_STORAGE_ADDRESS, ancestor.number % HISTORY_SERVE_WINDOW, ancestor.hash) ``` -Note that if this is the fork block, then it persists the additional requisite history that could be needed while resolving `BLOCKHASH` opcode for all of the `BLOCKHASH_PERSIST_WINDOW` ancestors (up until genesis). +Note that if this is the fork block, then it persists the additional requisite history that could be needed while resolving `BLOCKHASH` opcode for all of the `HISTORY_SERVE_WINDOW` > `256` ancestors (up until genesis). For resolving the `BLOCKHASH` opcode this fork onwards (`block.timestamp >= FORK_TIMESTAMP`), switch the logic to: @@ -71,7 +70,7 @@ Some activation scenarios: * For the fork to be activated at genesis, no history is written to the genesis state, and at the start of block `1`, genesis hash will be written as a normal operation to slot `0`. * for activation at block `1`, only genesis hash will be written at slot `0` as there is no additional history that needs to be persisted. * for activation at block `32`, block `31`'s hash will be written to slot `31` and additonal history for `0..30`'s hashes will be persisted, so all in all `0..31`'s hashes. - * for activation at block `1000`, block `744-999`'s hashes will be presisted in the slot and `BLOCKHASH` for `743` or less would resolve to `0` as only `BLOCKHASH_PERSIST_WINDOW` are persisted. + * for activation at block `10000`, block `1808-9999`'s hashes will be presisted in the slot and `BLOCKHASH` for `1807` or less would resolve to `0` as only `HISTORY_SERVE_WINDOW` are persisted. ### [EIP-158](./eip-158.md) handling @@ -97,11 +96,11 @@ However after weighing pros and cons, we decided to go with just a limited ring Second concern was how to best transition the BLOCKHASH resolution logic post fork by: -1. Either waiting for `BLOCKHASH_PERSIST_WINDOW` blocks for the entire relevant history to persist -2. Storing of all last `BLOCKHASH_PERSIST_WINDOW` block hashes on the fork block. +1. Either waiting for `HISTORY_SERVE_WINDOW` blocks for the entire relevant history to persist +2. Storing of all last `HISTORY_SERVE_WINDOW` block hashes on the fork block. We choose to go with later as it alleviates the need to detect fork activation height to transition to the new logic in backward compatible manner as the entire `BLOCKHASH` requisite history will be available from the first block of the fork itself. -The cost of doing so is marginal considering the `BLOCKHASH_PERSIST_WINDOW` being small and clients should already have it handy for resolving upto `BLOCKHASH_PERSIST_WINDOW` depth. +The cost of doing so is marginal considering the `HISTORY_SERVE_WINDOW` being relatively limited. Most clients write this into their flat db/memory caches and just requires reading last `HISTORY_SERVE_WINDOW` from the chain history. ## Backwards Compatibility From 34e09236a55f9a4e964b64078bce05dfb920f15a Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 17:31:08 +0530 Subject: [PATCH 06/15] update --- EIPS/eip-2935.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 3a5e2e37696b7..74de3e6125b9a 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -104,7 +104,7 @@ The cost of doing so is marginal considering the `HISTORY_SERVE_WINDOW` being re ## Backwards Compatibility -The behavior of `BLOCKHASH` opcode gets extended in backward compatible manner as the history it can serve will get extended with each block post the fork upto `HISTORY_SERVE_WINDOW`. However the gas charges will get bumped as per the additional `SLOAD` costs. +The behavior of `BLOCKHASH` opcode gets extended in backward compatible manner as the history it can serve will get extended upto `HISTORY_SERVE_WINDOW` on the fork block. However the gas charges will get bumped as per the additional `SLOAD` costs. ## Test Cases From d0ecf246684f8aae61ed85b950b78d9434af28aa Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 17:37:22 +0530 Subject: [PATCH 07/15] update --- EIPS/eip-2935.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 74de3e6125b9a..29b209abc902a 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -82,7 +82,7 @@ This address is currently exempt from [EIP-158](./eip-158.md) cleanup in Kaustin ### Gas costs and witnesses -Since now `BLOCKHASH` is served from state, the clients now **additionally** charge the corresponding warm or cold `SLOAD` costs. For verkle based networks this would imply doing and bundling similar accesses (and gas charges) to `SLOAD`. +Since now `BLOCKHASH` is served from state, the clients now **additionally** charge the corresponding warm or cold `SLOAD` costs. For verkle based networks this would imply doing and bundling corresponding accesses (and gas charges) of `SLOAD`. ## Rationale From a016b6cfd1c2fa97d85cf91099b88f46da0738de Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 17:38:54 +0530 Subject: [PATCH 08/15] update --- EIPS/eip-2935.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 29b209abc902a..3d0d85e10d615 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -104,7 +104,7 @@ The cost of doing so is marginal considering the `HISTORY_SERVE_WINDOW` being re ## Backwards Compatibility -The behavior of `BLOCKHASH` opcode gets extended in backward compatible manner as the history it can serve will get extended upto `HISTORY_SERVE_WINDOW` on the fork block. However the gas charges will get bumped as per the additional `SLOAD` costs. +The behavior of `BLOCKHASH` opcode gets extended in backward compatible manner as the history it can serve will get extended upto `HISTORY_SERVE_WINDOW` on the fork block. However the gas charges will also get bumped as per the additional `SLOAD` costs. ## Test Cases From 328152be185bdcc3446bdbe4dc9194da70cd1347 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 19:50:46 +0530 Subject: [PATCH 09/15] add contract assembley --- EIPS/eip-2935.md | 69 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 3d0d85e10d615..72bbb707e93f5 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -65,6 +65,75 @@ def resolve_blockhash(block: Block, state: State, arg: uint64): return state.load_slot(HISTORY_STORAGE_ADDRESS, arg % HISTORY_SERVE_WINDOW) ``` +Exact evm assembly that can be used for the blockhash contract: + +``` +// check for 32 byte size or else revert +calldatasize +push1 0x21 +gt +push1 0x0a +// jump and skip revert +jumpi + +push0 +push0 +revert + +// check if blocknumber > input or else return 0 +jumpdest +push0 +calldataload +number +gt +push1 0x1a +jumpi + +// return 0 +push0 +push0 +mstore +push1 0x20 +push0 +return + +// check if input + 8125 > blocknumber or else return 0 +jumpdest +number +push2 0x2001 +push0 +calldataload +add +gt +push1 0x2e +jumpi + +// return 0 +push0 +push0 +mstore +push1 0x2e +push0 +return + +// mod 8192 and sload +jumpdest +push2 0x2000 +push0 +calldataload +mod +sload + +// load into mem and return 32 bytes +push0 +mstore +push1 0x20 +push0 +return + +stop +``` + Some activation scenarios: * For the fork to be activated at genesis, no history is written to the genesis state, and at the start of block `1`, genesis hash will be written as a normal operation to slot `0`. From d49c765015f810f3f87c470cdca27398caba2c6f Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 19:57:35 +0530 Subject: [PATCH 10/15] apply feedback --- EIPS/eip-2935.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 72bbb707e93f5..3fbf45ce87743 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -12,7 +12,7 @@ created: 2020-09-03 ## Abstract -Store last `8192` historical block hashes in a contract, and modify the `BLOCKHASH (0x40)` opcode to read and serve from this contract storage. +Store last `HISTORY_SERVE_WINDOW` historical block hashes in a contract, and modify the `BLOCKHASH (0x40)` opcode to read and serve from this contract storage. ## Motivation @@ -20,7 +20,7 @@ Currently `BLOCKHASH` opcode accesses history to resolve hash of the block numbe Although this is possible even in Merkle trie state, but Verkle trie state further allows bundling the `BLOCKHASH` witnesses (along with other witnesses) in an efficient manner making it worthwhile to have these in state. -A side benefit of this approach could be that it allows building/validating proofs related to last `8192` ancestors directly against the current state. +A side benefit of this approach could be that it allows building/validating proofs related to last `HISTORY_SERVE_WINDOW` ancestors directly against the current state. ## Specification @@ -29,6 +29,7 @@ A side benefit of this approach could be that it allows building/validating proo | `FORK_TIMESTAMP` | TBD | | `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe`| | `HISTORY_SERVE_WINDOW` | `8192` | +| `BLOCKHASH_OLD_WINDOW` | `256` | This EIP specifies for storing last `HISTORY_SERVE_WINDOW` block hashes in a ring buffer storage of `HISTORY_SERVE_WINDOW` length. @@ -52,7 +53,7 @@ def process_block_hash_history(block: Block, state: State): state.insert_slot(HISTORY_STORAGE_ADDRESS, ancestor.number % HISTORY_SERVE_WINDOW, ancestor.hash) ``` -Note that if this is the fork block, then it persists the additional requisite history that could be needed while resolving `BLOCKHASH` opcode for all of the `HISTORY_SERVE_WINDOW` > `256` ancestors (up until genesis). +Note that if this is the fork block, then it persists the additional requisite history that could be needed while resolving `BLOCKHASH` opcode for all of the `HISTORY_SERVE_WINDOW` > `BLOCKHASH_OLD_WINDOW` ancestors (up until genesis). For resolving the `BLOCKHASH` opcode this fork onwards (`block.timestamp >= FORK_TIMESTAMP`), switch the logic to: From e7a0178925faaa4992357210961f0969d19e1331 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 20:23:14 +0530 Subject: [PATCH 11/15] add helpful comments --- EIPS/eip-2935.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 3fbf45ce87743..b7a98109e1f60 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -60,7 +60,7 @@ For resolving the `BLOCKHASH` opcode this fork onwards (`block.timestamp >= FORK ```python def resolve_blockhash(block: Block, state: State, arg: uint64): # check the wrap around range - if arg >= block.number or arg < max(block.number - HISTORY_SERVE_WINDOW, 0) + if arg >= block.number or (arg + HISTORY_SERVE_WINDOW) < block.number return 0 return state.load_slot(HISTORY_STORAGE_ADDRESS, arg % HISTORY_SERVE_WINDOW) @@ -69,7 +69,7 @@ def resolve_blockhash(block: Block, state: State, arg: uint64): Exact evm assembly that can be used for the blockhash contract: ``` -// check for 32 byte size or else revert +// check for <=32 (<33) byte size or else revert calldatasize push1 0x21 gt @@ -98,7 +98,7 @@ push1 0x20 push0 return -// check if input + 8125 > blocknumber or else return 0 +// check if input + 8125 > blocknumber (input >= blocknumber - 8124) or else return 0 jumpdest number push2 0x2001 From a00d184239efcbc926d24162149021eb70b80f15 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 21:35:58 +0530 Subject: [PATCH 12/15] fix the comment vals --- EIPS/eip-2935.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index b7a98109e1f60..427ac0be00717 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -98,7 +98,7 @@ push1 0x20 push0 return -// check if input + 8125 > blocknumber (input >= blocknumber - 8124) or else return 0 +// check if input + 8193 > blocknumber (input >= blocknumber - 8192) or else return 0 jumpdest number push2 0x2001 From 149a923f0fa2683af960de136eae19da67797af7 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 22:14:06 +0530 Subject: [PATCH 13/15] update the assembely as per feedback --- EIPS/eip-2935.md | 59 ++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 427ac0be00717..3fb2c9354887e 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -69,54 +69,34 @@ def resolve_blockhash(block: Block, state: State, arg: uint64): Exact evm assembly that can be used for the blockhash contract: ``` -// check for <=32 (<33) byte size or else revert +// check if inputsize>32 revert +push1 0x20 calldatasize -push1 0x21 gt -push1 0x0a -// jump and skip revert +push1 0x32 jumpi -push0 -push0 -revert - -// check if blocknumber > input or else return 0 -jumpdest +// check if input > blocknumber-1 then return 0 +push1 0x1 +number +sub push0 calldataload -number gt -push1 0x1a +push1 0x2a jumpi -// return 0 -push0 -push0 -mstore -push1 0x20 -push0 -return -// check if input + 8193 > blocknumber (input >= blocknumber - 8192) or else return 0 -jumpdest -number -push2 0x2001 +// check if blocknumber > input + 8192 then return 0 push0 calldataload +push2 0x2000 add +number gt -push1 0x2e +push1 0x2a jumpi -// return 0 -push0 -push0 -mstore -push1 0x2e -push0 -return - // mod 8192 and sload jumpdest push2 0x2000 @@ -132,6 +112,21 @@ push1 0x20 push0 return +// return 0 +jumpdest +push0 +push0 +mstore +push1 0x20 +push0 +return + +// revert +jumpdest +push0 +push0 +revert + stop ``` From c2de1868c8cda9802bb8ef82eb20d1a488b182f7 Mon Sep 17 00:00:00 2001 From: gajinder Date: Fri, 12 Apr 2024 23:56:34 +0530 Subject: [PATCH 14/15] improv --- EIPS/eip-2935.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 3fb2c9354887e..9b396deb9caa2 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -73,7 +73,7 @@ Exact evm assembly that can be used for the blockhash contract: push1 0x20 calldatasize gt -push1 0x32 +push1 0x31 jumpi // check if input > blocknumber-1 then return 0 @@ -83,10 +83,9 @@ sub push0 calldataload gt -push1 0x2a +push1 0x29 jumpi - // check if blocknumber > input + 8192 then return 0 push0 calldataload @@ -94,11 +93,10 @@ push2 0x2000 add number gt -push1 0x2a +push1 0x29 jumpi // mod 8192 and sload -jumpdest push2 0x2000 push0 calldataload From cbb3ee69b9cd237450ed63f6d9d154cfb3db00b6 Mon Sep 17 00:00:00 2001 From: gajinder Date: Sat, 13 Apr 2024 00:52:56 +0530 Subject: [PATCH 15/15] add and generate deployment tx, inputdata and sender/contract address --- EIPS/eip-2935.md | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/EIPS/eip-2935.md b/EIPS/eip-2935.md index 9b396deb9caa2..d2251359a3465 100644 --- a/EIPS/eip-2935.md +++ b/EIPS/eip-2935.md @@ -27,7 +27,7 @@ A side benefit of this approach could be that it allows building/validating proo | Parameter | Value | | - | - | | `FORK_TIMESTAMP` | TBD | -| `HISTORY_STORAGE_ADDRESS` | `0xfffffffffffffffffffffffffffffffffffffffe`| +| `HISTORY_STORAGE_ADDRESS` | `0x25a219378dad9b3503c8268c9ca836a52427a4fb`| | `HISTORY_SERVE_WINDOW` | `8192` | | `BLOCKHASH_OLD_WINDOW` | `256` | @@ -66,7 +66,9 @@ def resolve_blockhash(block: Block, state: State, arg: uint64): return state.load_slot(HISTORY_STORAGE_ADDRESS, arg % HISTORY_SERVE_WINDOW) ``` -Exact evm assembly that can be used for the blockhash contract: +### Contract Implementation + +Exact evm assembly that can be used for the contract to resolve `BLOCKHASH` ``` // check if inputsize>32 revert @@ -128,6 +130,36 @@ revert stop ``` +Corresponding bytecode: +`60203611603157600143035f35116029575f356120000143116029576120005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00` + +#### Deployment + +A special synthetic address is generated by working backwards from the desired deployment transaction: + +```json +{ + "type": "0x0", + "nonce": "0x0", + "to": null, + "gas": "0x3d090", + "gasPrice": "0xe8d4a51000", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "value": "0x0", + "input": "0x60368060095f395ff360203611603157600143035f35116029575f356120000143116029576120005f3506545f5260205ff35b5f5f5260205ff35b5f5ffd00", + "v": "0x1b", + "r": "0x539", + "s": "0x1b9b6eb1f0", + "hash": "7ba81426bfa88a2cf4ea5c9abbbe83619505acd1173bc8450f93cf17cde3784b", +} +``` + +Note, the input in the transaction has a simple constructor prefixing the desired runtime code. + +The sender of the transaction can be calculated as `0xa4690f0ed0d089faa1e0ad94c8f1b4a2fd4b0734`. The address of the first contract deployed from the account is `rlp([sender, 0])` which equals `0x25a219378dad9b3503c8268c9ca836a52427a4fb`. This is how `HISTORY_STORAGE_ADDRESS` is determined. Although this style of contract creation is not tied to any specific initcode like create2 is, the synthetic address is cryptographically bound to the input data of the transaction (e.g. the initcode). + + Some activation scenarios: * For the fork to be activated at genesis, no history is written to the genesis state, and at the start of block `1`, genesis hash will be written as a normal operation to slot `0`.