|
31 | 31 | REFERENCE_SPEC_GIT_PATH = ref_spec_7928.git_path
|
32 | 32 | REFERENCE_SPEC_VERSION = ref_spec_7928.version
|
33 | 33 |
|
| 34 | +pytestmark = pytest.mark.valid_from("Amsterdam") |
| 35 | + |
34 | 36 |
|
35 |
| -@pytest.mark.valid_from("Amsterdam") |
36 | 37 | def test_bal_nonce_changes(
|
37 | 38 | pre: Alloc,
|
38 | 39 | blockchain_test: BlockchainTestFiller,
|
@@ -68,7 +69,6 @@ def test_bal_nonce_changes(
|
68 | 69 | )
|
69 | 70 |
|
70 | 71 |
|
71 |
| -@pytest.mark.valid_from("Amsterdam") |
72 | 72 | def test_bal_balance_changes(
|
73 | 73 | pre: Alloc,
|
74 | 74 | blockchain_test: BlockchainTestFiller,
|
@@ -128,7 +128,6 @@ def test_bal_balance_changes(
|
128 | 128 | )
|
129 | 129 |
|
130 | 130 |
|
131 |
| -@pytest.mark.valid_from("Amsterdam") |
132 | 131 | def test_bal_storage_writes(
|
133 | 132 | pre: Alloc,
|
134 | 133 | blockchain_test: BlockchainTestFiller,
|
@@ -174,7 +173,6 @@ def test_bal_storage_writes(
|
174 | 173 | )
|
175 | 174 |
|
176 | 175 |
|
177 |
| -@pytest.mark.valid_from("Amsterdam") |
178 | 176 | def test_bal_storage_reads(
|
179 | 177 | pre: Alloc,
|
180 | 178 | blockchain_test: BlockchainTestFiller,
|
@@ -213,7 +211,6 @@ def test_bal_storage_reads(
|
213 | 211 | )
|
214 | 212 |
|
215 | 213 |
|
216 |
| -@pytest.mark.valid_from("Amsterdam") |
217 | 214 | def test_bal_code_changes(
|
218 | 215 | pre: Alloc,
|
219 | 216 | blockchain_test: BlockchainTestFiller,
|
@@ -290,7 +287,6 @@ def test_bal_code_changes(
|
290 | 287 | )
|
291 | 288 |
|
292 | 289 |
|
293 |
| -@pytest.mark.valid_from("Amsterdam") |
294 | 290 | @pytest.mark.parametrize("self_destruct_in_same_tx", [True, False], ids=["same_tx", "new_tx"])
|
295 | 291 | @pytest.mark.parametrize("pre_funded", [True, False], ids=["pre_funded", "not_pre_funded"])
|
296 | 292 | def test_bal_self_destruct(
|
@@ -424,3 +420,314 @@ def test_bal_self_destruct(
|
424 | 420 | blocks=[block],
|
425 | 421 | post=post,
|
426 | 422 | )
|
| 423 | + |
| 424 | + |
| 425 | +@pytest.mark.parametrize( |
| 426 | + "account_access_opcode", |
| 427 | + [ |
| 428 | + pytest.param(lambda target_addr: Op.BALANCE(target_addr), id="balance"), |
| 429 | + pytest.param(lambda target_addr: Op.EXTCODESIZE(target_addr), id="extcodesize"), |
| 430 | + pytest.param(lambda target_addr: Op.EXTCODECOPY(target_addr, 0, 0, 32), id="extcodecopy"), |
| 431 | + pytest.param(lambda target_addr: Op.EXTCODEHASH(target_addr), id="extcodehash"), |
| 432 | + pytest.param(lambda target_addr: Op.CALL(0, target_addr, 50, 0, 0, 0, 0), id="call"), |
| 433 | + pytest.param( |
| 434 | + lambda target_addr: Op.CALLCODE(0, target_addr, 0, 0, 0, 0, 0), id="callcode" |
| 435 | + ), |
| 436 | + pytest.param( |
| 437 | + lambda target_addr: Op.DELEGATECALL(0, target_addr, 0, 0, 0, 0), id="delegatecall" |
| 438 | + ), |
| 439 | + pytest.param( |
| 440 | + lambda target_addr: Op.STATICCALL(0, target_addr, 0, 0, 0, 0), id="staticcall" |
| 441 | + ), |
| 442 | + ], |
| 443 | +) |
| 444 | +def test_bal_account_access_target( |
| 445 | + pre: Alloc, |
| 446 | + blockchain_test: BlockchainTestFiller, |
| 447 | + account_access_opcode, |
| 448 | +): |
| 449 | + """Ensure BAL captures target address of account access opcodes.""" |
| 450 | + alice = pre.fund_eoa() |
| 451 | + target_contract = pre.deploy_contract(code=Op.STOP) |
| 452 | + |
| 453 | + oracle_contract = pre.deploy_contract( |
| 454 | + balance=100, |
| 455 | + code=account_access_opcode(target_contract), |
| 456 | + ) |
| 457 | + |
| 458 | + tx = Transaction(sender=alice, to=oracle_contract, gas_limit=5_000_000, gas_price=0xA) |
| 459 | + |
| 460 | + block = Block( |
| 461 | + txs=[tx], |
| 462 | + expected_block_access_list=BlockAccessListExpectation( |
| 463 | + account_expectations={ |
| 464 | + alice: BalAccountExpectation( |
| 465 | + nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)] |
| 466 | + ), |
| 467 | + target_contract: BalAccountExpectation(), |
| 468 | + oracle_contract: BalAccountExpectation(), |
| 469 | + } |
| 470 | + ), |
| 471 | + ) |
| 472 | + |
| 473 | + blockchain_test(pre=pre, blocks=[block], post={}) |
| 474 | + |
| 475 | + |
| 476 | +def test_bal_call_with_value_transfer( |
| 477 | + pre: Alloc, |
| 478 | + blockchain_test: BlockchainTestFiller, |
| 479 | +): |
| 480 | + """ |
| 481 | + Ensure BAL captures balance changes from CALL opcode with |
| 482 | + value transfer. |
| 483 | + """ |
| 484 | + alice = pre.fund_eoa() |
| 485 | + bob = pre.fund_eoa(amount=0) |
| 486 | + |
| 487 | + # Oracle contract that uses CALL to transfer 100 wei to Bob |
| 488 | + oracle_code = Op.CALL(0, bob, 100, 0, 0, 0, 0) |
| 489 | + oracle_contract = pre.deploy_contract(code=oracle_code, balance=200) |
| 490 | + |
| 491 | + tx = Transaction(sender=alice, to=oracle_contract, gas_limit=1_000_000, gas_price=0xA) |
| 492 | + |
| 493 | + block = Block( |
| 494 | + txs=[tx], |
| 495 | + expected_block_access_list=BlockAccessListExpectation( |
| 496 | + account_expectations={ |
| 497 | + alice: BalAccountExpectation( |
| 498 | + nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)], |
| 499 | + ), |
| 500 | + oracle_contract: BalAccountExpectation( |
| 501 | + balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)], |
| 502 | + ), |
| 503 | + bob: BalAccountExpectation( |
| 504 | + balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)], |
| 505 | + ), |
| 506 | + } |
| 507 | + ), |
| 508 | + ) |
| 509 | + |
| 510 | + blockchain_test(pre=pre, blocks=[block], post={}) |
| 511 | + |
| 512 | + |
| 513 | +def test_bal_callcode_with_value_transfer( |
| 514 | + pre: Alloc, |
| 515 | + blockchain_test: BlockchainTestFiller, |
| 516 | +): |
| 517 | + """ |
| 518 | + Ensure BAL captures balance changes from CALLCODE opcode with |
| 519 | + value transfer. |
| 520 | + """ |
| 521 | + alice = pre.fund_eoa() |
| 522 | + bob = pre.fund_eoa(amount=0) |
| 523 | + |
| 524 | + # TargetContract sends 100 wei to bob |
| 525 | + target_code = Op.CALL(0, bob, 100, 0, 0, 0, 0) |
| 526 | + target_contract = pre.deploy_contract(code=target_code) |
| 527 | + |
| 528 | + # Oracle contract that uses CALLCODE to execute TargetContract's code |
| 529 | + oracle_code = Op.CALLCODE(50_000, target_contract, 100, 0, 0, 0, 0) |
| 530 | + oracle_contract = pre.deploy_contract(code=oracle_code, balance=200) |
| 531 | + |
| 532 | + tx = Transaction(sender=alice, to=oracle_contract, gas_limit=1_000_000, gas_price=0xA) |
| 533 | + |
| 534 | + block = Block( |
| 535 | + txs=[tx], |
| 536 | + expected_block_access_list=BlockAccessListExpectation( |
| 537 | + account_expectations={ |
| 538 | + alice: BalAccountExpectation( |
| 539 | + nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)], |
| 540 | + ), |
| 541 | + oracle_contract: BalAccountExpectation( |
| 542 | + balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)], |
| 543 | + ), |
| 544 | + bob: BalAccountExpectation( |
| 545 | + balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)], |
| 546 | + ), |
| 547 | + target_contract: BalAccountExpectation(), |
| 548 | + } |
| 549 | + ), |
| 550 | + ) |
| 551 | + |
| 552 | + blockchain_test(pre=pre, blocks=[block], post={}) |
| 553 | + |
| 554 | + |
| 555 | +@pytest.mark.parametrize( |
| 556 | + "delegated_opcode", |
| 557 | + [ |
| 558 | + pytest.param( |
| 559 | + lambda target_addr: Op.DELEGATECALL(50000, target_addr, 0, 0, 0, 0), id="delegatecall" |
| 560 | + ), |
| 561 | + pytest.param( |
| 562 | + lambda target_addr: Op.CALLCODE(50000, target_addr, 0, 0, 0, 0, 0), id="callcode" |
| 563 | + ), |
| 564 | + ], |
| 565 | +) |
| 566 | +def test_bal_delegated_storage_writes( |
| 567 | + pre: Alloc, |
| 568 | + blockchain_test: BlockchainTestFiller, |
| 569 | + delegated_opcode, |
| 570 | +): |
| 571 | + """ |
| 572 | + Ensure BAL captures delegated storage writes via |
| 573 | + DELEGATECALL and CALLCODE. |
| 574 | + """ |
| 575 | + alice = pre.fund_eoa() |
| 576 | + |
| 577 | + # TargetContract that writes 0x42 to slot 0x01 |
| 578 | + target_code = Op.SSTORE(0x01, 0x42) |
| 579 | + target_contract = pre.deploy_contract(code=target_code) |
| 580 | + |
| 581 | + # Oracle contract that uses delegated opcode to execute |
| 582 | + # TargetContract's code |
| 583 | + oracle_code = delegated_opcode(target_contract) |
| 584 | + oracle_contract = pre.deploy_contract(code=oracle_code) |
| 585 | + |
| 586 | + tx = Transaction( |
| 587 | + sender=alice, |
| 588 | + to=oracle_contract, |
| 589 | + gas_limit=1_000_000, |
| 590 | + ) |
| 591 | + |
| 592 | + block = Block( |
| 593 | + txs=[tx], |
| 594 | + expected_block_access_list=BlockAccessListExpectation( |
| 595 | + account_expectations={ |
| 596 | + alice: BalAccountExpectation( |
| 597 | + nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)], |
| 598 | + ), |
| 599 | + oracle_contract: BalAccountExpectation( |
| 600 | + storage_changes=[ |
| 601 | + BalStorageSlot( |
| 602 | + slot=0x01, |
| 603 | + slot_changes=[BalStorageChange(tx_index=1, post_value=0x42)], |
| 604 | + ) |
| 605 | + ], |
| 606 | + ), |
| 607 | + target_contract: BalAccountExpectation(), |
| 608 | + } |
| 609 | + ), |
| 610 | + ) |
| 611 | + |
| 612 | + blockchain_test(pre=pre, blocks=[block], post={}) |
| 613 | + |
| 614 | + |
| 615 | +@pytest.mark.parametrize( |
| 616 | + "delegated_opcode", |
| 617 | + [ |
| 618 | + pytest.param( |
| 619 | + lambda target_addr: Op.DELEGATECALL(50000, target_addr, 0, 0, 0, 0), id="delegatecall" |
| 620 | + ), |
| 621 | + pytest.param( |
| 622 | + lambda target_addr: Op.CALLCODE(50000, target_addr, 0, 0, 0, 0, 0), id="callcode" |
| 623 | + ), |
| 624 | + ], |
| 625 | +) |
| 626 | +def test_bal_delegated_storage_reads( |
| 627 | + pre: Alloc, |
| 628 | + blockchain_test: BlockchainTestFiller, |
| 629 | + delegated_opcode, |
| 630 | +): |
| 631 | + """ |
| 632 | + Ensure BAL captures delegated storage reads via |
| 633 | + DELEGATECALL and CALLCODE. |
| 634 | + """ |
| 635 | + alice = pre.fund_eoa() |
| 636 | + |
| 637 | + # TargetContract that reads from slot 0x01 |
| 638 | + target_code = Op.SLOAD(0x01) + Op.STOP |
| 639 | + target_contract = pre.deploy_contract(code=target_code) |
| 640 | + |
| 641 | + # Oracle contract with storage slot 0x01 = 0x42, |
| 642 | + # uses delegated opcode to execute TargetContract's code |
| 643 | + oracle_code = delegated_opcode(target_contract) |
| 644 | + oracle_contract = pre.deploy_contract(code=oracle_code, storage={0x01: 0x42}) |
| 645 | + |
| 646 | + tx = Transaction( |
| 647 | + sender=alice, |
| 648 | + to=oracle_contract, |
| 649 | + gas_limit=1_000_000, |
| 650 | + ) |
| 651 | + |
| 652 | + block = Block( |
| 653 | + txs=[tx], |
| 654 | + expected_block_access_list=BlockAccessListExpectation( |
| 655 | + account_expectations={ |
| 656 | + alice: BalAccountExpectation( |
| 657 | + nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)], |
| 658 | + ), |
| 659 | + oracle_contract: BalAccountExpectation( |
| 660 | + storage_reads=[0x01], |
| 661 | + ), |
| 662 | + target_contract: BalAccountExpectation(), |
| 663 | + } |
| 664 | + ), |
| 665 | + ) |
| 666 | + |
| 667 | + blockchain_test(pre=pre, blocks=[block], post={}) |
| 668 | + |
| 669 | + |
| 670 | +def test_bal_block_rewards( |
| 671 | + pre: Alloc, |
| 672 | + blockchain_test: BlockchainTestFiller, |
| 673 | + fork, |
| 674 | +): |
| 675 | + """Ensure BAL captures fee recipient balance changes from block rewards.""" |
| 676 | + alice_initial_balance = 1_000_000 |
| 677 | + alice = pre.fund_eoa(amount=alice_initial_balance) |
| 678 | + bob = pre.fund_eoa(amount=0) |
| 679 | + charlie = pre.fund_eoa(amount=0) # fee recipient |
| 680 | + |
| 681 | + intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator() |
| 682 | + intrinsic_gas_cost = intrinsic_gas_calculator( |
| 683 | + calldata=b"", |
| 684 | + contract_creation=False, |
| 685 | + access_list=[], |
| 686 | + ) |
| 687 | + tx_gas_limit = intrinsic_gas_cost + 1000 # add a small buffer |
| 688 | + gas_price = 0xA |
| 689 | + base_fee_per_gas = 0x2 # Set base fee for EIP-1559 |
| 690 | + |
| 691 | + tx = Transaction( |
| 692 | + sender=alice, |
| 693 | + to=bob, |
| 694 | + value=100, |
| 695 | + gas_limit=tx_gas_limit, |
| 696 | + gas_price=gas_price, |
| 697 | + ) |
| 698 | + |
| 699 | + # EIP-1559 fee calculation: |
| 700 | + # - Total gas cost |
| 701 | + total_gas_cost = intrinsic_gas_cost * gas_price |
| 702 | + # - Tip portion |
| 703 | + tip_to_charlie = intrinsic_gas_cost * (gas_price - base_fee_per_gas) |
| 704 | + |
| 705 | + alice_final_balance = alice_initial_balance - 100 - total_gas_cost |
| 706 | + |
| 707 | + block = Block( |
| 708 | + txs=[tx], |
| 709 | + fee_recipient=charlie, # Set Charlie as the fee recipient |
| 710 | + base_fee_per_gas=base_fee_per_gas, # Set base fee for EIP-1559 |
| 711 | + expected_block_access_list=BlockAccessListExpectation( |
| 712 | + account_expectations={ |
| 713 | + alice: BalAccountExpectation( |
| 714 | + nonce_changes=[BalNonceChange(tx_index=1, post_nonce=1)], |
| 715 | + balance_changes=[ |
| 716 | + BalBalanceChange(tx_index=1, post_balance=alice_final_balance) |
| 717 | + ], |
| 718 | + ), |
| 719 | + bob: BalAccountExpectation( |
| 720 | + balance_changes=[BalBalanceChange(tx_index=1, post_balance=100)], |
| 721 | + ), |
| 722 | + charlie: BalAccountExpectation( |
| 723 | + balance_changes=[BalBalanceChange(tx_index=1, post_balance=tip_to_charlie)], |
| 724 | + ), |
| 725 | + } |
| 726 | + ), |
| 727 | + ) |
| 728 | + |
| 729 | + blockchain_test( |
| 730 | + pre=pre, |
| 731 | + blocks=[block], |
| 732 | + post={}, |
| 733 | + ) |
0 commit comments