Skip to content

Commit

Permalink
Merge pull request #963 from gurukamath/eips/prague/eip-2935-1
Browse files Browse the repository at this point in the history
Update EIP-2935 to the latest spec
  • Loading branch information
petertdavies authored Aug 11, 2024
2 parents 5dbd8a0 + 439e740 commit 95e2282
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 40 deletions.
73 changes: 53 additions & 20 deletions src/ethereum/prague/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class BlockChain:
chain_id: U64


def apply_fork(old: BlockChain, block: Block) -> BlockChain:
def apply_fork(old: BlockChain) -> BlockChain:
"""
Transforms the state from the previous hard fork (`old`) into the block
chain object for this hard fork and returns it.
Expand All @@ -125,34 +125,55 @@ def apply_fork(old: BlockChain, block: Block) -> BlockChain:
----------
old :
Previous block chain object.
block :
Block to apply to `old`.
Returns
-------
new : `BlockChain`
Upgraded block chain object for this hard fork.
"""
# At fork block, store the hashes for earlier blocks.
current_block = block
for i in range(HISTORY_SERVE_WINDOW - 1):
if current_block.header.number == 0:
break

set_storage(
old.state,
HISTORY_STORAGE_ADDRESS,
(
(current_block.header.number - 1) % HISTORY_SERVE_WINDOW
).to_be_bytes32(),
U256.from_be_bytes(current_block.header.parent_hash),
)
# Set the earlier block as the current block
current_block = old.blocks[-1 - i]

return old


def get_last_256_block_hashes(chain: BlockChain) -> List[Hash32]:
"""
Obtain the list of hashes of the previous 256 blocks in order of
increasing block number.
This function will return less hashes for the first 256 blocks.
The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
therefore this function retrieves them.
Parameters
----------
chain :
History and current state.
Returns
-------
recent_block_hashes : `List[Hash32]`
Hashes of the recent 256 blocks in order of increasing block number.
"""
recent_blocks = chain.blocks[-255:]
# TODO: This function has not been tested rigorously
if len(recent_blocks) == 0:
return []

recent_block_hashes = []

for block in recent_blocks:
prev_block_hash = block.header.parent_hash
recent_block_hashes.append(prev_block_hash)

# We are computing the hash only for the most recent block and not for
# the rest of the blocks as they have successors which have the hash of
# the current block as parent hash.
most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
recent_block_hashes.append(most_recent_block_hash)

return recent_block_hashes


def state_transition(chain: BlockChain, block: Block) -> None:
"""
Attempts to apply a block to an existing block chain.
Expand Down Expand Up @@ -193,6 +214,7 @@ def state_transition(chain: BlockChain, block: Block) -> None:

apply_body_output = apply_body(
chain.state,
get_last_256_block_hashes(chain),
block.header.coinbase,
block.header.number,
block.header.base_fee_per_gas,
Expand Down Expand Up @@ -511,6 +533,7 @@ class ApplyBodyOutput:
def process_system_transaction(
target_address: Address,
data: Bytes,
block_hashes: List[Hash32],
coinbase: Address,
block_number: Uint,
base_fee_per_gas: Uint,
Expand All @@ -530,6 +553,8 @@ def process_system_transaction(
Address of the contract to call.
data :
Data to pass to the contract.
block_hashes :
List of hashes of the previous 256 blocks.
coinbase :
Address of the block's coinbase.
block_number :
Expand Down Expand Up @@ -575,6 +600,7 @@ def process_system_transaction(

system_tx_env = vm.Environment(
caller=SYSTEM_ADDRESS,
block_hashes=block_hashes,
origin=SYSTEM_ADDRESS,
coinbase=coinbase,
number=block_number,
Expand Down Expand Up @@ -607,6 +633,7 @@ def process_system_transaction(

def apply_body(
state: State,
block_hashes: List[Hash32],
coinbase: Address,
block_number: Uint,
base_fee_per_gas: Uint,
Expand Down Expand Up @@ -634,6 +661,9 @@ def apply_body(
----------
state :
Current account state.
block_hashes :
List of hashes of the previous 256 blocks in the order of
increasing block number.
coinbase :
Address of account which receives block reward and transaction fees.
block_number :
Expand Down Expand Up @@ -687,6 +717,7 @@ def apply_body(
process_system_transaction(
BEACON_ROOTS_ADDRESS,
parent_beacon_block_root,
block_hashes,
coinbase,
block_number,
base_fee_per_gas,
Expand Down Expand Up @@ -718,6 +749,7 @@ def apply_body(

env = vm.Environment(
caller=sender_address,
block_hashes=block_hashes,
origin=sender_address,
coinbase=coinbase,
number=block_number,
Expand Down Expand Up @@ -771,6 +803,7 @@ def apply_body(
system_withdrawal_tx_output = process_system_transaction(
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
b"",
block_hashes,
coinbase,
block_number,
base_fee_per_gas,
Expand Down
2 changes: 2 additions & 0 deletions src/ethereum/prague/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import List, Optional, Set, Tuple, Union

from ethereum.base_types import U64, U256, Bytes, Bytes0, Bytes32, Uint
from ethereum.crypto.hash import Hash32

from ..blocks import Log
from ..fork_types import Address, VersionedHash
Expand All @@ -33,6 +34,7 @@ class Environment:
"""

caller: Address
block_hashes: List[Hash32]
origin: Address
coinbase: Address
number: Uint
Expand Down
1 change: 1 addition & 0 deletions src/ethereum/prague/vm/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
GAS_KECCAK256 = Uint(30)
GAS_KECCAK256_WORD = Uint(6)
GAS_COPY = Uint(3)
GAS_BLOCK_HASH = Uint(20)
GAS_LOG = Uint(375)
GAS_LOG_DATA = Uint(8)
GAS_LOG_TOPIC = Uint(375)
Expand Down
25 changes: 5 additions & 20 deletions src/ethereum/prague/vm/instructions/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@

from ethereum.base_types import U256

from ...state import get_storage
from .. import Evm
from ..gas import GAS_BASE, GAS_COLD_SLOAD, GAS_WARM_ACCESS, charge_gas
from ..gas import GAS_BASE, GAS_BLOCK_HASH, charge_gas
from ..stack import pop, push


Expand All @@ -37,34 +36,20 @@ def block_hash(evm: Evm) -> None:
:py:class:`~ethereum.prague.vm.exceptions.OutOfGasError`
If `evm.gas_left` is less than `20`.
"""
from ...fork import HISTORY_SERVE_WINDOW, HISTORY_STORAGE_ADDRESS

# STACK
block_number = pop(evm.stack)

# GAS
key = (block_number % HISTORY_SERVE_WINDOW).to_be_bytes32()
if (HISTORY_STORAGE_ADDRESS, key) in evm.accessed_storage_keys:
charge_gas(evm, GAS_WARM_ACCESS)
else:
evm.accessed_storage_keys.add((HISTORY_STORAGE_ADDRESS, key))
charge_gas(evm, GAS_COLD_SLOAD)
charge_gas(evm, GAS_BLOCK_HASH)

# OPERATION
if (
evm.env.number <= block_number
or evm.env.number > block_number + HISTORY_SERVE_WINDOW
):
if evm.env.number <= block_number or evm.env.number > block_number + 256:
# Default hash to 0, if the block of interest is not yet on the chain
# (including the block which has the current executing transaction),
# or if the block's age is more than HISTORY_SERVE_WINDOW.
# or if the block's age is more than 256.
hash = b"\x00"
else:
hash = get_storage(
evm.env.state,
HISTORY_STORAGE_ADDRESS,
key,
).to_be_bytes32()
hash = evm.env.block_hashes[-(evm.env.number - block_number)]

push(evm.stack, U256.from_be_bytes(hash))

Expand Down

0 comments on commit 95e2282

Please sign in to comment.