From 3adbb4e542f6193d82d0601dd4de1fa3d2ba5f53 Mon Sep 17 00:00:00 2001 From: Ezequiel Perez Date: Tue, 29 Jul 2025 17:33:56 -0300 Subject: [PATCH 1/4] fix: redundant account destruction after state modification --- src/ethereum/prague/fork.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index 815926feb1..d126572cc1 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -946,14 +946,11 @@ def process_transaction( coinbase_balance_after_mining_fee = get_account( block_env.state, block_env.coinbase ).balance + U256(transaction_fee) - if coinbase_balance_after_mining_fee != 0: - set_account_balance( - block_env.state, - block_env.coinbase, - coinbase_balance_after_mining_fee, - ) - elif account_exists_and_is_empty(block_env.state, block_env.coinbase): - destroy_account(block_env.state, block_env.coinbase) + set_account_balance( + block_env.state, + block_env.coinbase, + coinbase_balance_after_mining_fee, + ) for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) @@ -998,9 +995,6 @@ def increase_recipient_balance(recipient: Account) -> None: modify_state(block_env.state, wd.address, increase_recipient_balance) - if account_exists_and_is_empty(block_env.state, wd.address): - destroy_account(block_env.state, wd.address) - def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool: """ From a9ce06cdbfe2060b7aaa91d1d6f05f2a7bdee683 Mon Sep 17 00:00:00 2001 From: Ezequiel Perez Date: Tue, 29 Jul 2025 17:33:56 -0300 Subject: [PATCH 2/4] fix: redundant account destruction after state modification --- src/ethereum/prague/fork.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index 14b9b401a7..bcb74b0432 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -945,14 +945,11 @@ def process_transaction( coinbase_balance_after_mining_fee = get_account( block_env.state, block_env.coinbase ).balance + U256(transaction_fee) - if coinbase_balance_after_mining_fee != 0: - set_account_balance( - block_env.state, - block_env.coinbase, - coinbase_balance_after_mining_fee, - ) - elif account_exists_and_is_empty(block_env.state, block_env.coinbase): - destroy_account(block_env.state, block_env.coinbase) + set_account_balance( + block_env.state, + block_env.coinbase, + coinbase_balance_after_mining_fee, + ) for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) @@ -997,9 +994,6 @@ def increase_recipient_balance(recipient: Account) -> None: modify_state(block_env.state, wd.address, increase_recipient_balance) - if account_exists_and_is_empty(block_env.state, wd.address): - destroy_account(block_env.state, wd.address) - def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool: """ From 1311b2ed047f81f03239d18549e1a9004e666fbb Mon Sep 17 00:00:00 2001 From: Ezequiel Perez Date: Sun, 31 Aug 2025 00:00:36 -0300 Subject: [PATCH 3/4] fix(paris:osaka): redundant account destruction after state modification --- src/ethereum/cancun/fork.py | 17 +++++------------ src/ethereum/osaka/fork.py | 17 +++++------------ src/ethereum/paris/fork.py | 14 +++++--------- src/ethereum/prague/fork.py | 1 - src/ethereum/shanghai/fork.py | 17 +++++------------ 5 files changed, 20 insertions(+), 46 deletions(-) diff --git a/src/ethereum/cancun/fork.py b/src/ethereum/cancun/fork.py index 56b53b7ebf..1f8dc3e102 100644 --- a/src/ethereum/cancun/fork.py +++ b/src/ethereum/cancun/fork.py @@ -45,7 +45,6 @@ from .state import ( State, TransientStorage, - account_exists_and_is_empty, destroy_account, get_account, increment_nonce, @@ -784,14 +783,11 @@ def process_transaction( coinbase_balance_after_mining_fee = get_account( block_env.state, block_env.coinbase ).balance + U256(transaction_fee) - if coinbase_balance_after_mining_fee != 0: - set_account_balance( - block_env.state, - block_env.coinbase, - coinbase_balance_after_mining_fee, - ) - elif account_exists_and_is_empty(block_env.state, block_env.coinbase): - destroy_account(block_env.state, block_env.coinbase) + set_account_balance( + block_env.state, + block_env.coinbase, + coinbase_balance_after_mining_fee, + ) for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) @@ -836,9 +832,6 @@ def increase_recipient_balance(recipient: Account) -> None: modify_state(block_env.state, wd.address, increase_recipient_balance) - if account_exists_and_is_empty(block_env.state, wd.address): - destroy_account(block_env.state, wd.address) - def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool: """ diff --git a/src/ethereum/osaka/fork.py b/src/ethereum/osaka/fork.py index 22c07e0ae8..f44accb968 100644 --- a/src/ethereum/osaka/fork.py +++ b/src/ethereum/osaka/fork.py @@ -54,7 +54,6 @@ from .state import ( State, TransientStorage, - account_exists_and_is_empty, destroy_account, get_account, increment_nonce, @@ -960,14 +959,11 @@ def process_transaction( coinbase_balance_after_mining_fee = get_account( block_env.state, block_env.coinbase ).balance + U256(transaction_fee) - if coinbase_balance_after_mining_fee != 0: - set_account_balance( - block_env.state, - block_env.coinbase, - coinbase_balance_after_mining_fee, - ) - elif account_exists_and_is_empty(block_env.state, block_env.coinbase): - destroy_account(block_env.state, block_env.coinbase) + set_account_balance( + block_env.state, + block_env.coinbase, + coinbase_balance_after_mining_fee, + ) for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) @@ -1012,9 +1008,6 @@ def increase_recipient_balance(recipient: Account) -> None: modify_state(block_env.state, wd.address, increase_recipient_balance) - if account_exists_and_is_empty(block_env.state, wd.address): - destroy_account(block_env.state, wd.address) - def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool: """ diff --git a/src/ethereum/paris/fork.py b/src/ethereum/paris/fork.py index 2651441b9b..6337daeaaa 100644 --- a/src/ethereum/paris/fork.py +++ b/src/ethereum/paris/fork.py @@ -39,7 +39,6 @@ from .fork_types import Address from .state import ( State, - account_exists_and_is_empty, destroy_account, get_account, increment_nonce, @@ -578,14 +577,11 @@ def process_transaction( coinbase_balance_after_mining_fee = get_account( block_env.state, block_env.coinbase ).balance + U256(transaction_fee) - if coinbase_balance_after_mining_fee != 0: - set_account_balance( - block_env.state, - block_env.coinbase, - coinbase_balance_after_mining_fee, - ) - elif account_exists_and_is_empty(block_env.state, block_env.coinbase): - destroy_account(block_env.state, block_env.coinbase) + set_account_balance( + block_env.state, + block_env.coinbase, + coinbase_balance_after_mining_fee, + ) for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) diff --git a/src/ethereum/prague/fork.py b/src/ethereum/prague/fork.py index bcb74b0432..2129c16baf 100644 --- a/src/ethereum/prague/fork.py +++ b/src/ethereum/prague/fork.py @@ -53,7 +53,6 @@ from .state import ( State, TransientStorage, - account_exists_and_is_empty, destroy_account, get_account, increment_nonce, diff --git a/src/ethereum/shanghai/fork.py b/src/ethereum/shanghai/fork.py index e854502873..1cd59aa314 100644 --- a/src/ethereum/shanghai/fork.py +++ b/src/ethereum/shanghai/fork.py @@ -39,7 +39,6 @@ from .fork_types import Account, Address from .state import ( State, - account_exists_and_is_empty, destroy_account, get_account, increment_nonce, @@ -591,14 +590,11 @@ def process_transaction( coinbase_balance_after_mining_fee = get_account( block_env.state, block_env.coinbase ).balance + U256(transaction_fee) - if coinbase_balance_after_mining_fee != 0: - set_account_balance( - block_env.state, - block_env.coinbase, - coinbase_balance_after_mining_fee, - ) - elif account_exists_and_is_empty(block_env.state, block_env.coinbase): - destroy_account(block_env.state, block_env.coinbase) + set_account_balance( + block_env.state, + block_env.coinbase, + coinbase_balance_after_mining_fee, + ) for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) @@ -642,9 +638,6 @@ def increase_recipient_balance(recipient: Account) -> None: modify_state(block_env.state, wd.address, increase_recipient_balance) - if account_exists_and_is_empty(block_env.state, wd.address): - destroy_account(block_env.state, wd.address) - def check_gas_limit(gas_limit: Uint, parent_gas_limit: Uint) -> bool: """ From 07d945df4cb8fe15bdffeab76efbe2e380c9ef62 Mon Sep 17 00:00:00 2001 From: Ezequiel Perez Date: Sun, 31 Aug 2025 02:58:29 -0300 Subject: [PATCH 4/4] chore(paris:osaka): inline account_exists_and_is_empty function --- src/ethereum/cancun/state.py | 41 ++++++++++------------------------ src/ethereum/osaka/state.py | 41 ++++++++++------------------------ src/ethereum/paris/state.py | 41 ++++++++++------------------------ src/ethereum/prague/state.py | 41 ++++++++++------------------------ src/ethereum/shanghai/state.py | 41 ++++++++++------------------------ 5 files changed, 60 insertions(+), 145 deletions(-) diff --git a/src/ethereum/cancun/state.py b/src/ethereum/cancun/state.py index b067ed2286..f144079c2b 100644 --- a/src/ethereum/cancun/state.py +++ b/src/ethereum/cancun/state.py @@ -425,33 +425,6 @@ def account_has_storage(state: State, address: Address) -> bool: return address in state._storage_tries -def account_exists_and_is_empty(state: State, address: Address) -> bool: - """ - Checks if an account exists and has zero nonce, empty code and zero - balance. - - Parameters - ---------- - state: - The state - address: - Address of the account that needs to be checked. - - Returns - ------- - exists_and_is_empty : `bool` - True if an account exists and has zero nonce, empty code and zero - balance, False otherwise. - """ - account = get_account_optional(state, address) - return ( - account is not None - and account.nonce == Uint(0) - and account.code == b"" - and account.balance == 0 - ) - - def is_account_alive(state: State, address: Address) -> bool: """ Check whether is an account is both in the state and non empty. @@ -476,10 +449,20 @@ def modify_state( state: State, address: Address, f: Callable[[Account], None] ) -> None: """ - Modify an `Account` in the `State`. + Modify an `Account` in the `State`. If, after modification, the account + exists and has zero nonce, empty code, and zero balance, it is destroyed. """ set_account(state, address, modify(get_account(state, address), f)) - if account_exists_and_is_empty(state, address): + + account = get_account_optional(state, address) + account_exists_and_is_empty = ( + account is not None + and account.nonce == Uint(0) + and account.code == b"" + and account.balance == 0 + ) + + if account_exists_and_is_empty: destroy_account(state, address) diff --git a/src/ethereum/osaka/state.py b/src/ethereum/osaka/state.py index b067ed2286..f144079c2b 100644 --- a/src/ethereum/osaka/state.py +++ b/src/ethereum/osaka/state.py @@ -425,33 +425,6 @@ def account_has_storage(state: State, address: Address) -> bool: return address in state._storage_tries -def account_exists_and_is_empty(state: State, address: Address) -> bool: - """ - Checks if an account exists and has zero nonce, empty code and zero - balance. - - Parameters - ---------- - state: - The state - address: - Address of the account that needs to be checked. - - Returns - ------- - exists_and_is_empty : `bool` - True if an account exists and has zero nonce, empty code and zero - balance, False otherwise. - """ - account = get_account_optional(state, address) - return ( - account is not None - and account.nonce == Uint(0) - and account.code == b"" - and account.balance == 0 - ) - - def is_account_alive(state: State, address: Address) -> bool: """ Check whether is an account is both in the state and non empty. @@ -476,10 +449,20 @@ def modify_state( state: State, address: Address, f: Callable[[Account], None] ) -> None: """ - Modify an `Account` in the `State`. + Modify an `Account` in the `State`. If, after modification, the account + exists and has zero nonce, empty code, and zero balance, it is destroyed. """ set_account(state, address, modify(get_account(state, address), f)) - if account_exists_and_is_empty(state, address): + + account = get_account_optional(state, address) + account_exists_and_is_empty = ( + account is not None + and account.nonce == Uint(0) + and account.code == b"" + and account.balance == 0 + ) + + if account_exists_and_is_empty: destroy_account(state, address) diff --git a/src/ethereum/paris/state.py b/src/ethereum/paris/state.py index bc80ff282e..d38c5130d7 100644 --- a/src/ethereum/paris/state.py +++ b/src/ethereum/paris/state.py @@ -391,33 +391,6 @@ def account_has_storage(state: State, address: Address) -> bool: return address in state._storage_tries -def account_exists_and_is_empty(state: State, address: Address) -> bool: - """ - Checks if an account exists and has zero nonce, empty code and zero - balance. - - Parameters - ---------- - state: - The state - address: - Address of the account that needs to be checked. - - Returns - ------- - exists_and_is_empty : `bool` - True if an account exists and has zero nonce, empty code and zero - balance, False otherwise. - """ - account = get_account_optional(state, address) - return ( - account is not None - and account.nonce == Uint(0) - and account.code == b"" - and account.balance == 0 - ) - - def is_account_alive(state: State, address: Address) -> bool: """ Check whether is an account is both in the state and non empty. @@ -442,10 +415,20 @@ def modify_state( state: State, address: Address, f: Callable[[Account], None] ) -> None: """ - Modify an `Account` in the `State`. + Modify an `Account` in the `State`. If, after modification, the account + exists and has zero nonce, empty code, and zero balance, it is destroyed. """ set_account(state, address, modify(get_account(state, address), f)) - if account_exists_and_is_empty(state, address): + + account = get_account_optional(state, address) + account_exists_and_is_empty = ( + account is not None + and account.nonce == Uint(0) + and account.code == b"" + and account.balance == 0 + ) + + if account_exists_and_is_empty: destroy_account(state, address) diff --git a/src/ethereum/prague/state.py b/src/ethereum/prague/state.py index b067ed2286..f144079c2b 100644 --- a/src/ethereum/prague/state.py +++ b/src/ethereum/prague/state.py @@ -425,33 +425,6 @@ def account_has_storage(state: State, address: Address) -> bool: return address in state._storage_tries -def account_exists_and_is_empty(state: State, address: Address) -> bool: - """ - Checks if an account exists and has zero nonce, empty code and zero - balance. - - Parameters - ---------- - state: - The state - address: - Address of the account that needs to be checked. - - Returns - ------- - exists_and_is_empty : `bool` - True if an account exists and has zero nonce, empty code and zero - balance, False otherwise. - """ - account = get_account_optional(state, address) - return ( - account is not None - and account.nonce == Uint(0) - and account.code == b"" - and account.balance == 0 - ) - - def is_account_alive(state: State, address: Address) -> bool: """ Check whether is an account is both in the state and non empty. @@ -476,10 +449,20 @@ def modify_state( state: State, address: Address, f: Callable[[Account], None] ) -> None: """ - Modify an `Account` in the `State`. + Modify an `Account` in the `State`. If, after modification, the account + exists and has zero nonce, empty code, and zero balance, it is destroyed. """ set_account(state, address, modify(get_account(state, address), f)) - if account_exists_and_is_empty(state, address): + + account = get_account_optional(state, address) + account_exists_and_is_empty = ( + account is not None + and account.nonce == Uint(0) + and account.code == b"" + and account.balance == 0 + ) + + if account_exists_and_is_empty: destroy_account(state, address) diff --git a/src/ethereum/shanghai/state.py b/src/ethereum/shanghai/state.py index bc80ff282e..d38c5130d7 100644 --- a/src/ethereum/shanghai/state.py +++ b/src/ethereum/shanghai/state.py @@ -391,33 +391,6 @@ def account_has_storage(state: State, address: Address) -> bool: return address in state._storage_tries -def account_exists_and_is_empty(state: State, address: Address) -> bool: - """ - Checks if an account exists and has zero nonce, empty code and zero - balance. - - Parameters - ---------- - state: - The state - address: - Address of the account that needs to be checked. - - Returns - ------- - exists_and_is_empty : `bool` - True if an account exists and has zero nonce, empty code and zero - balance, False otherwise. - """ - account = get_account_optional(state, address) - return ( - account is not None - and account.nonce == Uint(0) - and account.code == b"" - and account.balance == 0 - ) - - def is_account_alive(state: State, address: Address) -> bool: """ Check whether is an account is both in the state and non empty. @@ -442,10 +415,20 @@ def modify_state( state: State, address: Address, f: Callable[[Account], None] ) -> None: """ - Modify an `Account` in the `State`. + Modify an `Account` in the `State`. If, after modification, the account + exists and has zero nonce, empty code, and zero balance, it is destroyed. """ set_account(state, address, modify(get_account(state, address), f)) - if account_exists_and_is_empty(state, address): + + account = get_account_optional(state, address) + account_exists_and_is_empty = ( + account is not None + and account.nonce == Uint(0) + and account.code == b"" + and account.balance == 0 + ) + + if account_exists_and_is_empty: destroy_account(state, address)