From 463beba15c6e220e78b3e9bd02dc30fdd2c419d6 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 12 May 2026 05:51:37 +0200 Subject: [PATCH 01/11] (Claude): add eth transfer cases for repricing --- .../scenario/test_transaction_types.py | 99 ++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/compute/scenario/test_transaction_types.py b/tests/benchmark/compute/scenario/test_transaction_types.py index 5d4c3d1b00..781d8c067b 100644 --- a/tests/benchmark/compute/scenario/test_transaction_types.py +++ b/tests/benchmark/compute/scenario/test_transaction_types.py @@ -19,6 +19,7 @@ Op, Transaction, compute_create_address, + keccak256, ) @@ -73,6 +74,61 @@ def get_single_receiver_list( yield receiver +# Bittrex Controller: created 1,586,350 contracts on mainnet that cannot +# selfdestruct, so they are guaranteed to be on-chain. Safe for benchmarks +# up to ~300M gas (at 2000 gas per cold address). +BITTREX_CONTROLLER_ADDRESS = Address( + 0xA3C1E324CA1CE40DB73ED6026C4A177F099B5770 +) + + +def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: + """ + Yield addresses of contracts created by the Bittrex Controller, + starting at nonce 1. + """ + nonce = 1 + while True: + yield compute_create_address( + address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce + ) + nonce += 1 + + +def get_distinct_existent_receiver_list() -> Generator[Address, None, None]: + """ + Yield sequential EOA addresses starting at 0x1000. + + The Spamoor EOA creator + (https://github.com/CPerezz/spamoor/pull/12) funded these addresses + on bloatnet. + """ + address = 0x1000 + while True: + yield Address(address) + address += 1 + + +def get_distinct_nonexistent_receiver_list() -> Generator[ + Address, None, None +]: + """Yield sequential addresses starting at keccak256("random").""" + address = int.from_bytes(keccak256(b"random")[-20:], "big") + while True: + yield Address(address) + address += 1 + + +# Cases where the receiver address is determined by case_id rather than +# being constructed in pre-state. The receiver_account_type parametrization +# is not multiplied with these. +RECEIVER_TYPED_CASES: set[str] = { + "diff_to_nonexistent", + "diff_to_existent", + "diff_to_contract", +} + + @dataclass(frozen=True) class ReceiverAccountType: """Receiver account type for ether transfer benchmarks.""" @@ -120,6 +176,21 @@ def ether_transfer_case( senders = get_distinct_sender_list(pre) receivers = get_distinct_receiver_list(pre, balance, delegation) + elif case_id == "diff_to_nonexistent": + """Multiple senders → distinct nonexistent receivers.""" + senders = get_distinct_sender_list(pre) + receivers = get_distinct_nonexistent_receiver_list() + + elif case_id == "diff_to_existent": + """Multiple senders → distinct existent EOA receivers.""" + senders = get_distinct_sender_list(pre) + receivers = get_distinct_existent_receiver_list() + + elif case_id == "diff_to_contract": + """Multiple senders → distinct contract receivers.""" + senders = get_distinct_sender_list(pre) + receivers = get_distinct_contract_receiver_list() + else: raise ValueError(f"Unknown case: {case_id}") @@ -134,6 +205,9 @@ def ether_transfer_case( "diff_acc_to_b", "a_to_diff_acc", "diff_acc_to_diff_acc", + "diff_to_nonexistent", + "diff_to_existent", + "diff_to_contract", ], ) @pytest.mark.parametrize("transfer_amount", [0, 1]) @@ -177,13 +251,34 @@ def test_ether_transfers( - diff_acc_to_b: multiple senders → one receiver - a_to_diff_acc: one sender → multiple receivers - diff_acc_to_diff_acc: multiple senders → multiple receivers + - diff_to_nonexistent: multiple senders → distinct nonexistent + receivers (matches AccountMode.NON_EXISTING_ACCOUNT) + - diff_to_existent: multiple senders → distinct existent EOA + receivers (matches AccountMode.EXISTING_EOA) + - diff_to_contract: multiple senders → distinct contract receivers + (matches AccountMode.EXISTING_CONTRACT) When warm_access is True, each transaction includes an access list entry for the receiver to warm the account before the transfer. """ - senders, receivers = ether_transfer_case + if case_id in RECEIVER_TYPED_CASES: + # Receiver address is determined by case_id; avoid multiplying with + # receiver_account_type and only run for the default variant. + if receiver_account_type != ReceiverAccountType( + balance=0, delegated=False + ): + pytest.skip( + "Receiver address is determined by case_id; skipping " + "non-default receiver_account_type." + ) + # Receivers are not allocated in pre-state during fill (they are + # expected to already exist on-chain at run time, e.g. on bloatnet), + # so their initial balance during fill is 0. + balance = 0 + else: + balance = receiver_account_type.balance - balance = receiver_account_type.balance + senders, receivers = ether_transfer_case txs = [] token_transfers: dict[Address, int] = {} From 10f88bc26ea8cd3d3cea752de130b04cbfde8842 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Tue, 12 May 2026 13:28:58 +0800 Subject: [PATCH 02/11] refactor: split test based on pre-alloc --- .../scenario/test_transaction_types.py | 322 +++++++++--------- 1 file changed, 156 insertions(+), 166 deletions(-) diff --git a/tests/benchmark/compute/scenario/test_transaction_types.py b/tests/benchmark/compute/scenario/test_transaction_types.py index 781d8c067b..e0e7bae255 100644 --- a/tests/benchmark/compute/scenario/test_transaction_types.py +++ b/tests/benchmark/compute/scenario/test_transaction_types.py @@ -1,9 +1,10 @@ """Benchmark different transaction types.""" +import itertools import math import random from dataclasses import dataclass -from typing import Generator, List, Tuple +from typing import Generator, List import pytest from execution_testing import ( @@ -19,7 +20,6 @@ Op, Transaction, compute_create_address, - keccak256, ) @@ -74,59 +74,36 @@ def get_single_receiver_list( yield receiver -# Bittrex Controller: created 1,586,350 contracts on mainnet that cannot -# selfdestruct, so they are guaranteed to be on-chain. Safe for benchmarks -# up to ~300M gas (at 2000 gas per cold address). +# Bitterex controller mainnet address +# Creates 1.5M contracts with deterministic address via CREATE +# It is guaranteed no contract is destructed +# Used for existing contract targets in benchmark BITTREX_CONTROLLER_ADDRESS = Address( 0xA3C1E324CA1CE40DB73ED6026C4A177F099B5770 ) def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: - """ - Yield addresses of contracts created by the Bittrex Controller, - starting at nonce 1. - """ - nonce = 1 - while True: + """Yield contract account created by Bitterex controller via CREATE.""" + for nonce in itertools.count(1): yield compute_create_address( address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce ) - nonce += 1 def get_distinct_existent_receiver_list() -> Generator[Address, None, None]: """ - Yield sequential EOA addresses starting at 0x1000. - - The Spamoor EOA creator - (https://github.com/CPerezz/spamoor/pull/12) funded these addresses - on bloatnet. + Yield existing balance-only EOA on bloatnet. pre-funded by Spamoor + (https://github.com/CPerezz/spamoor/pull/12). """ - address = 0x1000 - while True: + for address in itertools.count(0x1000): yield Address(address) - address += 1 -def get_distinct_nonexistent_receiver_list() -> Generator[ - Address, None, None -]: - """Yield sequential addresses starting at keccak256("random").""" - address = int.from_bytes(keccak256(b"random")[-20:], "big") - while True: +def get_distinct_nonexistent_receiver_list() -> Generator[Address, None, None]: + """Yield non-existent accounts starting from keccak256('random').""" + for address in itertools.count(0xF3CF193BB4AF1022AF7D2089F37D8BAE7157B85F): yield Address(address) - address += 1 - - -# Cases where the receiver address is determined by case_id rather than -# being constructed in pre-state. The receiver_account_type parametrization -# is not multiplied with these. -RECEIVER_TYPED_CASES: set[str] = { - "diff_to_nonexistent", - "diff_to_existent", - "diff_to_contract", -} @dataclass(frozen=True) @@ -137,64 +114,68 @@ class ReceiverAccountType: delegated: bool -@pytest.fixture -def ether_transfer_case( - case_id: str, +def _run_ether_transfer_benchmark( + benchmark_test: BenchmarkTestFiller, pre: Alloc, - receiver_account_type: ReceiverAccountType, -) -> Tuple[Generator[Address, None, None], Generator[Address, None, None]]: - """Generate sender and receiver generators based on the test case.""" - balance = receiver_account_type.balance - delegation = ( - pre.deploy_contract(code=Op.STOP) - if receiver_account_type.delegated - else None + fork: Fork, + gas_benchmark_value: int, + senders: Generator[Address, None, None], + receivers: Generator[Address, None, None], + transfer_amount: int, + warm_access: bool, + receiver_initial_balance: int, + track_post_state: bool, +) -> None: + """Fill a block with ether transfers between the given generators.""" + iteration_cost = fork.transaction_intrinsic_cost_calculator()( + access_list=( + [AccessList(address=Address(0x100), storage_keys=[])] + if warm_access + else None + ), ) + iteration_count = gas_benchmark_value // iteration_cost - if case_id == "a_to_a": - """Sending to self.""" - senders = get_single_sender_list(pre) - receivers = senders - - elif case_id == "a_to_b": - """One sender → one receiver.""" - senders = get_single_sender_list(pre) - receivers = get_single_receiver_list(pre, balance, delegation) - - elif case_id == "diff_acc_to_b": - """Multiple senders → one receiver.""" - senders = get_distinct_sender_list(pre) - receivers = get_single_receiver_list(pre, balance, delegation) - - elif case_id == "a_to_diff_acc": - """One sender → multiple receivers.""" - senders = get_single_sender_list(pre) - receivers = get_distinct_receiver_list(pre, balance, delegation) - - elif case_id == "diff_acc_to_diff_acc": - """Multiple senders → multiple receivers.""" - senders = get_distinct_sender_list(pre) - receivers = get_distinct_receiver_list(pre, balance, delegation) - - elif case_id == "diff_to_nonexistent": - """Multiple senders → distinct nonexistent receivers.""" - senders = get_distinct_sender_list(pre) - receivers = get_distinct_nonexistent_receiver_list() - - elif case_id == "diff_to_existent": - """Multiple senders → distinct existent EOA receivers.""" - senders = get_distinct_sender_list(pre) - receivers = get_distinct_existent_receiver_list() - - elif case_id == "diff_to_contract": - """Multiple senders → distinct contract receivers.""" - senders = get_distinct_sender_list(pre) - receivers = get_distinct_contract_receiver_list() + txs = [] + token_transfers: dict[Address, int] = {} + for _ in range(iteration_count): + receiver = next(receivers) + token_transfers[receiver] = ( + token_transfers.get(receiver, 0) + transfer_amount + ) + access_list = ( + [AccessList(address=receiver, storage_keys=[])] + if warm_access + else None + ) + txs.append( + Transaction( + to=receiver, + value=transfer_amount, + gas_limit=iteration_cost, + sender=next(senders), + access_list=access_list, + ) + ) - else: - raise ValueError(f"Unknown case: {case_id}") + post_state = ( + { + receiver: Account( + balance=receiver_initial_balance + transferred_amount + ) + for receiver, transferred_amount in token_transfers.items() + if receiver_initial_balance + transferred_amount > 0 + } + if track_post_state + else {} + ) - return senders, receivers + benchmark_test( + pre=pre, + post=post_state, + blocks=[Block(txs=txs)], + expected_benchmark_gas_used=iteration_count * iteration_cost, + ) @pytest.mark.parametrize( @@ -205,9 +186,6 @@ def ether_transfer_case( "diff_acc_to_b", "a_to_diff_acc", "diff_acc_to_diff_acc", - "diff_to_nonexistent", - "diff_to_existent", - "diff_to_contract", ], ) @pytest.mark.parametrize("transfer_amount", [0, 1]) @@ -238,95 +216,107 @@ def test_ether_transfers( fork: Fork, gas_benchmark_value: int, warm_access: bool, - ether_transfer_case: Tuple[ - Generator[Address, None, None], Generator[Address, None, None] - ], ) -> None: """ - Single test for ether transfer scenarios. + Ether transfers where receivers constructed in pre-allocation. Scenarios: - - a_to_a: one sender → one sender + - a_to_a: self-transfer - a_to_b: one sender → one receiver - diff_acc_to_b: multiple senders → one receiver - a_to_diff_acc: one sender → multiple receivers - diff_acc_to_diff_acc: multiple senders → multiple receivers - - diff_to_nonexistent: multiple senders → distinct nonexistent - receivers (matches AccountMode.NON_EXISTING_ACCOUNT) - - diff_to_existent: multiple senders → distinct existent EOA - receivers (matches AccountMode.EXISTING_EOA) - - diff_to_contract: multiple senders → distinct contract receivers - (matches AccountMode.EXISTING_CONTRACT) - - When warm_access is True, each transaction includes an access list - entry for the receiver to warm the account before the transfer. """ - if case_id in RECEIVER_TYPED_CASES: - # Receiver address is determined by case_id; avoid multiplying with - # receiver_account_type and only run for the default variant. - if receiver_account_type != ReceiverAccountType( - balance=0, delegated=False - ): - pytest.skip( - "Receiver address is determined by case_id; skipping " - "non-default receiver_account_type." - ) - # Receivers are not allocated in pre-state during fill (they are - # expected to already exist on-chain at run time, e.g. on bloatnet), - # so their initial balance during fill is 0. - balance = 0 - else: - balance = receiver_account_type.balance - - senders, receivers = ether_transfer_case + balance = receiver_account_type.balance + delegation = ( + pre.deploy_contract(code=Op.STOP) + if receiver_account_type.delegated + else None + ) - txs = [] - token_transfers: dict[Address, int] = {} + if case_id == "a_to_a": + senders = get_single_sender_list(pre) + receivers = senders + elif case_id == "a_to_b": + senders = get_single_sender_list(pre) + receivers = get_single_receiver_list(pre, balance, delegation) + elif case_id == "diff_acc_to_b": + senders = get_distinct_sender_list(pre) + receivers = get_single_receiver_list(pre, balance, delegation) + elif case_id == "a_to_diff_acc": + senders = get_single_sender_list(pre) + receivers = get_distinct_receiver_list(pre, balance, delegation) + elif case_id == "diff_acc_to_diff_acc": + senders = get_distinct_sender_list(pre) + receivers = get_distinct_receiver_list(pre, balance, delegation) + else: + raise ValueError(f"Unknown case: {case_id}") - iteration_cost = fork.transaction_intrinsic_cost_calculator()( - access_list=( - [AccessList(address=Address(0x100), storage_keys=[])] - if warm_access - else None - ), + _run_ether_transfer_benchmark( + benchmark_test=benchmark_test, + pre=pre, + fork=fork, + gas_benchmark_value=gas_benchmark_value, + senders=senders, + receivers=receivers, + transfer_amount=transfer_amount, + warm_access=warm_access, + receiver_initial_balance=balance, + track_post_state=(case_id != "a_to_a"), ) - iteration_count = gas_benchmark_value // iteration_cost - for _ in range(iteration_count): - receiver = next(receivers) - token_transfers[receiver] = ( - token_transfers.get(receiver, 0) + transfer_amount - ) - access_list = ( - [AccessList(address=receiver, storage_keys=[])] - if warm_access - else None - ) - txs.append( - Transaction( - to=receiver, - value=transfer_amount, - gas_limit=iteration_cost, - sender=next(senders), - access_list=access_list, - ) - ) - post_state = ( - {} - if case_id == "a_to_a" - else { - receiver: Account(balance=balance + transferred_amount) - for receiver, transferred_amount in token_transfers.items() - if balance + transferred_amount > 0 - } - ) +@pytest.mark.parametrize( + "case_id", + [ + "diff_to_nonexistent", + "diff_to_existent", + "diff_to_contract", + ], +) +@pytest.mark.parametrize("transfer_amount", [0, 1]) +@pytest.mark.parametrize("warm_access", [False, True]) +def test_ether_transfers_onchain_receivers( + benchmark_test: BenchmarkTestFiller, + pre: Alloc, + case_id: str, + transfer_amount: int, + fork: Fork, + gas_benchmark_value: int, + warm_access: bool, +) -> None: + """ + Ether transfers to receivers that exist on-chain at run time. - benchmark_test( + Scenarios: + - diff_to_nonexistent: distinct nonexistent receivers + (matches AccountMode.NON_EXISTING_ACCOUNT) + - diff_to_existent: distinct existent EOA receivers + (matches AccountMode.EXISTING_EOA) + - diff_to_contract: distinct contract receivers + (matches AccountMode.EXISTING_CONTRACT) + """ + senders = get_distinct_sender_list(pre) + if case_id == "diff_to_nonexistent": + receivers = get_distinct_nonexistent_receiver_list() + elif case_id == "diff_to_existent": + receivers = get_distinct_existent_receiver_list() + elif case_id == "diff_to_contract": + receivers = get_distinct_contract_receiver_list() + else: + raise ValueError(f"Unknown case: {case_id}") + + _run_ether_transfer_benchmark( + benchmark_test=benchmark_test, pre=pre, - post=post_state, - blocks=[Block(txs=txs)], - expected_benchmark_gas_used=iteration_count * iteration_cost, + fork=fork, + gas_benchmark_value=gas_benchmark_value, + senders=senders, + receivers=receivers, + transfer_amount=transfer_amount, + warm_access=warm_access, + receiver_initial_balance=0, + track_post_state=False, ) From 6c847889ed44afcfd089b6bb453fa36084c93cb0 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Tue, 12 May 2026 13:46:38 +0800 Subject: [PATCH 03/11] refactor: bump execution cost for contract ether reception --- .../scenario/test_transaction_types.py | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/benchmark/compute/scenario/test_transaction_types.py b/tests/benchmark/compute/scenario/test_transaction_types.py index e0e7bae255..05f950e6ec 100644 --- a/tests/benchmark/compute/scenario/test_transaction_types.py +++ b/tests/benchmark/compute/scenario/test_transaction_types.py @@ -83,6 +83,11 @@ def get_single_receiver_list( ) +# Ether reception cost for Bittrex-created contracts +# Exact: 51 gas, rounded up to 60. +RECEIVER_CONTRACT_EXECUTION_GAS = 60 + + def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: """Yield contract account created by Bitterex controller via CREATE.""" for nonce in itertools.count(1): @@ -125,14 +130,18 @@ def _run_ether_transfer_benchmark( warm_access: bool, receiver_initial_balance: int, track_post_state: bool, + receiver_execution_gas: int = 0, ) -> None: """Fill a block with ether transfers between the given generators.""" - iteration_cost = fork.transaction_intrinsic_cost_calculator()( - access_list=( - [AccessList(address=Address(0x100), storage_keys=[])] - if warm_access - else None - ), + iteration_cost = ( + fork.transaction_intrinsic_cost_calculator()( + access_list=( + [AccessList(address=Address(0x100), storage_keys=[])] + if warm_access + else None + ), + ) + + receiver_execution_gas ) iteration_count = gas_benchmark_value // iteration_cost @@ -297,12 +306,14 @@ def test_ether_transfers_onchain_receivers( (matches AccountMode.EXISTING_CONTRACT) """ senders = get_distinct_sender_list(pre) + receiver_execution_gas = 0 if case_id == "diff_to_nonexistent": receivers = get_distinct_nonexistent_receiver_list() elif case_id == "diff_to_existent": receivers = get_distinct_existent_receiver_list() elif case_id == "diff_to_contract": receivers = get_distinct_contract_receiver_list() + receiver_execution_gas = RECEIVER_CONTRACT_EXECUTION_GAS else: raise ValueError(f"Unknown case: {case_id}") @@ -317,6 +328,7 @@ def test_ether_transfers_onchain_receivers( warm_access=warm_access, receiver_initial_balance=0, track_post_state=False, + receiver_execution_gas=receiver_execution_gas, ) From 7e1231877c433825083234390cd0a1db329dc784 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Tue, 12 May 2026 15:06:23 +0800 Subject: [PATCH 04/11] chore: remove unnecessary parametrization --- tests/benchmark/compute/scenario/test_transaction_types.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/benchmark/compute/scenario/test_transaction_types.py b/tests/benchmark/compute/scenario/test_transaction_types.py index 05f950e6ec..a04dc5a086 100644 --- a/tests/benchmark/compute/scenario/test_transaction_types.py +++ b/tests/benchmark/compute/scenario/test_transaction_types.py @@ -84,8 +84,7 @@ def get_single_receiver_list( # Ether reception cost for Bittrex-created contracts -# Exact: 51 gas, rounded up to 60. -RECEIVER_CONTRACT_EXECUTION_GAS = 60 +RECEIVER_CONTRACT_EXECUTION_GAS = 51 def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: @@ -284,7 +283,6 @@ def test_ether_transfers( ], ) @pytest.mark.parametrize("transfer_amount", [0, 1]) -@pytest.mark.parametrize("warm_access", [False, True]) def test_ether_transfers_onchain_receivers( benchmark_test: BenchmarkTestFiller, pre: Alloc, @@ -292,7 +290,6 @@ def test_ether_transfers_onchain_receivers( transfer_amount: int, fork: Fork, gas_benchmark_value: int, - warm_access: bool, ) -> None: """ Ether transfers to receivers that exist on-chain at run time. @@ -325,7 +322,7 @@ def test_ether_transfers_onchain_receivers( senders=senders, receivers=receivers, transfer_amount=transfer_amount, - warm_access=warm_access, + warm_access=False, receiver_initial_balance=0, track_post_state=False, receiver_execution_gas=receiver_execution_gas, From 0aaa2ad1ef3b02cab42ee4a4bb546d8679c08a59 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Tue, 12 May 2026 15:31:20 +0800 Subject: [PATCH 05/11] refactor: move new benchmark under stateful folder --- .../scenario/test_transaction_types.py | 274 ++++++------------ .../bloatnet/test_transaction_types.py | 120 ++++++++ 2 files changed, 210 insertions(+), 184 deletions(-) create mode 100644 tests/benchmark/stateful/bloatnet/test_transaction_types.py diff --git a/tests/benchmark/compute/scenario/test_transaction_types.py b/tests/benchmark/compute/scenario/test_transaction_types.py index a04dc5a086..5d4c3d1b00 100644 --- a/tests/benchmark/compute/scenario/test_transaction_types.py +++ b/tests/benchmark/compute/scenario/test_transaction_types.py @@ -1,10 +1,9 @@ """Benchmark different transaction types.""" -import itertools import math import random from dataclasses import dataclass -from typing import Generator, List +from typing import Generator, List, Tuple import pytest from execution_testing import ( @@ -74,42 +73,6 @@ def get_single_receiver_list( yield receiver -# Bitterex controller mainnet address -# Creates 1.5M contracts with deterministic address via CREATE -# It is guaranteed no contract is destructed -# Used for existing contract targets in benchmark -BITTREX_CONTROLLER_ADDRESS = Address( - 0xA3C1E324CA1CE40DB73ED6026C4A177F099B5770 -) - - -# Ether reception cost for Bittrex-created contracts -RECEIVER_CONTRACT_EXECUTION_GAS = 51 - - -def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: - """Yield contract account created by Bitterex controller via CREATE.""" - for nonce in itertools.count(1): - yield compute_create_address( - address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce - ) - - -def get_distinct_existent_receiver_list() -> Generator[Address, None, None]: - """ - Yield existing balance-only EOA on bloatnet. pre-funded by Spamoor - (https://github.com/CPerezz/spamoor/pull/12). - """ - for address in itertools.count(0x1000): - yield Address(address) - - -def get_distinct_nonexistent_receiver_list() -> Generator[Address, None, None]: - """Yield non-existent accounts starting from keccak256('random').""" - for address in itertools.count(0xF3CF193BB4AF1022AF7D2089F37D8BAE7157B85F): - yield Address(address) - - @dataclass(frozen=True) class ReceiverAccountType: """Receiver account type for ether transfer benchmarks.""" @@ -118,72 +81,49 @@ class ReceiverAccountType: delegated: bool -def _run_ether_transfer_benchmark( - benchmark_test: BenchmarkTestFiller, +@pytest.fixture +def ether_transfer_case( + case_id: str, pre: Alloc, - fork: Fork, - gas_benchmark_value: int, - senders: Generator[Address, None, None], - receivers: Generator[Address, None, None], - transfer_amount: int, - warm_access: bool, - receiver_initial_balance: int, - track_post_state: bool, - receiver_execution_gas: int = 0, -) -> None: - """Fill a block with ether transfers between the given generators.""" - iteration_cost = ( - fork.transaction_intrinsic_cost_calculator()( - access_list=( - [AccessList(address=Address(0x100), storage_keys=[])] - if warm_access - else None - ), - ) - + receiver_execution_gas + receiver_account_type: ReceiverAccountType, +) -> Tuple[Generator[Address, None, None], Generator[Address, None, None]]: + """Generate sender and receiver generators based on the test case.""" + balance = receiver_account_type.balance + delegation = ( + pre.deploy_contract(code=Op.STOP) + if receiver_account_type.delegated + else None ) - iteration_count = gas_benchmark_value // iteration_cost - txs = [] - token_transfers: dict[Address, int] = {} - for _ in range(iteration_count): - receiver = next(receivers) - token_transfers[receiver] = ( - token_transfers.get(receiver, 0) + transfer_amount - ) - access_list = ( - [AccessList(address=receiver, storage_keys=[])] - if warm_access - else None - ) - txs.append( - Transaction( - to=receiver, - value=transfer_amount, - gas_limit=iteration_cost, - sender=next(senders), - access_list=access_list, - ) - ) + if case_id == "a_to_a": + """Sending to self.""" + senders = get_single_sender_list(pre) + receivers = senders - post_state = ( - { - receiver: Account( - balance=receiver_initial_balance + transferred_amount - ) - for receiver, transferred_amount in token_transfers.items() - if receiver_initial_balance + transferred_amount > 0 - } - if track_post_state - else {} - ) + elif case_id == "a_to_b": + """One sender → one receiver.""" + senders = get_single_sender_list(pre) + receivers = get_single_receiver_list(pre, balance, delegation) - benchmark_test( - pre=pre, - post=post_state, - blocks=[Block(txs=txs)], - expected_benchmark_gas_used=iteration_count * iteration_cost, - ) + elif case_id == "diff_acc_to_b": + """Multiple senders → one receiver.""" + senders = get_distinct_sender_list(pre) + receivers = get_single_receiver_list(pre, balance, delegation) + + elif case_id == "a_to_diff_acc": + """One sender → multiple receivers.""" + senders = get_single_sender_list(pre) + receivers = get_distinct_receiver_list(pre, balance, delegation) + + elif case_id == "diff_acc_to_diff_acc": + """Multiple senders → multiple receivers.""" + senders = get_distinct_sender_list(pre) + receivers = get_distinct_receiver_list(pre, balance, delegation) + + else: + raise ValueError(f"Unknown case: {case_id}") + + return senders, receivers @pytest.mark.parametrize( @@ -224,108 +164,74 @@ def test_ether_transfers( fork: Fork, gas_benchmark_value: int, warm_access: bool, + ether_transfer_case: Tuple[ + Generator[Address, None, None], Generator[Address, None, None] + ], ) -> None: """ - Ether transfers where receivers constructed in pre-allocation. + Single test for ether transfer scenarios. Scenarios: - - a_to_a: self-transfer + - a_to_a: one sender → one sender - a_to_b: one sender → one receiver - diff_acc_to_b: multiple senders → one receiver - a_to_diff_acc: one sender → multiple receivers - diff_acc_to_diff_acc: multiple senders → multiple receivers + + When warm_access is True, each transaction includes an access list + entry for the receiver to warm the account before the transfer. """ + senders, receivers = ether_transfer_case + balance = receiver_account_type.balance - delegation = ( - pre.deploy_contract(code=Op.STOP) - if receiver_account_type.delegated - else None - ) - if case_id == "a_to_a": - senders = get_single_sender_list(pre) - receivers = senders - elif case_id == "a_to_b": - senders = get_single_sender_list(pre) - receivers = get_single_receiver_list(pre, balance, delegation) - elif case_id == "diff_acc_to_b": - senders = get_distinct_sender_list(pre) - receivers = get_single_receiver_list(pre, balance, delegation) - elif case_id == "a_to_diff_acc": - senders = get_single_sender_list(pre) - receivers = get_distinct_receiver_list(pre, balance, delegation) - elif case_id == "diff_acc_to_diff_acc": - senders = get_distinct_sender_list(pre) - receivers = get_distinct_receiver_list(pre, balance, delegation) - else: - raise ValueError(f"Unknown case: {case_id}") + txs = [] + token_transfers: dict[Address, int] = {} - _run_ether_transfer_benchmark( - benchmark_test=benchmark_test, - pre=pre, - fork=fork, - gas_benchmark_value=gas_benchmark_value, - senders=senders, - receivers=receivers, - transfer_amount=transfer_amount, - warm_access=warm_access, - receiver_initial_balance=balance, - track_post_state=(case_id != "a_to_a"), + iteration_cost = fork.transaction_intrinsic_cost_calculator()( + access_list=( + [AccessList(address=Address(0x100), storage_keys=[])] + if warm_access + else None + ), ) + iteration_count = gas_benchmark_value // iteration_cost + for _ in range(iteration_count): + receiver = next(receivers) + token_transfers[receiver] = ( + token_transfers.get(receiver, 0) + transfer_amount + ) + access_list = ( + [AccessList(address=receiver, storage_keys=[])] + if warm_access + else None + ) + txs.append( + Transaction( + to=receiver, + value=transfer_amount, + gas_limit=iteration_cost, + sender=next(senders), + access_list=access_list, + ) + ) -@pytest.mark.parametrize( - "case_id", - [ - "diff_to_nonexistent", - "diff_to_existent", - "diff_to_contract", - ], -) -@pytest.mark.parametrize("transfer_amount", [0, 1]) -def test_ether_transfers_onchain_receivers( - benchmark_test: BenchmarkTestFiller, - pre: Alloc, - case_id: str, - transfer_amount: int, - fork: Fork, - gas_benchmark_value: int, -) -> None: - """ - Ether transfers to receivers that exist on-chain at run time. - - Scenarios: - - diff_to_nonexistent: distinct nonexistent receivers - (matches AccountMode.NON_EXISTING_ACCOUNT) - - diff_to_existent: distinct existent EOA receivers - (matches AccountMode.EXISTING_EOA) - - diff_to_contract: distinct contract receivers - (matches AccountMode.EXISTING_CONTRACT) - """ - senders = get_distinct_sender_list(pre) - receiver_execution_gas = 0 - if case_id == "diff_to_nonexistent": - receivers = get_distinct_nonexistent_receiver_list() - elif case_id == "diff_to_existent": - receivers = get_distinct_existent_receiver_list() - elif case_id == "diff_to_contract": - receivers = get_distinct_contract_receiver_list() - receiver_execution_gas = RECEIVER_CONTRACT_EXECUTION_GAS - else: - raise ValueError(f"Unknown case: {case_id}") + post_state = ( + {} + if case_id == "a_to_a" + else { + receiver: Account(balance=balance + transferred_amount) + for receiver, transferred_amount in token_transfers.items() + if balance + transferred_amount > 0 + } + ) - _run_ether_transfer_benchmark( - benchmark_test=benchmark_test, + benchmark_test( pre=pre, - fork=fork, - gas_benchmark_value=gas_benchmark_value, - senders=senders, - receivers=receivers, - transfer_amount=transfer_amount, - warm_access=False, - receiver_initial_balance=0, - track_post_state=False, - receiver_execution_gas=receiver_execution_gas, + post=post_state, + blocks=[Block(txs=txs)], + expected_benchmark_gas_used=iteration_count * iteration_cost, ) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py new file mode 100644 index 0000000000..90d1d3950a --- /dev/null +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -0,0 +1,120 @@ +"""Benchmark ether transfers to receivers that exist on-chain.""" + +import itertools +from typing import Generator + +import pytest +from execution_testing import ( + Address, + Alloc, + BenchmarkTestFiller, + Block, + Fork, + Transaction, + compute_create_address, +) + + +def get_distinct_sender_list(pre: Alloc) -> Generator[Address, None, None]: + """Get a list of distinct sender accounts.""" + while True: + yield pre.fund_eoa() + + +# Bitterex controller mainnet address +# Creates 1.5M contracts with deterministic address via CREATE +# It is guaranteed no contract is destructed +# Used for existing contract targets in benchmark +BITTREX_CONTROLLER_ADDRESS = Address( + 0xA3C1E324CA1CE40DB73ED6026C4A177F099B5770 +) + + +# Ether reception cost for Bittrex-created contracts +RECEIVER_CONTRACT_EXECUTION_GAS = 51 + + +def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: + """Yield contract account created by Bitterex controller via CREATE.""" + for nonce in itertools.count(1): + yield compute_create_address( + address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce + ) + + +def get_distinct_existent_receiver_list() -> Generator[Address, None, None]: + """ + Yield existing balance-only EOA on bloatnet. pre-funded by Spamoor + (https://github.com/CPerezz/spamoor/pull/12). + """ + for address in itertools.count(0x1000): + yield Address(address) + + +def get_distinct_nonexistent_receiver_list() -> Generator[Address, None, None]: + """Yield non-existent accounts starting from keccak256('random').""" + for address in itertools.count(0xF3CF193BB4AF1022AF7D2089F37D8BAE7157B85F): + yield Address(address) + + +@pytest.mark.parametrize( + "case_id", + [ + "diff_to_nonexistent", + "diff_to_existent", + "diff_to_contract", + ], +) +@pytest.mark.parametrize("transfer_amount", [0, 1]) +def test_ether_transfers_onchain_receivers( + benchmark_test: BenchmarkTestFiller, + pre: Alloc, + case_id: str, + transfer_amount: int, + fork: Fork, + gas_benchmark_value: int, +) -> None: + """ + Ether transfers to receivers that exist on-chain at run time. + + Scenarios: + - diff_to_nonexistent: distinct nonexistent receivers + (matches AccountMode.NON_EXISTING_ACCOUNT) + - diff_to_existent: distinct existent EOA receivers + (matches AccountMode.EXISTING_EOA) + - diff_to_contract: distinct contract receivers + (matches AccountMode.EXISTING_CONTRACT) + """ + senders = get_distinct_sender_list(pre) + receiver_execution_gas = 0 + if case_id == "diff_to_nonexistent": + receivers = get_distinct_nonexistent_receiver_list() + elif case_id == "diff_to_existent": + receivers = get_distinct_existent_receiver_list() + elif case_id == "diff_to_contract": + receivers = get_distinct_contract_receiver_list() + receiver_execution_gas = RECEIVER_CONTRACT_EXECUTION_GAS + else: + raise ValueError(f"Unknown case: {case_id}") + + iteration_cost = ( + fork.transaction_intrinsic_cost_calculator()() + receiver_execution_gas + ) + iteration_count = gas_benchmark_value // iteration_cost + + txs = [ + Transaction( + to=next(receivers), + value=transfer_amount, + gas_limit=iteration_cost, + sender=next(senders), + ) + for _ in range(iteration_count) + ] + + benchmark_test( + pre=pre, + post={}, + blocks=[Block(txs=txs)], + expected_benchmark_gas_used=iteration_count * iteration_cost, + ) From 8955e8a6fa3ae29a1830257f01b83b172859fc96 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Wed, 13 May 2026 22:28:49 +0800 Subject: [PATCH 06/11] fix: apply suggested changes --- .../bloatnet/test_transaction_types.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py index 90d1d3950a..d2030f4a1d 100644 --- a/tests/benchmark/stateful/bloatnet/test_transaction_types.py +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -15,13 +15,13 @@ ) -def get_distinct_sender_list(pre: Alloc) -> Generator[Address, None, None]: +def yield_distinct_sender(pre: Alloc) -> Generator[Address, None, None]: """Get a list of distinct sender accounts.""" while True: yield pre.fund_eoa() -# Bitterex controller mainnet address +# Bittrex controller mainnet address # Creates 1.5M contracts with deterministic address via CREATE # It is guaranteed no contract is destructed # Used for existing contract targets in benchmark @@ -34,15 +34,15 @@ def get_distinct_sender_list(pre: Alloc) -> Generator[Address, None, None]: RECEIVER_CONTRACT_EXECUTION_GAS = 51 -def get_distinct_contract_receiver_list() -> Generator[Address, None, None]: - """Yield contract account created by Bitterex controller via CREATE.""" - for nonce in itertools.count(1): +def yield_distinct_contract_receiver() -> Generator[Address, None, None]: + """Yield contract account created by Bittrex controller via CREATE.""" + for nonce in itertools.count(2): yield compute_create_address( address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce ) -def get_distinct_existent_receiver_list() -> Generator[Address, None, None]: +def yield_distinct_existent_receiver() -> Generator[Address, None, None]: """ Yield existing balance-only EOA on bloatnet. pre-funded by Spamoor (https://github.com/CPerezz/spamoor/pull/12). @@ -51,12 +51,13 @@ def get_distinct_existent_receiver_list() -> Generator[Address, None, None]: yield Address(address) -def get_distinct_nonexistent_receiver_list() -> Generator[Address, None, None]: +def yield_distinct_nonexistent_receiver() -> Generator[Address, None, None]: """Yield non-existent accounts starting from keccak256('random').""" for address in itertools.count(0xF3CF193BB4AF1022AF7D2089F37D8BAE7157B85F): yield Address(address) +@pytest.mark.repricing @pytest.mark.parametrize( "case_id", [ @@ -85,14 +86,14 @@ def test_ether_transfers_onchain_receivers( - diff_to_contract: distinct contract receivers (matches AccountMode.EXISTING_CONTRACT) """ - senders = get_distinct_sender_list(pre) + senders = yield_distinct_sender(pre) receiver_execution_gas = 0 if case_id == "diff_to_nonexistent": - receivers = get_distinct_nonexistent_receiver_list() + receivers = yield_distinct_nonexistent_receiver() elif case_id == "diff_to_existent": - receivers = get_distinct_existent_receiver_list() + receivers = yield_distinct_existent_receiver() elif case_id == "diff_to_contract": - receivers = get_distinct_contract_receiver_list() + receivers = yield_distinct_contract_receiver() receiver_execution_gas = RECEIVER_CONTRACT_EXECUTION_GAS else: raise ValueError(f"Unknown case: {case_id}") From 6dfea9021c359e5272d35628aa30d519105ec726 Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Wed, 13 May 2026 22:36:00 +0800 Subject: [PATCH 07/11] feat: add receipt check --- tests/benchmark/stateful/bloatnet/test_transaction_types.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py index d2030f4a1d..7f966d1089 100644 --- a/tests/benchmark/stateful/bloatnet/test_transaction_types.py +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -118,4 +118,5 @@ def test_ether_transfers_onchain_receivers( post={}, blocks=[Block(txs=txs)], expected_benchmark_gas_used=iteration_count * iteration_cost, + expected_receipt_status=1, ) From 61683b4e2cb77bc27cf7aa02f755db9708d979cb Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 13 May 2026 23:18:40 +0200 Subject: [PATCH 08/11] (Claude): add distinct senders --- .../bloatnet/test_transaction_types.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py index 7f966d1089..51940945fa 100644 --- a/tests/benchmark/stateful/bloatnet/test_transaction_types.py +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -5,6 +5,7 @@ import pytest from execution_testing import ( + EOA, Address, Alloc, BenchmarkTestFiller, @@ -14,11 +15,19 @@ compute_create_address, ) +# Deterministic sender pool: keys start at 0x111...111 (32 bytes) and +# increment by 1. Accounts are assumed to be pre-funded on bloatnet +# (e.g. by Spamoor), so they are intentionally NOT added to the pre-alloc. +SENDER_BASE_KEY = ( + 0x1111111111111111111111111111111111111111111111111111111111111111 +) +SENDER_COUNT = 15_000 + -def yield_distinct_sender(pre: Alloc) -> Generator[Address, None, None]: - """Get a list of distinct sender accounts.""" - while True: - yield pre.fund_eoa() +def yield_distinct_sender() -> Generator[EOA, None, None]: + """Yield deterministic sender EOAs pre-funded on-chain.""" + for i in range(SENDER_COUNT): + yield EOA(key=SENDER_BASE_KEY + i) # Bittrex controller mainnet address @@ -86,7 +95,7 @@ def test_ether_transfers_onchain_receivers( - diff_to_contract: distinct contract receivers (matches AccountMode.EXISTING_CONTRACT) """ - senders = yield_distinct_sender(pre) + senders = yield_distinct_sender() receiver_execution_gas = 0 if case_id == "diff_to_nonexistent": receivers = yield_distinct_nonexistent_receiver() From c9da3189be4a790d1a0c20a4d6cec51beb6932ac Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 13 May 2026 23:26:20 +0200 Subject: [PATCH 09/11] (Claude): do not limit distinct senders --- tests/benchmark/stateful/bloatnet/test_transaction_types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py index 51940945fa..3928670528 100644 --- a/tests/benchmark/stateful/bloatnet/test_transaction_types.py +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -21,12 +21,11 @@ SENDER_BASE_KEY = ( 0x1111111111111111111111111111111111111111111111111111111111111111 ) -SENDER_COUNT = 15_000 def yield_distinct_sender() -> Generator[EOA, None, None]: """Yield deterministic sender EOAs pre-funded on-chain.""" - for i in range(SENDER_COUNT): + for i in itertools.count(0): yield EOA(key=SENDER_BASE_KEY + i) From 700b154e9ec4c82957e229474a8dbbb149b56996 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 13 May 2026 23:58:06 +0200 Subject: [PATCH 10/11] (Claude): add uniq jumpdest contract test --- .../bloatnet/test_transaction_types.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py index 3928670528..b729f59f8c 100644 --- a/tests/benchmark/stateful/bloatnet/test_transaction_types.py +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -12,6 +12,7 @@ Block, Fork, Transaction, + compute_create2_address, compute_create_address, ) @@ -42,6 +43,33 @@ def yield_distinct_sender() -> Generator[EOA, None, None]: RECEIVER_CONTRACT_EXECUTION_GAS = 51 +# Arachnid's deterministic deployment proxy. Assumed to have already +# deployed the unique-code contracts via CREATE2 with salts 0, 1, 2, ... +DETERMINISTIC_FACTORY_ADDRESS = Address( + 0x4E59B44847B379578588920CA78FBF26C0B4956C +) + + +# Initcode deployed by DETERMINISTIC_FACTORY_ADDRESS for each +# diff_to_unique_code_jumpdest_contract receiver. Returns a 24,576-byte +# runtime whose entry is PUSH2 0x5fff; JUMP, landing on a JUMPDEST near +# the end of code (then implicit STOP). Each contract embeds its own +# address in code, so the deployed code is unique per address while +# initcode (and therefore the CREATE2 hash input) is shared. +UNIQUE_CODE_JUMPDEST_INITCODE = bytes.fromhex( + "7f5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b6000" + "526020600060205e6040600060405e6080600060805e61010060006101005e61020060" + "006102005e61040060006104005e61080060006108005e61100060006110005e612000" + "60006120005e61400060006140005e7f615fff565b5b5b5b5b5b5b5b5b5b5b5b5b5b5b" + "5b5b5b5b5b5b5b5b5b5b5b5b5b6000527f5b5b5b5b5b5b5b5b5b5b5b5b000000000000" + "000000000000000000000000000030176020526160006000f3" +) + + +# Runtime gas cost: PUSH2 (3) + JUMP (8) + JUMPDEST (1) = 12. +RECEIVER_JUMPDEST_EXECUTION_GAS = 3 + 8 + 1 + + def yield_distinct_contract_receiver() -> Generator[Address, None, None]: """Yield contract account created by Bittrex controller via CREATE.""" for nonce in itertools.count(2): @@ -50,6 +78,23 @@ def yield_distinct_contract_receiver() -> Generator[Address, None, None]: ) +def yield_distinct_unique_code_jumpdest_receiver() -> ( + Generator[Address, None, None] +): + """ + Yield contract addresses deployed by the deterministic CREATE2 factory. + + Each address corresponds to a contract with unique deployed code whose + runtime executes PUSH2 + JUMP + JUMPDEST (12 gas). + """ + for salt in itertools.count(0): + yield compute_create2_address( + address=DETERMINISTIC_FACTORY_ADDRESS, + salt=salt, + initcode=UNIQUE_CODE_JUMPDEST_INITCODE, + ) + + def yield_distinct_existent_receiver() -> Generator[Address, None, None]: """ Yield existing balance-only EOA on bloatnet. pre-funded by Spamoor @@ -72,6 +117,7 @@ def yield_distinct_nonexistent_receiver() -> Generator[Address, None, None]: "diff_to_nonexistent", "diff_to_existent", "diff_to_contract", + "diff_to_unique_code_jumpdest_contract", ], ) @pytest.mark.parametrize("transfer_amount", [0, 1]) @@ -93,6 +139,9 @@ def test_ether_transfers_onchain_receivers( (matches AccountMode.EXISTING_EOA) - diff_to_contract: distinct contract receivers (matches AccountMode.EXISTING_CONTRACT) + - diff_to_unique_code_jumpdest_contract: distinct CREATE2 contract + receivers each holding unique deployed code; runtime executes + PUSH2 + JUMP + JUMPDEST. """ senders = yield_distinct_sender() receiver_execution_gas = 0 @@ -103,6 +152,9 @@ def test_ether_transfers_onchain_receivers( elif case_id == "diff_to_contract": receivers = yield_distinct_contract_receiver() receiver_execution_gas = RECEIVER_CONTRACT_EXECUTION_GAS + elif case_id == "diff_to_unique_code_jumpdest_contract": + receivers = yield_distinct_unique_code_jumpdest_receiver() + receiver_execution_gas = RECEIVER_JUMPDEST_EXECUTION_GAS else: raise ValueError(f"Unknown case: {case_id}") From 6d1dd690bab77db4d17f4938203e904db5015d2d Mon Sep 17 00:00:00 2001 From: LouisTsai Date: Thu, 14 May 2026 15:46:39 +0800 Subject: [PATCH 11/11] refactor unique contract code receiver case --- .../bloatnet/test_transaction_types.py | 129 +++++++++++------- 1 file changed, 77 insertions(+), 52 deletions(-) diff --git a/tests/benchmark/stateful/bloatnet/test_transaction_types.py b/tests/benchmark/stateful/bloatnet/test_transaction_types.py index b729f59f8c..875c69fecd 100644 --- a/tests/benchmark/stateful/bloatnet/test_transaction_types.py +++ b/tests/benchmark/stateful/bloatnet/test_transaction_types.py @@ -5,20 +5,22 @@ import pytest from execution_testing import ( + DETERMINISTIC_FACTORY_ADDRESS, EOA, Address, Alloc, BenchmarkTestFiller, Block, Fork, + Op, Transaction, compute_create2_address, compute_create_address, ) -# Deterministic sender pool: keys start at 0x111...111 (32 bytes) and -# increment by 1. Accounts are assumed to be pre-funded on bloatnet -# (e.g. by Spamoor), so they are intentionally NOT added to the pre-alloc. +# Deterministic sender pool of 15K accounts. +# Funded via system contract withdrawals (funding.txt) in payload generation. +# Placed outside pre-allocation to ensure accounts remain uncached. SENDER_BASE_KEY = ( 0x1111111111111111111111111111111111111111111111111111111111111111 ) @@ -30,68 +32,81 @@ def yield_distinct_sender() -> Generator[EOA, None, None]: yield EOA(key=SENDER_BASE_KEY + i) -# Bittrex controller mainnet address -# Creates 1.5M contracts with deterministic address via CREATE -# It is guaranteed no contract is destructed -# Used for existing contract targets in benchmark -BITTREX_CONTROLLER_ADDRESS = Address( - 0xA3C1E324CA1CE40DB73ED6026C4A177F099B5770 -) - - -# Ether reception cost for Bittrex-created contracts -RECEIVER_CONTRACT_EXECUTION_GAS = 51 +def build_unique_contract_initcode() -> bytes: + """ + Deployed runtime contract layout. + + offset size contents + ------ ---- -------------------------------- + 0x0000 4 PUSH2 0x5FFF; JUMP <- entry + 0x0004 28 JUMPDEST padding + 0x0020 12 JUMPDEST padding + 0x002C 20 contract ADDRESS <- unique + 0x0040 24512 JUMPDEST <- 0x5FFF lands here + 0x6000 STOP + + Embedded ADDRESS makes runtime unique per contract; + initcode and its CREATE2 hash is shared across all salts. + """ + max_code_size = 0x6000 # EIP-170 contract code size limit + # MCOPY fills MEM[0:0x8000] with JUMPDEST. + # Runtime only uses MEM[0:0x6000]. + code = Op.MSTORE(0, bytes(Op.JUMPDEST * 32)) + for size in (1 << s for s in range(5, 15)): + code += Op.MCOPY(size, 0, size) -# Arachnid's deterministic deployment proxy. Assumed to have already -# deployed the unique-code contracts via CREATE2 with salts 0, 1, 2, ... -DETERMINISTIC_FACTORY_ADDRESS = Address( - 0x4E59B44847B379578588920CA78FBF26C0B4956C -) + # Runtime entry: JUMP to final JUMPDEST, then STOP. + entry = Op.JUMP(max_code_size - 1) + entry += Op.JUMPDEST * (32 - len(entry)) # Padding + code += Op.MSTORE(0, bytes(entry)) -# Initcode deployed by DETERMINISTIC_FACTORY_ADDRESS for each -# diff_to_unique_code_jumpdest_contract receiver. Returns a 24,576-byte -# runtime whose entry is PUSH2 0x5fff; JUMP, landing on a JUMPDEST near -# the end of code (then implicit STOP). Each contract embeds its own -# address in code, so the deployed code is unique per address while -# initcode (and therefore the CREATE2 hash input) is shared. -UNIQUE_CODE_JUMPDEST_INITCODE = bytes.fromhex( - "7f5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b6000" - "526020600060205e6040600060405e6080600060805e61010060006101005e61020060" - "006102005e61040060006104005e61080060006108005e61100060006110005e612000" - "60006120005e61400060006140005e7f615fff565b5b5b5b5b5b5b5b5b5b5b5b5b5b5b" - "5b5b5b5b5b5b5b5b5b5b5b5b5b6000527f5b5b5b5b5b5b5b5b5b5b5b5b000000000000" - "000000000000000000000000000030176020526160006000f3" -) + # Mask ADDRESS into a JUMPDEST template via OR: + # bytes 0..12 bytes 12..32 + # ----------- ------------ + # ADDRESS 00 .. 00 <20-byte address> + # addr_slot 5b .. 5b 00 .. 00 + # OR result 5b .. 5b <20-byte address> + addr_slot = Op.JUMPDEST * 12 + Op.STOP * 20 + code += Op.MSTORE(0x20, Op.OR(Op.ADDRESS, bytes(addr_slot))) + code += Op.RETURN(0, max_code_size) -# Runtime gas cost: PUSH2 (3) + JUMP (8) + JUMPDEST (1) = 12. -RECEIVER_JUMPDEST_EXECUTION_GAS = 3 + 8 + 1 + return bytes(code) -def yield_distinct_contract_receiver() -> Generator[Address, None, None]: - """Yield contract account created by Bittrex controller via CREATE.""" - for nonce in itertools.count(2): - yield compute_create_address( - address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce - ) +JOCHEMNET_UNIQUE_CONTRACT_INITCODE = build_unique_contract_initcode() -def yield_distinct_unique_code_jumpdest_receiver() -> ( - Generator[Address, None, None] -): +def yield_distinct_unique_code_jumpdest_receiver() -> Generator[ + Address, None, None +]: """ Yield contract addresses deployed by the deterministic CREATE2 factory. - - Each address corresponds to a contract with unique deployed code whose - runtime executes PUSH2 + JUMP + JUMPDEST (12 gas). """ for salt in itertools.count(0): yield compute_create2_address( address=DETERMINISTIC_FACTORY_ADDRESS, salt=salt, - initcode=UNIQUE_CODE_JUMPDEST_INITCODE, + initcode=JOCHEMNET_UNIQUE_CONTRACT_INITCODE, + ) + + +# Bittrex controller mainnet address +# Creates 1.5M contracts with deterministic address via CREATE +# It is guaranteed no contract is destructed +# Used for existing contract targets in benchmark +BITTREX_CONTROLLER_ADDRESS = Address( + 0xA3C1E324CA1CE40DB73ED6026C4A177F099B5770 +) + + +def yield_distinct_contract_receiver() -> Generator[Address, None, None]: + """Yield contract account created by Bittrex controller via CREATE.""" + for nonce in itertools.count(2): + yield compute_create_address( + address=BITTREX_CONTROLLER_ADDRESS, nonce=nonce ) @@ -140,8 +155,7 @@ def test_ether_transfers_onchain_receivers( - diff_to_contract: distinct contract receivers (matches AccountMode.EXISTING_CONTRACT) - diff_to_unique_code_jumpdest_contract: distinct CREATE2 contract - receivers each holding unique deployed code; runtime executes - PUSH2 + JUMP + JUMPDEST. + receivers each holding unique deployed code """ senders = yield_distinct_sender() receiver_execution_gas = 0 @@ -151,10 +165,21 @@ def test_ether_transfers_onchain_receivers( receivers = yield_distinct_existent_receiver() elif case_id == "diff_to_contract": receivers = yield_distinct_contract_receiver() - receiver_execution_gas = RECEIVER_CONTRACT_EXECUTION_GAS + # Runtime code is the same across all the receivers + # Example contract: https://etherscan.io/address/0xa888df3ef62286dde06a79395760b9bce6c83c83#code + runtime = ( + Op.MSTORE(0x40, 0x60, new_memory_size=0x60) + + Op.JUMPI(Op.PUSH2(0x49), Op.ISZERO(Op.CALLDATASIZE)) + + Op.JUMPDEST * 3 + + Op.JUMP(Op.PUSH2(0x50)) + + Op.JUMPDEST + ) + receiver_execution_gas = runtime.gas_cost(fork) elif case_id == "diff_to_unique_code_jumpdest_contract": receivers = yield_distinct_unique_code_jumpdest_receiver() - receiver_execution_gas = RECEIVER_JUMPDEST_EXECUTION_GAS + # Runtime code aligns entry code path. + runtime = Op.JUMP(Op.PUSH2(0x5FFF)) + Op.JUMPDEST + receiver_execution_gas = runtime.gas_cost(fork) else: raise ValueError(f"Unknown case: {case_id}")