mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-30 11:24:31 +00:00
Add max_blobs_per_block check to data column gossip validation (#8198)
Addresses this spec change https://github.com/ethereum/consensus-specs/pull/4650 Add `max_blobs_per_block` to gossip data column check so we reject large columns before processing. (we currently do this check during processing) Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
@@ -161,6 +161,15 @@ pub enum GossipDataColumnError {
|
|||||||
///
|
///
|
||||||
/// The column sidecar is invalid and the peer is faulty
|
/// The column sidecar is invalid and the peer is faulty
|
||||||
InconsistentProofsLength { cells_len: usize, proofs_len: usize },
|
InconsistentProofsLength { cells_len: usize, proofs_len: usize },
|
||||||
|
/// The number of KZG commitments exceeds the maximum number of blobs allowed for the fork. The
|
||||||
|
/// sidecar is invalid.
|
||||||
|
///
|
||||||
|
/// ## Peer scoring
|
||||||
|
/// The column sidecar is invalid and the peer is faulty
|
||||||
|
MaxBlobsPerBlockExceeded {
|
||||||
|
max_blobs_per_block: usize,
|
||||||
|
commitments_len: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BeaconChainError> for GossipDataColumnError {
|
impl From<BeaconChainError> for GossipDataColumnError {
|
||||||
@@ -220,7 +229,7 @@ impl<T: BeaconChainTypes, O: ObservationStrategy> GossipVerifiedDataColumn<T, O>
|
|||||||
column_sidecar: Arc<DataColumnSidecar<T::EthSpec>>,
|
column_sidecar: Arc<DataColumnSidecar<T::EthSpec>>,
|
||||||
chain: &BeaconChain<T>,
|
chain: &BeaconChain<T>,
|
||||||
) -> Result<Self, GossipDataColumnError> {
|
) -> Result<Self, GossipDataColumnError> {
|
||||||
verify_data_column_sidecar(&column_sidecar)?;
|
verify_data_column_sidecar(&column_sidecar, &chain.spec)?;
|
||||||
|
|
||||||
// Check if the data column is already in the DA checker cache. This happens when data columns
|
// Check if the data column is already in the DA checker cache. This happens when data columns
|
||||||
// are made available through the `engine_getBlobs` method. If it exists in the cache, we know
|
// are made available through the `engine_getBlobs` method. If it exists in the cache, we know
|
||||||
@@ -475,7 +484,7 @@ pub fn validate_data_column_sidecar_for_gossip<T: BeaconChainTypes, O: Observati
|
|||||||
chain: &BeaconChain<T>,
|
chain: &BeaconChain<T>,
|
||||||
) -> Result<GossipVerifiedDataColumn<T, O>, GossipDataColumnError> {
|
) -> Result<GossipVerifiedDataColumn<T, O>, GossipDataColumnError> {
|
||||||
let column_slot = data_column.slot();
|
let column_slot = data_column.slot();
|
||||||
verify_data_column_sidecar(&data_column)?;
|
verify_data_column_sidecar(&data_column, &chain.spec)?;
|
||||||
verify_index_matches_subnet(&data_column, subnet, &chain.spec)?;
|
verify_index_matches_subnet(&data_column, subnet, &chain.spec)?;
|
||||||
verify_sidecar_not_from_future_slot(chain, column_slot)?;
|
verify_sidecar_not_from_future_slot(chain, column_slot)?;
|
||||||
verify_slot_greater_than_latest_finalized_slot(chain, column_slot)?;
|
verify_slot_greater_than_latest_finalized_slot(chain, column_slot)?;
|
||||||
@@ -529,6 +538,7 @@ pub fn validate_data_column_sidecar_for_gossip<T: BeaconChainTypes, O: Observati
|
|||||||
/// Verify if the data column sidecar is valid.
|
/// Verify if the data column sidecar is valid.
|
||||||
fn verify_data_column_sidecar<E: EthSpec>(
|
fn verify_data_column_sidecar<E: EthSpec>(
|
||||||
data_column: &DataColumnSidecar<E>,
|
data_column: &DataColumnSidecar<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
) -> Result<(), GossipDataColumnError> {
|
) -> Result<(), GossipDataColumnError> {
|
||||||
if data_column.index >= E::number_of_columns() as u64 {
|
if data_column.index >= E::number_of_columns() as u64 {
|
||||||
return Err(GossipDataColumnError::InvalidColumnIndex(data_column.index));
|
return Err(GossipDataColumnError::InvalidColumnIndex(data_column.index));
|
||||||
@@ -540,6 +550,14 @@ fn verify_data_column_sidecar<E: EthSpec>(
|
|||||||
let cells_len = data_column.column.len();
|
let cells_len = data_column.column.len();
|
||||||
let commitments_len = data_column.kzg_commitments.len();
|
let commitments_len = data_column.kzg_commitments.len();
|
||||||
let proofs_len = data_column.kzg_proofs.len();
|
let proofs_len = data_column.kzg_proofs.len();
|
||||||
|
let max_blobs_per_block = spec.max_blobs_per_block(data_column.epoch()) as usize;
|
||||||
|
|
||||||
|
if commitments_len > max_blobs_per_block {
|
||||||
|
return Err(GossipDataColumnError::MaxBlobsPerBlockExceeded {
|
||||||
|
max_blobs_per_block,
|
||||||
|
commitments_len,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if cells_len != commitments_len {
|
if cells_len != commitments_len {
|
||||||
return Err(GossipDataColumnError::InconsistentCommitmentsLength {
|
return Err(GossipDataColumnError::InconsistentCommitmentsLength {
|
||||||
@@ -782,16 +800,22 @@ pub fn observe_gossip_data_column<T: BeaconChainTypes>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::data_column_verification::{
|
use crate::data_column_verification::{
|
||||||
GossipDataColumnError, validate_data_column_sidecar_for_gossip,
|
GossipDataColumnError, GossipVerifiedDataColumn, validate_data_column_sidecar_for_gossip,
|
||||||
};
|
};
|
||||||
use crate::observed_data_sidecars::Observe;
|
use crate::observed_data_sidecars::Observe;
|
||||||
use crate::test_utils::BeaconChainHarness;
|
use crate::test_utils::{
|
||||||
|
BeaconChainHarness, EphemeralHarnessType, generate_data_column_sidecars_from_block,
|
||||||
|
};
|
||||||
|
use eth2::types::BlobsBundle;
|
||||||
|
use execution_layer::test_utils::generate_blobs;
|
||||||
|
use std::sync::Arc;
|
||||||
use types::{DataColumnSidecar, DataColumnSubnetId, EthSpec, ForkName, MainnetEthSpec};
|
use types::{DataColumnSidecar, DataColumnSubnetId, EthSpec, ForkName, MainnetEthSpec};
|
||||||
|
|
||||||
type E = MainnetEthSpec;
|
type E = MainnetEthSpec;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn empty_data_column_sidecars_fails_validation() {
|
async fn test_validate_data_column_sidecar_for_gossip() {
|
||||||
|
// Setting up harness is slow, we initialise once and use it for all gossip validation tests.
|
||||||
let spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
|
let spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
|
||||||
let harness = BeaconChainHarness::builder(E::default())
|
let harness = BeaconChainHarness::builder(E::default())
|
||||||
.spec(spec.into())
|
.spec(spec.into())
|
||||||
@@ -801,6 +825,44 @@ mod test {
|
|||||||
.build();
|
.build();
|
||||||
harness.advance_slot();
|
harness.advance_slot();
|
||||||
|
|
||||||
|
let verify_fn = |column_sidecar: DataColumnSidecar<E>| {
|
||||||
|
let col_index = column_sidecar.index;
|
||||||
|
validate_data_column_sidecar_for_gossip::<_, Observe>(
|
||||||
|
column_sidecar.into(),
|
||||||
|
DataColumnSubnetId::from_column_index(col_index, &harness.spec),
|
||||||
|
&harness.chain,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
empty_data_column_sidecars_fails_validation(&harness, &verify_fn).await;
|
||||||
|
data_column_sidecar_commitments_exceed_max_blobs_per_block(&harness, &verify_fn).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_new_for_block_publishing() {
|
||||||
|
// Setting up harness is slow, we initialise once and use it for all gossip validation tests.
|
||||||
|
let spec = ForkName::Fulu.make_genesis_spec(E::default_spec());
|
||||||
|
let harness = BeaconChainHarness::builder(E::default())
|
||||||
|
.spec(spec.into())
|
||||||
|
.deterministic_keypairs(64)
|
||||||
|
.fresh_ephemeral_store()
|
||||||
|
.mock_execution_layer()
|
||||||
|
.build();
|
||||||
|
harness.advance_slot();
|
||||||
|
|
||||||
|
let verify_fn = |column_sidecar: DataColumnSidecar<E>| {
|
||||||
|
GossipVerifiedDataColumn::<_>::new_for_block_publishing(
|
||||||
|
column_sidecar.into(),
|
||||||
|
&harness.chain,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
empty_data_column_sidecars_fails_validation(&harness, &verify_fn).await;
|
||||||
|
data_column_sidecar_commitments_exceed_max_blobs_per_block(&harness, &verify_fn).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn empty_data_column_sidecars_fails_validation<D>(
|
||||||
|
harness: &BeaconChainHarness<EphemeralHarnessType<E>>,
|
||||||
|
verify_fn: &impl Fn(DataColumnSidecar<E>) -> Result<D, GossipDataColumnError>,
|
||||||
|
) {
|
||||||
let slot = harness.get_current_slot();
|
let slot = harness.get_current_slot();
|
||||||
let state = harness.get_current_state();
|
let state = harness.get_current_state();
|
||||||
let ((block, _blobs_opt), _state) = harness
|
let ((block, _blobs_opt), _state) = harness
|
||||||
@@ -823,14 +885,47 @@ mod test {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = validate_data_column_sidecar_for_gossip::<_, Observe>(
|
let result = verify_fn(column_sidecar);
|
||||||
column_sidecar.into(),
|
|
||||||
DataColumnSubnetId::from_column_index(index, &harness.spec),
|
|
||||||
&harness.chain,
|
|
||||||
);
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
result.err(),
|
result.err(),
|
||||||
Some(GossipDataColumnError::UnexpectedDataColumn)
|
Some(GossipDataColumnError::UnexpectedDataColumn)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn data_column_sidecar_commitments_exceed_max_blobs_per_block<D>(
|
||||||
|
harness: &BeaconChainHarness<EphemeralHarnessType<E>>,
|
||||||
|
verify_fn: &impl Fn(DataColumnSidecar<E>) -> Result<D, GossipDataColumnError>,
|
||||||
|
) {
|
||||||
|
let slot = harness.get_current_slot();
|
||||||
|
let epoch = slot.epoch(E::slots_per_epoch());
|
||||||
|
let state = harness.get_current_state();
|
||||||
|
let max_blobs_per_block = harness.spec.max_blobs_per_block(epoch) as usize;
|
||||||
|
let fork = harness.spec.fork_name_at_epoch(epoch);
|
||||||
|
|
||||||
|
// Generate data column sidecar with blob count exceeding max_blobs_per_block.
|
||||||
|
let blob_count = max_blobs_per_block + 1;
|
||||||
|
let BlobsBundle::<E> {
|
||||||
|
commitments: preloaded_commitments_single,
|
||||||
|
proofs: _,
|
||||||
|
blobs: _,
|
||||||
|
} = generate_blobs(1, fork).unwrap().0;
|
||||||
|
|
||||||
|
let ((block, _blobs_opt), _state) = harness
|
||||||
|
.make_block_with_modifier(state, slot, |block| {
|
||||||
|
*block.body_mut().blob_kzg_commitments_mut().unwrap() =
|
||||||
|
vec![preloaded_commitments_single[0]; blob_count].into();
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let column_sidecar = generate_data_column_sidecars_from_block(&block, &harness.spec)
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let result = verify_fn(Arc::try_unwrap(column_sidecar).unwrap());
|
||||||
|
assert!(matches!(
|
||||||
|
result.err(),
|
||||||
|
Some(GossipDataColumnError::MaxBlobsPerBlockExceeded { .. })
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3380,7 +3380,7 @@ pub fn generate_rand_block_and_data_columns<E: EthSpec>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate data column sidecars from pre-computed cells and proofs.
|
/// Generate data column sidecars from pre-computed cells and proofs.
|
||||||
fn generate_data_column_sidecars_from_block<E: EthSpec>(
|
pub fn generate_data_column_sidecars_from_block<E: EthSpec>(
|
||||||
block: &SignedBeaconBlock<E>,
|
block: &SignedBeaconBlock<E>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> DataColumnSidecarList<E> {
|
) -> DataColumnSidecarList<E> {
|
||||||
|
|||||||
@@ -708,6 +708,7 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
|
|||||||
| GossipDataColumnError::InvalidKzgProof { .. }
|
| GossipDataColumnError::InvalidKzgProof { .. }
|
||||||
| GossipDataColumnError::UnexpectedDataColumn
|
| GossipDataColumnError::UnexpectedDataColumn
|
||||||
| GossipDataColumnError::InvalidColumnIndex(_)
|
| GossipDataColumnError::InvalidColumnIndex(_)
|
||||||
|
| GossipDataColumnError::MaxBlobsPerBlockExceeded { .. }
|
||||||
| GossipDataColumnError::InconsistentCommitmentsLength { .. }
|
| GossipDataColumnError::InconsistentCommitmentsLength { .. }
|
||||||
| GossipDataColumnError::InconsistentProofsLength { .. }
|
| GossipDataColumnError::InconsistentProofsLength { .. }
|
||||||
| GossipDataColumnError::NotFinalizedDescendant { .. } => {
|
| GossipDataColumnError::NotFinalizedDescendant { .. } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user