|
8 | 8 | Account, |
9 | 9 | Address, |
10 | 10 | Alloc, |
| 11 | + AuthorizationTuple, |
11 | 12 | BalAccountExpectation, |
12 | 13 | BalBalanceChange, |
13 | 14 | BalCodeChange, |
|
23 | 24 | Header, |
24 | 25 | Op, |
25 | 26 | Transaction, |
| 27 | + add_kzg_version, |
26 | 28 | compute_create_address, |
27 | 29 | ) |
28 | 30 |
|
@@ -2145,3 +2147,335 @@ def test_bal_cross_block_ripemd160_state_leak( |
2145 | 2147 | ripemd160_addr: Account(balance=1), |
2146 | 2148 | }, |
2147 | 2149 | ) |
| 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