Skip to content

Commit 6ac8680

Browse files
committed
chore(test): cleanup / self-review for 8037 byte value updates
1 parent 1c7d28e commit 6ac8680

26 files changed

Lines changed: 221 additions & 276 deletions

File tree

packages/testing/src/execution_testing/cli/pytest_commands/plugins/shared/transaction_fixtures.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,7 @@ def type_3_default_transaction(sender: EOA) -> Transaction:
9191

9292

9393
@pytest.fixture
94-
def type_4_default_transaction(
95-
sender: EOA, pre: Alloc, fork: Fork
96-
) -> Transaction:
94+
def type_4_default_transaction(sender: EOA, pre: Alloc) -> Transaction:
9795
"""Type 4 (set code) default transaction introduced in Prague fork."""
9896
# Create authorized accounts with funds
9997
auth_signer1 = pre.fund_eoa(amount=10**18)
@@ -103,14 +101,12 @@ def type_4_default_transaction(
103101
target1 = pre.deploy_contract(Op.SSTORE(0, 1))
104102
target2 = pre.deploy_contract(Op.SSTORE(0, 1))
105103

106-
# Recipient may be a nonexistent account (e.g., EIP-7708 transfer
107-
# log tests), so cover one NEW_ACCOUNT state charge.
108104
return Transaction(
109105
ty=4,
110106
sender=sender,
111107
max_fee_per_gas=10**10,
112108
max_priority_fee_per_gas=10**9,
113-
gas_limit=500_000 + fork.gas_costs().NEW_ACCOUNT,
109+
gas_limit=500_000,
114110
data=b"\x00" * 200,
115111
access_list=[
116112
AccessList(address=0x4567, storage_keys=[1000, 2000, 3000]),

packages/testing/src/execution_testing/forks/forks/eips/amsterdam/eip_8037.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@
1919
from ....base_fork import BaseFork
2020
from ....gas_costs import GasCosts
2121

22+
# EIP-8037 state byte sizes (mirrors EELS amsterdam/vm/gas.py).
23+
STATE_BYTES_PER_NEW_ACCOUNT = 120
24+
STATE_BYTES_PER_STORAGE_SET = 64
25+
STATE_BYTES_PER_AUTH_BASE = 23
26+
27+
# EIP-8037 regular gas base costs.
28+
PER_AUTH_BASE_COST = 7_500
29+
REGULAR_GAS_CREATE = 9_000
30+
2231

2332
class EIP8037(BaseFork):
2433
"""EIP-8037 class."""
@@ -33,7 +42,6 @@ def cost_per_state_byte(cls) -> int:
3342
@classmethod
3443
def sstore_state_gas(cls) -> int:
3544
"""Return state gas for a zero-to-nonzero SSTORE (EIP-8037)."""
36-
STATE_BYTES_PER_STORAGE_SET = 64 # noqa: N806
3745
return STATE_BYTES_PER_STORAGE_SET * cls.cost_per_state_byte()
3846

3947
@classmethod
@@ -57,13 +65,6 @@ def gas_costs(cls) -> GasCosts:
5765
"""
5866
cpsb = cls.cost_per_state_byte()
5967
parent = super(EIP8037, cls).gas_costs()
60-
# EIP-8037 state byte sizes (EELS amsterdam/vm/gas.py)
61-
STATE_BYTES_PER_STORAGE_SET = 64 # noqa: N806
62-
STATE_BYTES_PER_NEW_ACCOUNT = 120 # noqa: N806
63-
STATE_BYTES_PER_AUTH_BASE = 23 # noqa: N806
64-
# EIP-8037 regular gas base costs
65-
PER_AUTH_BASE_COST = 7_500 # noqa: N806
66-
REGULAR_GAS_CREATE = 9_000 # noqa: N806
6768
new_acct = STATE_BYTES_PER_NEW_ACCOUNT * cpsb
6869
return replace(
6970
parent,
@@ -258,8 +259,6 @@ def transaction_intrinsic_state_gas(
258259
- Auth: (NEW_ACCOUNT + AUTH_BASE) * cpsb
259260
"""
260261
cpsb = cls.cost_per_state_byte()
261-
STATE_BYTES_PER_NEW_ACCOUNT = 120 # noqa: N806
262-
STATE_BYTES_PER_AUTH_BASE = 23 # noqa: N806
263262
state_gas = 0
264263
if contract_creation:
265264
state_gas += STATE_BYTES_PER_NEW_ACCOUNT * cpsb
@@ -324,7 +323,7 @@ def _calculate_sstore_state_gas(
324323
and current_value != new_value
325324
and original_value == 0
326325
):
327-
return 64 * cpsb
326+
return STATE_BYTES_PER_STORAGE_SET * cpsb
328327
return 0
329328

330329
@classmethod
@@ -384,7 +383,7 @@ def _calculate_sstore_state_refund(
384383
if current_value != new_value:
385384
if original_value == new_value:
386385
if original_value == 0:
387-
return 64 * cpsb
386+
return STATE_BYTES_PER_STORAGE_SET * cpsb
388387
return 0
389388

390389
@classmethod
@@ -412,9 +411,11 @@ def _calculate_selfdestruct_state_refund(
412411
]
413412
state_refund = 0
414413
if self_destructed_account:
415-
state_refund = 120 * cpsb
414+
state_refund = STATE_BYTES_PER_NEW_ACCOUNT * cpsb
416415
state_refund += (
417-
64 * cpsb * self_destructed_account_storage_slot_count
416+
STATE_BYTES_PER_STORAGE_SET
417+
* cpsb
418+
* self_destructed_account_storage_slot_count
418419
)
419420
state_refund += cpsb * self_destructed_account_code_deposit
420421
return state_refund

src/ethereum/forks/amsterdam/vm/eoa_delegation.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,9 @@ def set_delegation(message: Message) -> Uint:
205205
if authority_nonce != auth.nonce:
206206
continue
207207

208-
# For existing accounts no account creation happens, so the
209-
# NEW_ACCOUNT portion of the auth's intrinsic state cost is
210-
# refunded. The reservoir credit lowers `tx_gas_used` for the
211-
# sender; debiting `intrinsic_state_gas` lowers
212-
# `block_state_gas_used` so the block accounting matches the
213-
# state work that actually occurred.
208+
# For existing accounts, no account creation needed.
209+
# Refund the account creation state gas to the reservoir.
210+
# intrinsic_state_gas is immutable after validation.
214211
if account_exists(tx_state, authority):
215212
refund = STATE_BYTES_PER_NEW_ACCOUNT * COST_PER_STATE_BYTE
216213
message.state_gas_reservoir += refund

tests/amsterdam/eip7708_eth_transfer_logs/test_burn_logs.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def test_selfdestruct_to_self_same_tx(
8383
state_test: StateTestFiller,
8484
env: Environment,
8585
pre: Alloc,
86+
fork: Fork,
8687
sender: EOA,
8788
contract_balance: int,
8889
create_opcode: Op,
@@ -125,7 +126,9 @@ def test_selfdestruct_to_self_same_tx(
125126
sender=sender,
126127
to=factory,
127128
value=contract_balance,
128-
gas_limit=300_000,
129+
# Same-tx CREATE+SELFDESTRUCT charges NEW_ACCOUNT state gas
130+
# under EIP-8037 (0 otherwise).
131+
gas_limit=200_000 + fork.gas_costs().NEW_ACCOUNT,
129132
expected_receipt=TransactionReceipt(logs=expected_logs),
130133
)
131134

@@ -144,6 +147,7 @@ def test_selfdestruct_to_different_address_same_tx(
144147
state_test: StateTestFiller,
145148
env: Environment,
146149
pre: Alloc,
150+
fork: Fork,
147151
sender: EOA,
148152
contract_balance: int,
149153
create_opcode: Op,
@@ -189,7 +193,9 @@ def test_selfdestruct_to_different_address_same_tx(
189193
sender=sender,
190194
to=factory,
191195
value=contract_balance,
192-
gas_limit=300_000,
196+
# Same-tx CREATE+SELFDESTRUCT charges NEW_ACCOUNT state gas
197+
# under EIP-8037 (0 otherwise).
198+
gas_limit=200_000 + fork.gas_costs().NEW_ACCOUNT,
193199
expected_receipt=TransactionReceipt(logs=expected_logs),
194200
)
195201

@@ -222,6 +228,7 @@ def test_selfdestruct_same_tx_via_call(
222228
state_test: StateTestFiller,
223229
env: Environment,
224230
pre: Alloc,
231+
fork: Fork,
225232
sender: EOA,
226233
to_self: bool,
227234
call_twice: bool,
@@ -303,7 +310,9 @@ def test_selfdestruct_same_tx_via_call(
303310
sender=sender,
304311
to=factory,
305312
value=0,
306-
gas_limit=300_000,
313+
# CREATE-then-CALL with same-tx SELFDESTRUCT charges
314+
# NEW_ACCOUNT state gas under EIP-8037 (0 otherwise).
315+
gas_limit=200_000 + fork.gas_costs().NEW_ACCOUNT,
307316
expected_receipt=TransactionReceipt(logs=expected_logs),
308317
)
309318

tests/amsterdam/eip7708_eth_transfer_logs/test_fork_transition.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,14 @@ def test_burn_log_at_fork_transition(
119119
beneficiary: Account(balance=contract_balance * 3),
120120
}
121121

122-
# Same-tx CREATE+SELFDESTRUCT charges NEW_ACCOUNT state gas; the
123-
# pre-transition block has plenty of headroom. `fork` is a
124-
# TransitionFork here, so resolve to the post-transition fork
125-
# (timestamp 15_001) where the larger NEW_ACCOUNT applies.
126-
post_fork = fork.fork_at(timestamp=15_001)
122+
# `fork` is a TransitionFork here; resolve to the post-transition
123+
# fork (where the larger NEW_ACCOUNT applies) so the gas budget
124+
# covers the same-tx CREATE+SELFDESTRUCT on the post-transition
125+
# block. The pre-transition block has plenty of headroom.
126+
pre_transition_timestamp = 14_999
127+
transition_timestamp = 15_000
128+
post_transition_timestamp = 15_001
129+
post_fork = fork.fork_at(timestamp=post_transition_timestamp)
127130
gas_limit = 200_000 + post_fork.gas_costs().NEW_ACCOUNT
128131
blocks = [
129132
Block(
@@ -137,7 +140,13 @@ def test_burn_log_at_fork_transition(
137140
)
138141
],
139142
)
140-
for i, ts in enumerate([14_999, 15_000, 15_001])
143+
for i, ts in enumerate(
144+
[
145+
pre_transition_timestamp,
146+
transition_timestamp,
147+
post_transition_timestamp,
148+
]
149+
)
141150
]
142151

143152
blockchain_test(pre=pre, blocks=blocks, post=post)

tests/amsterdam/eip7708_eth_transfer_logs/test_transfer_logs.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,16 +1095,20 @@ def test_transfer_with_all_tx_types(
10951095
state_test: StateTestFiller,
10961096
env: Environment,
10971097
pre: Alloc,
1098+
fork: Fork,
10981099
sender: EOA,
10991100
typed_transaction: Transaction,
11001101
) -> None:
11011102
"""Test that ETH transfers emit logs for all transaction types."""
11021103
recipient = pre.nonexistent_account()
11031104
transfer_amount = 1000
11041105

1106+
# Sending value to a nonexistent recipient charges NEW_ACCOUNT
1107+
# state gas under EIP-8037 (0 otherwise).
11051108
tx = typed_transaction.copy(
11061109
to=recipient,
11071110
value=transfer_amount,
1111+
gas_limit=typed_transaction.gas_limit + fork.gas_costs().NEW_ACCOUNT,
11081112
expected_receipt=TransactionReceipt(
11091113
logs=[transfer_log(sender, recipient, transfer_amount)]
11101114
),

tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists_cross_index.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
BlockchainTestFiller,
2323
Bytecode,
2424
Fork,
25+
Header,
2526
Op,
2627
Transaction,
2728
)
@@ -275,9 +276,17 @@ def test_bal_noop_write_filtering(
275276
}
276277
)
277278

279+
# Header `gas_used = max(regular, state)` for the single tx; the
280+
# SSTORE metadata pins each transition so `regular_cost`/`state_cost`
281+
# return the actual fork-priced amount.
282+
expected_regular = intrinsic_cost + test_code.regular_cost(fork)
283+
expected_state = test_code.state_cost(fork)
278284
block = Block(
279285
txs=[tx],
280286
expected_block_access_list=expected_block_access_list,
287+
header_verify=Header(
288+
gas_used=max(expected_regular, expected_state),
289+
),
281290
)
282291

283292
blockchain_test(

tests/amsterdam/eip8037_state_creation_gas_cost_increase/spec.py

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ class Spec:
3939
# EIP-7825 transaction gas limit cap
4040
TX_MAX_GAS_LIMIT = 2**24 # 16,777,216
4141

42-
# TODO: replace with dynamic cost_per_state_byte(gas_limit) once
43-
# non-default block gas limits are supported in the test framework.
44-
COST_PER_STATE_BYTE = 1530 # at 150M reference block gas limit
42+
# CPSB is a fixed parameter derived from a 150M reference block
43+
# gas limit and a 120 GiB/year target state growth.
44+
COST_PER_STATE_BYTE = 1530
4545

4646
# State bytes per operation
4747
STATE_BYTES_PER_NEW_ACCOUNT = 120
@@ -52,25 +52,3 @@ class Spec:
5252
REGULAR_GAS_CREATE = 9000
5353
PER_AUTH_BASE_COST = 7500
5454
GAS_COLD_STORAGE_WRITE = 5000
55-
56-
# EIP-8037 state gas pricing parameters
57-
TARGET_STATE_GROWTH_PER_YEAR = 100 * 1024**3
58-
BLOCKS_PER_YEAR = 2_628_000
59-
COST_PER_STATE_BYTE_SIGNIFICANT_BITS = 5
60-
COST_PER_STATE_BYTE_OFFSET = 9578
61-
62-
@staticmethod
63-
def cost_per_state_byte(gas_limit: int) -> int:
64-
"""Calculate the dynamic state gas cost per byte."""
65-
numerator = gas_limit * Spec.BLOCKS_PER_YEAR
66-
denominator = 2 * Spec.TARGET_STATE_GROWTH_PER_YEAR
67-
raw = (numerator + denominator - 1) // denominator
68-
shifted = raw + Spec.COST_PER_STATE_BYTE_OFFSET
69-
shift = max(
70-
shifted.bit_length() - Spec.COST_PER_STATE_BYTE_SIGNIFICANT_BITS,
71-
0,
72-
)
73-
quantized = (shifted >> shift) << shift
74-
if quantized > Spec.COST_PER_STATE_BYTE_OFFSET:
75-
return quantized - Spec.COST_PER_STATE_BYTE_OFFSET
76-
return 1

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2464,14 +2464,46 @@ def test_create_collision_burned_gas_counted_in_block_regular(
24642464
sender=pre.fund_eoa(),
24652465
)
24662466

2467-
# CPSB-agnostic baseline: block_state_gas is zero for this tx, so
2468-
# header.gas_used equals the regular-gas total. The forwarded
2469-
# create_message_gas is `(gas_limit - NEW_ACCOUNT) * 63 // 64`,
2470-
# plus a CPSB-independent constant covering intrinsic + factory
2471-
# opcodes. A mutation that drops the burned create_message_gas
2472-
# from regular accounting would reduce this value.
2467+
# CPSB-agnostic baseline: block_state_gas is zero for this tx (the
2468+
# collision refunds the NEW_ACCOUNT state charge), so header.gas_used
2469+
# equals the regular-gas total. Decompose the parent + inner frame
2470+
# accounting from fork APIs so the baseline tracks future cost
2471+
# changes automatically.
2472+
intrinsic = fork.transaction_intrinsic_cost_calculator()()
24732473
new_account = fork.gas_costs().NEW_ACCOUNT
2474-
baseline_gas_used = (gas_limit - new_account) * 63 // 64 + 472
2474+
create_base = fork.gas_costs().OPCODE_CREATE_BASE
2475+
# POP + STOP run in the parent frame after CREATE returns; their
2476+
# cost comes out of the 1/64 retained gas.
2477+
post_create_static = (Op.POP + Op.STOP).gas_cost(fork)
2478+
# factory_code.gas_cost(fork) folds NEW_ACCOUNT into the CREATE op
2479+
# (state gas is treated as part of the opcode total). Strip it
2480+
# back out and split off the post-CREATE tail to isolate the
2481+
# pre-CREATE static gas.
2482+
factory_pre_create = (
2483+
factory_code.gas_cost(fork)
2484+
- new_account
2485+
- create_base
2486+
- post_create_static
2487+
)
2488+
# MSTORE writes the initcode at memory[0:32] (one word).
2489+
memory_expansion = fork.memory_expansion_gas_calculator()(new_bytes=32)
2490+
# gas_left at the moment NEW_ACCOUNT spills into the regular pool
2491+
# (reservoir is empty for tx_gas_limit < TX_MAX_GAS_LIMIT).
2492+
gas_at_create_after_state = (
2493+
gas_limit
2494+
- intrinsic
2495+
- factory_pre_create
2496+
- memory_expansion
2497+
- create_base
2498+
- new_account
2499+
)
2500+
# Inner burns 63/64 of the available gas on collision; the parent
2501+
# retains 1/64. The state-spill of NEW_ACCOUNT is refunded back to
2502+
# gas_left on collision (nets zero). Post-CREATE consumes from the
2503+
# retained pool. A mutation that drops the burned forwarded gas
2504+
# from regular accounting would reduce this baseline.
2505+
retained = gas_at_create_after_state // 64
2506+
baseline_gas_used = gas_limit - retained - new_account + post_create_static
24752507

24762508
blockchain_test(
24772509
pre=pre,

0 commit comments

Comments
 (0)