@@ -132,3 +132,133 @@ def test_multiple_pending_deposits_above_churn(spec, state):
132132 assert state .pending_balance_deposits == [
133133 spec .PendingBalanceDeposit (index = 2 , amount = amount )
134134 ]
135+
136+
137+ @with_electra_and_later
138+ @spec_state_test
139+ def test_skipped_deposit_exiting_validator (spec , state ):
140+ index = 0
141+ amount = spec .MIN_ACTIVATION_BALANCE
142+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = index , amount = amount ))
143+ pre_pending_balance_deposits = state .pending_balance_deposits .copy ()
144+ pre_balance = state .balances [index ]
145+ # Initiate the validator's exit
146+ spec .initiate_validator_exit (state , index )
147+ yield from run_epoch_processing_with (spec , state , 'process_pending_balance_deposits' )
148+ # Deposit is skipped because validator is exiting
149+ assert state .balances [index ] == pre_balance
150+ # All deposits either processed or postponed, no leftover deposit balance to consume
151+ assert state .deposit_balance_to_consume == 0
152+ # The deposit is still in the queue
153+ assert state .pending_balance_deposits == pre_pending_balance_deposits
154+
155+
156+ @with_electra_and_later
157+ @spec_state_test
158+ def test_multiple_skipped_deposits_exiting_validators (spec , state ):
159+ amount = spec .EFFECTIVE_BALANCE_INCREMENT
160+ for i in [0 , 1 , 2 ]:
161+ # Append pending deposit for validator i
162+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = i , amount = amount ))
163+
164+ # Initiate the exit of validator i
165+ spec .initiate_validator_exit (state , i )
166+ pre_pending_balance_deposits = state .pending_balance_deposits .copy ()
167+ pre_balances = state .balances .copy ()
168+ yield from run_epoch_processing_with (spec , state , 'process_pending_balance_deposits' )
169+ # All deposits are postponed, no balance changes
170+ assert state .balances == pre_balances
171+ # All deposits are postponed, no leftover deposit balance to consume
172+ assert state .deposit_balance_to_consume == 0
173+ # All deposits still in the queue, in the same order
174+ assert state .pending_balance_deposits == pre_pending_balance_deposits
175+
176+
177+ @with_electra_and_later
178+ @spec_state_test
179+ def test_multiple_pending_one_skipped (spec , state ):
180+ amount = spec .EFFECTIVE_BALANCE_INCREMENT
181+ for i in [0 , 1 , 2 ]:
182+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = i , amount = amount ))
183+ pre_balances = state .balances .copy ()
184+ # Initiate the second validator's exit
185+ spec .initiate_validator_exit (state , 1 )
186+ yield from run_epoch_processing_with (spec , state , 'process_pending_balance_deposits' )
187+ # First and last deposit are processed, second is not because of exiting
188+ for i in [0 , 2 ]:
189+ assert state .balances [i ] == pre_balances [i ] + amount
190+ assert state .balances [1 ] == pre_balances [1 ]
191+ # All deposits either processed or postponed, no leftover deposit balance to consume
192+ assert state .deposit_balance_to_consume == 0
193+ # second deposit is still in the queue
194+ assert state .pending_balance_deposits == [spec .PendingBalanceDeposit (index = 1 , amount = amount )]
195+
196+
197+ @with_electra_and_later
198+ @spec_state_test
199+ def test_mixture_of_skipped_and_above_churn (spec , state ):
200+ amount01 = spec .EFFECTIVE_BALANCE_INCREMENT
201+ amount2 = spec .MAX_EFFECTIVE_BALANCE_ELECTRA
202+ # First two validators have small deposit, third validators a large one
203+ for i in [0 , 1 ]:
204+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = i , amount = amount01 ))
205+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = 2 , amount = amount2 ))
206+ pre_balances = state .balances .copy ()
207+ # Initiate the second validator's exit
208+ spec .initiate_validator_exit (state , 1 )
209+ yield from run_epoch_processing_with (spec , state , 'process_pending_balance_deposits' )
210+ # First deposit is processed
211+ assert state .balances [0 ] == pre_balances [0 ] + amount01
212+ # Second deposit is postponed, third is above churn
213+ for i in [1 , 2 ]:
214+ assert state .balances [i ] == pre_balances [i ]
215+ # First deposit consumes some deposit balance
216+ # Deposit balance to consume is not reset because third deposit is not processed
217+ assert state .deposit_balance_to_consume == spec .get_activation_exit_churn_limit (state ) - amount01
218+ # second and third deposit still in the queue, but second is appended at the end
219+ assert state .pending_balance_deposits == [spec .PendingBalanceDeposit (index = 2 , amount = amount2 ),
220+ spec .PendingBalanceDeposit (index = 1 , amount = amount01 )]
221+
222+
223+ @with_electra_and_later
224+ @spec_state_test
225+ def test_processing_deposit_of_withdrawable_validator (spec , state ):
226+ index = 0
227+ amount = spec .MIN_ACTIVATION_BALANCE
228+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = index , amount = amount ))
229+ pre_balance = state .balances [index ]
230+ # Initiate the validator's exit
231+ spec .initiate_validator_exit (state , index )
232+ # Set epoch to withdrawable epoch + 1 to allow processing of the deposit
233+ state .slot = spec .SLOTS_PER_EPOCH * (state .validators [index ].withdrawable_epoch + 1 )
234+ yield from run_epoch_processing_with (spec , state , 'process_pending_balance_deposits' )
235+ # Deposit is correctly processed
236+ assert state .balances [index ] == pre_balance + amount
237+ # No leftover deposit balance to consume when there are no deposits left to process
238+ assert state .deposit_balance_to_consume == 0
239+ assert state .pending_balance_deposits == []
240+
241+
242+ @with_electra_and_later
243+ @spec_state_test
244+ def test_processing_deposit_of_withdrawable_validator_does_not_get_churned (spec , state ):
245+ amount = spec .MAX_EFFECTIVE_BALANCE_ELECTRA
246+ for i in [0 , 1 ]:
247+ state .pending_balance_deposits .append (spec .PendingBalanceDeposit (index = i , amount = amount ))
248+ pre_balances = state .balances .copy ()
249+ # Initiate the first validator's exit
250+ spec .initiate_validator_exit (state , 0 )
251+ # Set epoch to withdrawable epoch + 1 to allow processing of the deposit
252+ state .slot = spec .SLOTS_PER_EPOCH * (state .validators [0 ].withdrawable_epoch + 1 )
253+ # Don't use run_epoch_processing_with to avoid penalties being applied
254+ yield 'pre' , state
255+ spec .process_pending_balance_deposits (state )
256+ yield 'post' , state
257+ # First deposit is processed though above churn limit, because validator is withdrawable
258+ assert state .balances [0 ] == pre_balances [0 ] + amount
259+ # Second deposit is not processed because above churn
260+ assert state .balances [1 ] == pre_balances [1 ]
261+ # Second deposit is not processed, so there's leftover deposit balance to consume.
262+ # First deposit does not consume any.
263+ assert state .deposit_balance_to_consume == spec .get_activation_exit_churn_limit (state )
264+ assert state .pending_balance_deposits == [spec .PendingBalanceDeposit (index = 1 , amount = amount )]
0 commit comments