Skip to content

Commit 4d47a44

Browse files
authored
feat(tests): EIP-8037 reject tx when gas_limit covers regular but not state intrinsic (#2876)
1 parent 8ccd21f commit 4d47a44

2 files changed

Lines changed: 113 additions & 0 deletions

File tree

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,51 @@ def test_create_tx_intrinsic_gas_boundary(
520520
state_test(pre=pre, post={}, tx=tx)
521521

522522

523+
@pytest.mark.exception_test
524+
@pytest.mark.parametrize(
525+
"extra_gas",
526+
[
527+
pytest.param(0, id="at_regular_intrinsic"),
528+
pytest.param(1, id="one_above_regular_intrinsic"),
529+
],
530+
)
531+
@pytest.mark.valid_from("EIP8037")
532+
def test_create_tx_below_total_intrinsic(
533+
state_test: StateTestFiller,
534+
pre: Alloc,
535+
fork: Fork,
536+
extra_gas: int,
537+
) -> None:
538+
"""
539+
Reject CREATE tx when gas_limit covers regular but not state intrinsic.
540+
541+
EIP-8037 splits the CREATE intrinsic into regular and state
542+
components (`STATE_BYTES_PER_NEW_ACCOUNT * COST_PER_STATE_BYTE`).
543+
`test_create_tx_intrinsic_gas_boundary` pins the upper boundary
544+
(`total - 1`); this pins the lower end — `intrinsic_regular` and
545+
one gas above — to catch implementations that omit the state
546+
component from the pre-validate check.
547+
"""
548+
total_intrinsic = fork.transaction_intrinsic_cost_calculator()(
549+
contract_creation=True,
550+
)
551+
intrinsic_state = fork.transaction_intrinsic_state_gas(
552+
contract_creation=True,
553+
)
554+
intrinsic_regular = total_intrinsic - intrinsic_state
555+
gas_limit = intrinsic_regular + extra_gas
556+
assert gas_limit < total_intrinsic
557+
558+
tx = Transaction(
559+
to=None,
560+
gas_limit=gas_limit,
561+
sender=pre.fund_eoa(),
562+
error=TransactionException.INTRINSIC_GAS_TOO_LOW,
563+
)
564+
565+
state_test(pre=pre, post={}, tx=tx)
566+
567+
523568
@pytest.mark.valid_from("EIP8037")
524569
def test_code_deposit_oog_preserves_parent_reservoir(
525570
state_test: StateTestFiller,

tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_set_code.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,74 @@ def test_authorization_state_gas_scaling(
9090
state_test(env=env, pre=pre, post={}, tx=tx)
9191

9292

93+
@pytest.mark.exception_test
94+
@pytest.mark.parametrize(
95+
"num_auths",
96+
[
97+
pytest.param(1, id="single_auth"),
98+
pytest.param(2, id="two_auths"),
99+
pytest.param(3, id="three_auths"),
100+
],
101+
)
102+
@pytest.mark.parametrize(
103+
"extra_gas",
104+
[
105+
pytest.param(0, id="at_regular_intrinsic"),
106+
pytest.param(1, id="one_above_regular_intrinsic"),
107+
pytest.param(-1, id="one_below_total_intrinsic"),
108+
],
109+
)
110+
@pytest.mark.valid_from("EIP8037")
111+
def test_set_code_tx_below_total_intrinsic(
112+
state_test: StateTestFiller,
113+
pre: Alloc,
114+
fork: Fork,
115+
num_auths: int,
116+
extra_gas: int,
117+
) -> None:
118+
"""
119+
Reject set_code tx when gas_limit covers regular but not state intrinsic.
120+
121+
EIP-8037 charges each authorization a state component
122+
`(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) *
123+
COST_PER_STATE_BYTE`; total intrinsic = `regular + N * state` for
124+
N authorizations. Sweep N = 1, 2, 3 and pin gas_limit at the
125+
lower end of the rejected interval to catch implementations that
126+
omit the state component from the pre-validate check.
127+
"""
128+
intrinsic_state = fork.transaction_intrinsic_state_gas(
129+
authorization_count=num_auths,
130+
)
131+
total_intrinsic = fork.transaction_intrinsic_cost_calculator()(
132+
authorization_list_or_count=num_auths,
133+
)
134+
intrinsic_regular = total_intrinsic - intrinsic_state
135+
gas_limit = (
136+
intrinsic_regular if extra_gas >= 0 else total_intrinsic
137+
) + extra_gas
138+
assert gas_limit < total_intrinsic
139+
140+
contract = pre.deploy_contract(code=Op.STOP)
141+
authorization_list = [
142+
AuthorizationTuple(
143+
address=contract,
144+
nonce=1,
145+
signer=pre.fund_eoa(),
146+
)
147+
for _ in range(num_auths)
148+
]
149+
150+
tx = Transaction(
151+
to=contract,
152+
gas_limit=gas_limit,
153+
authorization_list=authorization_list,
154+
sender=pre.fund_eoa(),
155+
error=TransactionException.INTRINSIC_GAS_TOO_LOW,
156+
)
157+
158+
state_test(pre=pre, post={}, tx=tx)
159+
160+
93161
@pytest.mark.valid_from("EIP8037")
94162
def test_existing_account_refund(
95163
state_test: StateTestFiller,

0 commit comments

Comments
 (0)