diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index f0c772fd21..ae4999ad16 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -45,12 +45,12 @@ state_root, ) from .transactions import ( + FLOOR_CALLDATA_COST, + STANDARD_CALLDATA_TOKEN_COST, TX_ACCESS_LIST_ADDRESS_COST, TX_ACCESS_LIST_STORAGE_KEY_COST, TX_BASE_COST, TX_CREATE_COST, - TX_DATA_COST_PER_NON_ZERO, - TX_DATA_COST_PER_ZERO, AccessListTransaction, BlobTransaction, FeeMarketTransaction, @@ -395,7 +395,9 @@ def check_transaction( InvalidBlock : If the transaction is not includable. """ - if calculate_intrinsic_cost(tx) > tx.gas: + intrinsic_gas, tokens_in_calldata = calculate_intrinsic_cost(tx) + floor = Uint(tokens_in_calldata * FLOOR_CALLDATA_COST + TX_BASE_COST) + if max(intrinsic_gas, floor) > tx.gas: raise InvalidBlock if tx.nonce >= 2**64 - 1: raise InvalidBlock @@ -921,7 +923,9 @@ def process_transaction( effective_gas_fee = tx.gas * env.gas_price - gas = tx.gas - calculate_intrinsic_cost(tx) + intrinsic_gas, tokens_in_calldata = calculate_intrinsic_cost(tx) + + gas = tx.gas - intrinsic_gas increment_nonce(env.state, sender) sender_balance_after_gas_fee = ( @@ -964,7 +968,18 @@ def process_transaction( output = process_message_call(message, env) + # For EIP-7623 gas_used includes the intrinsic costs with the floor price + # for calldata. gas_used = tx.gas - output.gas_left + + # EIP-7623 floor price (note: no EVM costs) + + # Transactions with less gas used than the floor pay at the floor cost. + if gas_used < floor: + gas_used = floor + + output.gas_left = tx.gas - gas_used + gas_refund = min(gas_used // 5, output.refund_counter) gas_refund_amount = (output.gas_left + gas_refund) * env.gas_price @@ -1001,7 +1016,7 @@ def process_transaction( return total_gas_used, output.logs, output.error -def calculate_intrinsic_cost(tx: Transaction) -> Uint: +def calculate_intrinsic_cost(tx: Transaction) -> Tuple[Uint, Uint]: """ Calculates the gas that is charged before execution is started. @@ -1023,14 +1038,19 @@ def calculate_intrinsic_cost(tx: Transaction) -> Uint: ------- verified : `ethereum.base_types.Uint` The intrinsic cost of the transaction. + tokens_in_calldata : `ethereum.base_types.Uint` + The eip-7623 calldata tokens used by the transaction. """ data_cost = 0 + zero_bytes = 0 for byte in tx.data: if byte == 0: - data_cost += TX_DATA_COST_PER_ZERO - else: - data_cost += TX_DATA_COST_PER_NON_ZERO + zero_bytes += 1 + + tokens_in_calldata = zero_bytes + (len(tx.data) - zero_bytes) * 4 + + data_cost = tokens_in_calldata * STANDARD_CALLDATA_TOKEN_COST if tx.to == Bytes0(b""): create_cost = TX_CREATE_COST + int(init_code_cost(Uint(len(tx.data)))) @@ -1055,8 +1075,15 @@ def calculate_intrinsic_cost(tx: Transaction) -> Uint: if isinstance(tx, SetCodeTransaction): auth_cost += PER_EMPTY_ACCOUNT_COST * len(tx.authorizations) - return Uint( - TX_BASE_COST + data_cost + create_cost + access_list_cost + auth_cost + return ( + Uint( + TX_BASE_COST + + data_cost + + create_cost + + access_list_cost + + auth_cost + ), + Uint(tokens_in_calldata), ) diff --git a/src/ethereum/prague/transactions.py b/src/ethereum/prague/transactions.py index a6ca805b96..5778cc3a5d 100644 --- a/src/ethereum/prague/transactions.py +++ b/src/ethereum/prague/transactions.py @@ -21,8 +21,8 @@ from .fork_types import Address, Authorization, VersionedHash TX_BASE_COST = 21000 -TX_DATA_COST_PER_NON_ZERO = 16 -TX_DATA_COST_PER_ZERO = 4 +FLOOR_CALLDATA_COST = 10 +STANDARD_CALLDATA_TOKEN_COST = 4 TX_CREATE_COST = 32000 TX_ACCESS_LIST_ADDRESS_COST = 2400 TX_ACCESS_LIST_STORAGE_KEY_COST = 1900 diff --git a/whitelist.txt b/whitelist.txt index f309ebbb8f..ccc117c05d 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -12,12 +12,14 @@ Bytes64 Bytes8 Bytes256 Bytes0 +calldata copyreg copytree coinbase coincurve crypto E501 +EIP encodings endian eth @@ -459,4 +461,4 @@ exponentiate monomial impl -eoa \ No newline at end of file +eoa