diff --git a/src/ethereum/cancun/fork.py b/src/ethereum/cancun/fork.py index ada6431cf3..673ecbbf26 100644 --- a/src/ethereum/cancun/fork.py +++ b/src/ethereum/cancun/fork.py @@ -15,7 +15,7 @@ from dataclasses import dataclass from typing import List, Optional, Tuple, Union -from ethereum_types.bytes import Bytes, Bytes0, Bytes32 +from ethereum_types.bytes import Bytes, Bytes32 from ethereum_types.numeric import U64, U256, Uint from ethereum.crypto.hash import Hash32, keccak256 @@ -48,6 +48,7 @@ decode_transaction, encode_transaction, recover_sender, + validate_transaction, ) from .trie import Trie, root, trie_set from .utils.hexadecimal import hex_to_address @@ -59,7 +60,7 @@ calculate_excess_blob_gas, calculate_total_blob_gas, ) -from .vm.interpreter import MAX_CODE_SIZE, process_message_call +from .vm.interpreter import process_message_call BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8) ELASTICITY_MULTIPLIER = Uint(2) @@ -366,18 +367,10 @@ def check_transaction( InvalidBlock : If the transaction is not includable. """ - if calculate_intrinsic_cost(tx) > tx.gas: - raise InvalidBlock - if tx.nonce >= U256(U64.MAX_VALUE): - raise InvalidBlock - if tx.to == Bytes0(b"") and len(tx.data) > 2 * MAX_CODE_SIZE: - raise InvalidBlock - if tx.gas > gas_available: raise InvalidBlock - - sender = recover_sender(chain_id, tx) - sender_account = get_account(state, sender) + sender_address = recover_sender(chain_id, tx) + sender_account = get_account(state, sender_address) if isinstance(tx, (FeeMarketTransaction, BlobTransaction)): if tx.max_fee_per_gas < tx.max_priority_fee_per_gas: @@ -423,7 +416,7 @@ def check_transaction( if sender_account.code != bytearray(): raise InvalidSenderError("not EOA") - return sender, effective_gas_price, blob_versioned_hashes + return sender_address, effective_gas_price, blob_versioned_hashes def make_receipt( @@ -730,6 +723,9 @@ def process_transaction( logs : `Tuple[ethereum.blocks.Log, ...]` Logs generated during execution. """ + if not validate_transaction(tx): + raise InvalidBlock + sender = env.origin sender_account = get_account(env.state, sender) diff --git a/src/ethereum/cancun/transactions.py b/src/ethereum/cancun/transactions.py index d1f7df04f7..a5bf79f2e9 100644 --- a/src/ethereum/cancun/transactions.py +++ b/src/ethereum/cancun/transactions.py @@ -149,6 +149,43 @@ def decode_transaction(tx: Union[LegacyTransaction, Bytes]) -> Transaction: return tx +def validate_transaction(tx: Transaction) -> bool: + """ + Verifies a transaction. + + The gas in a transaction gets used to pay for the intrinsic cost of + operations, therefore if there is insufficient gas then it would not + be possible to execute a transaction and it will be declared invalid. + + Additionally, the nonce of a transaction must not equal or exceed the + limit defined in `EIP-2681 `_. + In practice, defining the limit as ``2**64-1`` has no impact because + sending ``2**64-1`` transactions is improbable. It's not strictly + impossible though, ``2**64-1`` transactions is the entire capacity of the + Ethereum blockchain at 2022 gas limits for a little over 22 years. + + Parameters + ---------- + tx : + Transaction to validate. + + Returns + ------- + verified : `bool` + True if the transaction can be executed, or False otherwise. + """ + from .vm.interpreter import MAX_CODE_SIZE + + if calculate_intrinsic_cost(tx) > tx.gas: + return False + if tx.nonce >= U256(U64.MAX_VALUE): + return False + if tx.to == Bytes0(b"") and len(tx.data) > 2 * MAX_CODE_SIZE: + return False + + return True + + def calculate_intrinsic_cost(tx: Transaction) -> Uint: """ Calculates the gas that is charged before execution is started.