diff --git a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py index 2a52558ee87..dbd5f0c0142 100644 --- a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py +++ b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_create.py @@ -527,6 +527,51 @@ def test_create_tx_intrinsic_gas_boundary( state_test(pre=pre, post={}, tx=tx) +@pytest.mark.exception_test +@pytest.mark.parametrize( + "extra_gas", + [ + pytest.param(0, id="at_regular_intrinsic"), + pytest.param(1, id="one_above_regular_intrinsic"), + ], +) +@pytest.mark.valid_from("EIP8037") +def test_create_tx_below_total_intrinsic( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + extra_gas: int, +) -> None: + """ + Reject CREATE tx when gas_limit covers regular but not state intrinsic. + + EIP-8037 splits the CREATE intrinsic into regular and state + components (`STATE_BYTES_PER_NEW_ACCOUNT * COST_PER_STATE_BYTE`). + `test_create_tx_intrinsic_gas_boundary` pins the upper boundary + (`total - 1`); this pins the lower end — `intrinsic_regular` and + one gas above — to catch implementations that omit the state + component from the pre-validate check. + """ + total_intrinsic = fork.transaction_intrinsic_cost_calculator()( + contract_creation=True, + ) + intrinsic_state = fork.transaction_intrinsic_state_gas( + contract_creation=True, + ) + intrinsic_regular = total_intrinsic - intrinsic_state + gas_limit = intrinsic_regular + extra_gas + assert gas_limit < total_intrinsic + + tx = Transaction( + to=None, + gas_limit=gas_limit, + sender=pre.fund_eoa(), + error=TransactionException.INTRINSIC_GAS_TOO_LOW, + ) + + state_test(pre=pre, post={}, tx=tx) + + @pytest.mark.valid_from("EIP8037") def test_code_deposit_oog_preserves_parent_reservoir( state_test: StateTestFiller, diff --git a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_set_code.py b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_set_code.py index 6c26a3b2b1e..30b783546af 100644 --- a/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_set_code.py +++ b/tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_set_code.py @@ -90,6 +90,71 @@ def test_authorization_state_gas_scaling( state_test(env=env, pre=pre, post={}, tx=tx) +@pytest.mark.exception_test +@pytest.mark.parametrize( + "num_auths", + [ + pytest.param(1, id="single_auth"), + pytest.param(2, id="two_auths"), + pytest.param(3, id="three_auths"), + ], +) +@pytest.mark.parametrize( + "extra_gas", + [ + pytest.param(0, id="at_regular_intrinsic"), + pytest.param(1, id="one_above_regular_intrinsic"), + ], +) +@pytest.mark.valid_from("EIP8037") +def test_set_code_tx_below_total_intrinsic( + state_test: StateTestFiller, + pre: Alloc, + fork: Fork, + num_auths: int, + extra_gas: int, +) -> None: + """ + Reject set_code tx when gas_limit covers regular but not state intrinsic. + + EIP-8037 charges each authorization a state component + `(STATE_BYTES_PER_NEW_ACCOUNT + STATE_BYTES_PER_AUTH_BASE) * + COST_PER_STATE_BYTE`; total intrinsic = `regular + N * state` for + N authorizations. Sweep N = 1, 2, 3 and pin gas_limit at the + lower end of the rejected interval to catch implementations that + omit the state component from the pre-validate check. + """ + intrinsic_state = fork.transaction_intrinsic_state_gas( + authorization_count=num_auths, + ) + total_intrinsic = fork.transaction_intrinsic_cost_calculator()( + authorization_list_or_count=num_auths, + ) + intrinsic_regular = total_intrinsic - intrinsic_state + gas_limit = intrinsic_regular + extra_gas + assert gas_limit < total_intrinsic + + contract = pre.deploy_contract(code=Op.STOP) + authorization_list = [ + AuthorizationTuple( + address=contract, + nonce=1, + signer=pre.fund_eoa(), + ) + for _ in range(num_auths) + ] + + tx = Transaction( + to=contract, + gas_limit=gas_limit, + authorization_list=authorization_list, + sender=pre.fund_eoa(), + error=TransactionException.INTRINSIC_GAS_TOO_LOW, + ) + + state_test(pre=pre, post={}, tx=tx) + + @pytest.mark.valid_from("EIP8037") def test_existing_account_refund( state_test: StateTestFiller,