diff --git a/src/ethereum/cancun/fork.py b/src/ethereum/cancun/fork.py index 8e858b45a6..2e996bf2cc 100644 --- a/src/ethereum/cancun/fork.py +++ b/src/ethereum/cancun/fork.py @@ -58,7 +58,9 @@ state_root, ) from .trie import Trie, root, trie_set +from .utils.hexadecimal import hex_to_address from .utils.message import prepare_message +from .vm import Message from .vm.gas import init_code_cost from .vm.interpreter import MAX_CODE_SIZE, process_message_call @@ -67,6 +69,11 @@ GAS_LIMIT_ADJUSTMENT_FACTOR = 1024 GAS_LIMIT_MINIMUM = 5000 EMPTY_OMMER_HASH = keccak256(rlp.encode([])) +SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe") +BEACON_ROOTS_ADDRESS = hex_to_address( + "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02" +) +SYSTEM_TRANSACTION_GAS = 30000000 @dataclass @@ -186,6 +193,7 @@ def state_transition(chain: BlockChain, block: Block) -> None: block.transactions, chain.chain_id, block.withdrawals, + block.header.parent_beacon_block_root, ) ensure(gas_used == block.header.gas_used, InvalidBlock) ensure(transactions_root == block.header.transactions_root, InvalidBlock) @@ -411,6 +419,7 @@ def apply_body( transactions: Tuple[Union[LegacyTransaction, Bytes], ...], chain_id: U64, withdrawals: Tuple[Withdrawal, ...], + parent_beacon_block_root: Root, ) -> Tuple[Uint, Root, Root, Bloom, State, Root]: """ Executes a block. @@ -450,6 +459,8 @@ def apply_body( ID of the executing chain. withdrawals : Withdrawals to be processed in the current block. + parent_beacon_block_root : + The root of the beacon block from the parent block. Returns ------- @@ -477,6 +488,45 @@ def apply_body( ) block_logs: Tuple[Log, ...] = () + beacon_block_roots_contract_code = get_account( + state, BEACON_ROOTS_ADDRESS + ).code + + system_tx_message = Message( + caller=SYSTEM_ADDRESS, + target=BEACON_ROOTS_ADDRESS, + gas=Uint(SYSTEM_TRANSACTION_GAS), + value=U256(0), + data=parent_beacon_block_root, + code=beacon_block_roots_contract_code, + depth=Uint(0), + current_target=BEACON_ROOTS_ADDRESS, + code_address=BEACON_ROOTS_ADDRESS, + should_transfer_value=False, + is_static=False, + accessed_addresses=set(), + accessed_storage_keys=set(), + parent_evm=None, + ) + + system_tx_env = vm.Environment( + caller=SYSTEM_ADDRESS, + origin=SYSTEM_ADDRESS, + block_hashes=block_hashes, + coinbase=coinbase, + number=block_number, + gas_limit=block_gas_limit, + base_fee_per_gas=base_fee_per_gas, + gas_price=base_fee_per_gas, + time=block_time, + prev_randao=prev_randao, + state=state, + chain_id=chain_id, + traces=[], + ) + + process_message_call(system_tx_message, system_tx_env) + for i, tx in enumerate(map(decode_transaction, transactions)): trie_set( transactions_trie, rlp.encode(Uint(i)), encode_transaction(tx) diff --git a/src/ethereum/cancun/fork_types.py b/src/ethereum/cancun/fork_types.py index c5861e3c0c..f6fa7572d2 100644 --- a/src/ethereum/cancun/fork_types.py +++ b/src/ethereum/cancun/fork_types.py @@ -210,6 +210,7 @@ class Header: nonce: Bytes8 base_fee_per_gas: Uint withdrawals_root: Root + parent_beacon_block_root: Root @slotted_freezable diff --git a/src/ethereum_spec_tools/evm_tools/fixture_loader.py b/src/ethereum_spec_tools/evm_tools/fixture_loader.py index b1e4d8de64..432a3f1370 100644 --- a/src/ethereum_spec_tools/evm_tools/fixture_loader.py +++ b/src/ethereum_spec_tools/evm_tools/fixture_loader.py @@ -193,6 +193,11 @@ def State(self) -> Any: """State class of the fork""" return self._module("state").State + @property + def get_account(self) -> Any: + """get_account function of the fork""" + return self._module("state").get_account + @property def set_account(self) -> Any: """set_account function of the fork""" diff --git a/src/ethereum_spec_tools/evm_tools/t8n/__init__.py b/src/ethereum_spec_tools/evm_tools/t8n/__init__.py index 3700804fd1..69400d6cb2 100644 --- a/src/ethereum_spec_tools/evm_tools/t8n/__init__.py +++ b/src/ethereum_spec_tools/evm_tools/t8n/__init__.py @@ -116,6 +116,15 @@ def __init__(self, options: Any) -> None: self.env.block_difficulty, self.env.base_fee_per_gas ) + if self.is_after_fork("ethereum.cancun"): + self.SYSTEM_ADDRESS = self.hex_to_address( + "0xfffffffffffffffffffffffffffffffffffffffe" + ) + self.BEACON_ROOTS_ADDRESS = self.hex_to_address( + "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02" + ) + self.SYSTEM_TRANSACTION_GAS = 30000000 + @property def fork(self) -> Any: """The fork module of the given fork.""" @@ -146,6 +155,11 @@ def vm(self) -> Any: """The vm module of the given fork.""" return self._module("vm") + @property + def interpreter(self) -> Any: + """The interpreter module of the given fork.""" + return self._module("vm.interpreter") + @property def BLOCK_REWARD(self) -> Any: """ @@ -320,6 +334,48 @@ def apply_body(self) -> None: receipts_trie = self.trie.Trie(secured=False, default=None) block_logs = () + if self.is_after_fork("ethereum.cancun"): + beacon_block_roots_contract_code = self.get_account( + self.alloc.state, self.BEACON_ROOTS_ADDRESS + ).code + + system_tx_message = self.vm.Message( + caller=self.SYSTEM_ADDRESS, + target=self.BEACON_ROOTS_ADDRESS, + gas=Uint(self.SYSTEM_TRANSACTION_GAS), + value=U256(0), + data=self.env.parent_beacon_block_root, + code=beacon_block_roots_contract_code, + depth=Uint(0), + current_target=self.BEACON_ROOTS_ADDRESS, + code_address=self.BEACON_ROOTS_ADDRESS, + should_transfer_value=False, + is_static=False, + accessed_addresses=set(), + accessed_storage_keys=set(), + parent_evm=None, + ) + + system_tx_env = self.vm.Environment( + caller=self.SYSTEM_ADDRESS, + origin=self.SYSTEM_ADDRESS, + block_hashes=self.env.block_hashes, + coinbase=self.env.coinbase, + number=self.env.block_number, + gas_limit=self.env.block_gas_limit, + base_fee_per_gas=self.env.base_fee_per_gas, + gas_price=self.env.base_fee_per_gas, + time=self.env.block_timestamp, + prev_randao=self.env.prev_randao, + state=self.alloc.state, + chain_id=self.chain_id, + traces=[], + ) + + self.interpreter.process_message_call( + system_tx_message, system_tx_env + ) + for i, (tx_idx, tx) in enumerate(self.txs.transactions): # i is the index among valid transactions # tx_idx is the index among all transactions. tx_idx is only used diff --git a/src/ethereum_spec_tools/evm_tools/t8n/env.py b/src/ethereum_spec_tools/evm_tools/t8n/env.py index d08f322b01..9d2e091629 100644 --- a/src/ethereum_spec_tools/evm_tools/t8n/env.py +++ b/src/ethereum_spec_tools/evm_tools/t8n/env.py @@ -43,6 +43,7 @@ class Env: block_hashes: Optional[List[Any]] parent_ommers_hash: Optional[Hash32] ommers: Any + parent_beacon_block_root: Optional[Hash32] def __init__(self, t8n: Any, stdin: Optional[Dict] = None): if t8n.options.input_env == "stdin": @@ -64,6 +65,13 @@ def __init__(self, t8n: Any, stdin: Optional[Dict] = None): self.read_ommers(data, t8n) self.read_withdrawals(data, t8n) + if t8n.is_after_fork("ethereum.cancun"): + self.parent_beacon_block_root = Bytes32( + hex_to_bytes(data["parentBeaconBlockRoot"]) + ) + else: + self.parent_beacon_block_root = None + def read_base_fee_per_gas(self, data: Any, t8n: Any) -> None: """ Read the base_fee_per_gas from the data. If the base fee is diff --git a/tests/cancun/test_rlp.py b/tests/cancun/test_rlp.py index bb331ff7d1..ba1e6dabcd 100644 --- a/tests/cancun/test_rlp.py +++ b/tests/cancun/test_rlp.py @@ -105,6 +105,7 @@ nonce=Bytes8(b"12345678"), base_fee_per_gas=Uint(6), withdrawals_root=hash6, + parent_beacon_block_root=Bytes32(b"1234567890abcdef1234567890abcdef"), ) block = Block(