|
3 | 3 | """ |
4 | 4 |
|
5 | 5 | from enum import Enum, auto, unique |
| 6 | +from typing import Callable |
6 | 7 |
|
7 | 8 | import pytest |
8 | 9 | from execution_testing import ( |
@@ -1629,3 +1630,95 @@ def test_two_step_balance_change( |
1629 | 1630 | post={contract_address: Account(storage=storage)}, |
1630 | 1631 | blocks=[Block(txs=[tx])], |
1631 | 1632 | ) |
| 1633 | + |
| 1634 | + |
| 1635 | +@pytest.mark.parametrize( |
| 1636 | + "violation_index_fn", |
| 1637 | + [ |
| 1638 | + pytest.param(lambda _n: None, id="no_violation"), |
| 1639 | + pytest.param(lambda _n: 0, id="first_violates"), |
| 1640 | + pytest.param(lambda n: n // 2, id="middle_violates"), |
| 1641 | + pytest.param(lambda n: n - 1, id="last_violates"), |
| 1642 | + ], |
| 1643 | +) |
| 1644 | +def test_many_accounts_balance_change( |
| 1645 | + blockchain_test: BlockchainTestFiller, |
| 1646 | + pre: Alloc, |
| 1647 | + violation_index_fn: Callable[[int], int | None], |
| 1648 | + fork: Fork, |
| 1649 | +) -> None: |
| 1650 | + """ |
| 1651 | + Test reserve balance with many accounts having their balance changed. |
| 1652 | +
|
| 1653 | + A single wallet is deployed and many EOAs delegate to it. Each EOA sends |
| 1654 | + a transfer when the wallet code executes. The violation_index parameter |
| 1655 | + determines which account (if any) ends up in violation. |
| 1656 | +
|
| 1657 | + The number of accounts to involve depends on how cheaply can we call them |
| 1658 | + and on the transaction gas limit cap. |
| 1659 | + """ |
| 1660 | + gas_costs = fork.gas_costs() |
| 1661 | + gas_per_account = ( |
| 1662 | + Op.CALL( |
| 1663 | + # Warmed using access lists for cheapest call |
| 1664 | + address_warm=True, |
| 1665 | + value_transfer=True, |
| 1666 | + account_new=False, |
| 1667 | + delegated_address=True, |
| 1668 | + # Warmed using access lists for cheapest call |
| 1669 | + delegated_address_warm=True, |
| 1670 | + ).gas_cost(fork) |
| 1671 | + + gas_costs.G_ACCESS_LIST_ADDRESS |
| 1672 | + ) |
| 1673 | + gas_limit = fork.transaction_gas_limit_cap() |
| 1674 | + assert gas_limit is not None |
| 1675 | + # Using generous_gas(fork) as margin for constant gas expenses. |
| 1676 | + num_accounts = (gas_limit - generous_gas(fork)) // gas_per_account |
| 1677 | + assert num_accounts >= 2560 # 2570 minus margin |
| 1678 | + violation_index = violation_index_fn(num_accounts) |
| 1679 | + |
| 1680 | + value = 1 |
| 1681 | + |
| 1682 | + initial_sink_balance = 1 |
| 1683 | + sink_address = pre.fund_eoa(initial_sink_balance) |
| 1684 | + wallet_code = Op.CALL(address=sink_address, value=value) |
| 1685 | + wallet_address = pre.deploy_contract(code=wallet_code) |
| 1686 | + |
| 1687 | + senders = [] |
| 1688 | + for i in range(num_accounts): |
| 1689 | + if i == violation_index: |
| 1690 | + balance = Spec.RESERVE_BALANCE |
| 1691 | + else: |
| 1692 | + balance = Spec.RESERVE_BALANCE + value |
| 1693 | + senders.append(pre.fund_eoa(balance, delegation=wallet_address)) |
| 1694 | + |
| 1695 | + contract_code = Op.SSTORE(slot_code_worked, value_code_worked) |
| 1696 | + for sender in senders: |
| 1697 | + contract_code += Op.CALL(address=sender) + Op.POP |
| 1698 | + contract_address = pre.deploy_contract(contract_code) |
| 1699 | + |
| 1700 | + tx = Transaction( |
| 1701 | + gas_limit=gas_limit, |
| 1702 | + to=contract_address, |
| 1703 | + sender=pre.fund_eoa(), |
| 1704 | + access_list=[AccessList(address=s, storage_keys=[]) for s in senders] |
| 1705 | + + [AccessList(address=wallet_address, storage_keys=[])], |
| 1706 | + ) |
| 1707 | + |
| 1708 | + reverted = violation_index is not None |
| 1709 | + total_sent = value * num_accounts |
| 1710 | + |
| 1711 | + blockchain_test( |
| 1712 | + pre=pre, |
| 1713 | + post={ |
| 1714 | + contract_address: Account( |
| 1715 | + storage={} |
| 1716 | + if reverted |
| 1717 | + else {slot_code_worked: value_code_worked} |
| 1718 | + ), |
| 1719 | + sink_address: Account( |
| 1720 | + balance=initial_sink_balance + (0 if reverted else total_sent) |
| 1721 | + ), |
| 1722 | + }, |
| 1723 | + blocks=[Block(txs=[tx])], |
| 1724 | + ) |
0 commit comments