mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 18:21:45 +00:00
Merge remote-tracking branch 'origin/stable' into unstable-merge-v8
This commit is contained in:
@@ -166,10 +166,17 @@ impl BeaconProposerCache {
|
||||
}
|
||||
|
||||
/// Compute the proposer duties using the head state without cache.
|
||||
///
|
||||
/// Return:
|
||||
/// - Proposer indices.
|
||||
/// - True dependent root.
|
||||
/// - Legacy dependent root (last block of epoch `N - 1`).
|
||||
/// - Head execution status.
|
||||
/// - Fork at `request_epoch`.
|
||||
pub fn compute_proposer_duties_from_head<T: BeaconChainTypes>(
|
||||
request_epoch: Epoch,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(Vec<usize>, Hash256, ExecutionStatus, Fork), BeaconChainError> {
|
||||
) -> Result<(Vec<usize>, Hash256, Hash256, ExecutionStatus, Fork), BeaconChainError> {
|
||||
// Atomically collect information about the head whilst holding the canonical head `Arc` as
|
||||
// short as possible.
|
||||
let (mut state, head_state_root, head_block_root) = {
|
||||
@@ -203,11 +210,23 @@ pub fn compute_proposer_duties_from_head<T: BeaconChainTypes>(
|
||||
.proposer_shuffling_decision_root_at_epoch(request_epoch, head_block_root, &chain.spec)
|
||||
.map_err(BeaconChainError::from)?;
|
||||
|
||||
// This is only required because the V1 proposer duties endpoint spec wasn't updated for Fulu. We
|
||||
// can delete this once the V1 endpoint is deprecated at the Glamsterdam fork.
|
||||
let legacy_dependent_root = state
|
||||
.legacy_proposer_shuffling_decision_root_at_epoch(request_epoch, head_block_root)
|
||||
.map_err(BeaconChainError::from)?;
|
||||
|
||||
// Use fork_at_epoch rather than the state's fork, because post-Fulu we may not have advanced
|
||||
// the state completely into the new epoch.
|
||||
let fork = chain.spec.fork_at_epoch(request_epoch);
|
||||
|
||||
Ok((indices, dependent_root, execution_status, fork))
|
||||
Ok((
|
||||
indices,
|
||||
dependent_root,
|
||||
legacy_dependent_root,
|
||||
execution_status,
|
||||
fork,
|
||||
))
|
||||
}
|
||||
|
||||
/// If required, advance `state` to the epoch required to determine proposer indices in `target_epoch`.
|
||||
|
||||
@@ -120,9 +120,7 @@ impl ValidatorRegistrations {
|
||||
let effective_epoch =
|
||||
(current_slot + effective_delay_slots).epoch(E::slots_per_epoch()) + 1;
|
||||
self.epoch_validator_custody_requirements
|
||||
.entry(effective_epoch)
|
||||
.and_modify(|old_custody| *old_custody = validator_custody_requirement)
|
||||
.or_insert(validator_custody_requirement);
|
||||
.insert(effective_epoch, validator_custody_requirement);
|
||||
Some((effective_epoch, validator_custody_requirement))
|
||||
} else {
|
||||
None
|
||||
@@ -134,8 +132,17 @@ impl ValidatorRegistrations {
|
||||
///
|
||||
/// This is done by pruning all values on/after `effective_epoch` and updating the map to store
|
||||
/// the latest validator custody requirements for the `effective_epoch`.
|
||||
pub fn backfill_validator_custody_requirements(&mut self, effective_epoch: Epoch) {
|
||||
pub fn backfill_validator_custody_requirements(
|
||||
&mut self,
|
||||
effective_epoch: Epoch,
|
||||
expected_cgc: u64,
|
||||
) {
|
||||
if let Some(latest_validator_custody) = self.latest_validator_custody_requirement() {
|
||||
// If the expected cgc isn't equal to the latest validator custody a very recent cgc change may have occurred.
|
||||
// We should not update the mapping.
|
||||
if expected_cgc != latest_validator_custody {
|
||||
return;
|
||||
}
|
||||
// Delete records if
|
||||
// 1. The epoch is greater than or equal than `effective_epoch`
|
||||
// 2. the cgc requirements match the latest validator custody requirements
|
||||
@@ -145,11 +152,25 @@ impl ValidatorRegistrations {
|
||||
});
|
||||
|
||||
self.epoch_validator_custody_requirements
|
||||
.entry(effective_epoch)
|
||||
.and_modify(|old_custody| *old_custody = latest_validator_custody)
|
||||
.or_insert(latest_validator_custody);
|
||||
.insert(effective_epoch, latest_validator_custody);
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the `epoch -> cgc` map by pruning records before `effective_epoch`
|
||||
/// while setting the `cgc` at `effective_epoch` to the latest validator custody requirement.
|
||||
///
|
||||
/// This is used to restart custody backfill sync at `effective_epoch`
|
||||
pub fn reset_validator_custody_requirements(&mut self, effective_epoch: Epoch) {
|
||||
if let Some(latest_validator_custody_requirements) =
|
||||
self.latest_validator_custody_requirement()
|
||||
{
|
||||
self.epoch_validator_custody_requirements
|
||||
.retain(|&epoch, _| epoch >= effective_epoch);
|
||||
|
||||
self.epoch_validator_custody_requirements
|
||||
.insert(effective_epoch, latest_validator_custody_requirements);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Given the `validator_custody_units`, return the custody requirement based on
|
||||
@@ -517,10 +538,22 @@ impl<E: EthSpec> CustodyContext<E> {
|
||||
|
||||
/// The node has completed backfill for this epoch. Update the internal records so the function
|
||||
/// [`Self::custody_columns_for_epoch()`] returns up-to-date results.
|
||||
pub fn update_and_backfill_custody_count_at_epoch(&self, effective_epoch: Epoch) {
|
||||
pub fn update_and_backfill_custody_count_at_epoch(
|
||||
&self,
|
||||
effective_epoch: Epoch,
|
||||
expected_cgc: u64,
|
||||
) {
|
||||
self.validator_registrations
|
||||
.write()
|
||||
.backfill_validator_custody_requirements(effective_epoch);
|
||||
.backfill_validator_custody_requirements(effective_epoch, expected_cgc);
|
||||
}
|
||||
|
||||
/// The node is attempting to restart custody backfill. Update the internal records so that
|
||||
/// custody backfill can start backfilling at `effective_epoch`.
|
||||
pub fn reset_validator_custody_requirements(&self, effective_epoch: Epoch) {
|
||||
self.validator_registrations
|
||||
.write()
|
||||
.reset_validator_custody_requirements(effective_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,11 +637,13 @@ mod tests {
|
||||
custody_context: &CustodyContext<E>,
|
||||
start_epoch: Epoch,
|
||||
end_epoch: Epoch,
|
||||
expected_cgc: u64,
|
||||
) {
|
||||
assert!(start_epoch >= end_epoch);
|
||||
// Call from end_epoch down to start_epoch (inclusive), simulating backfill
|
||||
for epoch in (end_epoch.as_u64()..=start_epoch.as_u64()).rev() {
|
||||
custody_context.update_and_backfill_custody_count_at_epoch(Epoch::new(epoch));
|
||||
custody_context
|
||||
.update_and_backfill_custody_count_at_epoch(Epoch::new(epoch), expected_cgc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1368,7 +1403,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Backfill from epoch 20 down to 15 (simulating backfill)
|
||||
complete_backfill_for_epochs(&custody_context, head_epoch, Epoch::new(15));
|
||||
complete_backfill_for_epochs(&custody_context, head_epoch, Epoch::new(15), final_cgc);
|
||||
|
||||
// After backfilling to epoch 15, it should use latest CGC (32)
|
||||
assert_eq!(
|
||||
@@ -1406,7 +1441,7 @@ mod tests {
|
||||
let custody_context = setup_custody_context(&spec, head_epoch, epoch_and_cgc_tuples);
|
||||
|
||||
// Backfill to epoch 15 (between the two CGC increases)
|
||||
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(15));
|
||||
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(15), final_cgc);
|
||||
|
||||
// Verify epochs 15 - 20 return latest CGC (32)
|
||||
for epoch in 15..=20 {
|
||||
@@ -1424,4 +1459,105 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn attempt_backfill_with_invalid_cgc() {
|
||||
let spec = E::default_spec();
|
||||
let initial_cgc = 8u64;
|
||||
let mid_cgc = 16u64;
|
||||
let final_cgc = 32u64;
|
||||
|
||||
// Setup: Node restart after multiple validator registrations causing CGC increases
|
||||
let head_epoch = Epoch::new(20);
|
||||
let epoch_and_cgc_tuples = vec![
|
||||
(Epoch::new(0), initial_cgc),
|
||||
(Epoch::new(10), mid_cgc),
|
||||
(head_epoch, final_cgc),
|
||||
];
|
||||
let custody_context = setup_custody_context(&spec, head_epoch, epoch_and_cgc_tuples);
|
||||
|
||||
// Backfill to epoch 15 (between the two CGC increases)
|
||||
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(15), final_cgc);
|
||||
|
||||
// Verify epochs 15 - 20 return latest CGC (32)
|
||||
for epoch in 15..=20 {
|
||||
assert_eq!(
|
||||
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
|
||||
final_cgc,
|
||||
);
|
||||
}
|
||||
|
||||
// Attempt backfill with an incorrect cgc value
|
||||
complete_backfill_for_epochs(
|
||||
&custody_context,
|
||||
Epoch::new(20),
|
||||
Epoch::new(15),
|
||||
initial_cgc,
|
||||
);
|
||||
|
||||
// Verify epochs 15 - 20 still return latest CGC (32)
|
||||
for epoch in 15..=20 {
|
||||
assert_eq!(
|
||||
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
|
||||
final_cgc,
|
||||
);
|
||||
}
|
||||
|
||||
// Verify epochs 10-14 still return mid_cgc (16)
|
||||
for epoch in 10..14 {
|
||||
assert_eq!(
|
||||
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
|
||||
mid_cgc,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_validator_custody_requirements() {
|
||||
let spec = E::default_spec();
|
||||
let minimum_cgc = 4u64;
|
||||
let initial_cgc = 8u64;
|
||||
let mid_cgc = 16u64;
|
||||
let final_cgc = 32u64;
|
||||
|
||||
// Setup: Node restart after multiple validator registrations causing CGC increases
|
||||
let head_epoch = Epoch::new(20);
|
||||
let epoch_and_cgc_tuples = vec![
|
||||
(Epoch::new(0), initial_cgc),
|
||||
(Epoch::new(10), mid_cgc),
|
||||
(head_epoch, final_cgc),
|
||||
];
|
||||
let custody_context = setup_custody_context(&spec, head_epoch, epoch_and_cgc_tuples);
|
||||
|
||||
// Backfill from epoch 20 to 9
|
||||
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(9), final_cgc);
|
||||
|
||||
// Reset validator custody requirements to the latest cgc requirements at `head_epoch` up to the boundary epoch
|
||||
custody_context.reset_validator_custody_requirements(head_epoch);
|
||||
|
||||
// Verify epochs 0 - 19 return the minimum cgc requirement because of the validator custody requirement reset
|
||||
for epoch in 0..=19 {
|
||||
assert_eq!(
|
||||
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
|
||||
minimum_cgc,
|
||||
);
|
||||
}
|
||||
|
||||
// Verify epoch 20 returns a CGC of 32
|
||||
assert_eq!(
|
||||
custody_context.custody_group_count_at_epoch(head_epoch, &spec),
|
||||
final_cgc
|
||||
);
|
||||
|
||||
// Rerun Backfill to epoch 20
|
||||
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(0), final_cgc);
|
||||
|
||||
// Verify epochs 0 - 20 return the final cgc requirements
|
||||
for epoch in 0..=20 {
|
||||
assert_eq!(
|
||||
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
|
||||
final_cgc,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
historical_data_column_sidecar_list: DataColumnSidecarList<T::EthSpec>,
|
||||
expected_cgc: u64,
|
||||
) -> Result<usize, HistoricalDataColumnError> {
|
||||
let mut total_imported = 0;
|
||||
let mut ops = vec![];
|
||||
@@ -88,11 +89,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.get_data_column(&block_root, &data_column.index)?
|
||||
.is_some()
|
||||
{
|
||||
debug!(
|
||||
block_root = ?block_root,
|
||||
column_index = data_column.index,
|
||||
"Skipping data column import as identical data column exists"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if block_root != data_column.block_root() {
|
||||
@@ -136,7 +132,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
self.data_availability_checker
|
||||
.custody_context()
|
||||
.update_and_backfill_custody_count_at_epoch(epoch);
|
||||
.update_and_backfill_custody_count_at_epoch(epoch, expected_cgc);
|
||||
|
||||
self.safely_backfill_data_column_custody_info(epoch)
|
||||
.map_err(|e| HistoricalDataColumnError::BeaconChainError(Box::new(e)))?;
|
||||
|
||||
Reference in New Issue
Block a user