@@ -14,7 +14,7 @@ use eth2::lighthouse::StandardAttestationRewards;
14
14
use eth2:: types:: ValidatorId ;
15
15
use lazy_static:: lazy_static;
16
16
use types:: beacon_state:: Error as BeaconStateError ;
17
- use types:: { BeaconState , ChainSpec } ;
17
+ use types:: { BeaconState , ChainSpec , ForkName , Slot } ;
18
18
19
19
pub const VALIDATOR_COUNT : usize = 64 ;
20
20
@@ -219,6 +219,100 @@ async fn test_verify_attestation_rewards_base_inactivity_leak() {
219
219
assert_eq ! ( expected_balances, balances) ;
220
220
}
221
221
222
+ #[ tokio:: test]
223
+ async fn test_verify_attestation_rewards_altair_inactivity_leak ( ) {
224
+ let spec = ForkName :: Altair . make_genesis_spec ( E :: default_spec ( ) ) ;
225
+ let harness = get_harness ( spec. clone ( ) ) ;
226
+
227
+ let half = VALIDATOR_COUNT / 2 ;
228
+ let half_validators: Vec < usize > = ( 0 ..half) . collect ( ) ;
229
+ // target epoch is the epoch where the chain enters inactivity leak
230
+ let target_epoch = & spec. min_epochs_to_inactivity_penalty + 1 ;
231
+
232
+ // advance until beginning of epoch N + 1 and get balances
233
+ harness
234
+ . extend_slots_some_validators (
235
+ ( E :: slots_per_epoch ( ) * ( target_epoch + 1 ) ) as usize ,
236
+ half_validators. clone ( ) ,
237
+ )
238
+ . await ;
239
+ let initial_balances: Vec < u64 > = harness. get_current_state ( ) . balances ( ) . clone ( ) . into ( ) ;
240
+
241
+ // advance until epoch N + 2 and build proposal rewards map
242
+ let mut proposal_rewards_map: HashMap < u64 , u64 > = HashMap :: new ( ) ;
243
+ let mut sync_committee_rewards_map: HashMap < u64 , i64 > = HashMap :: new ( ) ;
244
+ for _ in 0 ..E :: slots_per_epoch ( ) {
245
+ let state = harness. get_current_state ( ) ;
246
+ let slot = state. slot ( ) + Slot :: new ( 1 ) ;
247
+
248
+ // calculate beacon block rewards / penalties
249
+ let ( ( signed_block, _maybe_blob_sidecars) , mut state) =
250
+ harness. make_block_return_pre_state ( state, slot) . await ;
251
+ let beacon_block_reward = harness
252
+ . chain
253
+ . compute_beacon_block_reward (
254
+ signed_block. message ( ) ,
255
+ signed_block. canonical_root ( ) ,
256
+ & mut state,
257
+ )
258
+ . unwrap ( ) ;
259
+
260
+ let total_proposer_reward = proposal_rewards_map
261
+ . get ( & beacon_block_reward. proposer_index )
262
+ . unwrap_or ( & 0u64 )
263
+ + beacon_block_reward. total ;
264
+
265
+ proposal_rewards_map. insert ( beacon_block_reward. proposer_index , total_proposer_reward) ;
266
+
267
+ // calculate sync committee rewards / penalties
268
+ let reward_payload = harness
269
+ . chain
270
+ . compute_sync_committee_rewards ( signed_block. message ( ) , & mut state)
271
+ . unwrap ( ) ;
272
+
273
+ reward_payload. iter ( ) . for_each ( |reward| {
274
+ let mut amount = * sync_committee_rewards_map
275
+ . get ( & reward. validator_index )
276
+ . unwrap_or ( & 0 ) ;
277
+ amount += reward. reward ;
278
+ sync_committee_rewards_map. insert ( reward. validator_index , amount) ;
279
+ } ) ;
280
+
281
+ harness
282
+ . extend_slots_some_validators ( 1 , half_validators. clone ( ) )
283
+ . await ;
284
+ }
285
+
286
+ // compute reward deltas for all validators in epoch N
287
+ let StandardAttestationRewards {
288
+ ideal_rewards,
289
+ total_rewards,
290
+ } = harness
291
+ . chain
292
+ . compute_attestation_rewards ( Epoch :: new ( target_epoch) , vec ! [ ] )
293
+ . unwrap ( ) ;
294
+
295
+ // assert inactivity penalty for both ideal rewards and individual validators
296
+ assert ! ( ideal_rewards. iter( ) . all( |reward| reward. inactivity == 0 ) ) ;
297
+ assert ! ( total_rewards[ ..half]
298
+ . iter( )
299
+ . all( |reward| reward. inactivity == 0 ) ) ;
300
+ assert ! ( total_rewards[ half..]
301
+ . iter( )
302
+ . all( |reward| reward. inactivity < 0 ) ) ;
303
+
304
+ // apply attestation, proposal, and sync committee rewards and penalties to initial balances
305
+ let expected_balances = apply_attestation_rewards ( & initial_balances, total_rewards) ;
306
+ let expected_balances = apply_beacon_block_rewards ( & proposal_rewards_map, expected_balances) ;
307
+ let expected_balances =
308
+ apply_sync_committee_rewards ( & sync_committee_rewards_map, expected_balances) ;
309
+
310
+ // verify expected balances against actual balances
311
+ let balances: Vec < u64 > = harness. get_current_state ( ) . balances ( ) . clone ( ) . into ( ) ;
312
+
313
+ assert_eq ! ( expected_balances, balances) ;
314
+ }
315
+
222
316
#[ tokio:: test]
223
317
async fn test_verify_attestation_rewards_base_subset_only ( ) {
224
318
let harness = get_harness ( E :: default_spec ( ) ) ;
@@ -297,3 +391,32 @@ fn get_validator_balances(state: BeaconState<E>, validators: &[usize]) -> Vec<u6
297
391
} )
298
392
. collect ( )
299
393
}
394
+
395
+ fn apply_beacon_block_rewards (
396
+ proposal_rewards_map : & HashMap < u64 , u64 > ,
397
+ expected_balances : Vec < u64 > ,
398
+ ) -> Vec < u64 > {
399
+ let calculated_balances = expected_balances
400
+ . iter ( )
401
+ . enumerate ( )
402
+ . map ( |( i, balance) | balance + proposal_rewards_map. get ( & ( i as u64 ) ) . unwrap_or ( & 0u64 ) )
403
+ . collect ( ) ;
404
+
405
+ calculated_balances
406
+ }
407
+
408
+ fn apply_sync_committee_rewards (
409
+ sync_committee_rewards_map : & HashMap < u64 , i64 > ,
410
+ expected_balances : Vec < u64 > ,
411
+ ) -> Vec < u64 > {
412
+ let calculated_balances = expected_balances
413
+ . iter ( )
414
+ . enumerate ( )
415
+ . map ( |( i, balance) | {
416
+ ( * balance as i64 + sync_committee_rewards_map. get ( & ( i as u64 ) ) . unwrap_or ( & 0i64 ) )
417
+ . unsigned_abs ( )
418
+ } )
419
+ . collect ( ) ;
420
+
421
+ calculated_balances
422
+ }
0 commit comments