Compare commits

...

2 Commits

Author SHA1 Message Date
dapplion
d7000fc0d1 Simplify reconstruction test assertion
Replace `assert_event_journal_contains_at_least_ordered` helper with an
inline drain that just counts the gossip + reconstruction events. The
helper was carrying around `WORKER_FREED` bookkeeping and a strict
prefix-match for one caller; counting the two relevant work types until
both thresholds are met is the same check with much less code.
2026-04-27 09:25:44 +02:00
dapplion
51e295229b Fix beacon-chain and network test failures under FORK_NAME=gloas/fulu
- block_verification: skip ParentEnvelopeUnknown check when parent is the
  proto-array anchor. The anchor's `payload_received` is intentionally
  false per spec (never added to `store.payloads`), but no envelope is
  expected for it; without this exception the check rejects every
  post-anchor gloas block.

- network tests: disable `engineGetBlobs` in the TestRig harness. Under
  real crypto the mock EL's blob fetch raced the gossip path, importing
  via a spawned task that the test didn't await -- leaving `head_root()`
  unchanged when the assertion ran. The tests are designed to exercise
  the gossip + data-column path; the engine fetch was incidental.

- network tests: relax `data_column_reconstruction_at_deadline` to allow
  trailing duplicate reconstruction work items. The reprocess queue
  removes its dedup entry on dispatch, so a column processed during an
  in-flight reconstruction can dispatch a second one. The second is a
  no-op via `reconstruction_started`, so accept >= 1 trailing event.
2026-04-27 09:16:22 +02:00
2 changed files with 39 additions and 10 deletions

View File

@@ -942,12 +942,18 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
// Check that we've received the parent envelope. If not, issue a single envelope
// lookup for the parent and queue this block in the reprocess queue.
//
// The anchor block (proto-array root) is implicitly considered to have its payload
// received: there is no envelope to fetch for the anchor (per spec, the anchor is
// never added to `store.payloads`), and the anchor is trusted by definition.
let parent_is_gloas = chain
.spec
.fork_name_at_slot::<T::EthSpec>(parent_block.slot)
.gloas_enabled();
let parent_is_anchor = parent_block.parent_root.is_none();
if parent_is_gloas
&& !parent_is_anchor
&& !fork_choice_read_lock.is_payload_received(&block.message().parent_root())
{
return Err(BlockError::ParentEnvelopeUnknown {

View File

@@ -9,6 +9,7 @@ use crate::{
sync::{SyncMessage, manager::BlockProcessType},
};
use beacon_chain::block_verification_types::LookupBlock;
use beacon_chain::chain_config::ChainConfig;
use beacon_chain::custody_context::NodeCustodyType;
use beacon_chain::data_column_verification::validate_data_column_sidecar_for_gossip_fulu;
use beacon_chain::kzg_utils::blobs_to_data_column_sidecars;
@@ -134,7 +135,10 @@ impl TestRig {
.fresh_ephemeral_store()
.mock_execution_layer()
.node_custody_type(NodeCustodyType::Fullnode)
.chain_config(<_>::default())
.chain_config(ChainConfig {
disable_get_blobs: true,
..ChainConfig::default()
})
.build();
harness.advance_slot();
@@ -169,7 +173,10 @@ impl TestRig {
.fresh_ephemeral_store()
.mock_execution_layer()
.node_custody_type(node_custody_type)
.chain_config(<_>::default())
.chain_config(ChainConfig {
disable_get_blobs: true,
..ChainConfig::default()
})
.build();
harness.advance_slot();
@@ -1001,14 +1008,30 @@ async fn data_column_reconstruction_at_deadline() {
rig.enqueue_gossip_data_columns(i);
}
// Expect all gossip events + reconstruction
let mut expected_events: Vec<WorkType> = (0..min_columns_for_reconstruction)
.map(|_| WorkType::GossipDataColumnSidecar)
.collect();
expected_events.push(WorkType::ColumnReconstruction);
rig.assert_event_journal_contains_ordered(&expected_events)
.await;
// Drain the journal until we've seen all gossip events plus at least one
// reconstruction. Under real crypto the reprocess queue can dispatch the
// reconstruction work item more than once (the second is a no-op via
// `reconstruction_started`), so we don't pin the count — we just require >= 1.
let gsc: &str = WorkType::GossipDataColumnSidecar.into();
let cr: &str = WorkType::ColumnReconstruction.into();
let (mut gossip_seen, mut recon_seen) = (0usize, 0usize);
let drain = async {
while let Some(event) = rig.work_journal_rx.recv().await {
if event == gsc {
gossip_seen += 1;
} else if event == cr {
recon_seen += 1;
}
if gossip_seen == min_columns_for_reconstruction && recon_seen >= 1 {
break;
}
}
};
if tokio::time::timeout(STANDARD_TIMEOUT, drain).await.is_err() {
panic!("timeout: gossip_seen={gossip_seen}, recon_seen={recon_seen}");
}
assert_eq!(gossip_seen, min_columns_for_reconstruction);
assert!(recon_seen >= 1);
}
// Test the column reconstruction is delayed for columns that arrive for a previous slot.