Skip to content

Commit a3804aa

Browse files
authored
Merge of #7594
2 parents 5472cb8 + 52911a3 commit a3804aa

File tree

1 file changed

+111
-9
lines changed

1 file changed

+111
-9
lines changed

beacon_node/beacon_chain/src/validator_custody.rs

Lines changed: 111 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ use types::{ChainSpec, Epoch, EthSpec, Slot};
1111
/// A delay before making the CGC change effective to the data availability checker.
1212
const 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+
1417
type 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

Comments
 (0)