From 2f77cd84548f7667bdf1bcac10ced08901341777 Mon Sep 17 00:00:00 2001 From: Nerolation Date: Wed, 19 Jun 2024 11:20:48 +0200 Subject: [PATCH] fix gas_used --- src/ethereum/prague/fork.py | 248 +++++++++++------------------------- 1 file changed, 77 insertions(+), 171 deletions(-) diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index 581f2037a8..a2c584e2ae 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -29,9 +29,8 @@ Log, Receipt, Withdrawal, - encode_receipt, - parse_deposit_requests_from_receipt, - parse_withdrawal_requests_from_system_tx, + validate_deposit_requests, + validate_requests, ) from .bloom import logs_bloom from .fork_types import Address, Bloom, Root, VersionedHash @@ -49,12 +48,12 @@ state_root, ) from .transactions import ( - COST_FLOOR_PER_CALLDATA_TOKEN, - LEGACY_CALLDATA_TOKEN_COST, TX_ACCESS_LIST_ADDRESS_COST, TX_ACCESS_LIST_STORAGE_KEY_COST, TX_BASE_COST, TX_CREATE_COST, + LEGACY_CALLDATA_TOKEN_COST, + COST_FLOOR_PER_CALLDATA_TOKEN, AccessListTransaction, BlobTransaction, FeeMarketTransaction, @@ -74,11 +73,7 @@ calculate_total_blob_gas, init_code_cost, ) -from .vm.interpreter import ( - MAX_CODE_SIZE, - MessageCallOutput, - process_message_call, -) +from .vm.interpreter import MAX_CODE_SIZE, process_message_call BASE_FEE_MAX_CHANGE_DENOMINATOR = 8 ELASTICITY_MULTIPLIER = 2 @@ -92,9 +87,6 @@ HISTORY_STORAGE_ADDRESS = hex_to_address( "0x25a219378dad9b3503c8268c9ca836a52427a4fb" ) -WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address( - "0x00A3ca265EBcb825B45F985A16CEFB49958cE017" -) SYSTEM_TRANSACTION_GAS = Uint(30000000) MAX_BLOB_GAS_PER_BLOCK = 786432 VERSIONED_HASH_VERSION_KZG = b"\x01" @@ -220,8 +212,6 @@ def state_transition(chain: BlockChain, block: Block) -> None: raise InvalidBlock if apply_body_output.blob_gas_used != block.header.blob_gas_used: raise InvalidBlock - if apply_body_output.requests_root != block.header.requests_root: - raise InvalidBlock chain.blocks.append(block) if len(chain.blocks) > 255: @@ -469,7 +459,14 @@ def make_receipt( logs=logs, ) - return encode_receipt(tx, receipt) + if isinstance(tx, AccessListTransaction): + return b"\x01" + rlp.encode(receipt) + elif isinstance(tx, FeeMarketTransaction): + return b"\x02" + rlp.encode(receipt) + elif isinstance(tx, BlobTransaction): + return b"\x03" + rlp.encode(receipt) + else: + return receipt @dataclass @@ -508,103 +505,6 @@ class ApplyBodyOutput: requests_root: Root -def process_system_transaction( - target_address: Address, - data: Bytes, - coinbase: Address, - block_number: Uint, - base_fee_per_gas: Uint, - block_gas_limit: Uint, - block_time: U256, - prev_randao: Bytes32, - state: State, - chain_id: U64, - excess_blob_gas: U64, -) -> MessageCallOutput: - """ - Process a system transaction. - - Parameters - ---------- - target_address : - Address of the contract to call. - data : - Data to pass to the contract. - coinbase : - Address of the block's coinbase. - block_number : - Block number. - base_fee_per_gas : - Base fee per gas. - block_gas_limit : - Gas limit of the block. - block_time : - Time the block was produced. - prev_randao : - Previous randao value. - state : - Current state. - chain_id : - ID of the chain. - excess_blob_gas : - Excess blob gas. - - Returns - ------- - system_tx_output : `MessageCallOutput` - Output of processing the system transaction. - """ - system_contract_code = get_account(state, target_address).code - - system_tx_message = Message( - caller=SYSTEM_ADDRESS, - target=target_address, - gas=SYSTEM_TRANSACTION_GAS, - value=U256(0), - data=data, - code=system_contract_code, - depth=Uint(0), - current_target=target_address, - code_address=target_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, - 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=[], - excess_blob_gas=excess_blob_gas, - blob_versioned_hashes=(), - transient_storage=TransientStorage(), - ) - - system_tx_output = process_message_call(system_tx_message, system_tx_env) - - # TODO: Empty accounts in post-merge forks are impossible - # see Ethereum Improvement Proposal 7523. - # This line is only included to support invalid tests in the test suite - # and will have to be removed in the future. - # See https://github.com/ethereum/execution-specs/issues/955 - destroy_touched_empty_accounts( - system_tx_env.state, system_tx_output.touched_accounts - ) - - return system_tx_output - - def apply_body( state: State, coinbase: Address, @@ -682,20 +582,51 @@ def apply_body( secured=False, default=None ) block_logs: Tuple[Log, ...] = () - requests_from_execution: Tuple[Bytes, ...] = () - - process_system_transaction( - BEACON_ROOTS_ADDRESS, - parent_beacon_block_root, - coinbase, - block_number, - base_fee_per_gas, - block_gas_limit, - block_time, - prev_randao, - state, - chain_id, - excess_blob_gas, + receipts: Tuple[Receipt, ...] = () + + beacon_block_roots_contract_code = get_account( + state, BEACON_ROOTS_ADDRESS + ).code + + system_tx_message = Message( + caller=SYSTEM_ADDRESS, + target=BEACON_ROOTS_ADDRESS, + gas=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, + 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=[], + excess_blob_gas=excess_blob_gas, + blob_versioned_hashes=(), + transient_storage=TransientStorage(), + ) + + system_tx_output = process_message_call(system_tx_message, system_tx_env) + + destroy_touched_empty_accounts( + system_tx_env.state, system_tx_output.touched_accounts ) for i, tx in enumerate(map(decode_transaction, transactions)): @@ -746,9 +677,8 @@ def apply_body( rlp.encode(Uint(i)), receipt, ) - - deposit_requests = parse_deposit_requests_from_receipt(receipt) - requests_from_execution += deposit_requests + if isinstance(receipt, Receipt): + receipts += (receipt,) block_logs += logs blob_gas_used += calculate_total_blob_gas(tx) @@ -767,31 +697,10 @@ def apply_body( destroy_account(state, wd.address) # Requests are to be in ascending order of request type + validate_requests(requests) - system_withdrawal_tx_output = process_system_transaction( - WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, - b"", - coinbase, - block_number, - base_fee_per_gas, - block_gas_limit, - block_time, - prev_randao, - state, - chain_id, - excess_blob_gas, - ) - - withdrawal_requests = parse_withdrawal_requests_from_system_tx( - system_withdrawal_tx_output.return_data - ) - - requests_from_execution += withdrawal_requests - - if requests_from_execution != requests: - raise InvalidBlock - - for i, request in enumerate(requests_from_execution): + validate_deposit_requests(receipts, requests) + for i, request in enumerate(requests): trie_set(requests_trie, rlp.encode(Uint(i)), request) return ApplyBodyOutput( @@ -877,19 +786,17 @@ def process_transaction( ) output = process_message_call(message, env) - - floor = Uint( - tokens_in_calldata * COST_FLOOR_PER_CALLDATA_TOKEN + TX_BASE_COST - ) - - legacy_cost = Uint(gas_used + tokens_in_calldata * LEGACY_TOKEN_COST) - + + gas_used = tx.gas - output.gas_left + + floor = Uint(tokens_in_calldata * COST_FLOOR_PER_CALLDATA_TOKEN + TX_BASE_COST) + + legacy_cost = Uint(gas_used - tokens_in_calldata * (COST_FLOOR_PER_CALLDATA_TOKEN - LEGACY_TOKEN_COST)) + if legacy_cost < floor: output.gas_left -= floor - gas_used gas_used = floor - else: - gas_used = tx.gas - output.gas_left - + gas_refund = min(gas_used // 5, output.refund_counter) gas_refund_amount = (output.gas_left + gas_refund) * env.gas_price @@ -948,18 +855,18 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]: ------- verified : `ethereum.base_types.Uint` The intrinsic cost of the transaction. - verified : `ethereum.base_types.Uint` + tokens_in_calldata : `ethereum.base_types.Uint` The eip-7623 calldata tokens used by the transaction. """ data_cost = 0 - + zerobytes = 0 for byte in tx.data: if byte == 0: zerobytes += 1 tokens_in_calldata = zerobytes + (len(tx.data) - zerobytes) * 4 - + data_cost = tokens_in_calldata * COST_FLOOR_PER_CALLDATA_TOKEN if tx.to == Bytes0(b""): @@ -975,10 +882,9 @@ def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]: access_list_cost += TX_ACCESS_LIST_ADDRESS_COST access_list_cost += len(keys) * TX_ACCESS_LIST_STORAGE_KEY_COST - return ( - Uint(TX_BASE_COST + data_cost + create_cost + access_list_cost), - tokens_in_calldata, - ) + return Uint(TX_BASE_COST + + data_cost + + create_cost + access_list_cost), tokens_in_calldata def recover_sender(chain_id: U64, tx: Transaction) -> Address: