@@ -11,7 +11,11 @@ use types::{ChainSpec, Epoch, EthSpec, Slot};
1111/// A delay before making the CGC change effective to the data availability checker.
1212const CUSTODY_CHANGE_DA_EFFECTIVE_DELAY_SECONDS : u64 = 30 ;
1313
14+ /// Number of slots after which a validator's registration is removed if it has not re-registered.
15+ const VALIDATOR_REGISTRATION_EXPIRY_SLOTS : Slot = Slot :: new ( 256 ) ;
16+
1417type ValidatorsAndBalances = Vec < ( usize , u64 ) > ;
18+ type SlotAndEffectiveBalance = ( Slot , u64 ) ;
1519
1620/// This currently just registers increases in validator count.
1721/// Does not handle decreasing validator counts
@@ -20,7 +24,7 @@ struct ValidatorRegistrations {
2024 /// Set of all validators that is registered to this node along with its effective balance
2125 ///
2226 /// Key is validator index and value is effective_balance.
23- validators : HashMap < usize , u64 > ,
27+ validators : HashMap < usize , SlotAndEffectiveBalance > ,
2428 /// Maintains the validator custody requirement at a given epoch.
2529 ///
2630 /// Note: Only stores the epoch value when there's a change in custody requirement.
@@ -51,16 +55,21 @@ impl ValidatorRegistrations {
5155 pub ( crate ) fn register_validators < E : EthSpec > (
5256 & mut self ,
5357 validators_and_balance : ValidatorsAndBalances ,
54- slot : Slot ,
58+ current_slot : Slot ,
5559 spec : & ChainSpec ,
5660 ) -> Option < ( Epoch , u64 ) > {
5761 for ( validator_index, effective_balance) in validators_and_balance {
58- self . validators . insert ( validator_index, effective_balance) ;
62+ self . validators
63+ . insert ( validator_index, ( current_slot, effective_balance) ) ;
5964 }
6065
66+ // Drop validators that haven't re-registered with the node for `VALIDATOR_REGISTRATION_EXPIRY_SLOTS`.
67+ self . validators
68+ . retain ( |_, ( slot, _) | * slot >= current_slot - VALIDATOR_REGISTRATION_EXPIRY_SLOTS ) ;
69+
6170 // Each `BALANCE_PER_ADDITIONAL_CUSTODY_GROUP` effectively contributes one unit of "weight".
62- let validator_custody_units =
63- self . validators . values ( ) . sum :: < u64 > ( ) / spec. balance_per_additional_custody_group ;
71+ let validator_custody_units = self . validators . values ( ) . map ( | ( _ , eb ) | eb ) . sum :: < u64 > ( )
72+ / spec. balance_per_additional_custody_group ;
6473 let validator_custody_requirement =
6574 get_validators_custody_requirement ( validator_custody_units, spec) ;
6675
@@ -72,13 +81,14 @@ impl ValidatorRegistrations {
7281
7382 // If registering the new validator increased the total validator "units", then
7483 // add a new entry for the current epoch
75- if Some ( validator_custody_requirement) != self . latest_validator_custody_requirement ( ) {
84+ if Some ( validator_custody_requirement) > self . latest_validator_custody_requirement ( ) {
7685 // Apply the change from the next epoch after adding some delay buffer to ensure
7786 // the node has enough time to subscribe to subnets etc, and to avoid having
7887 // inconsistent column counts within an epoch.
7988 let effective_delay_slots =
8089 CUSTODY_CHANGE_DA_EFFECTIVE_DELAY_SECONDS / spec. seconds_per_slot ;
81- let effective_epoch = ( slot + effective_delay_slots) . epoch ( E :: slots_per_epoch ( ) ) + 1 ;
90+ let effective_epoch =
91+ ( current_slot + effective_delay_slots) . epoch ( E :: slots_per_epoch ( ) ) + 1 ;
8292 self . epoch_validator_custody_requirements
8393 . entry ( effective_epoch)
8494 . and_modify ( |old_custody| * old_custody = validator_custody_requirement)
@@ -166,13 +176,13 @@ impl CustodyContext {
166176 pub fn register_validators < E : EthSpec > (
167177 & self ,
168178 validators_and_balance : ValidatorsAndBalances ,
169- slot : Slot ,
179+ current_slot : Slot ,
170180 spec : & ChainSpec ,
171181 ) -> Option < CustodyCountChanged > {
172182 let Some ( ( effective_epoch, new_validator_custody) ) = self
173183 . validator_registrations
174184 . write ( )
175- . register_validators :: < E > ( validators_and_balance, slot , spec)
185+ . register_validators :: < E > ( validators_and_balance, current_slot , spec)
176186 else {
177187 return None ;
178188 } ;
@@ -423,6 +433,98 @@ mod tests {
423433 ) ;
424434 }
425435
436+ #[ test]
437+ fn validator_dropped_after_no_registrations_within_expiry_should_not_reduce_cgc ( ) {
438+ let custody_context = CustodyContext :: new ( false ) ;
439+ let spec = E :: default_spec ( ) ;
440+ let current_slot = Slot :: new ( 10 ) ;
441+ let val_custody_units_1 = 10 ;
442+ let val_custody_units_2 = 5 ;
443+
444+ // GIVEN val_1 and val_2 registered at `current_slot`
445+ let _ = custody_context. register_validators :: < E > (
446+ vec ! [
447+ (
448+ 1 ,
449+ val_custody_units_1 * spec. balance_per_additional_custody_group,
450+ ) ,
451+ (
452+ 2 ,
453+ val_custody_units_2 * spec. balance_per_additional_custody_group,
454+ ) ,
455+ ] ,
456+ current_slot,
457+ & spec,
458+ ) ;
459+
460+ // WHEN val_1 re-registered, but val_2 did not re-register after `VALIDATOR_REGISTRATION_EXPIRY_SLOTS + 1` slots
461+ let cgc_changed_opt = custody_context. register_validators :: < E > (
462+ vec ! [ (
463+ 1 ,
464+ val_custody_units_1 * spec. balance_per_additional_custody_group,
465+ ) ] ,
466+ current_slot + VALIDATOR_REGISTRATION_EXPIRY_SLOTS + 1 ,
467+ & spec,
468+ ) ;
469+
470+ // THEN the reduction from dropping val_2 balance should NOT result in a CGC reduction
471+ assert ! ( cgc_changed_opt. is_none( ) , "CGC should remain unchanged" ) ;
472+ assert_eq ! (
473+ custody_context. custody_group_count_at_head( & spec) ,
474+ val_custody_units_1 + val_custody_units_2
475+ )
476+ }
477+
478+ #[ test]
479+ fn validator_dropped_after_no_registrations_within_expiry ( ) {
480+ let custody_context = CustodyContext :: new ( false ) ;
481+ let spec = E :: default_spec ( ) ;
482+ let current_slot = Slot :: new ( 10 ) ;
483+ let val_custody_units_1 = 10 ;
484+ let val_custody_units_2 = 5 ;
485+ let val_custody_units_3 = 6 ;
486+
487+ // GIVEN val_1 and val_2 registered at `current_slot`
488+ let _ = custody_context. register_validators :: < E > (
489+ vec ! [
490+ (
491+ 1 ,
492+ val_custody_units_1 * spec. balance_per_additional_custody_group,
493+ ) ,
494+ (
495+ 2 ,
496+ val_custody_units_2 * spec. balance_per_additional_custody_group,
497+ ) ,
498+ ] ,
499+ current_slot,
500+ & spec,
501+ ) ;
502+
503+ // WHEN val_1 and val_3 registered, but val_3 did not re-register after `VALIDATOR_REGISTRATION_EXPIRY_SLOTS + 1` slots
504+ let cgc_changed = custody_context. register_validators :: < E > (
505+ vec ! [
506+ (
507+ 1 ,
508+ val_custody_units_1 * spec. balance_per_additional_custody_group,
509+ ) ,
510+ (
511+ 3 ,
512+ val_custody_units_3 * spec. balance_per_additional_custody_group,
513+ ) ,
514+ ] ,
515+ current_slot + VALIDATOR_REGISTRATION_EXPIRY_SLOTS + 1 ,
516+ & spec,
517+ ) ;
518+
519+ // THEN CGC should increase, BUT val_2 balance should NOT be included in CGC
520+ assert_eq ! (
521+ cgc_changed
522+ . expect( "CGC should change" )
523+ . new_custody_group_count,
524+ val_custody_units_1 + val_custody_units_3
525+ ) ;
526+ }
527+
426528 /// Update validator every epoch and assert cgc against expected values.
427529 fn register_validators_and_assert_cgc (
428530 custody_context : & CustodyContext ,
0 commit comments