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
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
)

from .spec import ref_spec_7928
from .test_block_access_lists_eip4788 import (
SYSTEM_ADDRESS,
beacon_root_system_call_expectations,
)

REFERENCE_SPEC_GIT_PATH = ref_spec_7928.git_path
REFERENCE_SPEC_VERSION = ref_spec_7928.version
Expand Down Expand Up @@ -746,6 +750,43 @@ def test_bal_invalid_missing_withdrawal_account_empty_block(
)


@pytest.mark.valid_from("Amsterdam")
@pytest.mark.exception_test
def test_bal_invalid_surplus_system_address_from_system_call(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
) -> None:
"""
Test that clients reject a BAL that includes SYSTEM_ADDRESS solely because
it was the synthetic caller of a pre-execution system operation.
"""
block_timestamp = 12
beacon_root = Hash(0xABCDEF)

blockchain_test(
pre=pre,
post={},
blocks=[
Block(
txs=[],
parent_beacon_block_root=beacon_root,
timestamp=block_timestamp,
exception=BlockException.INVALID_BLOCK_ACCESS_LIST,
expected_block_access_list=BlockAccessListExpectation(
account_expectations=beacon_root_system_call_expectations(
block_timestamp,
beacon_root,
)
).modify(
append_account(
BalAccountChange(address=SYSTEM_ADDRESS),
)
),
)
],
)


@pytest.mark.valid_from("Amsterdam")
@pytest.mark.exception_test
def test_bal_invalid_balance_value(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
)

from .spec import ref_spec_7928
from .test_block_access_lists_eip4788 import SYSTEM_ADDRESS

REFERENCE_SPEC_GIT_PATH = ref_spec_7928.git_path
REFERENCE_SPEC_VERSION = ref_spec_7928.version
Expand Down Expand Up @@ -321,6 +322,69 @@ def test_bal_balance_and_oog(
)


@pytest.mark.parametrize(
"access_opcode",
[
pytest.param(lambda target: Op.BALANCE(target), id="balance"),
pytest.param(lambda target: Op.EXTCODESIZE(target), id="extcodesize"),
pytest.param(lambda target: Op.EXTCODEHASH(target), id="extcodehash"),
pytest.param(
lambda target: Op.EXTCODECOPY(target, 0, 0, 0),
id="extcodecopy",
),
pytest.param(lambda target: Op.CALL(address=target), id="call"),
pytest.param(
lambda target: Op.STATICCALL(address=target),
id="staticcall",
),
],
)
def test_bal_account_touch_system_address(
pre: Alloc,
blockchain_test: BlockchainTestFiller,
access_opcode: Callable[[Address], Bytecode],
) -> None:
"""
Ensure a regular transaction that explicitly touches SYSTEM_ADDRESS via
an account-accessing opcode includes SYSTEM_ADDRESS as an account-only
BAL entry.

This confirms that SYSTEM_ADDRESS is only excluded from the BAL when it
appears as the synthetic caller of a pre-execution system call; a real
EVM state access from user code MUST still land in the BAL.
"""
alice = pre.fund_eoa()
pre.fund_address(SYSTEM_ADDRESS, amount=1)

toucher = pre.deploy_contract(code=access_opcode(SYSTEM_ADDRESS) + Op.STOP)

tx = Transaction(
sender=alice,
to=toucher,
gas_limit=200_000,
)

block = Block(
txs=[tx],
expected_block_access_list=BlockAccessListExpectation(
account_expectations={
toucher: BalAccountExpectation.empty(),
SYSTEM_ADDRESS: BalAccountExpectation.empty(),
}
),
)

blockchain_test(
pre=pre,
blocks=[block],
post={
alice: Account(nonce=1),
toucher: Account(),
SYSTEM_ADDRESS: Account(balance=1),
},
)


@pytest.mark.parametrize(
"fails_at_extcodesize",
[True, False],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
| `test_bal_sstore_static_context` | SSTORE in static context must not leak storage reads into BAL | Contract A STATICCALLs Contract B which attempts `SSTORE`. Parametrized: `original_value` (0, nonzero) to catch clients that perform the implicit SLOAD before the static check. | Contract B IS in BAL (accessed via STATICCALL) but **MUST NOT** have `storage_reads`. | βœ… 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_account_touch_system_address` | Ensure BAL includes `SYSTEM_ADDRESS` when a regular transaction touches it via any account-accessing opcode | Alice calls a contract that executes one of `BALANCE`, `EXTCODESIZE`, `EXTCODEHASH`, `EXTCODECOPY`, `CALL`, or `STATICCALL` against `SYSTEM_ADDRESS`. Parametrized over the six opcodes. | BAL **MUST** include `SYSTEM_ADDRESS` as an account-only entry for every opcode because the address experienced a real EVM state access. This is distinct from excluding the synthetic system-operation caller. | βœ… 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 |
| `test_bal_delegatecall_no_delegation_and_oog_before_target_access` | Ensure BAL handles OOG before target access and success for non-delegated DELEGATECALL | Parametrized: target warm/cold, memory expansion, OOG boundary (before_target_access/success). | OOG: target in BAL ONLY if pre-warmed. Success: target always in BAL. | βœ… Completed |
| `test_bal_delegatecall_7702_delegation_and_oog` | Ensure BAL handles OOG at all 4 boundaries for DELEGATECALL to 7702 delegated accounts | Parametrized: target warm/cold, delegation warm/cold, memory expansion, OOG boundary (before_target_access/after_target_access/success_minus_1/success). | OOG before: neither in BAL. OOG after & success_minus_1: target in BAL, delegation NOT in BAL (static check optimization). Success: all in BAL. | βœ… Completed |
Expand Down Expand Up @@ -142,6 +143,7 @@
| `test_bal_invalid_missing_coinbase` | Verify clients reject blocks where BAL is missing the coinbase/fee recipient | Alice sends 100 wei to Bob with gas_price > base_fee so coinbase (charlie) receives a non-zero tip. BAL modifier removes charlie's entry entirely. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** include fee recipients that received tips in the BAL. | βœ… Completed |
| `test_bal_invalid_coinbase_balance_value` | Verify clients reject blocks where BAL has an incorrect balance for the coinbase/fee recipient | Same setup as test_bal_invalid_missing_coinbase. BAL modifier changes charlie's post-balance from the actual tip to 999. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Clients **MUST** validate coinbase balance values match actual fee accounting (priority fee x gas used). | βœ… Completed |
| `test_bal_invalid_extraneous_coinbase` | Verify clients reject blocks with a spurious coinbase entry when coinbase received no fees | Parameterized: (1) empty_block: no txs, no withdrawals β€” only system contracts in valid BAL, (2) withdrawal_only: no txs, one withdrawal to a different address β€” withdrawals don't pay fees so coinbase is still untouched. BAL modifier appends spurious coinbase entry with empty changes. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST` exception. Coinbase **MUST NOT** appear in BAL when it receives no transaction tips, even if the block has other state-modifying activity (withdrawals). | βœ… Completed |
| `test_bal_invalid_surplus_system_address_from_system_call` | Verify clients reject a BAL containing `SYSTEM_ADDRESS` solely due to a system operation caller | Empty block with an EIP-4788 pre-execution system call to `BEACON_ROOTS_ADDRESS`. Helper `beacon_root_system_call_expectations` builds the valid baseline BAL: `BEACON_ROOTS_ADDRESS` has timestamp/root storage changes at `block_access_index=0`, while `SYSTEM_ADDRESS` is marked absent (`None`). The fixture BAL is then corrupted by appending an empty `SYSTEM_ADDRESS` entry. | Block **MUST** be rejected with `INVALID_BLOCK_ACCESS_LIST`. The synthetic system caller address **MUST NOT** be accepted unless it experienced an actual state access. | βœ… Completed |
| `test_bal_2935_simple` | Ensure BAL captures EIP-2935 history storage writes during pre-execution system call alongside normal transactions | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), `SYSTEM_ADDRESS` calls `HISTORY_STORAGE_ADDRESS` to store parent block hash. | BAL **MUST** include `HISTORY_STORAGE_ADDRESS` with `storage_changes` (ring buffer slot 0, empty `slot_changes` since parent hash is framework-computed); `SYSTEM_ADDRESS` **MUST NOT** be included in BAL. At `block_access_index=1`: Alice with `nonce_changes`, Charlie with `balance_changes` (10 wei). At `block_access_index=2`: Bob with `nonce_changes`, Charlie with `balance_changes` (20 wei total). | βœ… Completed |
| `test_bal_2935_empty_block` | Ensure BAL captures EIP-2935 history storage writes in empty block | Block with no transactions. At block start (pre-execution), `SYSTEM_ADDRESS` calls `HISTORY_STORAGE_ADDRESS` to store parent block hash. | BAL **MUST** include `HISTORY_STORAGE_ADDRESS` with `storage_changes` (ring buffer slot 0, empty `slot_changes`); `SYSTEM_ADDRESS` **MUST NOT** be included in BAL. No transaction-related BAL entries. | βœ… Completed |
| `test_bal_2935_query` | Ensure BAL captures storage reads when querying EIP-2935 historical block hashes (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 (empty, stores genesis hash via system call). Block 2: Oracle contract queries `HISTORY_STORAGE_ADDRESS` with block number. Two block number scenarios (valid=0 genesis hash, invalid=1042 out of range) and value (0 or 100 wei). Valid query (block_number=0): reads genesis hash slot, oracle writes returned value. If value > 0, history storage contract receives balance. Invalid query (block_number=1042, out of range): reverts before storage access, oracle has implicit SLOAD recorded, value stays in oracle (not transferred to history storage). | Block 2 BAL **MUST** include: Valid case at `block_access_index=1`: `HISTORY_STORAGE_ADDRESS` with `storage_reads` [slot 0] and `balance_changes` if value > 0, oracle with `storage_changes` (empty `slot_changes`). Invalid case at `block_access_index=1`: `HISTORY_STORAGE_ADDRESS` with NO `storage_reads` (reverts before access) and NO `balance_changes`, oracle with `storage_reads` [0], NO `storage_changes`, and `balance_changes` if value > 0 (value stays in oracle). Alice with `nonce_changes` at `block_access_index=1`. | βœ… Completed |
Expand Down