Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/configs/eels_resolutions.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@
"Amsterdam": {
"git_url": "https://github.com/fselmo/execution-specs.git",
"branch": "feat/amsterdam-fork-and-block-access-lists",
"commit": "f3ad59980a68fe5974244f41bcda7b22294bf98b"
"commit": "3496e719b515bc82f35c42f83e78d426d31283ba"
}
}
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Test fixtures for use by clients are available for each release on the [Github r
### πŸ§ͺ Test Cases

- 🐞Fix BALs opcode OOG test vectors by updating the Amsterdam commit hash in specs and validating appropriately on the testing side ([#2293](https://github.com/ethereum/execution-spec-tests/pull/2293)).
- ✨Fix test vector for BALs SSTORE with OOG by pointing to updated specs; add new boundary conditions cases for SSTORE w/ OOG ([#2297](https://github.com/ethereum/execution-spec-tests/pull/2297)).

## [v5.3.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.3.0) - 2025-10-09

Expand Down
12 changes: 7 additions & 5 deletions src/ethereum_test_types/block_access_list/expectations.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,9 @@ def _compare_account_expectations(

# Check if explicitly set to empty but actual has values
if not expected_list and actual_list:
raise AssertionError(f"Expected {field_name} to be empty but found {actual_list}")
raise BlockAccessListValidationError(
f"Expected {field_name} to be empty but found {actual_list}"
)

if field_name == "storage_reads":
# storage_reads is a simple list of StorageKey
Expand All @@ -365,7 +367,7 @@ def _compare_account_expectations(
actual_idx += 1

if not found:
raise AssertionError(
raise BlockAccessListValidationError(
f"Storage read {expected_read} not found or not in correct order. "
f"Actual reads: {actual_list}"
)
Expand Down Expand Up @@ -403,7 +405,7 @@ def _compare_account_expectations(
slot_actual_idx += 1

if not slot_found:
raise AssertionError(
raise BlockAccessListValidationError(
f"Storage change {expected_change} not found "
f"or not in correct order in slot "
f"{expected_slot.slot}. "
Expand All @@ -416,7 +418,7 @@ def _compare_account_expectations(
actual_idx += 1

if not found:
raise AssertionError(
raise BlockAccessListValidationError(
f"Storage slot {expected_slot.slot} not found "
f"or not in correct order. Actual slots: "
f"{[s.slot for s in actual_list]}"
Expand Down Expand Up @@ -453,7 +455,7 @@ def _compare_account_expectations(
actual_idx += 1

if not found:
raise AssertionError(
raise BlockAccessListValidationError(
f"{item_type.capitalize()} change {exp_tuple} not found "
f"or not in correct order. Actual changes: {actual_tuples}"
)
Expand Down
2 changes: 1 addition & 1 deletion src/pytest_plugins/eels_resolutions.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,6 @@
"Amsterdam": {
"git_url": "https://github.com/fselmo/execution-specs.git",
"branch": "feat/amsterdam-fork-and-block-access-lists",
"commit": "f3ad59980a68fe5974244f41bcda7b22294bf98b"
"commit": "3496e719b515bc82f35c42f83e78d426d31283ba"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
preventing consensus issues.
"""

from enum import Enum

import pytest

from ethereum_test_forks import Fork
Expand Down Expand Up @@ -44,28 +46,45 @@
pytestmark = pytest.mark.valid_from("Amsterdam")


class OutOfGasAt(Enum):
"""
Enumeration of specific gas boundaries where OOG can occur.
"""

EIP_2200_STIPEND = "oog_at_eip2200_stipend"
EIP_2200_STIPEND_PLUS_1 = "oog_at_eip2200_stipend_plus_1"
EXACT_GAS_MINUS_1 = "oog_at_exact_gas_minus_1"


@pytest.mark.parametrize(
"fails_at_sstore", [True, False], ids=["oog_at_sstore", "successful_sstore"]
"out_of_gas_at",
[
OutOfGasAt.EIP_2200_STIPEND,
OutOfGasAt.EIP_2200_STIPEND_PLUS_1,
OutOfGasAt.EXACT_GAS_MINUS_1,
None, # no oog, successful sstore
],
ids=lambda x: x.value if x else "successful_sstore",
)
def test_bal_sstore_and_oog(
pre: Alloc,
blockchain_test: BlockchainTestFiller,
fork: Fork,
fails_at_sstore: bool,
out_of_gas_at: OutOfGasAt | None,
) -> None:
"""
Ensure BAL handles SSTORE and OOG during SSTORE appropriately.
Test BAL recording with SSTORE at various OOG boundaries and success.

1. OOG at EIP-2200 stipend check & implicit SLOAD -> no BAL changes
2. OOG post EIP-2200 stipend check & implicit SLOAD -> storage read in BAL
3. OOG at exact gas minus 1 -> storage read in BAL
4. exact gas (success) -> storage write in BAL
"""
alice = pre.fund_eoa()
gas_costs = fork.gas_costs()

# Create contract that attempts SSTORE to cold storage slot 0x01
storage_contract_code = Bytecode(
Op.PUSH1(0x42) # Value to store
+ Op.PUSH1(0x01) # Storage slot (cold)
+ Op.SSTORE # Store value in slot - this will OOG
+ Op.STOP
)
storage_contract_code = Bytecode(Op.SSTORE(0x01, 0x42))

storage_contract = pre.deploy_contract(code=storage_contract_code)

Expand All @@ -75,33 +94,53 @@ def test_bal_sstore_and_oog(
# Costs:
# - PUSH1 (value and slot) = G_VERY_LOW * 2
# - SSTORE cold (to zero slot) = G_STORAGE_SET + G_COLD_SLOAD
sstore_cold_cost = gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD
sload_cost = gas_costs.G_COLD_SLOAD
sstore_cost = gas_costs.G_STORAGE_SET
sstore_cold_cost = sstore_cost + sload_cost
push_cost = gas_costs.G_VERY_LOW * 2
tx_gas_limit = intrinsic_gas_cost + push_cost + sstore_cold_cost

if fails_at_sstore:
# subtract 1 gas to ensure OOG at SSTORE
tx_gas_limit -= 1
stipend = gas_costs.G_CALL_STIPEND

if out_of_gas_at == OutOfGasAt.EIP_2200_STIPEND:
# 2300 after PUSHes (fails stipend check: 2300 <= 2300)
tx_gas_limit = intrinsic_gas_cost + push_cost + stipend
elif out_of_gas_at == OutOfGasAt.EIP_2200_STIPEND_PLUS_1:
# 2301 after PUSHes (passes stipend, does SLOAD, fails charge_gas)
tx_gas_limit = intrinsic_gas_cost + push_cost + stipend + 1
elif out_of_gas_at == OutOfGasAt.EXACT_GAS_MINUS_1:
# fail at charge_gas() at exact gas - 1 (boundary condition)
tx_gas_limit = intrinsic_gas_cost + push_cost + sstore_cold_cost - 1
else:
# exact gas for successful SSTORE
tx_gas_limit = intrinsic_gas_cost + push_cost + sstore_cold_cost

tx = Transaction(
sender=alice,
to=storage_contract,
gas_limit=tx_gas_limit,
)

# Storage read recorded only if we pass the stipend check and reach
# implicit SLOAD (STIPEND_PLUS_1 and EXACT_GAS_MINUS_1)
expect_storage_read = out_of_gas_at in (
OutOfGasAt.EIP_2200_STIPEND_PLUS_1,
OutOfGasAt.EXACT_GAS_MINUS_1,
)
expect_storage_write = out_of_gas_at is None

block = Block(
txs=[tx],
expected_block_access_list=BlockAccessListExpectation(
account_expectations={
storage_contract: BalAccountExpectation(
storage_changes=[]
if fails_at_sstore
else [
storage_changes=[
BalStorageSlot(
slot=0x01,
slot_changes=[BalStorageChange(tx_index=1, post_value=0x42)],
),
]
if expect_storage_write
else [],
storage_reads=[0x01] if expect_storage_read else [],
)
}
),
Expand All @@ -112,7 +151,7 @@ def test_bal_sstore_and_oog(
blocks=[block],
post={
alice: Account(nonce=1),
storage_contract: Account(storage={} if fails_at_sstore else {0x01: 0x42}),
storage_contract: Account(storage={0x01: 0x42} if expect_storage_write else {}),
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
| `test_bal_7702_invalid_nonce_authorization` | Ensure BAL handles failed authorization due to wrong nonce | `Relayer` sends sponsored transaction to Bob (10 wei transfer succeeds) but Alice's authorization to delegate to `Oracle` uses incorrect nonce, causing silent authorization failure | BAL **MUST** include Alice with empty changes (account access), Bob with `balance_changes` (receives 10 wei), Relayer with `nonce_changes`. **MUST NOT** include `Oracle` (authorization failed, no delegation) | βœ… Completed |
| `test_bal_7702_invalid_chain_id_authorization` | Ensure BAL handles failed authorization due to wrong chain id | `Relayer` sends sponsored transaction to Bob (10 wei transfer succeeds) but Alice's authorization to delegate to `Oracle` uses incorrect chain id, causing authorization failure before account access | BAL **MUST** include Bob with `balance_changes` (receives 10 wei), Relayer with `nonce_changes`. **MUST NOT** include Alice (authorization fails before loading account) or `Oracle` (authorization failed, no delegation) | βœ… Completed |
| `test_bal_7702_delegated_via_call_opcode` | Ensure BAL captures delegation target when a contract uses *CALL opcodes to call a delegated account | Pre-deployed contract `Alice` delegated to `Oracle`. `Caller` contract uses CALL/CALLCODE/DELEGATECALL/STATICCALL to call `Alice`. Bob sends transaction to `Caller`. | BAL **MUST** include Bob: `nonce_changes`. `Caller`: empty changes (account access). `Alice`: empty changes (account access - delegated account being called). `Oracle`: empty changes (delegation target access). | βœ… Completed |
| `test_bal_sstore_and_oog` | Ensure BAL handles OOG during SSTORE execution correctly | Alice calls contract that attempts `SSTORE` to cold slot `0x01`. Parameterized: (1) OOG at SSTORE opcode (insufficient gas), (2) Successful SSTORE execution. | For OOG case: BAL **MUST NOT** contain slot `0x01` in `storage_changes` since storage wasn't modified. For success case: BAL **MUST** contain slot `0x01` in `storage_changes`. | βœ… Completed |
| `test_bal_sstore_and_oog` | Ensure BAL handles OOG during SSTORE execution at various gas boundaries (EIP-2200 stipend and implicit SLOAD) | Alice calls contract that attempts `SSTORE` to cold slot `0x01`. Parameterized: (1) OOG at EIP-2200 stipend check (2300 gas after PUSH opcodes) - fails before implicit SLOAD, (2) OOG at stipend + 1 (2301 gas) - passes stipend check but fails after implicit SLOAD, (3) OOG at exact gas - 1, (4) Successful SSTORE with exact gas. | For case (1): BAL **MUST NOT** include slot `0x01` in `storage_reads` or `storage_changes` (fails before implicit SLOAD). For cases (2) and (3): BAL **MUST** include slot `0x01` in `storage_reads` (implicit SLOAD occurred) but **MUST NOT** include in `storage_changes` (write didn't complete). For case (4): BAL **MUST** include slot `0x01` in `storage_changes` only (successful write; read is filtered by builder). | βœ… Completed |
| `test_bal_sload_and_oog` | Ensure BAL handles OOG during SLOAD execution correctly | Alice calls contract that attempts `SLOAD` from cold slot `0x01`. Parameterized: (1) OOG at SLOAD opcode (insufficient gas), (2) Successful SLOAD execution. | For OOG case: BAL **MUST NOT** contain slot `0x01` in `storage_reads` since storage wasn't accessed. For success case: BAL **MUST** contain slot `0x01` in `storage_reads`. | βœ… Completed |
| `test_bal_balance_and_oog` | Ensure BAL handles OOG during BALANCE opcode execution correctly | Alice calls contract that attempts `BALANCE` opcode on cold target account. Parameterized: (1) OOG at BALANCE opcode (insufficient gas), (2) Successful BALANCE execution. | For OOG case: BAL **MUST NOT** include target account (wasn't accessed). For success case: BAL **MUST** include target account in `account_changes`. | βœ… Completed |
| `test_bal_extcodesize_and_oog` | Ensure BAL handles OOG during EXTCODESIZE opcode execution correctly | Alice calls contract that attempts `EXTCODESIZE` opcode on cold target contract. Parameterized: (1) OOG at EXTCODESIZE opcode (insufficient gas), (2) Successful EXTCODESIZE execution. | For OOG case: BAL **MUST NOT** include target contract (wasn't accessed). For success case: BAL **MUST** include target contract in `account_changes`. | βœ… Completed |
Expand Down
Loading