Merge sigp/unstable into gloas-lookup-sync-fixes

Brings in the gossip-blob deprecation (#9126) and 17 other unstable
commits. Conflict resolutions (8 files):

- Kept our unified `SyncMessage::UnknownParentSidecarHeader` design over
  unstable's separate `UnknownParentDataColumn`/`UnknownParentPartialDataColumn`
  variants (gossip_methods, manager, single_block_lookup, mod, tests).
- Adopted unstable's gossip-blob deprecation: dropped `process_gossip_blob`,
  `process_gossip_verified_blob`, and the blob parent-unknown test path.
- Took unstable's `process_gossip_verified_data_column` (Result-returning
  `to_partial`), router PayloadEnvelopesByRoot flattened match, and combined
  `BlockProcessType::id` arm.
- Dropped unstable's gloas-lookup-sync boilerplate stubs (#9322) that
  duplicated our real impls: `process_lookup_envelope`,
  `rpc_payload_envelope_received`, `on_single_payload_envelope_response`,
  and the `SinglePayloadEnvelope` processing-result arm.

cargo check -p network passes clean.
This commit is contained in:
dapplion
2026-06-01 06:15:12 +02:00
134 changed files with 3321 additions and 3910 deletions

View File

@@ -203,9 +203,9 @@ impl<T: BeaconChainTypes> BlockLookups<T> {
block_root,
Some(block_component),
Some(awaiting_parent),
// On a `UnknownParentBlock` or `UnknownParentBlob` event the peer is not required
// to have the rest of the block components (refer to decoupled blob gossip). Create
// the lookup with zero peers to house the block components.
// On a `UnknownParentBlock` or `UnknownParentSidecarHeader` event the peer is not
// required to have the rest of the block components (refer to decoupled blob
// gossip). Create the lookup with zero peers to house the block components.
&[],
&PeerType::PreGloas,
new_lookup_trigger,

View File

@@ -593,7 +593,7 @@ impl<T: BeaconChainTypes> CustodyBackFillSync<T> {
Err(err) => {
debug!(batch_epoch = %batch_id, error = ?err, "Batch download failed");
// If there are any coupling errors, penalize the appropriate peers
// If there are any coupling errors, penalize the appropriate peers.
if let RpcResponseError::BlockComponentCouplingError(coupling_error) = err
&& let CouplingError::DataColumnPeerFailure {
error,
@@ -601,15 +601,19 @@ impl<T: BeaconChainTypes> CustodyBackFillSync<T> {
exceeded_retries: _,
} = coupling_error
{
let mut failed_peers = HashSet::new();
for (column_index, faulty_peer) in faulty_peers {
debug!(
?error,
?column_index,
?faulty_peer,
"Custody backfill sync penalizing peer"
"Custody backfill sync: peer failed to serve column"
);
failed_peers.insert(faulty_peer);
}
for peer in failed_peers {
network.report_peer(
faulty_peer,
peer,
PeerAction::LowToleranceError,
"Peer failed to serve column",
);

View File

@@ -198,8 +198,9 @@ pub enum BlockProcessType {
impl BlockProcessType {
pub fn id(&self) -> Id {
match self {
BlockProcessType::SingleBlock { id } | BlockProcessType::SingleBlob { id } => *id,
BlockProcessType::SingleCustodyColumn(id)
BlockProcessType::SingleBlock { id }
| BlockProcessType::SingleBlob { id }
| BlockProcessType::SingleCustodyColumn(id)
| BlockProcessType::SinglePayloadEnvelope(id) => *id,
}
}

View File

@@ -305,7 +305,12 @@ impl<T: BeaconChainTypes> ActiveCustodyRequest<T> {
// must have its columns in custody. In that case, set `true = enforce max_requests`
// and downscore if data_columns_by_root does not return the expected custody
// columns. For the rest of peers, don't downscore if columns are missing.
lookup_peers.contains(&peer_id),
//
// Post-Gloas, blocks and payload envelopes are decoupled. A peer may
// have the block but not yet imported the envelope and data columns.
// Don't enforce max_responses in this case.
lookup_peers.contains(&peer_id)
&& !cx.fork_context.current_fork_name().gloas_enabled(),
)
.map_err(Error::SendFailed)?;

View File

@@ -33,6 +33,7 @@ impl<E: EthSpec> ActiveRequestItems for BlobsByRangeRequestItems<E> {
if blob.index >= self.max_blobs_per_block {
return Err(LookupVerifyError::UnrequestedIndex(blob.index));
}
if !blob.verify_blob_sidecar_inclusion_proof() {
return Err(LookupVerifyError::InvalidInclusionProof);
}

View File

@@ -50,9 +50,11 @@ impl<E: EthSpec> ActiveRequestItems for BlobsByRootRequestItems<E> {
if self.request.block_root != block_root {
return Err(LookupVerifyError::UnrequestedBlockRoot(block_root));
}
if !blob.verify_blob_sidecar_inclusion_proof() {
return Err(LookupVerifyError::InvalidInclusionProof);
}
if !self.request.indices.contains(&blob.index) {
return Err(LookupVerifyError::UnrequestedIndex(blob.index));
}

View File

@@ -1278,17 +1278,6 @@ impl TestRig {
self.trigger_unknown_parent_block(peer_id, last_block);
}
fn trigger_with_last_unknown_blob_parent(&mut self) {
let peer_id = self.new_connected_supernode_peer();
let blobs = self
.get_last_block()
.block_data()
.blobs()
.expect("no blobs");
let blob = blobs.first().expect("empty blobs");
self.trigger_unknown_parent_blob(peer_id, blob.clone());
}
fn trigger_with_last_unknown_data_column_parent(&mut self) {
let peer_id = self.new_connected_supernode_peer();
let columns = self
@@ -1297,7 +1286,7 @@ impl TestRig {
.data_columns()
.expect("No data columns");
let column = columns.first().expect("empty columns");
self.trigger_unknown_parent_column(peer_id, column.clone());
self.trigger_unknown_parent_data_column(peer_id, column.clone());
}
// Post-test assertions
@@ -1501,6 +1490,10 @@ impl TestRig {
genesis_fork().deneb_enabled().then(Self::default)
}
fn new_after_fulu() -> Option<Self> {
genesis_fork().fulu_enabled().then(Self::default)
}
fn new_after_deneb_before_fulu() -> Option<Self> {
let fork = genesis_fork();
if fork.deneb_enabled() && !fork.fulu_enabled() {
@@ -1536,27 +1529,18 @@ impl TestRig {
self.send_sync_message(SyncMessage::UnknownParentBlock(peer_id, block, block_root))
}
fn trigger_unknown_parent_blob(&mut self, peer_id: PeerId, blob: Arc<BlobSidecar<E>>) {
self.send_sync_message(SyncMessage::UnknownParentSidecarHeader {
peer_id,
block_root: blob.block_root(),
parent_root: blob.block_parent_root(),
slot: blob.slot(),
});
}
fn trigger_unknown_parent_column(
fn trigger_unknown_parent_data_column(
&mut self,
peer_id: PeerId,
column: Arc<DataColumnSidecar<E>>,
data_column: Arc<DataColumnSidecar<E>>,
) {
let DataColumnSidecar::Fulu(col) = column.as_ref() else {
let DataColumnSidecar::Fulu(col) = data_column.as_ref() else {
// Gloas data columns don't carry a parent block root, so the
// `UnknownParentSidecarHeader` trigger doesn't apply post-Gloas. The production
// path drops these with a `warn!` (see `manager.rs` handler). Mirror that here
// so Gloas test paths can call the same helper as Fulu without panicking.
self.log(&format!(
"trigger_unknown_parent_column noop (post-Gloas column has no parent root) peer {peer_id:?}"
"trigger_unknown_parent_data_column noop (post-Gloas column has no parent root) peer {peer_id:?}"
));
return;
};
@@ -1850,9 +1834,9 @@ impl TestRig {
)
.unwrap()
{
Availability::Available(_) => panic!("blob removed from da_checker, available"),
Availability::Available(_) => panic!("column removed from da_checker, available"),
Availability::MissingComponents(block_root) => {
self.log(&format!("inserted blob to da_checker {block_root:?}"))
self.log(&format!("inserted column to da_checker {block_root:?}"))
}
};
}
@@ -2037,9 +2021,9 @@ async fn happy_path_unknown_block_parent(depth: usize) {
}
}
/// Assert that sync completes from a GossipUnknownParentBlob / UnknownDataColumnParent
/// Assert that sync completes from an UnknownDataColumnParent
async fn happy_path_unknown_data_parent(depth: usize) {
let Some(mut r) = TestRig::new_after_deneb() else {
let Some(mut r) = TestRig::new_after_fulu() else {
return;
};
// Post-Gloas data columns don't carry a parent block root, so the unknown-parent-data
@@ -2048,29 +2032,23 @@ async fn happy_path_unknown_data_parent(depth: usize) {
return;
}
r.build_chain(depth).await;
if r.is_after_fulu() {
r.trigger_with_last_unknown_data_column_parent();
} else if r.is_after_deneb() {
r.trigger_with_last_unknown_blob_parent();
}
r.trigger_with_last_unknown_data_column_parent();
r.simulate(SimulateConfig::happy_path()).await;
r.assert_successful_lookup_sync_parent_trigger();
}
/// Assert that multiple trigger types don't create extra lookups
async fn happy_path_multiple_triggers(depth: usize) {
let mut r = TestRig::default();
let Some(mut r) = TestRig::new_after_fulu() else {
return;
};
// + 1, because the unknown parent trigger needs two new blocks
r.build_chain(depth + 1).await;
r.trigger_with_last_block();
r.trigger_with_last_block();
r.trigger_with_last_unknown_block_parent();
r.trigger_with_last_unknown_block_parent();
if r.is_after_fulu() {
r.trigger_with_last_unknown_data_column_parent();
} else if r.is_after_deneb() {
r.trigger_with_last_unknown_blob_parent();
}
r.trigger_with_last_unknown_data_column_parent();
r.simulate(SimulateConfig::happy_path()).await;
assert_eq!(r.created_lookups(), depth + 1, "Don't create extra lookups");
r.assert_successful_lookup_sync();
@@ -2209,18 +2187,14 @@ async fn too_many_processing_failures(depth: usize) {
#[tokio::test]
/// Assert that multiple trigger types don't create extra lookups
async fn unknown_parent_does_not_add_peers_to_itself() {
let Some(mut r) = TestRig::new_after_deneb() else {
let Some(mut r) = TestRig::new_after_fulu() else {
return;
};
// 2, because the unknown parent trigger needs two new blocks
r.build_chain(2).await;
r.trigger_with_last_unknown_block_parent();
r.trigger_with_last_unknown_block_parent();
if r.is_after_fulu() {
r.trigger_with_last_unknown_data_column_parent();
} else if r.is_after_deneb() {
r.trigger_with_last_unknown_blob_parent();
}
r.trigger_with_last_unknown_data_column_parent();
r.simulate(SimulateConfig::happy_path()).await;
r.assert_peers_at_lookup_of_slot(2, 0);
// Post-Gloas the data-column trigger is a no-op (Gloas columns don't carry a parent

View File

@@ -26,7 +26,7 @@ use types::{ForkName, Hash256, MinimalEthSpec as E, SignedExecutionPayloadEnvelo
mod lookups;
mod range;
type T = Witness<ManualSlotClock, E, MemoryStore<E>, MemoryStore<E>>;
type T = Witness<ManualSlotClock, E, MemoryStore, MemoryStore>;
/// This test utility enables integration testing of Lighthouse sync components.
///