Custody backfill sync (#7907)

#7603


  #### Custody backfill sync service
Similar in many ways to the current backfill service. There may be ways to unify the two services. The difficulty there is that the current backfill service tightly couples blocks and their associated blobs/data columns. Any attempts to unify the two services should be left to a separate PR in my opinion.

#### `SyncNeworkContext`
`SyncNetworkContext` manages custody sync data columns by range requests separetly from other sync RPC requests. I think this is a nice separation considering that custody backfill is its own service.

#### Data column import logic
The import logic verifies KZG committments and that the data columns block root matches the block root in the nodes store before importing columns

#### New channel to send messages to `SyncManager`
Now external services can communicate with the `SyncManager`. In this PR this channel is used to trigger a custody sync. Alternatively we may be able to use the existing `mpsc` channel that the `SyncNetworkContext` uses to communicate with the `SyncManager`. I will spend some time reviewing this.


Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>

Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>

Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com>
This commit is contained in:
Eitan Seri-Levi
2025-10-21 20:51:34 -07:00
committed by GitHub
parent 46dde9afee
commit 33e21634cb
30 changed files with 2958 additions and 200 deletions

View File

@@ -57,6 +57,9 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
// Store info if we are required to do a backfill sync.
let original_oldest_block_slot = beacon_chain.store.get_anchor_info().oldest_block_slot;
// Use this info during custody backfill sync.
let mut original_earliest_data_column_slot = None;
let interval_future = async move {
// Perform pre-genesis logging.
loop {
@@ -80,6 +83,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
// Perform post-genesis logging.
let mut last_backfill_log_slot = None;
let mut last_custody_backfill_log_slot = None;
loop {
// Run the notifier half way through each slot.
@@ -112,6 +116,18 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
let mut speedo = speedo.lock().await;
speedo.clear();
}
(_, SyncState::CustodyBackFillSyncing { .. }) => {
// We have transitioned to a custody backfill sync. Reset the speedo.
let mut speedo = speedo.lock().await;
last_custody_backfill_log_slot = None;
speedo.clear();
}
(SyncState::CustodyBackFillSyncing { .. }, _) => {
// We have transitioned from a custody backfill sync, reset the speedo
let mut speedo = speedo.lock().await;
last_custody_backfill_log_slot = None;
speedo.clear();
}
(_, _) => {}
}
current_sync_state = sync_state;
@@ -154,6 +170,38 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
Instant::now(),
);
}
SyncState::CustodyBackFillSyncing { .. } => {
match beacon_chain.store.get_data_column_custody_info() {
Ok(data_column_custody_info) => {
if let Some(earliest_data_column_slot) = data_column_custody_info
.and_then(|info| info.earliest_data_column_slot)
&& let Some(da_boundary) = beacon_chain.get_column_da_boundary()
{
sync_distance = earliest_data_column_slot.saturating_sub(
da_boundary.start_slot(T::EthSpec::slots_per_epoch()),
);
// We keep track of our starting point for custody backfill sync
// so we can measure our speed of progress.
if original_earliest_data_column_slot.is_none() {
original_earliest_data_column_slot =
Some(earliest_data_column_slot)
}
if let Some(original_earliest_data_column_slot) =
original_earliest_data_column_slot
{
speedo.observe(
original_earliest_data_column_slot
.saturating_sub(earliest_data_column_slot),
Instant::now(),
);
}
}
}
Err(e) => error!(error=?e, "Unable to get data column custody info"),
}
}
SyncState::SyncingFinalized { .. }
| SyncState::SyncingHead { .. }
| SyncState::SyncTransition => {
@@ -190,6 +238,8 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
// Log if we are backfilling.
let is_backfilling = matches!(current_sync_state, SyncState::BackFillSyncing { .. });
let is_custody_backfilling =
matches!(current_sync_state, SyncState::CustodyBackFillSyncing { .. });
if is_backfilling
&& last_backfill_log_slot
.is_none_or(|slot| slot + BACKFILL_LOG_INTERVAL <= current_slot)
@@ -234,6 +284,51 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
info!("Historical block download complete");
}
if is_custody_backfilling
&& last_custody_backfill_log_slot
.is_none_or(|slot| slot + BACKFILL_LOG_INTERVAL <= current_slot)
{
last_custody_backfill_log_slot = Some(current_slot);
let distance = format!(
"{} slots ({})",
sync_distance.as_u64(),
slot_distance_pretty(sync_distance, slot_duration)
);
let speed = speedo.slots_per_second();
let display_speed = speed.is_some_and(|speed| speed != 0.0);
if display_speed {
info!(
distance,
speed = sync_speed_pretty(speed),
est_time =
estimated_time_pretty(beacon_chain.get_column_da_boundary().and_then(
|da_boundary| speedo.estimated_time_till_slot(
da_boundary.start_slot(T::EthSpec::slots_per_epoch())
)
)),
"Downloading historical data columns"
);
} else {
info!(
distance,
est_time =
estimated_time_pretty(beacon_chain.get_column_da_boundary().and_then(
|da_boundary| speedo.estimated_time_till_slot(
da_boundary.start_slot(T::EthSpec::slots_per_epoch())
)
)),
"Downloading historical data columns"
);
}
} else if !is_custody_backfilling && last_custody_backfill_log_slot.is_some() {
last_custody_backfill_log_slot = None;
original_earliest_data_column_slot = None;
info!("Historical data column download complete");
}
// Log if we are syncing
if current_sync_state.is_syncing() {
metrics::set_gauge(&metrics::IS_SYNCED, 0);