|
| 1 | +""" |
| 2 | +Absence validator functions for BAL testing. |
| 3 | +
|
| 4 | +This module provides validator functions that check for the absence of specific |
| 5 | +changes in Block Access Lists. These validators are used with the |
| 6 | +``should_not_exist`` field in BalAccountExpectation to ensure certain changes |
| 7 | +do *not* occur. |
| 8 | +
|
| 9 | +All validator functions must be decorated with |
| 10 | +``@validate_call(validate_return=True)`` to ensure proper type validation. |
| 11 | +This is enforced via tests. |
| 12 | +""" |
| 13 | + |
| 14 | +from typing import Set |
| 15 | + |
| 16 | +from pydantic import validate_call |
| 17 | + |
| 18 | +from ethereum_test_base_types import Number, StorageKey |
| 19 | + |
| 20 | +from . import AbsenceValidator, BalAccountChange |
| 21 | + |
| 22 | + |
| 23 | +@validate_call(validate_return=True) |
| 24 | +def no_nonce_changes(tx_indices: Set[Number] | None = None) -> AbsenceValidator: |
| 25 | + """ |
| 26 | + Forbid nonce changes at specified transaction indices or all indices if None. |
| 27 | +
|
| 28 | + Args: |
| 29 | + tx_indices: Set of transaction indices to check. If None, |
| 30 | + checks all transactions. |
| 31 | +
|
| 32 | + """ |
| 33 | + |
| 34 | + def check(account: BalAccountChange) -> None: |
| 35 | + for nonce_change in account.nonce_changes: |
| 36 | + if tx_indices is None or nonce_change.tx_index in tx_indices: |
| 37 | + raise AssertionError( |
| 38 | + f"Unexpected nonce change found at tx {nonce_change.tx_index}" |
| 39 | + ) |
| 40 | + |
| 41 | + return check |
| 42 | + |
| 43 | + |
| 44 | +@validate_call(validate_return=True) |
| 45 | +def no_balance_changes(tx_indices: Set[Number] | None = None) -> AbsenceValidator: |
| 46 | + """ |
| 47 | + Forbid balance changes at specified transaction indices or all indices |
| 48 | + if None. |
| 49 | +
|
| 50 | + Args: |
| 51 | + tx_indices: Set of transaction indices to check. If None, |
| 52 | + checks all transactions. |
| 53 | +
|
| 54 | + """ |
| 55 | + |
| 56 | + def check(account: BalAccountChange) -> None: |
| 57 | + for balance_change in account.balance_changes: |
| 58 | + if tx_indices is None or balance_change.tx_index in tx_indices: |
| 59 | + raise AssertionError( |
| 60 | + f"Unexpected balance change found at tx {balance_change.tx_index}" |
| 61 | + ) |
| 62 | + |
| 63 | + return check |
| 64 | + |
| 65 | + |
| 66 | +@validate_call(validate_return=True) |
| 67 | +def no_storage_changes( |
| 68 | + slots: Set[StorageKey] | None = None, |
| 69 | + tx_indices: Set[Number] | None = None, |
| 70 | +) -> AbsenceValidator: |
| 71 | + """ |
| 72 | + Forbid storage changes at specified slots and/or transaction indices. |
| 73 | +
|
| 74 | + Args: |
| 75 | + slots: Set of storage slots to check. If None, checks all slots. |
| 76 | + tx_indices: Set of transaction indices to check. If None, |
| 77 | + checks all transactions. |
| 78 | +
|
| 79 | + """ |
| 80 | + |
| 81 | + def check(account: BalAccountChange) -> None: |
| 82 | + for storage_slot in account.storage_changes: |
| 83 | + if slots is None or storage_slot.slot in slots: |
| 84 | + for slot_change in storage_slot.slot_changes: |
| 85 | + if tx_indices is None or slot_change.tx_index in tx_indices: |
| 86 | + raise AssertionError( |
| 87 | + "Unexpected storage change found at slot " |
| 88 | + f"{storage_slot.slot} in tx " |
| 89 | + f"{slot_change.tx_index}" |
| 90 | + ) |
| 91 | + |
| 92 | + return check |
| 93 | + |
| 94 | + |
| 95 | +@validate_call(validate_return=True) |
| 96 | +def no_storage_reads(slots: Set[StorageKey] | None = None) -> AbsenceValidator: |
| 97 | + """ |
| 98 | + Forbid storage reads at specified slots or all slots if None. |
| 99 | +
|
| 100 | + Args: |
| 101 | + slots: Set of storage slots to check. If None, checks all slots. |
| 102 | +
|
| 103 | + """ |
| 104 | + |
| 105 | + def check(account: BalAccountChange) -> None: |
| 106 | + for read_slot in account.storage_reads: |
| 107 | + if slots is None or read_slot in slots: |
| 108 | + raise AssertionError(f"Unexpected storage read found at slot {read_slot}") |
| 109 | + |
| 110 | + return check |
| 111 | + |
| 112 | + |
| 113 | +@validate_call(validate_return=True) |
| 114 | +def no_code_changes(tx_indices: Set[Number] | None = None) -> AbsenceValidator: |
| 115 | + """ |
| 116 | + Forbid code changes at specified transaction indices or all indices |
| 117 | + if None. |
| 118 | +
|
| 119 | + Args: |
| 120 | + tx_indices: Set of transaction indices to check. If None, |
| 121 | + checks all transactions. |
| 122 | +
|
| 123 | + """ |
| 124 | + |
| 125 | + def check(account: BalAccountChange) -> None: |
| 126 | + for code_change in account.code_changes: |
| 127 | + if tx_indices is None or code_change.tx_index in tx_indices: |
| 128 | + raise AssertionError(f"Unexpected code change found at tx {code_change.tx_index}") |
| 129 | + |
| 130 | + return check |
| 131 | + |
| 132 | + |
| 133 | +__all__ = [ |
| 134 | + "AbsenceValidator", |
| 135 | + "no_nonce_changes", |
| 136 | + "no_balance_changes", |
| 137 | + "no_storage_changes", |
| 138 | + "no_storage_reads", |
| 139 | + "no_code_changes", |
| 140 | +] |
0 commit comments