Skip to content

Commit 0507eca

Browse files
committed
Merge remote-tracking branch 'origin/stable' into unstable-merge-v8
2 parents bc86dc0 + e3ee7fe commit 0507eca

File tree

23 files changed

+505
-96
lines changed

23 files changed

+505
-96
lines changed

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ resolver = "2"
9494

9595
[workspace.package]
9696
edition = "2024"
97-
version = "8.0.0-rc.2"
97+
version = "8.0.0"
9898

9999
[workspace.dependencies]
100100
account_utils = { path = "common/account_utils" }

beacon_node/beacon_chain/src/beacon_proposer_cache.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,17 @@ impl BeaconProposerCache {
166166
}
167167

168168
/// Compute the proposer duties using the head state without cache.
169+
///
170+
/// Return:
171+
/// - Proposer indices.
172+
/// - True dependent root.
173+
/// - Legacy dependent root (last block of epoch `N - 1`).
174+
/// - Head execution status.
175+
/// - Fork at `request_epoch`.
169176
pub fn compute_proposer_duties_from_head<T: BeaconChainTypes>(
170177
request_epoch: Epoch,
171178
chain: &BeaconChain<T>,
172-
) -> Result<(Vec<usize>, Hash256, ExecutionStatus, Fork), BeaconChainError> {
179+
) -> Result<(Vec<usize>, Hash256, Hash256, ExecutionStatus, Fork), BeaconChainError> {
173180
// Atomically collect information about the head whilst holding the canonical head `Arc` as
174181
// short as possible.
175182
let (mut state, head_state_root, head_block_root) = {
@@ -203,11 +210,23 @@ pub fn compute_proposer_duties_from_head<T: BeaconChainTypes>(
203210
.proposer_shuffling_decision_root_at_epoch(request_epoch, head_block_root, &chain.spec)
204211
.map_err(BeaconChainError::from)?;
205212

213+
// This is only required because the V1 proposer duties endpoint spec wasn't updated for Fulu. We
214+
// can delete this once the V1 endpoint is deprecated at the Glamsterdam fork.
215+
let legacy_dependent_root = state
216+
.legacy_proposer_shuffling_decision_root_at_epoch(request_epoch, head_block_root)
217+
.map_err(BeaconChainError::from)?;
218+
206219
// Use fork_at_epoch rather than the state's fork, because post-Fulu we may not have advanced
207220
// the state completely into the new epoch.
208221
let fork = chain.spec.fork_at_epoch(request_epoch);
209222

210-
Ok((indices, dependent_root, execution_status, fork))
223+
Ok((
224+
indices,
225+
dependent_root,
226+
legacy_dependent_root,
227+
execution_status,
228+
fork,
229+
))
211230
}
212231

213232
/// If required, advance `state` to the epoch required to determine proposer indices in `target_epoch`.

beacon_node/beacon_chain/src/custody_context.rs

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ impl ValidatorRegistrations {
120120
let effective_epoch =
121121
(current_slot + effective_delay_slots).epoch(E::slots_per_epoch()) + 1;
122122
self.epoch_validator_custody_requirements
123-
.entry(effective_epoch)
124-
.and_modify(|old_custody| *old_custody = validator_custody_requirement)
125-
.or_insert(validator_custody_requirement);
123+
.insert(effective_epoch, validator_custody_requirement);
126124
Some((effective_epoch, validator_custody_requirement))
127125
} else {
128126
None
@@ -134,8 +132,17 @@ impl ValidatorRegistrations {
134132
///
135133
/// This is done by pruning all values on/after `effective_epoch` and updating the map to store
136134
/// the latest validator custody requirements for the `effective_epoch`.
137-
pub fn backfill_validator_custody_requirements(&mut self, effective_epoch: Epoch) {
135+
pub fn backfill_validator_custody_requirements(
136+
&mut self,
137+
effective_epoch: Epoch,
138+
expected_cgc: u64,
139+
) {
138140
if let Some(latest_validator_custody) = self.latest_validator_custody_requirement() {
141+
// If the expected cgc isn't equal to the latest validator custody a very recent cgc change may have occurred.
142+
// We should not update the mapping.
143+
if expected_cgc != latest_validator_custody {
144+
return;
145+
}
139146
// Delete records if
140147
// 1. The epoch is greater than or equal than `effective_epoch`
141148
// 2. the cgc requirements match the latest validator custody requirements
@@ -145,11 +152,25 @@ impl ValidatorRegistrations {
145152
});
146153

147154
self.epoch_validator_custody_requirements
148-
.entry(effective_epoch)
149-
.and_modify(|old_custody| *old_custody = latest_validator_custody)
150-
.or_insert(latest_validator_custody);
155+
.insert(effective_epoch, latest_validator_custody);
151156
}
152157
}
158+
159+
/// Updates the `epoch -> cgc` map by pruning records before `effective_epoch`
160+
/// while setting the `cgc` at `effective_epoch` to the latest validator custody requirement.
161+
///
162+
/// This is used to restart custody backfill sync at `effective_epoch`
163+
pub fn reset_validator_custody_requirements(&mut self, effective_epoch: Epoch) {
164+
if let Some(latest_validator_custody_requirements) =
165+
self.latest_validator_custody_requirement()
166+
{
167+
self.epoch_validator_custody_requirements
168+
.retain(|&epoch, _| epoch >= effective_epoch);
169+
170+
self.epoch_validator_custody_requirements
171+
.insert(effective_epoch, latest_validator_custody_requirements);
172+
};
173+
}
153174
}
154175

155176
/// Given the `validator_custody_units`, return the custody requirement based on
@@ -517,10 +538,22 @@ impl<E: EthSpec> CustodyContext<E> {
517538

518539
/// The node has completed backfill for this epoch. Update the internal records so the function
519540
/// [`Self::custody_columns_for_epoch()`] returns up-to-date results.
520-
pub fn update_and_backfill_custody_count_at_epoch(&self, effective_epoch: Epoch) {
541+
pub fn update_and_backfill_custody_count_at_epoch(
542+
&self,
543+
effective_epoch: Epoch,
544+
expected_cgc: u64,
545+
) {
521546
self.validator_registrations
522547
.write()
523-
.backfill_validator_custody_requirements(effective_epoch);
548+
.backfill_validator_custody_requirements(effective_epoch, expected_cgc);
549+
}
550+
551+
/// The node is attempting to restart custody backfill. Update the internal records so that
552+
/// custody backfill can start backfilling at `effective_epoch`.
553+
pub fn reset_validator_custody_requirements(&self, effective_epoch: Epoch) {
554+
self.validator_registrations
555+
.write()
556+
.reset_validator_custody_requirements(effective_epoch);
524557
}
525558
}
526559

@@ -604,11 +637,13 @@ mod tests {
604637
custody_context: &CustodyContext<E>,
605638
start_epoch: Epoch,
606639
end_epoch: Epoch,
640+
expected_cgc: u64,
607641
) {
608642
assert!(start_epoch >= end_epoch);
609643
// Call from end_epoch down to start_epoch (inclusive), simulating backfill
610644
for epoch in (end_epoch.as_u64()..=start_epoch.as_u64()).rev() {
611-
custody_context.update_and_backfill_custody_count_at_epoch(Epoch::new(epoch));
645+
custody_context
646+
.update_and_backfill_custody_count_at_epoch(Epoch::new(epoch), expected_cgc);
612647
}
613648
}
614649

@@ -1368,7 +1403,7 @@ mod tests {
13681403
);
13691404

13701405
// Backfill from epoch 20 down to 15 (simulating backfill)
1371-
complete_backfill_for_epochs(&custody_context, head_epoch, Epoch::new(15));
1406+
complete_backfill_for_epochs(&custody_context, head_epoch, Epoch::new(15), final_cgc);
13721407

13731408
// After backfilling to epoch 15, it should use latest CGC (32)
13741409
assert_eq!(
@@ -1406,7 +1441,43 @@ mod tests {
14061441
let custody_context = setup_custody_context(&spec, head_epoch, epoch_and_cgc_tuples);
14071442

14081443
// Backfill to epoch 15 (between the two CGC increases)
1409-
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(15));
1444+
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(15), final_cgc);
1445+
1446+
// Verify epochs 15 - 20 return latest CGC (32)
1447+
for epoch in 15..=20 {
1448+
assert_eq!(
1449+
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
1450+
final_cgc,
1451+
);
1452+
}
1453+
1454+
// Verify epochs 10-14 still return mid_cgc (16)
1455+
for epoch in 10..14 {
1456+
assert_eq!(
1457+
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
1458+
mid_cgc,
1459+
);
1460+
}
1461+
}
1462+
1463+
#[test]
1464+
fn attempt_backfill_with_invalid_cgc() {
1465+
let spec = E::default_spec();
1466+
let initial_cgc = 8u64;
1467+
let mid_cgc = 16u64;
1468+
let final_cgc = 32u64;
1469+
1470+
// Setup: Node restart after multiple validator registrations causing CGC increases
1471+
let head_epoch = Epoch::new(20);
1472+
let epoch_and_cgc_tuples = vec![
1473+
(Epoch::new(0), initial_cgc),
1474+
(Epoch::new(10), mid_cgc),
1475+
(head_epoch, final_cgc),
1476+
];
1477+
let custody_context = setup_custody_context(&spec, head_epoch, epoch_and_cgc_tuples);
1478+
1479+
// Backfill to epoch 15 (between the two CGC increases)
1480+
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(15), final_cgc);
14101481

14111482
// Verify epochs 15 - 20 return latest CGC (32)
14121483
for epoch in 15..=20 {
@@ -1416,6 +1487,22 @@ mod tests {
14161487
);
14171488
}
14181489

1490+
// Attempt backfill with an incorrect cgc value
1491+
complete_backfill_for_epochs(
1492+
&custody_context,
1493+
Epoch::new(20),
1494+
Epoch::new(15),
1495+
initial_cgc,
1496+
);
1497+
1498+
// Verify epochs 15 - 20 still return latest CGC (32)
1499+
for epoch in 15..=20 {
1500+
assert_eq!(
1501+
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
1502+
final_cgc,
1503+
);
1504+
}
1505+
14191506
// Verify epochs 10-14 still return mid_cgc (16)
14201507
for epoch in 10..14 {
14211508
assert_eq!(
@@ -1424,4 +1511,53 @@ mod tests {
14241511
);
14251512
}
14261513
}
1514+
1515+
#[test]
1516+
fn reset_validator_custody_requirements() {
1517+
let spec = E::default_spec();
1518+
let minimum_cgc = 4u64;
1519+
let initial_cgc = 8u64;
1520+
let mid_cgc = 16u64;
1521+
let final_cgc = 32u64;
1522+
1523+
// Setup: Node restart after multiple validator registrations causing CGC increases
1524+
let head_epoch = Epoch::new(20);
1525+
let epoch_and_cgc_tuples = vec![
1526+
(Epoch::new(0), initial_cgc),
1527+
(Epoch::new(10), mid_cgc),
1528+
(head_epoch, final_cgc),
1529+
];
1530+
let custody_context = setup_custody_context(&spec, head_epoch, epoch_and_cgc_tuples);
1531+
1532+
// Backfill from epoch 20 to 9
1533+
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(9), final_cgc);
1534+
1535+
// Reset validator custody requirements to the latest cgc requirements at `head_epoch` up to the boundary epoch
1536+
custody_context.reset_validator_custody_requirements(head_epoch);
1537+
1538+
// Verify epochs 0 - 19 return the minimum cgc requirement because of the validator custody requirement reset
1539+
for epoch in 0..=19 {
1540+
assert_eq!(
1541+
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
1542+
minimum_cgc,
1543+
);
1544+
}
1545+
1546+
// Verify epoch 20 returns a CGC of 32
1547+
assert_eq!(
1548+
custody_context.custody_group_count_at_epoch(head_epoch, &spec),
1549+
final_cgc
1550+
);
1551+
1552+
// Rerun Backfill to epoch 20
1553+
complete_backfill_for_epochs(&custody_context, Epoch::new(20), Epoch::new(0), final_cgc);
1554+
1555+
// Verify epochs 0 - 20 return the final cgc requirements
1556+
for epoch in 0..=20 {
1557+
assert_eq!(
1558+
custody_context.custody_group_count_at_epoch(Epoch::new(epoch), &spec),
1559+
final_cgc,
1560+
);
1561+
}
1562+
}
14271563
}

beacon_node/beacon_chain/src/historical_data_columns.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
5454
&self,
5555
epoch: Epoch,
5656
historical_data_column_sidecar_list: DataColumnSidecarList<T::EthSpec>,
57+
expected_cgc: u64,
5758
) -> Result<usize, HistoricalDataColumnError> {
5859
let mut total_imported = 0;
5960
let mut ops = vec![];
@@ -88,11 +89,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
8889
.get_data_column(&block_root, &data_column.index)?
8990
.is_some()
9091
{
91-
debug!(
92-
block_root = ?block_root,
93-
column_index = data_column.index,
94-
"Skipping data column import as identical data column exists"
95-
);
9692
continue;
9793
}
9894
if block_root != data_column.block_root() {
@@ -136,7 +132,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
136132

137133
self.data_availability_checker
138134
.custody_context()
139-
.update_and_backfill_custody_count_at_epoch(epoch);
135+
.update_and_backfill_custody_count_at_epoch(epoch, expected_cgc);
140136

141137
self.safely_backfill_data_column_custody_info(epoch)
142138
.map_err(|e| HistoricalDataColumnError::BeaconChainError(Box::new(e)))?;

0 commit comments

Comments
 (0)