Skip to content

Commit ceede1d

Browse files
qu0bfselmo
andcommitted
Qu0b/add bal test cases (#1812)
* rebase onto upstream * chore(fix) balance check in call before target access * merge test cases with usptream * merge test cases with usptream * chore(fix) format with ruff * chore(fix) revert call changes and add target to bal * merge test cases with usptream * improve wording * chore(formate) fix formatting and line length * refactor(test-tests): Use pre API where possible; explicit check for none in BAL * refactor(test-tests): Refactor opcode tests to bal opcodes test file --------- Co-authored-by: fselmo <fselmo2@gmail.com>
1 parent 4f2f925 commit ceede1d

File tree

4 files changed

+1204
-4
lines changed

4 files changed

+1204
-4
lines changed

tests/amsterdam/eip7928_block_level_access_lists/test_block_access_lists.py

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Account,
99
Address,
1010
Alloc,
11+
AuthorizationTuple,
1112
BalAccountExpectation,
1213
BalBalanceChange,
1314
BalCodeChange,
@@ -23,6 +24,7 @@
2324
Header,
2425
Op,
2526
Transaction,
27+
add_kzg_version,
2628
compute_create_address,
2729
)
2830

@@ -2145,3 +2147,335 @@ def test_bal_cross_block_ripemd160_state_leak(
21452147
ripemd160_addr: Account(balance=1),
21462148
},
21472149
)
2150+
2151+
2152+
def test_bal_all_transaction_types(
2153+
pre: Alloc,
2154+
blockchain_test: BlockchainTestFiller,
2155+
) -> None:
2156+
"""
2157+
Test BAL with all 5 tx types in single block.
2158+
2159+
Types: Legacy, EIP-2930, EIP-1559, Blob, EIP-7702.
2160+
Each tx writes to contract storage. Access list addresses are pre-warmed
2161+
but NOT in BAL.
2162+
2163+
Expected BAL:
2164+
- All 5 senders: nonce_changes
2165+
- Contracts 0-3: storage_changes
2166+
- Alice (7702): nonce_changes, code_changes (delegation), storage_changes
2167+
- Oracle: empty (delegation target, accessed)
2168+
"""
2169+
from tests.prague.eip7702_set_code_tx.spec import Spec as Spec7702
2170+
2171+
# Create senders for each transaction type
2172+
sender_0 = pre.fund_eoa() # Type 0 - Legacy
2173+
sender_1 = pre.fund_eoa() # Type 1 - Access List
2174+
sender_2 = pre.fund_eoa() # Type 2 - EIP-1559
2175+
sender_3 = pre.fund_eoa() # Type 3 - Blob
2176+
sender_4 = pre.fund_eoa() # Type 4 - EIP-7702
2177+
2178+
# Create contracts for each tx type (except 7702 which uses delegation)
2179+
contract_code = Op.SSTORE(0x01, Op.CALLDATALOAD(0)) + Op.STOP
2180+
contract_0 = pre.deploy_contract(code=contract_code)
2181+
contract_1 = pre.deploy_contract(code=contract_code)
2182+
contract_2 = pre.deploy_contract(code=contract_code)
2183+
contract_3 = pre.deploy_contract(code=contract_code)
2184+
2185+
# For Type 4 (EIP-7702): Alice delegates to Oracle
2186+
alice = pre.fund_eoa()
2187+
oracle = pre.deploy_contract(code=Op.SSTORE(0x01, 0x05) + Op.STOP)
2188+
2189+
# Dummy address to warm in access list
2190+
warmed_address = pre.fund_eoa(amount=1)
2191+
2192+
# TX1: Type 0 - Legacy transaction
2193+
tx_type_0 = Transaction(
2194+
ty=0,
2195+
sender=sender_0,
2196+
to=contract_0,
2197+
gas_limit=100_000,
2198+
gas_price=10,
2199+
data=Hash(0x01), # Value to store
2200+
)
2201+
2202+
# TX2: Type 1 - Access List transaction (EIP-2930)
2203+
tx_type_1 = Transaction(
2204+
ty=1,
2205+
sender=sender_1,
2206+
to=contract_1,
2207+
gas_limit=100_000,
2208+
gas_price=10,
2209+
data=Hash(0x02),
2210+
access_list=[
2211+
AccessList(
2212+
address=warmed_address,
2213+
storage_keys=[],
2214+
)
2215+
],
2216+
)
2217+
2218+
# TX3: Type 2 - EIP-1559 Dynamic fee transaction
2219+
tx_type_2 = Transaction(
2220+
ty=2,
2221+
sender=sender_2,
2222+
to=contract_2,
2223+
gas_limit=100_000,
2224+
max_fee_per_gas=50,
2225+
max_priority_fee_per_gas=5,
2226+
data=Hash(0x03),
2227+
)
2228+
2229+
# TX4: Type 3 - Blob transaction (EIP-4844)
2230+
# Blob versioned hashes need KZG version prefix (0x01)
2231+
blob_hashes = add_kzg_version([Hash(0xBEEF)], 1)
2232+
tx_type_3 = Transaction(
2233+
ty=3,
2234+
sender=sender_3,
2235+
to=contract_3,
2236+
gas_limit=100_000,
2237+
max_fee_per_gas=50,
2238+
max_priority_fee_per_gas=5,
2239+
max_fee_per_blob_gas=10,
2240+
blob_versioned_hashes=blob_hashes,
2241+
data=Hash(0x04),
2242+
)
2243+
2244+
# TX5: Type 4 - EIP-7702 Set Code transaction
2245+
tx_type_4 = Transaction(
2246+
ty=4,
2247+
sender=sender_4,
2248+
to=alice,
2249+
gas_limit=100_000,
2250+
max_fee_per_gas=50,
2251+
max_priority_fee_per_gas=5,
2252+
authorization_list=[
2253+
AuthorizationTuple(
2254+
address=oracle,
2255+
nonce=0,
2256+
signer=alice,
2257+
)
2258+
],
2259+
)
2260+
2261+
block = Block(
2262+
txs=[tx_type_0, tx_type_1, tx_type_2, tx_type_3, tx_type_4],
2263+
expected_block_access_list=BlockAccessListExpectation(
2264+
account_expectations={
2265+
# Type 0 sender
2266+
sender_0: BalAccountExpectation(
2267+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
2268+
),
2269+
# Type 1 sender
2270+
sender_1: BalAccountExpectation(
2271+
nonce_changes=[BalNonceChange(tx_index=2, post_nonce=1)],
2272+
),
2273+
# Type 2 sender
2274+
sender_2: BalAccountExpectation(
2275+
nonce_changes=[BalNonceChange(tx_index=3, post_nonce=1)],
2276+
),
2277+
# Type 3 sender
2278+
sender_3: BalAccountExpectation(
2279+
nonce_changes=[BalNonceChange(tx_index=4, post_nonce=1)],
2280+
),
2281+
# Type 4 sender
2282+
sender_4: BalAccountExpectation(
2283+
nonce_changes=[BalNonceChange(tx_index=5, post_nonce=1)],
2284+
),
2285+
# Contract touched by Type 0
2286+
contract_0: BalAccountExpectation(
2287+
storage_changes=[
2288+
BalStorageSlot(
2289+
slot=0x01,
2290+
slot_changes=[
2291+
BalStorageChange(tx_index=1, post_value=0x01)
2292+
],
2293+
)
2294+
],
2295+
),
2296+
# Contract touched by Type 1
2297+
contract_1: BalAccountExpectation(
2298+
storage_changes=[
2299+
BalStorageSlot(
2300+
slot=0x01,
2301+
slot_changes=[
2302+
BalStorageChange(tx_index=2, post_value=0x02)
2303+
],
2304+
)
2305+
],
2306+
),
2307+
# Note: warmed_address from access_list is NOT in BAL
2308+
# because access lists pre-warm but don't record in BAL
2309+
# Contract touched by Type 2
2310+
warmed_address: None, # explicit check
2311+
contract_2: BalAccountExpectation(
2312+
storage_changes=[
2313+
BalStorageSlot(
2314+
slot=0x01,
2315+
slot_changes=[
2316+
BalStorageChange(tx_index=3, post_value=0x03)
2317+
],
2318+
)
2319+
],
2320+
),
2321+
# Contract touched by Type 3
2322+
contract_3: BalAccountExpectation(
2323+
storage_changes=[
2324+
BalStorageSlot(
2325+
slot=0x01,
2326+
slot_changes=[
2327+
BalStorageChange(tx_index=4, post_value=0x04)
2328+
],
2329+
)
2330+
],
2331+
),
2332+
# Alice (Type 4 delegation target, executes oracle code)
2333+
alice: BalAccountExpectation(
2334+
nonce_changes=[BalNonceChange(tx_index=5, post_nonce=1)],
2335+
code_changes=[
2336+
BalCodeChange(
2337+
tx_index=5,
2338+
new_code=Spec7702.delegation_designation(oracle),
2339+
)
2340+
],
2341+
storage_changes=[
2342+
BalStorageSlot(
2343+
slot=0x01,
2344+
slot_changes=[
2345+
BalStorageChange(tx_index=5, post_value=0x05)
2346+
],
2347+
)
2348+
],
2349+
),
2350+
# Oracle (accessed via delegation)
2351+
oracle: BalAccountExpectation.empty(),
2352+
}
2353+
),
2354+
)
2355+
2356+
blockchain_test(
2357+
pre=pre,
2358+
blocks=[block],
2359+
post={
2360+
sender_0: Account(nonce=1),
2361+
sender_1: Account(nonce=1),
2362+
sender_2: Account(nonce=1),
2363+
sender_3: Account(nonce=1),
2364+
sender_4: Account(nonce=1),
2365+
contract_0: Account(storage={0x01: 0x01}),
2366+
contract_1: Account(storage={0x01: 0x02}),
2367+
contract_2: Account(storage={0x01: 0x03}),
2368+
contract_3: Account(storage={0x01: 0x04}),
2369+
alice: Account(
2370+
nonce=1,
2371+
code=Spec7702.delegation_designation(oracle),
2372+
storage={0x01: 0x05},
2373+
),
2374+
},
2375+
)
2376+
2377+
2378+
def test_bal_lexicographic_address_ordering(
2379+
pre: Alloc,
2380+
blockchain_test: BlockchainTestFiller,
2381+
) -> None:
2382+
"""
2383+
Test BAL enforces strict lexicographic byte-wise address ordering.
2384+
2385+
Addresses: addr_low (0x...01), addr_mid (0x...0100), addr_high (0x01...00).
2386+
Endian-trap: addr_endian_low (0x01...02), addr_endian_high (0x02...01).
2387+
Contract touches them in reverse order to verify sorting.
2388+
2389+
Expected BAL order: low < mid < high < endian_low < endian_high.
2390+
Catches endianness bugs in address comparison.
2391+
"""
2392+
alice = pre.fund_eoa()
2393+
2394+
# Create addresses with specific byte patterns for lexicographic testing
2395+
# In lexicographic (byte-wise) order: low < mid < high
2396+
# addr_low: 0x00...01 (rightmost byte = 0x01)
2397+
# addr_mid: 0x00...0100 (second-rightmost byte = 0x01)
2398+
# addr_high: 0x01...00 (leftmost byte = 0x01)
2399+
addr_low = Address("0x0000000000000000000000000000000000000001")
2400+
addr_mid = Address("0x0000000000000000000000000000000000000100")
2401+
addr_high = Address("0x0100000000000000000000000000000000000000")
2402+
2403+
# Endian-trap addresses: byte-reversals to catch byte-order bugs
2404+
# addr_endian_low: 0x01...02 (0x01 at byte 0, 0x02 at byte 19)
2405+
# addr_endian_high: 0x02...01 (0x02 at byte 0, 0x01 at byte 19)
2406+
# Note: reverse(addr_endian_low) = addr_endian_high
2407+
# Correct order: endian_low < endian_high (0x01 < 0x02 at byte 0)
2408+
# Reversed bytes would incorrectly get opposite order
2409+
addr_endian_low = Address("0x0100000000000000000000000000000000000002")
2410+
addr_endian_high = Address("0x0200000000000000000000000000000000000001")
2411+
2412+
# Give each address a balance so they exist
2413+
addr_balance = 100
2414+
pre[addr_low] = Account(balance=addr_balance)
2415+
pre[addr_mid] = Account(balance=addr_balance)
2416+
pre[addr_high] = Account(balance=addr_balance)
2417+
pre[addr_endian_low] = Account(balance=addr_balance)
2418+
pre[addr_endian_high] = Account(balance=addr_balance)
2419+
2420+
# Contract that accesses addresses in REVERSE lexicographic order
2421+
# to verify sorting is applied correctly
2422+
contract_code = (
2423+
Op.BALANCE(addr_high) # Access high first
2424+
+ Op.POP
2425+
+ Op.BALANCE(addr_low) # Access low second
2426+
+ Op.POP
2427+
+ Op.BALANCE(addr_mid) # Access mid third
2428+
+ Op.POP
2429+
# Access endian-trap addresses in reverse order
2430+
+ Op.BALANCE(addr_endian_high) # Access endian_high before endian_low
2431+
+ Op.POP
2432+
+ Op.BALANCE(addr_endian_low)
2433+
+ Op.POP
2434+
+ Op.STOP
2435+
)
2436+
2437+
contract = pre.deploy_contract(code=contract_code)
2438+
2439+
tx = Transaction(
2440+
sender=alice,
2441+
to=contract,
2442+
gas_limit=1_000_000,
2443+
)
2444+
2445+
# BAL must be sorted lexicographically by address bytes
2446+
# Order: low < mid < high < endian_low < endian_high
2447+
# (sorted by raw address bytes, regardless of access order)
2448+
block = Block(
2449+
txs=[tx],
2450+
expected_block_access_list=BlockAccessListExpectation(
2451+
account_expectations={
2452+
alice: BalAccountExpectation(
2453+
nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)],
2454+
),
2455+
contract: BalAccountExpectation.empty(),
2456+
# These addresses appear in BAL due to BALANCE access
2457+
# The expectation framework verifies correct order
2458+
addr_low: BalAccountExpectation.empty(),
2459+
addr_mid: BalAccountExpectation.empty(),
2460+
addr_high: BalAccountExpectation.empty(),
2461+
# Endian-trap addresses: must be sorted correctly despite being
2462+
# byte-reversals of each other
2463+
addr_endian_low: BalAccountExpectation.empty(),
2464+
addr_endian_high: BalAccountExpectation.empty(),
2465+
}
2466+
),
2467+
)
2468+
2469+
blockchain_test(
2470+
pre=pre,
2471+
blocks=[block],
2472+
post={
2473+
alice: Account(nonce=1),
2474+
contract: Account(),
2475+
addr_low: Account(balance=addr_balance),
2476+
addr_mid: Account(balance=addr_balance),
2477+
addr_high: Account(balance=addr_balance),
2478+
addr_endian_low: Account(balance=addr_balance),
2479+
addr_endian_high: Account(balance=addr_balance),
2480+
},
2481+
)

0 commit comments

Comments
 (0)