mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-18 03:08:28 +00:00
Gloas payload cache (#9209)
In Gloas, beacon blocks are imported into fork choice immediately - the payload envelope and data columns arrive separately. KZG commitments moved from the column sidecar into the execution payload bid, so the existing `DataAvailabilityChecker` (which assumes block and data are coupled) can't be used for Gloas. * Introduced `PendingPayloadCache` to keep track of payload and data columns per block root. * Added gossip column verification * Added support for Gloas data column reconstruction * Payload envelope verification simplified: removed `MaybeAvailableEnvelope`, `ExecutedEnvelope`, `EnvelopeImportData` Not yet implemented (tracked with TODOs): - Proper lookup sync for Gloas columns arriving before blocks - Partial column merging for Gloas - Moving `load_gloas_payload_bid` disk reads off the async runtime - Backfill/range sync for Gloas Based on @eserilev's PR and work in progress. See also #9202 for verification. Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu> Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com> Co-Authored-By: Daniel Knopik <daniel@dknopik.de> Co-Authored-By: Daniel Knopik <107140945+dknopik@users.noreply.github.com> Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com> Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
@@ -33,6 +33,7 @@ use crate::data_column_verification::{
|
||||
GossipVerifiedDataColumn, KzgVerifiedCustodyDataColumn, KzgVerifiedDataColumn,
|
||||
verify_kzg_for_data_column_list,
|
||||
};
|
||||
use crate::kzg_utils::validate_data_columns_with_commitments;
|
||||
use crate::metrics::{
|
||||
KZG_DATA_COLUMN_RECONSTRUCTION_ATTEMPTS, KZG_DATA_COLUMN_RECONSTRUCTION_FAILURES,
|
||||
};
|
||||
@@ -490,8 +491,7 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
||||
AvailableBlockData::Blobs(blobs) => verify_kzg_for_blob_list(blobs.iter(), &self.kzg)
|
||||
.map_err(AvailabilityCheckError::InvalidBlobs),
|
||||
AvailableBlockData::DataColumns(columns) => {
|
||||
verify_kzg_for_data_column_list(columns.iter(), &self.kzg)
|
||||
.map_err(AvailabilityCheckError::InvalidColumn)
|
||||
verify_columns_against_block(&self.kzg, available_block.block(), columns)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,13 +504,17 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
||||
available_blocks: &[AvailableBlock<T::EthSpec>],
|
||||
) -> Result<(), AvailabilityCheckError> {
|
||||
let mut all_blobs = Vec::new();
|
||||
let mut all_data_columns = Vec::new();
|
||||
|
||||
for available_block in available_blocks {
|
||||
match available_block.data().to_owned() {
|
||||
match available_block.data() {
|
||||
AvailableBlockData::NoData => {}
|
||||
AvailableBlockData::Blobs(blobs) => all_blobs.extend(blobs),
|
||||
AvailableBlockData::DataColumns(columns) => all_data_columns.extend(columns),
|
||||
AvailableBlockData::Blobs(blobs) => all_blobs.extend(blobs.iter().cloned()),
|
||||
AvailableBlockData::DataColumns(columns) => {
|
||||
// Each block has its own commitments. For Gloas they live in the bid; for
|
||||
// Fulu they live inline on the column. Verify per block and let the helper
|
||||
// pick the right path.
|
||||
verify_columns_against_block(&self.kzg, available_block.block(), columns)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,11 +523,6 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
||||
.map_err(AvailabilityCheckError::InvalidBlobs)?;
|
||||
}
|
||||
|
||||
if !all_data_columns.is_empty() {
|
||||
verify_kzg_for_data_column_list(all_data_columns.iter(), &self.kzg)
|
||||
.map_err(AvailabilityCheckError::InvalidColumn)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -605,9 +604,21 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
||||
metrics::inc_counter(&KZG_DATA_COLUMN_RECONSTRUCTION_ATTEMPTS);
|
||||
let timer = metrics::start_timer(&metrics::DATA_AVAILABILITY_RECONSTRUCTION_TIME);
|
||||
|
||||
let columns: Vec<_> = verified_data_columns
|
||||
.into_iter()
|
||||
.map(|c| c.into_inner())
|
||||
.collect();
|
||||
// Fulu columns carry their commitments; reconstruction needs the count to drive the
|
||||
// per-blob recovery loop.
|
||||
let kzg_commitments = columns
|
||||
.first()
|
||||
.and_then(|c| c.kzg_commitments().ok().cloned())
|
||||
.ok_or(AvailabilityCheckError::InvalidVariant)?;
|
||||
|
||||
let all_data_columns = KzgVerifiedCustodyDataColumn::reconstruct_columns(
|
||||
&self.kzg,
|
||||
&verified_data_columns,
|
||||
columns,
|
||||
&kzg_commitments,
|
||||
&self.spec,
|
||||
)
|
||||
.map_err(|e| {
|
||||
@@ -676,6 +687,35 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify a batch of data columns belonging to a single block, picking the right commitment
|
||||
/// source for the block's fork (Fulu: inline on column; Gloas: from the embedded payload bid).
|
||||
fn verify_columns_against_block<E: EthSpec>(
|
||||
kzg: &Kzg,
|
||||
block: &SignedBeaconBlock<E>,
|
||||
columns: &[Arc<DataColumnSidecar<E>>],
|
||||
) -> Result<(), AvailabilityCheckError> {
|
||||
if columns.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
if block.fork_name_unchecked().gloas_enabled() {
|
||||
let commitments = block
|
||||
.message()
|
||||
.body()
|
||||
.signed_execution_payload_bid()
|
||||
.map(|bid| bid.message.blob_kzg_commitments.clone())
|
||||
.map_err(|_| {
|
||||
AvailabilityCheckError::Unexpected(
|
||||
"Gloas block missing signed_execution_payload_bid".to_string(),
|
||||
)
|
||||
})?;
|
||||
validate_data_columns_with_commitments(kzg, columns.iter(), commitments.as_ref())
|
||||
.map_err(AvailabilityCheckError::InvalidColumn)
|
||||
} else {
|
||||
verify_kzg_for_data_column_list(columns.iter(), kzg)
|
||||
.map_err(AvailabilityCheckError::InvalidColumn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to group data availability checker metrics.
|
||||
pub struct DataAvailabilityCheckerMetrics {
|
||||
pub block_cache_size: usize,
|
||||
@@ -874,10 +914,13 @@ impl<E: EthSpec> AvailableBlock<E> {
|
||||
|
||||
match &block_data {
|
||||
AvailableBlockData::NoData => {
|
||||
if columns_required {
|
||||
return Err(AvailabilityCheckError::MissingCustodyColumns);
|
||||
} else if blobs_required {
|
||||
return Err(AvailabilityCheckError::MissingBlobs);
|
||||
// For Gloas, DA is checked for the PayloadEnvelope, not for the block.
|
||||
if !block.fork_name_unchecked().gloas_enabled() {
|
||||
if columns_required {
|
||||
return Err(AvailabilityCheckError::MissingCustodyColumns);
|
||||
} else if blobs_required {
|
||||
return Err(AvailabilityCheckError::MissingBlobs);
|
||||
}
|
||||
}
|
||||
}
|
||||
AvailableBlockData::Blobs(blobs) => {
|
||||
|
||||
Reference in New Issue
Block a user