test_process_voluntary_exit.py
1 import eth2spec.phase0.spec as spec 2 from eth2spec.phase0.spec import ( 3 get_active_validator_indices, 4 get_churn_limit, 5 get_current_epoch, 6 process_voluntary_exit, 7 ) 8 from eth2spec.test.context import spec_state_test, expect_assertion_error, always_bls 9 from eth2spec.test.helpers.keys import pubkey_to_privkey 10 from eth2spec.test.helpers.voluntary_exits import build_voluntary_exit, sign_voluntary_exit 11 12 13 def run_voluntary_exit_processing(state, voluntary_exit, valid=True): 14 """ 15 Run ``process_voluntary_exit``, yielding: 16 - pre-state ('pre') 17 - voluntary_exit ('voluntary_exit') 18 - post-state ('post'). 19 If ``valid == False``, run expecting ``AssertionError`` 20 """ 21 validator_index = voluntary_exit.validator_index 22 23 yield 'pre', state 24 yield 'voluntary_exit', voluntary_exit 25 26 if not valid: 27 expect_assertion_error(lambda: process_voluntary_exit(state, voluntary_exit)) 28 yield 'post', None 29 return 30 31 pre_exit_epoch = state.validator_registry[validator_index].exit_epoch 32 33 process_voluntary_exit(state, voluntary_exit) 34 35 yield 'post', state 36 37 assert pre_exit_epoch == spec.FAR_FUTURE_EPOCH 38 assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH 39 40 41 @spec_state_test 42 def test_success(state): 43 # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit 44 state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH 45 46 current_epoch = get_current_epoch(state) 47 validator_index = get_active_validator_indices(state, current_epoch)[0] 48 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 49 50 voluntary_exit = build_voluntary_exit(state, current_epoch, validator_index, privkey, signed=True) 51 52 yield from run_voluntary_exit_processing(state, voluntary_exit) 53 54 55 @always_bls 56 @spec_state_test 57 def test_invalid_signature(state): 58 # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit 59 state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH 60 61 current_epoch = get_current_epoch(state) 62 validator_index = get_active_validator_indices(state, current_epoch)[0] 63 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 64 65 voluntary_exit = build_voluntary_exit(state, current_epoch, validator_index, privkey) 66 67 yield from run_voluntary_exit_processing(state, voluntary_exit, False) 68 69 70 @spec_state_test 71 def test_success_exit_queue(state): 72 # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit 73 state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH 74 75 current_epoch = get_current_epoch(state) 76 77 # exit `MAX_EXITS_PER_EPOCH` 78 initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)] 79 80 # Prepare a bunch of exits, based on the current state 81 exit_queue = [] 82 for index in initial_indices: 83 privkey = pubkey_to_privkey[state.validator_registry[index].pubkey] 84 exit_queue.append(build_voluntary_exit( 85 state, 86 current_epoch, 87 index, 88 privkey, 89 signed=True, 90 )) 91 92 # Now run all the exits 93 for voluntary_exit in exit_queue: 94 # the function yields data, but we are just interested in running it here, ignore yields. 95 for _ in run_voluntary_exit_processing(state, voluntary_exit): 96 continue 97 98 # exit an additional validator 99 validator_index = get_active_validator_indices(state, current_epoch)[-1] 100 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 101 voluntary_exit = build_voluntary_exit( 102 state, 103 current_epoch, 104 validator_index, 105 privkey, 106 signed=True, 107 ) 108 109 # This is the interesting part of the test: on a pre-state with a full exit queue, 110 # when processing an additional exit, it results in an exit in a later epoch 111 yield from run_voluntary_exit_processing(state, voluntary_exit) 112 113 assert ( 114 state.validator_registry[validator_index].exit_epoch == 115 state.validator_registry[initial_indices[0]].exit_epoch + 1 116 ) 117 118 119 @spec_state_test 120 def test_validator_exit_in_future(state): 121 # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit 122 state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH 123 124 current_epoch = get_current_epoch(state) 125 validator_index = get_active_validator_indices(state, current_epoch)[0] 126 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 127 128 voluntary_exit = build_voluntary_exit( 129 state, 130 current_epoch, 131 validator_index, 132 privkey, 133 signed=False, 134 ) 135 voluntary_exit.epoch += 1 136 sign_voluntary_exit(state, voluntary_exit, privkey) 137 138 yield from run_voluntary_exit_processing(state, voluntary_exit, False) 139 140 141 @spec_state_test 142 def test_validator_invalid_validator_index(state): 143 # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit 144 state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH 145 146 current_epoch = get_current_epoch(state) 147 validator_index = get_active_validator_indices(state, current_epoch)[0] 148 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 149 150 voluntary_exit = build_voluntary_exit( 151 state, 152 current_epoch, 153 validator_index, 154 privkey, 155 signed=False, 156 ) 157 voluntary_exit.validator_index = len(state.validator_registry) 158 sign_voluntary_exit(state, voluntary_exit, privkey) 159 160 yield from run_voluntary_exit_processing(state, voluntary_exit, False) 161 162 163 @spec_state_test 164 def test_validator_not_active(state): 165 current_epoch = get_current_epoch(state) 166 validator_index = get_active_validator_indices(state, current_epoch)[0] 167 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 168 169 state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH 170 171 # build and test voluntary exit 172 voluntary_exit = build_voluntary_exit( 173 state, 174 current_epoch, 175 validator_index, 176 privkey, 177 signed=True, 178 ) 179 180 yield from run_voluntary_exit_processing(state, voluntary_exit, False) 181 182 183 @spec_state_test 184 def test_validator_already_exited(state): 185 # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit 186 state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH 187 188 current_epoch = get_current_epoch(state) 189 validator_index = get_active_validator_indices(state, current_epoch)[0] 190 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 191 192 # but validator already has exited 193 state.validator_registry[validator_index].exit_epoch = current_epoch + 2 194 195 voluntary_exit = build_voluntary_exit( 196 state, 197 current_epoch, 198 validator_index, 199 privkey, 200 signed=True, 201 ) 202 203 yield from run_voluntary_exit_processing(state, voluntary_exit, False) 204 205 206 @spec_state_test 207 def test_validator_not_active_long_enough(state): 208 current_epoch = get_current_epoch(state) 209 validator_index = get_active_validator_indices(state, current_epoch)[0] 210 privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] 211 212 voluntary_exit = build_voluntary_exit( 213 state, 214 current_epoch, 215 validator_index, 216 privkey, 217 signed=True, 218 ) 219 220 assert ( 221 current_epoch - state.validator_registry[validator_index].activation_epoch < 222 spec.PERSISTENT_COMMITTEE_PERIOD 223 ) 224 225 yield from run_voluntary_exit_processing(state, voluntary_exit, False)