|
1 | 1 | from eth2spec.test.helpers.forks import ( |
2 | 2 | is_post_capella, |
3 | 3 | ) |
4 | | -from eth2spec.test.helpers.state import get_state_root |
5 | 4 | from eth2spec.test.context import ( |
6 | 5 | spec_state_test, |
7 | 6 | with_all_phases, |
8 | 7 | ) |
| 8 | +from eth2spec.test.helpers.state import ( |
| 9 | + get_state_root, |
| 10 | + next_epoch, |
| 11 | + next_slot, |
| 12 | + transition_to |
| 13 | +) |
9 | 14 |
|
10 | 15 |
|
11 | 16 | @with_all_phases |
@@ -87,3 +92,49 @@ def test_historical_accumulator(spec, state): |
87 | 92 | assert len(state.historical_summaries) == len(pre_historical_summaries) + 1 |
88 | 93 | else: |
89 | 94 | assert len(state.historical_roots) == len(pre_historical_roots) + 1 |
| 95 | + |
| 96 | + |
| 97 | +@with_all_phases |
| 98 | +@spec_state_test |
| 99 | +def test_balance_change_affects_proposer(spec, state): |
| 100 | + # Brute-force an instance where a validator's balance change prevents it from proposing. |
| 101 | + # We must brute-force this because sometimes the balance change doesn't make a difference. |
| 102 | + # Give this approach 100 attempts to find such a case. |
| 103 | + for _ in range(100): |
| 104 | + original_state = state.copy() |
| 105 | + |
| 106 | + # Get the proposer of the first slot in the next epoch |
| 107 | + next_epoch_state = state.copy() |
| 108 | + next_epoch(spec, next_epoch_state) |
| 109 | + proposer_next_epoch = spec.get_beacon_proposer_index(next_epoch_state) |
| 110 | + |
| 111 | + # Reduce the validator's balance, making it less likely to propose |
| 112 | + # The validator's effective balance will be updated during epoch processing |
| 113 | + spec.decrease_balance(state, proposer_next_epoch, 10 * spec.EFFECTIVE_BALANCE_INCREMENT) |
| 114 | + |
| 115 | + # Check if the proposer changed as a result of the balance change |
| 116 | + tmp_state = state.copy() |
| 117 | + next_epoch(spec, tmp_state) |
| 118 | + if proposer_next_epoch != spec.get_beacon_proposer_index(tmp_state): |
| 119 | + # Use this state |
| 120 | + break |
| 121 | + else: |
| 122 | + # Try another state |
| 123 | + state = original_state.copy() |
| 124 | + next_epoch(spec, state) |
| 125 | + |
| 126 | + # Transition to the last slot of the current epoch |
| 127 | + slot = state.slot + spec.SLOTS_PER_EPOCH - (state.slot % spec.SLOTS_PER_EPOCH) - 1 |
| 128 | + transition_to(spec, state, slot) |
| 129 | + |
| 130 | + yield 'pre', state |
| 131 | + yield 'slots', 1 |
| 132 | + |
| 133 | + # Transition to the next epoch |
| 134 | + next_slot(spec, state) |
| 135 | + |
| 136 | + yield 'post', state |
| 137 | + |
| 138 | + # Verify that the proposer changed because of the balance change |
| 139 | + proposer_next_epoch_after_change = spec.get_beacon_proposer_index(state) |
| 140 | + assert proposer_next_epoch != proposer_next_epoch_after_change |
0 commit comments