mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-17 10:48:28 +00:00
Fix and add new tests
This commit is contained in:
@@ -4,6 +4,7 @@ use crate::data_availability_checker::DataAvailabilityChecker;
|
||||
use crate::graffiti_calculator::GraffitiSettings;
|
||||
use crate::kzg_utils::{build_data_column_sidecars_fulu, build_data_column_sidecars_gloas};
|
||||
use crate::observed_operations::ObservationOutcome;
|
||||
use crate::payload_envelope_verification::AvailableEnvelope;
|
||||
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||
use crate::{BeaconBlockResponseWrapper, CustodyContext, get_block_root};
|
||||
use crate::{
|
||||
@@ -2929,18 +2930,29 @@ where
|
||||
block: Arc<SignedBeaconBlock<E>>,
|
||||
) -> RangeSyncBlock<E> {
|
||||
let block_root = block_root.unwrap_or_else(|| get_block_root(&block));
|
||||
let is_gloas = block.fork_name_unchecked().gloas_enabled();
|
||||
// For Gloas, kzg commitments live in the bid (`signed_execution_payload_bid`), so the
|
||||
// body's `blob_kzg_commitments()` accessor returns Err. `num_expected_blobs` already
|
||||
// handles both shapes.
|
||||
let has_blobs = block.num_expected_blobs() > 0;
|
||||
if !has_blobs {
|
||||
return RangeSyncBlock::new(
|
||||
block,
|
||||
AvailableBlockData::NoData,
|
||||
&self.chain.data_availability_checker,
|
||||
self.chain.spec.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
return if is_gloas {
|
||||
let envelope = self
|
||||
.chain
|
||||
.get_payload_envelope(&block_root)
|
||||
.unwrap()
|
||||
.map(Arc::new)
|
||||
.map(|envelope| Box::new(AvailableEnvelope::new(envelope, vec![])));
|
||||
RangeSyncBlock::new_gloas(block, envelope).unwrap()
|
||||
} else {
|
||||
RangeSyncBlock::new(
|
||||
block,
|
||||
AvailableBlockData::NoData,
|
||||
&self.chain.data_availability_checker,
|
||||
self.chain.spec.clone(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
// Blobs are stored as data columns from Fulu (PeerDAS)
|
||||
@@ -2952,14 +2964,24 @@ where
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let custody_columns = columns.into_iter().collect::<Vec<_>>();
|
||||
let block_data = AvailableBlockData::new_with_data_columns(custody_columns);
|
||||
RangeSyncBlock::new(
|
||||
block,
|
||||
block_data,
|
||||
&self.chain.data_availability_checker,
|
||||
self.chain.spec.clone(),
|
||||
)
|
||||
.unwrap()
|
||||
if is_gloas {
|
||||
let envelope = self
|
||||
.chain
|
||||
.get_payload_envelope(&block_root)
|
||||
.unwrap()
|
||||
.map(Arc::new)
|
||||
.map(|envelope| Box::new(AvailableEnvelope::new(envelope, custody_columns)));
|
||||
RangeSyncBlock::new_gloas(block, envelope).unwrap()
|
||||
} else {
|
||||
let block_data = AvailableBlockData::new_with_data_columns(custody_columns);
|
||||
RangeSyncBlock::new(
|
||||
block,
|
||||
block_data,
|
||||
&self.chain.data_availability_checker,
|
||||
self.chain.spec.clone(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
} else {
|
||||
let blobs = self.chain.get_blobs(&block_root).unwrap().blobs();
|
||||
let block_data = if let Some(blobs) = blobs {
|
||||
@@ -2984,6 +3006,19 @@ where
|
||||
block: Arc<SignedBeaconBlock<E, FullPayload<E>>>,
|
||||
blob_items: Option<(KzgProofs<E>, BlobsList<E>)>,
|
||||
) -> Result<RangeSyncBlock<E>, BlockError> {
|
||||
if block.fork_name_unchecked().gloas_enabled() {
|
||||
let columns = blob_items
|
||||
.map(|_| generate_data_column_sidecars_from_block(&block, &self.spec))
|
||||
.unwrap_or_default();
|
||||
let envelope = self
|
||||
.chain
|
||||
.get_payload_envelope(&block.canonical_root())
|
||||
.map_err(|e| BlockError::BeaconChainError(Box::new(e)))?
|
||||
.map(Arc::new)
|
||||
.map(|envelope| Box::new(AvailableEnvelope::new(envelope, columns)));
|
||||
return RangeSyncBlock::new_gloas(block, envelope).map_err(BlockError::InternalError);
|
||||
}
|
||||
|
||||
Ok(if self.spec.is_peer_das_enabled_for_epoch(block.epoch()) {
|
||||
let epoch = block.slot().epoch(E::slots_per_epoch());
|
||||
let sampling_columns = self.chain.sampling_columns_for_epoch(epoch);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
use beacon_chain::block_verification_types::{AsBlock, ExecutedBlock, LookupBlock, RangeSyncBlock};
|
||||
use beacon_chain::data_availability_checker::{AvailabilityCheckError, AvailableBlockData};
|
||||
use beacon_chain::data_column_verification::CustodyDataColumn;
|
||||
use beacon_chain::payload_envelope_verification::AvailableEnvelope;
|
||||
use beacon_chain::{
|
||||
AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes, ExecutionPendingBlock,
|
||||
WhenSlotSkipped,
|
||||
@@ -148,19 +149,38 @@ where
|
||||
.zip(chain_segment_sidecars.iter())
|
||||
.map(|(snapshot, data_sidecars)| {
|
||||
let block = snapshot.beacon_block.clone();
|
||||
build_range_sync_block(block, data_sidecars, chain.clone())
|
||||
build_range_sync_block(
|
||||
block,
|
||||
snapshot.execution_envelope.clone(),
|
||||
data_sidecars,
|
||||
chain.clone(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_range_sync_block<T>(
|
||||
block: Arc<SignedBeaconBlock<E>>,
|
||||
execution_envelope: Option<Arc<SignedExecutionPayloadEnvelope<E>>>,
|
||||
data_sidecars: &Option<DataSidecars<E>>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
) -> RangeSyncBlock<E>
|
||||
where
|
||||
T: BeaconChainTypes<EthSpec = E>,
|
||||
{
|
||||
if block.fork_name_unchecked().gloas_enabled() {
|
||||
let columns = match data_sidecars {
|
||||
Some(DataSidecars::DataColumns(columns)) => columns
|
||||
.iter()
|
||||
.map(|c| c.as_data_column().clone())
|
||||
.collect::<Vec<_>>(),
|
||||
Some(DataSidecars::Blobs(_)) | None => vec![],
|
||||
};
|
||||
let envelope = execution_envelope
|
||||
.map(|envelope| Box::new(AvailableEnvelope::new(envelope, columns)));
|
||||
return RangeSyncBlock::new_gloas(block, envelope).unwrap();
|
||||
}
|
||||
|
||||
match data_sidecars {
|
||||
Some(DataSidecars::Blobs(blobs)) => {
|
||||
let block_data = AvailableBlockData::new_with_blobs(blobs.clone());
|
||||
@@ -581,7 +601,12 @@ async fn assert_invalid_signature(
|
||||
.iter()
|
||||
.zip(chain_segment_blobs.iter())
|
||||
.map(|(snapshot, blobs)| {
|
||||
build_range_sync_block(snapshot.beacon_block.clone(), blobs, harness.chain.clone())
|
||||
build_range_sync_block(
|
||||
snapshot.beacon_block.clone(),
|
||||
snapshot.execution_envelope.clone(),
|
||||
blobs,
|
||||
harness.chain.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -620,7 +645,12 @@ async fn assert_invalid_signature(
|
||||
.zip(chain_segment_blobs.iter())
|
||||
.filter(|(snapshot, _)| snapshot.beacon_block.slot() > finalized_slot)
|
||||
.map(|(snapshot, blobs)| {
|
||||
build_range_sync_block(snapshot.beacon_block.clone(), blobs, harness.chain.clone())
|
||||
build_range_sync_block(
|
||||
snapshot.beacon_block.clone(),
|
||||
snapshot.execution_envelope.clone(),
|
||||
blobs,
|
||||
harness.chain.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
// We don't care if this fails, we just call this to ensure that all prior blocks have been
|
||||
@@ -634,13 +664,14 @@ async fn assert_invalid_signature(
|
||||
|
||||
let process_res = harness
|
||||
.chain
|
||||
.process_block(
|
||||
snapshots[block_index].beacon_block.canonical_root(),
|
||||
build_range_sync_block(
|
||||
snapshots[block_index].beacon_block.clone(),
|
||||
&chain_segment_blobs[block_index],
|
||||
harness.chain.clone(),
|
||||
),
|
||||
.process_block(
|
||||
snapshots[block_index].beacon_block.canonical_root(),
|
||||
build_range_sync_block(
|
||||
snapshots[block_index].beacon_block.clone(),
|
||||
snapshots[block_index].execution_envelope.clone(),
|
||||
&chain_segment_blobs[block_index],
|
||||
harness.chain.clone(),
|
||||
),
|
||||
NotifyExecutionLayer::Yes,
|
||||
BlockImportSource::Lookup,
|
||||
|| Ok(()),
|
||||
@@ -699,7 +730,12 @@ async fn invalid_signature_gossip_block() {
|
||||
.take(block_index)
|
||||
.zip(chain_segment_blobs.iter())
|
||||
.map(|(snapshot, blobs)| {
|
||||
build_range_sync_block(snapshot.beacon_block.clone(), blobs, harness.chain.clone())
|
||||
build_range_sync_block(
|
||||
snapshot.beacon_block.clone(),
|
||||
snapshot.execution_envelope.clone(),
|
||||
blobs,
|
||||
harness.chain.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
harness
|
||||
@@ -752,7 +788,12 @@ async fn invalid_signature_block_proposal() {
|
||||
.iter()
|
||||
.zip(chain_segment_blobs.iter())
|
||||
.map(|(snapshot, blobs)| {
|
||||
build_range_sync_block(snapshot.beacon_block.clone(), blobs, harness.chain.clone())
|
||||
build_range_sync_block(
|
||||
snapshot.beacon_block.clone(),
|
||||
snapshot.execution_envelope.clone(),
|
||||
blobs,
|
||||
harness.chain.clone(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// Ensure the block will be rejected if imported in a chain segment.
|
||||
@@ -1071,7 +1112,12 @@ async fn invalid_signature_deposit() {
|
||||
.iter()
|
||||
.zip(chain_segment_blobs.iter())
|
||||
.map(|(snapshot, blobs)| {
|
||||
build_range_sync_block(snapshot.beacon_block.clone(), blobs, harness.chain.clone())
|
||||
build_range_sync_block(
|
||||
snapshot.beacon_block.clone(),
|
||||
snapshot.execution_envelope.clone(),
|
||||
blobs,
|
||||
harness.chain.clone(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
assert!(
|
||||
@@ -2186,6 +2232,131 @@ async fn import_execution_pending_block<T: BeaconChainTypes>(
|
||||
}
|
||||
}
|
||||
|
||||
async fn make_gloas_range_sync_block_inputs(
|
||||
) -> Option<(
|
||||
Arc<SignedBeaconBlock<E>>,
|
||||
SignedExecutionPayloadEnvelope<E>,
|
||||
)> {
|
||||
let spec = test_spec::<E>();
|
||||
if !spec.fork_name_at_slot::<E>(Slot::new(1)).gloas_enabled() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let harness = BeaconChainHarness::builder(MainnetEthSpec)
|
||||
.spec(spec.into())
|
||||
.keypairs(KEYPAIRS[0..VALIDATOR_COUNT].to_vec())
|
||||
.node_custody_type(NodeCustodyType::Supernode)
|
||||
.fresh_ephemeral_store()
|
||||
.mock_execution_layer()
|
||||
.build();
|
||||
|
||||
harness.advance_slot();
|
||||
|
||||
let state = harness.get_current_state();
|
||||
let slot = harness.get_current_slot();
|
||||
let ((block, _), envelope, _) = harness.make_block_with_envelope(state, slot).await;
|
||||
Some((block, envelope.expect("gloas block should have envelope")))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_new_gloas_accepts_matching_envelope() {
|
||||
let Some((block, envelope)) = make_gloas_range_sync_block_inputs().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let available_envelope = Box::new(AvailableEnvelope::new(Arc::new(envelope), vec![]));
|
||||
let result = RangeSyncBlock::new_gloas(block, Some(available_envelope));
|
||||
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"new_gloas should accept matching block/envelope, got: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_new_gloas_allows_missing_envelope() {
|
||||
let Some((block, _)) = make_gloas_range_sync_block_inputs().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let result = RangeSyncBlock::new_gloas(block, None);
|
||||
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"new_gloas should allow None envelope, got: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_new_gloas_rejects_mismatched_block_root() {
|
||||
let Some((block, mut envelope)) = make_gloas_range_sync_block_inputs().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
envelope.message.beacon_block_root = Hash256::repeat_byte(0x11);
|
||||
let available_envelope = Box::new(AvailableEnvelope::new(Arc::new(envelope), vec![]));
|
||||
let result = RangeSyncBlock::new_gloas(block, Some(available_envelope));
|
||||
|
||||
assert!(
|
||||
matches!(result, Err(ref err) if err.contains("envelope block root mismatch")),
|
||||
"new_gloas should reject mismatched block root, got: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_new_gloas_rejects_slot_mismatch() {
|
||||
let Some((block, mut envelope)) = make_gloas_range_sync_block_inputs().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
envelope.message.payload.slot_number += 1;
|
||||
let available_envelope = Box::new(AvailableEnvelope::new(Arc::new(envelope), vec![]));
|
||||
let result = RangeSyncBlock::new_gloas(block, Some(available_envelope));
|
||||
|
||||
assert!(
|
||||
matches!(result, Err(ref err) if err.contains("envelope slot mismatch")),
|
||||
"new_gloas should reject mismatched slot, got: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_new_gloas_rejects_builder_index_mismatch() {
|
||||
let Some((block, mut envelope)) = make_gloas_range_sync_block_inputs().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
envelope.message.builder_index += 1;
|
||||
let available_envelope = Box::new(AvailableEnvelope::new(Arc::new(envelope), vec![]));
|
||||
let result = RangeSyncBlock::new_gloas(block, Some(available_envelope));
|
||||
|
||||
assert!(
|
||||
matches!(result, Err(ref err) if err.contains("envelope builder index mismatch")),
|
||||
"new_gloas should reject mismatched builder index, got: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_new_gloas_rejects_block_hash_mismatch() {
|
||||
let Some((block, mut envelope)) = make_gloas_range_sync_block_inputs().await else {
|
||||
return;
|
||||
};
|
||||
|
||||
envelope.message.payload.block_hash = ExecutionBlockHash::repeat_byte(0x22);
|
||||
let available_envelope = Box::new(AvailableEnvelope::new(Arc::new(envelope), vec![]));
|
||||
let result = RangeSyncBlock::new_gloas(block, Some(available_envelope));
|
||||
|
||||
assert!(
|
||||
matches!(result, Err(ref err) if err.contains("envelope block hash mismatch")),
|
||||
"new_gloas should reject mismatched block hash, got: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
// Test that RpcBlock::new() rejects blocks when blob count doesn't match expected.
|
||||
#[tokio::test]
|
||||
async fn range_sync_block_construction_fails_with_wrong_blob_count() {
|
||||
|
||||
Reference in New Issue
Block a user