From f869cb2615f30b6abdec31633fa6203ae5d1f519 Mon Sep 17 00:00:00 2001 From: Davide Crapis Date: Wed, 11 Jun 2025 12:11:41 +0200 Subject: [PATCH 1/3] refactor gas pricing --- src/ethereum/prague/fork.py | 7 +- src/ethereum/prague/gas_pricing.py | 118 ++++++++++++++++++ src/ethereum/prague/vm/gas.py | 116 ++--------------- .../prague/vm/instructions/environment.py | 4 +- 4 files changed, 133 insertions(+), 112 deletions(-) create mode 100644 src/ethereum/prague/gas_pricing.py diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index 70f5578dd8..df50797891 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -67,12 +67,16 @@ from .vm import Message from .vm.eoa_delegation import is_valid_delegation from .vm.gas import ( + calculate_base_fee_per_gas, +) +from .vm.interpreter import MessageCallOutput, process_message_call +from .gas_pricing import ( calculate_blob_gas_price, calculate_data_fee, calculate_excess_blob_gas, calculate_total_blob_gas, + MAX_BLOB_GAS_PER_BLOCK, ) -from .vm.interpreter import MessageCallOutput, process_message_call BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8) ELASTICITY_MULTIPLIER = Uint(2) @@ -84,7 +88,6 @@ "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02" ) SYSTEM_TRANSACTION_GAS = Uint(30000000) -MAX_BLOB_GAS_PER_BLOCK = U64(1179648) VERSIONED_HASH_VERSION_KZG = b"\x01" WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address( diff --git a/src/ethereum/prague/gas_pricing.py b/src/ethereum/prague/gas_pricing.py new file mode 100644 index 0000000000..66889f55cf --- /dev/null +++ b/src/ethereum/prague/gas_pricing.py @@ -0,0 +1,118 @@ +""" +Gas Pricing Calculations +^^^^^^^^^^^^^^^^^^^^^^^ + +.. contents:: Table of Contents + :backlinks: none + :local: + +Introduction +------------ + +Gas pricing calculations for blocks and transactions. +""" + +from ethereum_types.numeric import U64, Uint + +from .blocks import Header +from .transactions import BlobTransaction, Transaction +from .utils.numeric import taylor_exponential + +# Blob gas constants +TARGET_BLOB_GAS_PER_BLOCK = U64(786432) +GAS_PER_BLOB = U64(2**17) +MIN_BLOB_GASPRICE = Uint(1) +BLOB_BASE_FEE_UPDATE_FRACTION = Uint(5007716) + + +def calculate_excess_blob_gas(parent_header: Header) -> U64: + """ + Calculated the excess blob gas for the current block based + on the gas used in the parent block. + + Parameters + ---------- + parent_header : + The parent block of the current block. + + Returns + ------- + excess_blob_gas: `ethereum.base_types.U64` + The excess blob gas for the current block. + """ + # At the fork block, these are defined as zero. + excess_blob_gas = U64(0) + blob_gas_used = U64(0) + + if isinstance(parent_header, Header): + # After the fork block, read them from the parent header. + excess_blob_gas = parent_header.excess_blob_gas + blob_gas_used = parent_header.blob_gas_used + + parent_blob_gas = excess_blob_gas + blob_gas_used + if parent_blob_gas < TARGET_BLOB_GAS_PER_BLOCK: + return U64(0) + else: + return parent_blob_gas - TARGET_BLOB_GAS_PER_BLOCK + + +def calculate_total_blob_gas(tx: Transaction) -> U64: + """ + Calculate the total blob gas for a transaction. + + Parameters + ---------- + tx : + The transaction for which the blob gas is to be calculated. + + Returns + ------- + total_blob_gas: `ethereum.base_types.Uint` + The total blob gas for the transaction. + """ + if isinstance(tx, BlobTransaction): + return GAS_PER_BLOB * U64(len(tx.blob_versioned_hashes)) + else: + return U64(0) + + +def calculate_blob_gas_price(excess_blob_gas: U64) -> Uint: + """ + Calculate the blob gasprice for a block. + + Parameters + ---------- + excess_blob_gas : + The excess blob gas for the block. + + Returns + ------- + blob_gasprice: `Uint` + The blob gasprice. + """ + return taylor_exponential( + MIN_BLOB_GASPRICE, + Uint(excess_blob_gas), + BLOB_BASE_FEE_UPDATE_FRACTION, + ) + + +def calculate_data_fee(excess_blob_gas: U64, tx: Transaction) -> Uint: + """ + Calculate the blob data fee for a transaction. + + Parameters + ---------- + excess_blob_gas : + The excess_blob_gas for the execution. + tx : + The transaction for which the blob data fee is to be calculated. + + Returns + ------- + data_fee: `Uint` + The blob data fee. + """ + return Uint(calculate_total_blob_gas(tx)) * calculate_blob_gas_price( + excess_blob_gas + ) \ No newline at end of file diff --git a/src/ethereum/prague/vm/gas.py b/src/ethereum/prague/vm/gas.py index b3a96ef970..dc931316ff 100644 --- a/src/ethereum/prague/vm/gas.py +++ b/src/ethereum/prague/vm/gas.py @@ -20,7 +20,7 @@ from ethereum.utils.numeric import ceil32, taylor_exponential from ..blocks import Header -from ..transactions import BlobTransaction, Transaction +from ..transactions import Transaction from . import Evm from .exceptions import OutOfGasError @@ -68,11 +68,6 @@ GAS_BLOBHASH_OPCODE = Uint(3) GAS_POINT_EVALUATION = Uint(50000) -TARGET_BLOB_GAS_PER_BLOCK = U64(786432) -GAS_PER_BLOB = U64(2**17) -MIN_BLOB_GASPRICE = Uint(1) -BLOB_BASE_FEE_UPDATE_FRACTION = Uint(5007716) - GAS_BLS_G1_ADD = Uint(375) GAS_BLS_G1_MUL = Uint(12000) GAS_BLS_G1_MAP = Uint(5500) @@ -258,113 +253,18 @@ def max_message_call_gas(gas: Uint) -> Uint: return gas - (gas // Uint(64)) -def init_code_cost(init_code_length: Uint) -> Uint: - """ - Calculates the gas to be charged for the init code in CREAT* - opcodes as well as create transactions. - - Parameters - ---------- - init_code_length : - The length of the init code provided to the opcode - or a create transaction - - Returns - ------- - init_code_gas: `ethereum.base_types.Uint` - The gas to be charged for the init code. - """ - return GAS_INIT_CODE_WORD_COST * ceil32(init_code_length) // Uint(32) - - -def calculate_excess_blob_gas(parent_header: Header) -> U64: - """ - Calculated the excess blob gas for the current block based - on the gas used in the parent block. - - Parameters - ---------- - parent_header : - The parent block of the current block. - - Returns - ------- - excess_blob_gas: `ethereum.base_types.U64` - The excess blob gas for the current block. - """ - # At the fork block, these are defined as zero. - excess_blob_gas = U64(0) - blob_gas_used = U64(0) - - if isinstance(parent_header, Header): - # After the fork block, read them from the parent header. - excess_blob_gas = parent_header.excess_blob_gas - blob_gas_used = parent_header.blob_gas_used - - parent_blob_gas = excess_blob_gas + blob_gas_used - if parent_blob_gas < TARGET_BLOB_GAS_PER_BLOCK: - return U64(0) - else: - return parent_blob_gas - TARGET_BLOB_GAS_PER_BLOCK - - -def calculate_total_blob_gas(tx: Transaction) -> U64: - """ - Calculate the total blob gas for a transaction. - - Parameters - ---------- - tx : - The transaction for which the blob gas is to be calculated. - - Returns - ------- - total_blob_gas: `ethereum.base_types.Uint` - The total blob gas for the transaction. - """ - if isinstance(tx, BlobTransaction): - return GAS_PER_BLOB * U64(len(tx.blob_versioned_hashes)) - else: - return U64(0) - - -def calculate_blob_gas_price(excess_blob_gas: U64) -> Uint: - """ - Calculate the blob gasprice for a block. - - Parameters - ---------- - excess_blob_gas : - The excess blob gas for the block. - - Returns - ------- - blob_gasprice: `Uint` - The blob gasprice. - """ - return taylor_exponential( - MIN_BLOB_GASPRICE, - Uint(excess_blob_gas), - BLOB_BASE_FEE_UPDATE_FRACTION, - ) - - -def calculate_data_fee(excess_blob_gas: U64, tx: Transaction) -> Uint: +def init_code_cost(length: Uint) -> Uint: """ - Calculate the blob data fee for a transaction. + Calculate the gas cost for init code. Parameters ---------- - excess_blob_gas : - The excess_blob_gas for the execution. - tx : - The transaction for which the blob data fee is to be calculated. + length : + The length of the init code. Returns ------- - data_fee: `Uint` - The blob data fee. + gas_cost : `ethereum.base_types.Uint` + The gas cost for the init code. """ - return Uint(calculate_total_blob_gas(tx)) * calculate_blob_gas_price( - excess_blob_gas - ) + return length * GAS_INIT_CODE_WORD_COST diff --git a/src/ethereum/prague/vm/instructions/environment.py b/src/ethereum/prague/vm/instructions/environment.py index 5ddd12dac8..e11043c197 100644 --- a/src/ethereum/prague/vm/instructions/environment.py +++ b/src/ethereum/prague/vm/instructions/environment.py @@ -22,6 +22,7 @@ from ...state import get_account from ...utils.address import to_address from ...vm.memory import buffer_read, memory_write +from ...gas_pricing import calculate_blob_gas_price from .. import Evm from ..exceptions import OutOfBoundsRead from ..gas import ( @@ -33,7 +34,6 @@ GAS_RETURN_DATA_COPY, GAS_VERY_LOW, GAS_WARM_ACCESS, - calculate_blob_gas_price, calculate_gas_extend_memory, charge_gas, ) @@ -460,7 +460,7 @@ def returndatacopy(evm: Evm) -> None: def extcodehash(evm: Evm) -> None: """ - Returns the keccak256 hash of a contract’s bytecode + Returns the keccak256 hash of a contract's bytecode Parameters ---------- evm : From b94d0fdc8747e94f6de3d9b8ec91f15d3d9b36f2 Mon Sep 17 00:00:00 2001 From: Davide Crapis Date: Tue, 8 Jul 2025 21:56:36 -0700 Subject: [PATCH 2/3] address comments --- src/ethereum/prague/{gas_pricing.py => blobs.py} | 7 ++++--- src/ethereum/prague/fork.py | 2 +- src/ethereum/prague/vm/gas.py | 16 +++++++++------- .../prague/vm/instructions/environment.py | 4 ++-- 4 files changed, 16 insertions(+), 13 deletions(-) rename src/ethereum/prague/{gas_pricing.py => blobs.py} (95%) diff --git a/src/ethereum/prague/gas_pricing.py b/src/ethereum/prague/blobs.py similarity index 95% rename from src/ethereum/prague/gas_pricing.py rename to src/ethereum/prague/blobs.py index 66889f55cf..814997e1a9 100644 --- a/src/ethereum/prague/gas_pricing.py +++ b/src/ethereum/prague/blobs.py @@ -1,6 +1,6 @@ """ -Gas Pricing Calculations -^^^^^^^^^^^^^^^^^^^^^^^ +Blob Gas Calculations +^^^^^^^^^^^^^^^^^^^^^ .. contents:: Table of Contents :backlinks: none @@ -9,7 +9,7 @@ Introduction ------------ -Gas pricing calculations for blocks and transactions. +Blob gas calculations for blocks and transactions. """ from ethereum_types.numeric import U64, Uint @@ -23,6 +23,7 @@ GAS_PER_BLOB = U64(2**17) MIN_BLOB_GASPRICE = Uint(1) BLOB_BASE_FEE_UPDATE_FRACTION = Uint(5007716) +MAX_BLOB_GAS_PER_BLOCK = U64(786432) def calculate_excess_blob_gas(parent_header: Header) -> U64: diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index df50797891..ace075afb5 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -70,7 +70,7 @@ calculate_base_fee_per_gas, ) from .vm.interpreter import MessageCallOutput, process_message_call -from .gas_pricing import ( +from .blobs import ( calculate_blob_gas_price, calculate_data_fee, calculate_excess_blob_gas, diff --git a/src/ethereum/prague/vm/gas.py b/src/ethereum/prague/vm/gas.py index dc931316ff..90ab6c227f 100644 --- a/src/ethereum/prague/vm/gas.py +++ b/src/ethereum/prague/vm/gas.py @@ -253,18 +253,20 @@ def max_message_call_gas(gas: Uint) -> Uint: return gas - (gas // Uint(64)) -def init_code_cost(length: Uint) -> Uint: +def init_code_cost(init_code_length: Uint) -> Uint: """ - Calculate the gas cost for init code. + Calculates the gas to be charged for the init code in CREAT* + opcodes as well as create transactions. Parameters ---------- - length : - The length of the init code. + init_code_length : + The length of the init code provided to the opcode + or a create transaction Returns ------- - gas_cost : `ethereum.base_types.Uint` - The gas cost for the init code. + init_code_gas: `ethereum.base_types.Uint` + The gas to be charged for the init code. """ - return length * GAS_INIT_CODE_WORD_COST + return GAS_INIT_CODE_WORD_COST * ceil32(init_code_length) // Uint(32) diff --git a/src/ethereum/prague/vm/instructions/environment.py b/src/ethereum/prague/vm/instructions/environment.py index e11043c197..a584583673 100644 --- a/src/ethereum/prague/vm/instructions/environment.py +++ b/src/ethereum/prague/vm/instructions/environment.py @@ -22,7 +22,7 @@ from ...state import get_account from ...utils.address import to_address from ...vm.memory import buffer_read, memory_write -from ...gas_pricing import calculate_blob_gas_price +from ...blobs import calculate_blob_gas_price from .. import Evm from ..exceptions import OutOfBoundsRead from ..gas import ( @@ -460,7 +460,7 @@ def returndatacopy(evm: Evm) -> None: def extcodehash(evm: Evm) -> None: """ - Returns the keccak256 hash of a contract's bytecode + Returns the keccak256 hash of a contracts bytecode Parameters ---------- evm : From e0dc47ed416cdcacade66d3b62888778a7ca85ab Mon Sep 17 00:00:00 2001 From: Davide Crapis Date: Tue, 8 Jul 2025 23:00:15 -0700 Subject: [PATCH 3/3] fix static tests --- src/ethereum/prague/blobs.py | 4 ++-- src/ethereum/prague/fork.py | 17 +++++++---------- src/ethereum/prague/vm/gas.py | 6 ++---- .../prague/vm/instructions/environment.py | 2 +- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/ethereum/prague/blobs.py b/src/ethereum/prague/blobs.py index 814997e1a9..78d78615fc 100644 --- a/src/ethereum/prague/blobs.py +++ b/src/ethereum/prague/blobs.py @@ -14,9 +14,9 @@ from ethereum_types.numeric import U64, Uint +from ..utils.numeric import taylor_exponential from .blocks import Header from .transactions import BlobTransaction, Transaction -from .utils.numeric import taylor_exponential # Blob gas constants TARGET_BLOB_GAS_PER_BLOCK = U64(786432) @@ -116,4 +116,4 @@ def calculate_data_fee(excess_blob_gas: U64, tx: Transaction) -> Uint: """ return Uint(calculate_total_blob_gas(tx)) * calculate_blob_gas_price( excess_blob_gas - ) \ No newline at end of file + ) diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index ace075afb5..40d62a6a0f 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -27,6 +27,13 @@ ) from . import vm +from .blobs import ( + MAX_BLOB_GAS_PER_BLOCK, + calculate_blob_gas_price, + calculate_data_fee, + calculate_excess_blob_gas, + calculate_total_blob_gas, +) from .blocks import Block, Header, Log, Receipt, Withdrawal, encode_receipt from .bloom import logs_bloom from .fork_types import Account, Address, Authorization, VersionedHash @@ -66,17 +73,7 @@ from .utils.message import prepare_message from .vm import Message from .vm.eoa_delegation import is_valid_delegation -from .vm.gas import ( - calculate_base_fee_per_gas, -) from .vm.interpreter import MessageCallOutput, process_message_call -from .blobs import ( - calculate_blob_gas_price, - calculate_data_fee, - calculate_excess_blob_gas, - calculate_total_blob_gas, - MAX_BLOB_GAS_PER_BLOCK, -) BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8) ELASTICITY_MULTIPLIER = Uint(2) diff --git a/src/ethereum/prague/vm/gas.py b/src/ethereum/prague/vm/gas.py index 90ab6c227f..798d1b73a7 100644 --- a/src/ethereum/prague/vm/gas.py +++ b/src/ethereum/prague/vm/gas.py @@ -14,13 +14,11 @@ from dataclasses import dataclass from typing import List, Tuple -from ethereum_types.numeric import U64, U256, Uint +from ethereum_types.numeric import U256, Uint from ethereum.trace import GasAndRefund, evm_trace -from ethereum.utils.numeric import ceil32, taylor_exponential +from ethereum.utils.numeric import ceil32 -from ..blocks import Header -from ..transactions import Transaction from . import Evm from .exceptions import OutOfGasError diff --git a/src/ethereum/prague/vm/instructions/environment.py b/src/ethereum/prague/vm/instructions/environment.py index a584583673..7dc5fff686 100644 --- a/src/ethereum/prague/vm/instructions/environment.py +++ b/src/ethereum/prague/vm/instructions/environment.py @@ -18,11 +18,11 @@ from ethereum.crypto.hash import keccak256 from ethereum.utils.numeric import ceil32 +from ...blobs import calculate_blob_gas_price from ...fork_types import EMPTY_ACCOUNT from ...state import get_account from ...utils.address import to_address from ...vm.memory import buffer_read, memory_write -from ...blobs import calculate_blob_gas_price from .. import Evm from ..exceptions import OutOfBoundsRead from ..gas import (