Merge remote-tracking branch 'origin/unstable' into max-blobs-preset

This commit is contained in:
Michael Sproul
2025-01-06 14:02:50 +11:00
430 changed files with 14304 additions and 9470 deletions

View File

@@ -10,7 +10,6 @@ pub enum Error {
blob_commitment: KzgCommitment,
block_commitment: KzgCommitment,
},
UnableToDetermineImportRequirement,
Unexpected,
SszTypes(ssz_types::Error),
MissingBlobs,
@@ -44,7 +43,6 @@ impl Error {
| Error::Unexpected
| Error::ParentStateMissing(_)
| Error::BlockReplayError(_)
| Error::UnableToDetermineImportRequirement
| Error::RebuildingStateCaches(_)
| Error::SlotClockError => ErrorCategory::Internal,
Error::InvalidBlobs { .. }

View File

@@ -34,11 +34,6 @@ pub struct PendingComponents<E: EthSpec> {
pub reconstruction_started: bool,
}
pub enum BlockImportRequirement {
AllBlobs,
ColumnSampling(usize),
}
impl<E: EthSpec> PendingComponents<E> {
/// Returns an immutable reference to the cached block.
pub fn get_cached_block(&self) -> &Option<DietAvailabilityPendingExecutedBlock<E>> {
@@ -192,63 +187,49 @@ impl<E: EthSpec> PendingComponents<E> {
///
/// Returns `true` if both the block exists and the number of received blobs / custody columns
/// matches the number of expected blobs / custody columns.
pub fn is_available(
&self,
block_import_requirement: &BlockImportRequirement,
log: &Logger,
) -> bool {
pub fn is_available(&self, custody_column_count: usize, log: &Logger) -> bool {
let block_kzg_commitments_count_opt = self.block_kzg_commitments_count();
let expected_blobs_msg = block_kzg_commitments_count_opt
.as_ref()
.map(|num| num.to_string())
.unwrap_or("unknown".to_string());
match block_import_requirement {
BlockImportRequirement::AllBlobs => {
let received_blobs = self.num_received_blobs();
let expected_blobs_msg = block_kzg_commitments_count_opt
.as_ref()
.map(|num| num.to_string())
.unwrap_or("unknown".to_string());
debug!(log,
"Component(s) added to data availability checker";
"block_root" => ?self.block_root,
"received_block" => block_kzg_commitments_count_opt.is_some(),
"received_blobs" => received_blobs,
"expected_blobs" => expected_blobs_msg,
);
block_kzg_commitments_count_opt.map_or(false, |num_expected_blobs| {
num_expected_blobs == received_blobs
})
// No data columns when there are 0 blobs
let expected_columns_opt = block_kzg_commitments_count_opt.map(|blob_count| {
if blob_count > 0 {
custody_column_count
} else {
0
}
BlockImportRequirement::ColumnSampling(num_expected_columns) => {
// No data columns when there are 0 blobs
let expected_columns_opt = block_kzg_commitments_count_opt.map(|blob_count| {
if blob_count > 0 {
*num_expected_columns
} else {
0
}
});
});
let expected_columns_msg = expected_columns_opt
.as_ref()
.map(|num| num.to_string())
.unwrap_or("unknown".to_string());
let expected_columns_msg = expected_columns_opt
.as_ref()
.map(|num| num.to_string())
.unwrap_or("unknown".to_string());
let num_received_blobs = self.num_received_blobs();
let num_received_columns = self.num_received_data_columns();
let num_received_columns = self.num_received_data_columns();
debug!(
log,
"Component(s) added to data availability checker";
"block_root" => ?self.block_root,
"received_blobs" => num_received_blobs,
"expected_blobs" => expected_blobs_msg,
"received_columns" => num_received_columns,
"expected_columns" => expected_columns_msg,
);
debug!(log,
"Component(s) added to data availability checker";
"block_root" => ?self.block_root,
"received_block" => block_kzg_commitments_count_opt.is_some(),
"received_columns" => num_received_columns,
"expected_columns" => expected_columns_msg,
);
let all_blobs_received = block_kzg_commitments_count_opt
.map_or(false, |num_expected_blobs| {
num_expected_blobs == num_received_blobs
});
expected_columns_opt.map_or(false, |num_expected_columns| {
num_expected_columns == num_received_columns
})
}
}
let all_columns_received = expected_columns_opt.map_or(false, |num_expected_columns| {
num_expected_columns == num_received_columns
});
all_blobs_received || all_columns_received
}
/// Returns an empty `PendingComponents` object with the given block root.
@@ -271,7 +252,6 @@ impl<E: EthSpec> PendingComponents<E> {
/// reconstructed from disk. Ensure you are not holding any write locks while calling this.
pub fn make_available<R>(
self,
block_import_requirement: BlockImportRequirement,
spec: &Arc<ChainSpec>,
recover: R,
) -> Result<Availability<E>, AvailabilityCheckError>
@@ -298,31 +278,29 @@ impl<E: EthSpec> PendingComponents<E> {
return Err(AvailabilityCheckError::Unexpected);
};
let (blobs, data_columns) = match block_import_requirement {
BlockImportRequirement::AllBlobs => {
let num_blobs_expected = diet_executed_block.num_blobs_expected();
let Some(verified_blobs) = verified_blobs
.into_iter()
.map(|b| b.map(|b| b.to_blob()))
.take(num_blobs_expected)
.collect::<Option<Vec<_>>>()
else {
return Err(AvailabilityCheckError::Unexpected);
};
let max_len =
spec.max_blobs_per_block(diet_executed_block.as_block().epoch()) as usize;
(
Some(RuntimeVariableList::new(verified_blobs, max_len)?),
None,
)
}
BlockImportRequirement::ColumnSampling(_) => {
let verified_data_columns = verified_data_columns
.into_iter()
.map(|d| d.into_inner())
.collect();
(None, Some(verified_data_columns))
}
let is_peer_das_enabled = spec.is_peer_das_enabled_for_epoch(diet_executed_block.epoch());
let (blobs, data_columns) = if is_peer_das_enabled {
let data_columns = verified_data_columns
.into_iter()
.map(|d| d.into_inner())
.collect::<Vec<_>>();
(None, Some(data_columns))
} else {
let num_blobs_expected = diet_executed_block.num_blobs_expected();
let Some(verified_blobs) = verified_blobs
.into_iter()
.map(|b| b.map(|b| b.to_blob()))
.take(num_blobs_expected)
.collect::<Option<Vec<_>>>()
.map(Into::into)
else {
return Err(AvailabilityCheckError::Unexpected);
};
let max_len = spec.max_blobs_per_block(diet_executed_block.as_block().epoch()) as usize;
(
Some(RuntimeVariableList::new(verified_blobs, max_len)?),
None,
)
};
let executed_block = recover(diet_executed_block)?;
@@ -364,10 +342,7 @@ impl<E: EthSpec> PendingComponents<E> {
}
if let Some(kzg_verified_data_column) = self.verified_data_columns.first() {
let epoch = kzg_verified_data_column
.as_data_column()
.slot()
.epoch(E::slots_per_epoch());
let epoch = kzg_verified_data_column.as_data_column().epoch();
return Some(epoch);
}
@@ -474,27 +449,22 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
f(self.critical.read().peek(block_root))
}
fn block_import_requirement(
&self,
epoch: Epoch,
) -> Result<BlockImportRequirement, AvailabilityCheckError> {
let peer_das_enabled = self.spec.is_peer_das_enabled_for_epoch(epoch);
if peer_das_enabled {
Ok(BlockImportRequirement::ColumnSampling(
self.sampling_column_count,
))
} else {
Ok(BlockImportRequirement::AllBlobs)
}
}
pub fn put_kzg_verified_blobs<I: IntoIterator<Item = KzgVerifiedBlob<T::EthSpec>>>(
&self,
block_root: Hash256,
epoch: Epoch,
kzg_verified_blobs: I,
log: &Logger,
) -> Result<Availability<T::EthSpec>, AvailabilityCheckError> {
let mut kzg_verified_blobs = kzg_verified_blobs.into_iter().peekable();
let Some(epoch) = kzg_verified_blobs
.peek()
.map(|verified_blob| verified_blob.as_blob().epoch())
else {
// Verified blobs list should be non-empty.
return Err(AvailabilityCheckError::Unexpected);
};
let mut fixed_blobs =
RuntimeFixedList::new(vec![None; self.spec.max_blobs_per_block(epoch) as usize]);
@@ -517,12 +487,11 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
// Merge in the blobs.
pending_components.merge_blobs(fixed_blobs);
let block_import_requirement = self.block_import_requirement(epoch)?;
if pending_components.is_available(&block_import_requirement, log) {
if pending_components.is_available(self.sampling_column_count, log) {
write_lock.put(block_root, pending_components.clone());
// No need to hold the write lock anymore
drop(write_lock);
pending_components.make_available(block_import_requirement, &self.spec, |diet_block| {
pending_components.make_available(&self.spec, |diet_block| {
self.state_cache.recover_pending_executed_block(diet_block)
})
} else {
@@ -537,10 +506,18 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
>(
&self,
block_root: Hash256,
epoch: Epoch,
kzg_verified_data_columns: I,
log: &Logger,
) -> Result<Availability<T::EthSpec>, AvailabilityCheckError> {
let mut kzg_verified_data_columns = kzg_verified_data_columns.into_iter().peekable();
let Some(epoch) = kzg_verified_data_columns
.peek()
.map(|verified_blob| verified_blob.as_data_column().epoch())
else {
// Verified data_columns list should be non-empty.
return Err(AvailabilityCheckError::Unexpected);
};
let mut write_lock = self.critical.write();
// Grab existing entry or create a new entry.
@@ -554,13 +531,11 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
// Merge in the data columns.
pending_components.merge_data_columns(kzg_verified_data_columns)?;
let block_import_requirement = self.block_import_requirement(epoch)?;
if pending_components.is_available(&block_import_requirement, log) {
if pending_components.is_available(self.sampling_column_count, log) {
write_lock.put(block_root, pending_components.clone());
// No need to hold the write lock anymore
drop(write_lock);
pending_components.make_available(block_import_requirement, &self.spec, |diet_block| {
pending_components.make_available(&self.spec, |diet_block| {
self.state_cache.recover_pending_executed_block(diet_block)
})
} else {
@@ -628,8 +603,8 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
log: &Logger,
) -> Result<Availability<T::EthSpec>, AvailabilityCheckError> {
let mut write_lock = self.critical.write();
let epoch = executed_block.as_block().epoch();
let block_root = executed_block.import_data.block_root;
let epoch = executed_block.block.epoch();
// register the block to get the diet block
let diet_executed_block = self
@@ -648,12 +623,11 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
pending_components.merge_block(diet_executed_block);
// Check if we have all components and entire set is consistent.
let block_import_requirement = self.block_import_requirement(epoch)?;
if pending_components.is_available(&block_import_requirement, log) {
if pending_components.is_available(self.sampling_column_count, log) {
write_lock.put(block_root, pending_components.clone());
// No need to hold the write lock anymore
drop(write_lock);
pending_components.make_available(block_import_requirement, &self.spec, |diet_block| {
pending_components.make_available(&self.spec, |diet_block| {
self.state_cache.recover_pending_executed_block(diet_block)
})
} else {
@@ -709,6 +683,7 @@ impl<T: BeaconChainTypes> DataAvailabilityCheckerInner<T> {
#[cfg(test)]
mod test {
use super::*;
use crate::{
blob_verification::GossipVerifiedBlob,
block_verification::PayloadVerificationOutcome,
@@ -718,6 +693,7 @@ mod test {
test_utils::{BaseHarnessType, BeaconChainHarness, DiskHarnessType},
};
use fork_choice::PayloadVerificationStatus;
use logging::test_logger;
use slog::{info, Logger};
use state_processing::ConsensusContext;
@@ -938,7 +914,6 @@ mod test {
let (pending_block, blobs) = availability_pending_block(&harness).await;
let root = pending_block.import_data.block_root;
let epoch = pending_block.block.epoch();
let blobs_expected = pending_block.num_blobs_expected();
assert_eq!(
@@ -987,7 +962,7 @@ mod test {
for (blob_index, gossip_blob) in blobs.into_iter().enumerate() {
kzg_verified_blobs.push(gossip_blob.into_inner());
let availability = cache
.put_kzg_verified_blobs(root, epoch, kzg_verified_blobs.clone(), harness.logger())
.put_kzg_verified_blobs(root, kzg_verified_blobs.clone(), harness.logger())
.expect("should put blob");
if blob_index == blobs_expected - 1 {
assert!(matches!(availability, Availability::Available(_)));
@@ -1011,12 +986,11 @@ mod test {
"should have expected number of blobs"
);
let root = pending_block.import_data.block_root;
let epoch = pending_block.block.epoch();
let mut kzg_verified_blobs = vec![];
for gossip_blob in blobs {
kzg_verified_blobs.push(gossip_blob.into_inner());
let availability = cache
.put_kzg_verified_blobs(root, epoch, kzg_verified_blobs.clone(), harness.logger())
.put_kzg_verified_blobs(root, kzg_verified_blobs.clone(), harness.logger())
.expect("should put blob");
assert_eq!(
availability,

View File

@@ -57,6 +57,11 @@ impl<E: EthSpec> DietAvailabilityPendingExecutedBlock<E> {
.cloned()
.unwrap_or_default()
}
/// Returns the epoch corresponding to `self.slot()`.
pub fn epoch(&self) -> Epoch {
self.block.slot().epoch(E::slots_per_epoch())
}
}
/// This LRU cache holds BeaconStates used for block import. If the cache overflows,