-
Notifications
You must be signed in to change notification settings - Fork 456
feat(test-benchmark): add eth transfer cases for repricing #2837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
LouisTsai-Csie
merged 11 commits into
ethereum:forks/amsterdam
from
jochem-brouwer:eth-transfer-bench-repricing
May 14, 2026
+207
−0
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
463beba
(Claude): add eth transfer cases for repricing
jochem-brouwer 10f88bc
refactor: split test based on pre-alloc
LouisTsai-Csie 6c84788
refactor: bump execution cost for contract ether reception
LouisTsai-Csie 7e12318
chore: remove unnecessary parametrization
LouisTsai-Csie 0aaa2ad
refactor: move new benchmark under stateful folder
LouisTsai-Csie 8955e8a
fix: apply suggested changes
LouisTsai-Csie 6dfea90
feat: add receipt check
LouisTsai-Csie 61683b4
(Claude): add distinct senders
jochem-brouwer c9da318
(Claude): do not limit distinct senders
jochem-brouwer 700b154
(Claude): add uniq jumpdest contract test
jochem-brouwer 6d1dd69
refactor unique contract code receiver case
LouisTsai-Csie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
207 changes: 207 additions & 0 deletions
207
tests/benchmark/stateful/bloatnet/test_transaction_types.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| """Benchmark ether transfers to receivers that exist on-chain.""" | ||
|
|
||
| import itertools | ||
| from typing import Generator | ||
|
|
||
| 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 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 | ||
| ) | ||
|
|
||
|
|
||
| def yield_distinct_sender() -> Generator[EOA, None, None]: | ||
| """Yield deterministic sender EOAs pre-funded on-chain.""" | ||
| for i in itertools.count(0): | ||
| yield EOA(key=SENDER_BASE_KEY + i) | ||
|
|
||
|
|
||
| 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) | ||
|
|
||
| # 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)) | ||
|
|
||
| # 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) | ||
|
|
||
| return bytes(code) | ||
|
|
||
|
|
||
| JOCHEMNET_UNIQUE_CONTRACT_INITCODE = build_unique_contract_initcode() | ||
|
|
||
|
|
||
| def yield_distinct_unique_code_jumpdest_receiver() -> Generator[ | ||
| Address, None, None | ||
| ]: | ||
| """ | ||
| Yield contract addresses deployed by the deterministic CREATE2 factory. | ||
| """ | ||
| for salt in itertools.count(0): | ||
| yield compute_create2_address( | ||
| address=DETERMINISTIC_FACTORY_ADDRESS, | ||
| salt=salt, | ||
| 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 | ||
| ) | ||
|
|
||
|
|
||
| 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). | ||
| """ | ||
| for address in itertools.count(0x1000): | ||
| yield Address(address) | ||
|
|
||
|
|
||
| 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", | ||
| [ | ||
| "diff_to_nonexistent", | ||
| "diff_to_existent", | ||
| "diff_to_contract", | ||
| "diff_to_unique_code_jumpdest_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) | ||
| - diff_to_unique_code_jumpdest_contract: distinct CREATE2 contract | ||
| receivers each holding unique deployed code | ||
| """ | ||
| senders = yield_distinct_sender() | ||
| receiver_execution_gas = 0 | ||
| if case_id == "diff_to_nonexistent": | ||
| receivers = yield_distinct_nonexistent_receiver() | ||
| elif case_id == "diff_to_existent": | ||
| receivers = yield_distinct_existent_receiver() | ||
| elif case_id == "diff_to_contract": | ||
| receivers = yield_distinct_contract_receiver() | ||
| # 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() | ||
| # 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}") | ||
|
|
||
| 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, | ||
| expected_receipt_status=1, | ||
| ) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.