Skip to content

Commit b48879a

Browse files
authored
Remove KZG verification from local block production and blobs fetched from the EL (#7713)
#7700 As described in title, the EL already performs KZG verification on all blobs when they entered the mempool, so it's redundant to perform extra validation on blobs returned from the EL. This PR removes - KZG verification for both blobs and data columns during block production - KZG verification for data columns after fetch engine blobs call. I have not done this for blobs because it requires extra changes to check the observed cache, and doesn't feel like it's a worthy optimisation given the number of blobs per block. This PR does not remove KZG verification on the block publishing path yet.
1 parent 4a3e248 commit b48879a

File tree

6 files changed

+32
-80
lines changed

6 files changed

+32
-80
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ use crate::validator_monitor::{
7070
};
7171
use crate::validator_pubkey_cache::ValidatorPubkeyCache;
7272
use crate::{
73-
kzg_utils, metrics, AvailabilityPendingExecutedBlock, BeaconChainError, BeaconForkChoiceStore,
73+
metrics, AvailabilityPendingExecutedBlock, BeaconChainError, BeaconForkChoiceStore,
7474
BeaconSnapshot, CachedHead,
7575
};
7676
use eth2::types::{
@@ -5748,8 +5748,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
57485748
let (mut block, _) = block.deconstruct();
57495749
*block.state_root_mut() = state_root;
57505750

5751-
let blobs_verification_timer =
5752-
metrics::start_timer(&metrics::BLOCK_PRODUCTION_BLOBS_VERIFICATION_TIMES);
57535751
let blob_items = match maybe_blobs_and_proofs {
57545752
Some((blobs, proofs)) => {
57555753
let expected_kzg_commitments =
@@ -5768,37 +5766,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
57685766
)));
57695767
}
57705768

5771-
let kzg_proofs = Vec::from(proofs);
5772-
5773-
let kzg = self.kzg.as_ref();
5774-
if self
5775-
.spec
5776-
.is_peer_das_enabled_for_epoch(slot.epoch(T::EthSpec::slots_per_epoch()))
5777-
{
5778-
kzg_utils::validate_blobs_and_cell_proofs::<T::EthSpec>(
5779-
kzg,
5780-
blobs.iter().collect(),
5781-
&kzg_proofs,
5782-
expected_kzg_commitments,
5783-
)
5784-
.map_err(BlockProductionError::KzgError)?;
5785-
} else {
5786-
kzg_utils::validate_blobs::<T::EthSpec>(
5787-
kzg,
5788-
expected_kzg_commitments,
5789-
blobs.iter().collect(),
5790-
&kzg_proofs,
5791-
)
5792-
.map_err(BlockProductionError::KzgError)?;
5793-
}
5794-
5795-
Some((kzg_proofs.into(), blobs))
5769+
Some((proofs, blobs))
57965770
}
57975771
None => None,
57985772
};
57995773

5800-
drop(blobs_verification_timer);
5801-
58025774
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_SUCCESSES);
58035775

58045776
trace!(

beacon_node/beacon_chain/src/data_column_verification.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,12 @@ impl<E: EthSpec> KzgVerifiedDataColumn<E> {
266266
verify_kzg_for_data_column(data_column, kzg)
267267
}
268268

269+
/// Mark a data column as KZG verified. Caller must ONLY use this on columns constructed
270+
/// from EL blobs.
271+
pub fn from_execution_verified(data_column: Arc<DataColumnSidecar<E>>) -> Self {
272+
Self { data: data_column }
273+
}
274+
269275
/// Create a `KzgVerifiedDataColumn` from `DataColumnSidecar` for testing ONLY.
270276
pub(crate) fn __new_for_testing(data_column: Arc<DataColumnSidecar<E>>) -> Self {
271277
Self { data: data_column }

beacon_node/beacon_chain/src/fetch_blobs/fetch_blobs_beacon_adapter.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
use crate::blob_verification::{GossipBlobError, GossipVerifiedBlob};
2-
use crate::data_column_verification::KzgVerifiedDataColumn;
32
use crate::fetch_blobs::{EngineGetBlobsOutput, FetchEngineBlobError};
43
use crate::observed_block_producers::ProposalKey;
54
use crate::observed_data_sidecars::DoNotObserve;
65
use crate::{AvailabilityProcessingStatus, BeaconChain, BeaconChainTypes};
76
use execution_layer::json_structures::{BlobAndProofV1, BlobAndProofV2};
8-
use kzg::{Error as KzgError, Kzg};
7+
use kzg::Kzg;
98
#[cfg(test)]
109
use mockall::automock;
1110
use std::collections::HashSet;
1211
use std::sync::Arc;
1312
use task_executor::TaskExecutor;
14-
use types::{BlobSidecar, ChainSpec, ColumnIndex, DataColumnSidecar, Hash256, Slot};
13+
use types::{BlobSidecar, ChainSpec, ColumnIndex, Hash256, Slot};
1514

1615
/// An adapter to the `BeaconChain` functionalities to remove `BeaconChain` from direct dependency to enable testing fetch blobs logic.
1716
pub(crate) struct FetchBlobsBeaconAdapter<T: BeaconChainTypes> {
@@ -77,14 +76,7 @@ impl<T: BeaconChainTypes> FetchBlobsBeaconAdapter<T> {
7776
GossipVerifiedBlob::<T, DoNotObserve>::new(blob.clone(), blob.index, &self.chain)
7877
}
7978

80-
pub(crate) fn verify_data_columns_kzg(
81-
&self,
82-
data_columns: Vec<Arc<DataColumnSidecar<T::EthSpec>>>,
83-
) -> Result<Vec<KzgVerifiedDataColumn<T::EthSpec>>, KzgError> {
84-
KzgVerifiedDataColumn::from_batch(data_columns, &self.chain.kzg)
85-
}
86-
87-
pub(crate) fn known_for_proposal(
79+
pub(crate) fn data_column_known_for_proposal(
8880
&self,
8981
proposal_key: ProposalKey,
9082
) -> Option<HashSet<ColumnIndex>> {

beacon_node/beacon_chain/src/fetch_blobs/mod.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod tests;
1414

1515
use crate::blob_verification::{GossipBlobError, GossipVerifiedBlob};
1616
use crate::block_verification_types::AsBlock;
17-
use crate::data_column_verification::KzgVerifiedCustodyDataColumn;
17+
use crate::data_column_verification::{KzgVerifiedCustodyDataColumn, KzgVerifiedDataColumn};
1818
#[cfg_attr(test, double)]
1919
use crate::fetch_blobs::fetch_blobs_beacon_adapter::FetchBlobsBeaconAdapter;
2020
use crate::kzg_utils::blobs_to_data_column_sidecars;
@@ -311,6 +311,9 @@ async fn fetch_and_process_blobs_v2<T: BeaconChainTypes>(
311311
return Ok(None);
312312
}
313313

314+
// Up until this point we have not observed the data columns in the gossip cache, which allows
315+
// them to arrive independently while this function is running. In publish_fn we will observe
316+
// them and then publish any columns that had not already been observed.
314317
publish_fn(EngineGetBlobsOutput::CustodyColumns(
315318
custody_columns_to_import.clone(),
316319
));
@@ -358,17 +361,24 @@ async fn compute_custody_columns_to_import<T: BeaconChainTypes>(
358361
// `DataAvailabilityChecker` requires a strict match on custody columns count to
359362
// consider a block available.
360363
let mut custody_columns = data_columns_result
361-
.map(|mut data_columns| {
362-
data_columns.retain(|col| custody_columns_indices.contains(&col.index));
364+
.map(|data_columns| {
363365
data_columns
366+
.into_iter()
367+
.filter(|col| custody_columns_indices.contains(&col.index))
368+
.map(|col| {
369+
KzgVerifiedCustodyDataColumn::from_asserted_custody(
370+
KzgVerifiedDataColumn::from_execution_verified(col),
371+
)
372+
})
373+
.collect::<Vec<_>>()
364374
})
365375
.map_err(FetchEngineBlobError::DataColumnSidecarError)?;
366376

367377
// Only consider columns that are not already observed on gossip.
368-
if let Some(observed_columns) = chain_adapter_cloned.known_for_proposal(
378+
if let Some(observed_columns) = chain_adapter_cloned.data_column_known_for_proposal(
369379
ProposalKey::new(block.message().proposer_index(), block.slot()),
370380
) {
371-
custody_columns.retain(|col| !observed_columns.contains(&col.index));
381+
custody_columns.retain(|col| !observed_columns.contains(&col.index()));
372382
if custody_columns.is_empty() {
373383
return Ok(vec![]);
374384
}
@@ -378,26 +388,13 @@ async fn compute_custody_columns_to_import<T: BeaconChainTypes>(
378388
if let Some(known_columns) =
379389
chain_adapter_cloned.cached_data_column_indexes(&block_root)
380390
{
381-
custody_columns.retain(|col| !known_columns.contains(&col.index));
391+
custody_columns.retain(|col| !known_columns.contains(&col.index()));
382392
if custody_columns.is_empty() {
383393
return Ok(vec![]);
384394
}
385395
}
386396

387-
// KZG verify data columns before publishing. This prevents blobs with invalid
388-
// KZG proofs from the EL making it into the data availability checker. We do not
389-
// immediately add these blobs to the observed blobs/columns cache because we want
390-
// to allow blobs/columns to arrive on gossip and be accepted (and propagated) while
391-
// we are waiting to publish. Just before publishing we will observe the blobs/columns
392-
// and only proceed with publishing if they are not yet seen.
393-
let verified = chain_adapter_cloned
394-
.verify_data_columns_kzg(custody_columns)
395-
.map_err(FetchEngineBlobError::KzgError)?;
396-
397-
Ok(verified
398-
.into_iter()
399-
.map(KzgVerifiedCustodyDataColumn::from_asserted_custody)
400-
.collect())
397+
Ok(custody_columns)
401398
},
402399
"compute_custody_columns_to_import",
403400
)

beacon_node/beacon_chain/src/fetch_blobs/tests.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::data_column_verification::KzgVerifiedDataColumn;
21
use crate::fetch_blobs::fetch_blobs_beacon_adapter::MockFetchBlobsBeaconAdapter;
32
use crate::fetch_blobs::{
43
fetch_and_process_engine_blobs_inner, EngineGetBlobsOutput, FetchEngineBlobError,
@@ -156,7 +155,7 @@ mod get_blobs_v2 {
156155
mock_fork_choice_contains_block(&mut mock_adapter, vec![]);
157156
// All data columns already seen on gossip
158157
mock_adapter
159-
.expect_known_for_proposal()
158+
.expect_data_column_known_for_proposal()
160159
.returning(|_| Some(hashset![0, 1, 2]));
161160
// No blobs should be processed
162161
mock_adapter.expect_process_engine_blobs().times(0);
@@ -192,17 +191,12 @@ mod get_blobs_v2 {
192191
// All blobs returned, fork choice doesn't contain block
193192
mock_get_blobs_v2_response(&mut mock_adapter, Some(blobs_and_proofs));
194193
mock_fork_choice_contains_block(&mut mock_adapter, vec![]);
195-
mock_adapter.expect_known_for_proposal().returning(|_| None);
196194
mock_adapter
197-
.expect_cached_data_column_indexes()
195+
.expect_data_column_known_for_proposal()
198196
.returning(|_| None);
199197
mock_adapter
200-
.expect_verify_data_columns_kzg()
201-
.returning(|c| {
202-
Ok(c.into_iter()
203-
.map(KzgVerifiedDataColumn::__new_for_testing)
204-
.collect())
205-
});
198+
.expect_cached_data_column_indexes()
199+
.returning(|_| None);
206200
mock_process_engine_blobs_result(
207201
&mut mock_adapter,
208202
Ok(AvailabilityProcessingStatus::Imported(block_root)),

beacon_node/beacon_chain/src/metrics.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,15 +1828,6 @@ pub static KZG_VERIFICATION_DATA_COLUMN_BATCH_TIMES: LazyLock<Result<Histogram>>
18281828
)
18291829
});
18301830

1831-
pub static BLOCK_PRODUCTION_BLOBS_VERIFICATION_TIMES: LazyLock<Result<Histogram>> = LazyLock::new(
1832-
|| {
1833-
try_create_histogram(
1834-
"beacon_block_production_blobs_verification_seconds",
1835-
"Time taken to verify blobs against commitments and creating BlobSidecar objects in block production"
1836-
)
1837-
},
1838-
);
1839-
18401831
/*
18411832
* Data Availability cache metrics
18421833
*/

0 commit comments

Comments
 (0)