Skip to content

Commit 58368d6

Browse files
authored
chore(tests): fix modexp conftest for tx gas limit cap (#2111)
* chore(tests): fix modexp conftest for tx gas limit cap. * chore(tests): add zero base, zero exp, large modulus tx gas cap case. * chore(tests): pr comments. * chore(tests): fail on gas over tx gas cap unexpected. * chore(tests): move z16 over tx gas limit case. * chore(tests): fix update comment table.
1 parent b3cc636 commit 58368d6

File tree

2 files changed

+83
-27
lines changed

2 files changed

+83
-27
lines changed

tests/osaka/eip7883_modexp_gas_increase/conftest.py

Lines changed: 56 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
import pytest
66

77
from ethereum_test_forks import Fork, Osaka
8-
from ethereum_test_tools import Account, Address, Alloc, Bytes, Storage, Transaction, keccak256
8+
from ethereum_test_tools import (
9+
Account,
10+
Address,
11+
Alloc,
12+
Bytes,
13+
Environment,
14+
Storage,
15+
Transaction,
16+
keccak256,
17+
)
918
from ethereum_test_tools.vm.opcode import Opcodes as Op
1019

1120
from ...byzantium.eip198_modexp_precompile.helpers import ModExpInput
@@ -40,12 +49,50 @@ def call_contract_post_storage() -> Storage:
4049

4150

4251
@pytest.fixture
43-
def call_succeeds() -> bool:
52+
def total_tx_gas_needed(
53+
fork: Fork, modexp_expected: bytes, modexp_input: ModExpInput, precompile_gas: int
54+
) -> int:
55+
"""Calculate total tx gas needed for the transaction."""
56+
intrinsic_gas_cost_calculator = fork.transaction_intrinsic_cost_calculator()
57+
memory_expansion_gas_calculator = fork.memory_expansion_gas_calculator()
58+
sstore_gas = fork.gas_costs().G_STORAGE_SET * (len(modexp_expected) // 32)
59+
extra_gas = 100_000
60+
61+
return (
62+
extra_gas
63+
+ intrinsic_gas_cost_calculator(calldata=bytes(modexp_input))
64+
+ memory_expansion_gas_calculator(new_bytes=len(bytes(modexp_input)))
65+
+ precompile_gas
66+
+ sstore_gas
67+
)
68+
69+
70+
@pytest.fixture
71+
def exceeds_tx_gas_cap(total_tx_gas_needed: int, fork: Fork, env: Environment) -> bool:
72+
"""Determine if total gas requirements exceed transaction gas cap."""
73+
tx_gas_limit_cap = fork.transaction_gas_limit_cap() or env.gas_limit
74+
return total_tx_gas_needed > tx_gas_limit_cap
75+
76+
77+
@pytest.fixture
78+
def expected_tx_cap_fail() -> bool:
79+
"""Whether this test is expected to fail due to transaction gas cap."""
80+
return False
81+
82+
83+
@pytest.fixture
84+
def call_succeeds(exceeds_tx_gas_cap: bool, expected_tx_cap_fail: bool) -> bool:
4485
"""
45-
By default, depending on the expected output, we can deduce if the call is expected to succeed
46-
or fail.
86+
Determine whether the ModExp precompile call should succeed or fail.
87+
By default, depending on the expected output, we assume it succeeds.
88+
Under EIP-7825, transactions requiring more gas than the cap should fail only if unexpected.
4789
"""
48-
return True
90+
if exceeds_tx_gas_cap and not expected_tx_cap_fail:
91+
pytest.fail(
92+
"Test unexpectedly exceeds tx gas cap. "
93+
"Either mark with `expected_tx_cap_fail=True` or adjust inputs."
94+
)
95+
return not exceeds_tx_gas_cap
4996

5097

5198
@pytest.fixture
@@ -111,7 +158,7 @@ def gas_measure_contract(
111158
Op.CALLDATACOPY(dest_offset=0, offset=0, size=Op.CALLDATASIZE)
112159
+ Op.SSTORE(call_contract_post_storage.store_next(call_succeeds), call_result_measurement)
113160
+ Op.SSTORE(
114-
call_contract_post_storage.store_next(len(modexp_expected)),
161+
call_contract_post_storage.store_next(len(modexp_expected) if call_succeeds else 0),
115162
Op.RETURNDATASIZE(),
116163
)
117164
)
@@ -174,28 +221,10 @@ def tx(
174221

175222

176223
@pytest.fixture
177-
def tx_gas_limit(
178-
fork: Fork, modexp_expected: bytes, modexp_input: ModExpInput, precompile_gas: int
179-
) -> int:
224+
def tx_gas_limit(total_tx_gas_needed: int, fork: Fork, env: Environment) -> int:
180225
"""Transaction gas limit used for the test (Can be overridden in the test)."""
181-
intrinsic_gas_cost_calculator = fork.transaction_intrinsic_cost_calculator()
182-
memory_expansion_gas_calculator = fork.memory_expansion_gas_calculator()
183-
sstore_gas = fork.gas_costs().G_STORAGE_SET * (len(modexp_expected) // 32)
184-
extra_gas = 100_000
185-
186-
total_gas = (
187-
extra_gas
188-
+ intrinsic_gas_cost_calculator(calldata=bytes(modexp_input))
189-
+ memory_expansion_gas_calculator(new_bytes=len(bytes(modexp_input)))
190-
+ precompile_gas
191-
+ sstore_gas
192-
)
193-
194-
tx_gas_limit_cap = fork.transaction_gas_limit_cap()
195-
196-
if tx_gas_limit_cap is not None:
197-
return min(tx_gas_limit_cap, total_gas)
198-
return total_gas
226+
tx_gas_limit_cap = fork.transaction_gas_limit_cap() or env.gas_limit
227+
return min(tx_gas_limit_cap, total_tx_gas_needed)
199228

200229

201230
@pytest.fixture

tests/osaka/eip7883_modexp_gas_increase/test_modexp_thresholds.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ def create_modexp_variable_gas_test_cases():
416416
# │ Z13 │ L │ > │ A │ False │ 32768 │ Large zero base, zero exp, non-zero modulus │
417417
# │ Z14 │ S │ = │ C │ False │253936 │ Base, large zero exp, zero modulus │
418418
# │ Z15 │ L │ < │ B │ False │ 32768 │ Base, small exp, large zero modulus │
419+
# │ Z16 │ L │ < │ C │ False │520060928│ Zero base, zero exp, large modulus (gas cap) |
419420
# │ M1 │ L │ = │ D │ False │ 98176 │ Maximum values stress test │
420421
# │ M2 │ S │ = │ B │ True │ 500 │ Max base/mod, small exponent │
421422
# │ M3 │ L │ < │ D │ False │ 98176 │ Small base, max exponent/mod │
@@ -460,3 +461,29 @@ def test_modexp_variable_gas_cost(
460461
):
461462
"""Test ModExp variable gas cost."""
462463
state_test(pre=pre, tx=tx, post=post)
464+
465+
466+
@pytest.mark.parametrize(
467+
"modexp_input,modexp_expected,expected_tx_cap_fail",
468+
[
469+
pytest.param(
470+
ModExpInput(base="00" * 32, exponent="00" * 1024, modulus="01" * 1024),
471+
bytes.fromhex("00" * 1023 + "01"),
472+
True,
473+
id="Z16-gas-cap-test",
474+
),
475+
],
476+
)
477+
@pytest.mark.valid_from("Berlin")
478+
def test_modexp_variable_gas_cost_exceed_tx_gas_cap(state_test, pre, tx, post):
479+
"""
480+
Test ModExp variable gas cost.
481+
Inputs with an expected gas cost over the EIP-7825 tx gas cap.
482+
"""
483+
# Test case coverage table (gas cap):
484+
# ┌─────┬──────┬─────┬──────┬───────┬─────────┬───────────────────────────────────────────────┐
485+
# │ ID │ Comp │ Rel │ Iter │ Clamp │ Gas │ Description │
486+
# ├─────┼──────┼─────┼──────┼───────┼─────────┼───────────────────────────────────────────────┤
487+
# │ Z16 │ L │ < │ C │ False │520060928│ Zero base, zero exp, large modulus (gas cap) |
488+
# └─────┴──────┴─────┴──────┴───────┴─────────┴───────────────────────────────────────────────┘
489+
state_test(pre=pre, tx=tx, post=post)

0 commit comments

Comments
 (0)