Skip to content

Commit e90d197

Browse files
authored
Merge of #7973
2 parents 3820519 + f2405f6 commit e90d197

File tree

2 files changed

+58
-59
lines changed

2 files changed

+58
-59
lines changed

beacon_node/beacon_chain/src/data_column_verification.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,40 @@ impl<T: BeaconChainTypes, O: ObservationStrategy> GossipVerifiedDataColumn<T, O>
215215
)
216216
}
217217

218+
/// Create a `GossipVerifiedDataColumn` from `DataColumnSidecar` for block production ONLY.
219+
/// When publishing a block constructed locally, the EL will have already verified the cell proofs.
220+
/// When publishing a block constructed externally, there will be no columns here.
221+
pub fn new_for_block_publishing(
222+
column_sidecar: Arc<DataColumnSidecar<T::EthSpec>>,
223+
chain: &BeaconChain<T>,
224+
) -> Result<Self, GossipDataColumnError> {
225+
verify_data_column_sidecar(&column_sidecar)?;
226+
227+
// Check if the data column is already in the DA checker cache. This happens when data columns
228+
// are made available through the `engine_getBlobs` method. If it exists in the cache, we know
229+
// it has already passed the gossip checks, even though this particular instance hasn't been
230+
// seen / published on the gossip network yet (passed the `verify_is_unknown_sidecar` check above).
231+
// In this case, we should accept it for gossip propagation.
232+
verify_is_unknown_sidecar(chain, &column_sidecar)?;
233+
234+
if chain
235+
.data_availability_checker
236+
.is_data_column_cached(&column_sidecar.block_root(), &column_sidecar)
237+
{
238+
// Observe this data column so we don't process it again.
239+
if O::observe() {
240+
observe_gossip_data_column(&column_sidecar, chain)?;
241+
}
242+
return Err(GossipDataColumnError::PriorKnownUnpublished);
243+
}
244+
245+
Ok(Self {
246+
block_root: column_sidecar.block_root(),
247+
data_column: KzgVerifiedDataColumn::from_execution_verified(column_sidecar),
248+
_phantom: Default::default(),
249+
})
250+
}
251+
218252
/// Create a `GossipVerifiedDataColumn` from `DataColumnSidecar` for testing ONLY.
219253
pub fn __new_for_testing(column_sidecar: Arc<DataColumnSidecar<T::EthSpec>>) -> Self {
220254
Self {
@@ -447,12 +481,12 @@ pub fn validate_data_column_sidecar_for_gossip<T: BeaconChainTypes, O: Observati
447481
verify_index_matches_subnet(&data_column, subnet, &chain.spec)?;
448482
verify_sidecar_not_from_future_slot(chain, column_slot)?;
449483
verify_slot_greater_than_latest_finalized_slot(chain, column_slot)?;
450-
verify_is_first_sidecar(chain, &data_column)?;
484+
verify_is_unknown_sidecar(chain, &data_column)?;
451485

452486
// Check if the data column is already in the DA checker cache. This happens when data columns
453487
// are made available through the `engine_getBlobs` method. If it exists in the cache, we know
454488
// it has already passed the gossip checks, even though this particular instance hasn't been
455-
// seen / published on the gossip network yet (passed the `verify_is_first_sidecar` check above).
489+
// seen / published on the gossip network yet (passed the `verify_is_unknown_sidecar` check above).
456490
// In this case, we should accept it for gossip propagation.
457491
if chain
458492
.data_availability_checker
@@ -526,22 +560,22 @@ fn verify_data_column_sidecar<E: EthSpec>(
526560
Ok(())
527561
}
528562

529-
// Verify that this is the first column sidecar received for the tuple:
530-
// (block_header.slot, block_header.proposer_index, column_sidecar.index)
531-
fn verify_is_first_sidecar<T: BeaconChainTypes>(
563+
/// Verify that `column_sidecar` is not yet known, i.e. this is the first time `column_sidecar` has been received for the tuple:
564+
/// `(block_header.slot, block_header.proposer_index, column_sidecar.index)`
565+
fn verify_is_unknown_sidecar<T: BeaconChainTypes>(
532566
chain: &BeaconChain<T>,
533-
data_column: &DataColumnSidecar<T::EthSpec>,
567+
column_sidecar: &DataColumnSidecar<T::EthSpec>,
534568
) -> Result<(), GossipDataColumnError> {
535569
if chain
536570
.observed_column_sidecars
537571
.read()
538-
.proposer_is_known(data_column)
572+
.proposer_is_known(column_sidecar)
539573
.map_err(|e| GossipDataColumnError::BeaconChainError(Box::new(e.into())))?
540574
{
541575
return Err(GossipDataColumnError::PriorKnown {
542-
proposer: data_column.block_proposer_index(),
543-
slot: data_column.slot(),
544-
index: data_column.index,
576+
proposer: column_sidecar.block_proposer_index(),
577+
slot: column_sidecar.slot(),
578+
index: column_sidecar.index,
545579
});
546580
}
547581
Ok(())

beacon_node/http_api/src/publish_blocks.rs

Lines changed: 14 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::future::Future;
33

44
use beacon_chain::blob_verification::{GossipBlobError, GossipVerifiedBlob};
55
use beacon_chain::block_verification_types::{AsBlock, RpcBlock};
6-
use beacon_chain::data_column_verification::{GossipDataColumnError, GossipVerifiedDataColumn};
6+
use beacon_chain::data_column_verification::GossipVerifiedDataColumn;
77
use beacon_chain::validator_monitor::{get_block_delay_ms, timestamp_now};
88
use beacon_chain::{
99
AvailabilityProcessingStatus, BeaconChain, BeaconChainError, BeaconChainTypes, BlockError,
@@ -216,7 +216,7 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlock<T>>(
216216
}
217217
}
218218

219-
if gossip_verified_columns.iter().map(Option::is_some).count() > 0 {
219+
if !gossip_verified_columns.is_empty() {
220220
if let Some(data_column_publishing_delay) = data_column_publishing_delay_for_testing {
221221
// Subtract block publishing delay if it is also used.
222222
// Note: if `data_column_publishing_delay` is less than `block_publishing_delay`, it
@@ -240,7 +240,6 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlock<T>>(
240240
let sampling_columns_indices = chain.sampling_columns_for_epoch(epoch);
241241
let sampling_columns = gossip_verified_columns
242242
.into_iter()
243-
.flatten()
244243
.filter(|data_column| sampling_columns_indices.contains(&data_column.index()))
245244
.collect::<Vec<_>>();
246245

@@ -348,7 +347,7 @@ pub async fn publish_block<T: BeaconChainTypes, B: IntoGossipVerifiedBlock<T>>(
348347
type BuildDataSidecarTaskResult<T> = Result<
349348
(
350349
Vec<Option<GossipVerifiedBlob<T>>>,
351-
Vec<Option<GossipVerifiedDataColumn<T>>>,
350+
Vec<GossipVerifiedDataColumn<T>>,
352351
),
353352
Rejection,
354353
>;
@@ -382,7 +381,7 @@ fn spawn_build_data_sidecar_task<T: BeaconChainTypes>(
382381
} else {
383382
// Post PeerDAS: construct data columns.
384383
let gossip_verified_data_columns =
385-
build_gossip_verified_data_columns(&chain, &block, blobs, kzg_proofs)?;
384+
build_data_columns(&chain, &block, blobs, kzg_proofs)?;
386385
Ok((vec![], gossip_verified_data_columns))
387386
}
388387
},
@@ -397,12 +396,16 @@ fn spawn_build_data_sidecar_task<T: BeaconChainTypes>(
397396
})
398397
}
399398

400-
fn build_gossip_verified_data_columns<T: BeaconChainTypes>(
399+
/// Build data columns as wrapped `GossipVerifiedDataColumn`s.
400+
/// There is no need to actually perform gossip verification on columns that a block producer
401+
/// is publishing. In the locally constructed case, cell proof verification happens in the EL.
402+
/// In the externally constructed case, there wont be any columns here.
403+
fn build_data_columns<T: BeaconChainTypes>(
401404
chain: &BeaconChain<T>,
402405
block: &SignedBeaconBlock<T::EthSpec, FullPayload<T::EthSpec>>,
403406
blobs: BlobsList<T::EthSpec>,
404407
kzg_cell_proofs: KzgProofs<T::EthSpec>,
405-
) -> Result<Vec<Option<GossipVerifiedDataColumn<T>>>, Rejection> {
408+
) -> Result<Vec<GossipVerifiedDataColumn<T>>, Rejection> {
406409
let slot = block.slot();
407410
let data_column_sidecars =
408411
build_blob_data_column_sidecars(chain, block, blobs, kzg_cell_proofs).map_err(|e| {
@@ -414,49 +417,12 @@ fn build_gossip_verified_data_columns<T: BeaconChainTypes>(
414417
warp_utils::reject::custom_bad_request(format!("{e:?}"))
415418
})?;
416419

417-
let slot = block.slot();
418420
let gossip_verified_data_columns = data_column_sidecars
419421
.into_iter()
420-
.map(|data_column_sidecar| {
421-
let column_index = data_column_sidecar.index;
422-
let subnet = DataColumnSubnetId::from_column_index(column_index, &chain.spec);
423-
let gossip_verified_column =
424-
GossipVerifiedDataColumn::new(data_column_sidecar, subnet, chain);
425-
426-
match gossip_verified_column {
427-
Ok(blob) => Ok(Some(blob)),
428-
Err(GossipDataColumnError::PriorKnown { proposer, .. }) => {
429-
// Log the error but do not abort publication, we may need to publish the block
430-
// or some of the other data columns if the block & data columns are only
431-
// partially published by the other publisher.
432-
debug!(
433-
column_index,
434-
%slot,
435-
proposer,
436-
"Data column for publication already known"
437-
);
438-
Ok(None)
439-
}
440-
Err(GossipDataColumnError::PriorKnownUnpublished) => {
441-
debug!(
442-
column_index,
443-
%slot,
444-
"Data column for publication already known via the EL"
445-
);
446-
Ok(None)
447-
}
448-
Err(e) => {
449-
error!(
450-
column_index,
451-
%slot,
452-
error = ?e,
453-
"Data column for publication is gossip-invalid"
454-
);
455-
Err(warp_utils::reject::custom_bad_request(format!("{e:?}")))
456-
}
457-
}
422+
.filter_map(|data_column_sidecar| {
423+
GossipVerifiedDataColumn::new_for_block_publishing(data_column_sidecar, chain).ok()
458424
})
459-
.collect::<Result<Vec<_>, Rejection>>()?;
425+
.collect::<Vec<_>>();
460426

461427
Ok(gossip_verified_data_columns)
462428
}
@@ -533,13 +499,12 @@ fn publish_blob_sidecars<T: BeaconChainTypes>(
533499

534500
fn publish_column_sidecars<T: BeaconChainTypes>(
535501
sender_clone: &UnboundedSender<NetworkMessage<T::EthSpec>>,
536-
data_column_sidecars: &[Option<GossipVerifiedDataColumn<T>>],
502+
data_column_sidecars: &[GossipVerifiedDataColumn<T>],
537503
chain: &BeaconChain<T>,
538504
) -> Result<(), BlockError> {
539505
let malicious_withhold_count = chain.config.malicious_withhold_count;
540506
let mut data_column_sidecars = data_column_sidecars
541507
.iter()
542-
.flatten()
543508
.map(|d| d.clone_data_column())
544509
.collect::<Vec<_>>();
545510
if malicious_withhold_count > 0 {

0 commit comments

Comments
 (0)