gossip validate blobs prior to publish

This commit is contained in:
realbigsean
2023-07-12 12:32:14 -04:00
parent 1599487933
commit c016f5d787
11 changed files with 135 additions and 82 deletions

View File

@@ -1981,7 +1981,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self: &Arc<Self>,
blob_sidecar: SignedBlobSidecar<T::EthSpec>,
subnet_id: u64,
) -> Result<GossipVerifiedBlob<T::EthSpec>, BlobError<T::EthSpec>> {
) -> Result<GossipVerifiedBlob<T>, BlobError<T::EthSpec>> {
blob_verification::validate_blob_sidecar_for_gossip(blob_sidecar, subnet_id, self)
}
@@ -2711,7 +2711,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// Returns an `Err` if the given block was invalid, or an error was encountered during
pub async fn verify_block_for_gossip(
self: &Arc<Self>,
block: BlockWrapper<T::EthSpec>,
block: Arc<SignedBeaconBlock<T::EthSpec>>,
) -> Result<GossipVerifiedBlock<T>, BlockError<T::EthSpec>> {
let chain = self.clone();
self.task_executor
@@ -2755,7 +2755,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
pub async fn process_blob(
self: &Arc<Self>,
blob: GossipVerifiedBlob<T::EthSpec>,
blob: GossipVerifiedBlob<T>,
) -> Result<AvailabilityProcessingStatus, BlockError<T::EthSpec>> {
self.check_availability_and_maybe_import(blob.slot(), |chain| {
chain.data_availability_checker.put_gossip_blob(blob)

View File

@@ -16,7 +16,7 @@ use eth2::types::BlockContentsTuple;
use kzg::Kzg;
use slog::{debug, warn};
use ssz_derive::{Decode, Encode};
use ssz_types::FixedVector;
use ssz_types::{FixedVector, VariableList};
use std::borrow::Cow;
use types::blob_sidecar::{BlobIdentifier, FixedBlobSidecarList};
use types::{
@@ -125,25 +125,40 @@ impl<T: EthSpec> From<BeaconStateError> for BlobError<T> {
}
}
pub type GossipVerifiedBlobList<T> = VariableList<
GossipVerifiedBlob<T>,
<<T as BeaconChainTypes>::EthSpec as EthSpec>::MaxBlobsPerBlock,
>;
/// A wrapper around a `BlobSidecar` that indicates it has been approved for re-gossiping on
/// the p2p network.
#[derive(Debug, Clone)]
pub struct GossipVerifiedBlob<T: EthSpec> {
blob: Arc<BlobSidecar<T>>,
#[derive(Debug)]
pub struct GossipVerifiedBlob<T: BeaconChainTypes> {
blob: SignedBlobSidecar<T::EthSpec>,
}
impl<T: EthSpec> GossipVerifiedBlob<T> {
impl<T: BeaconChainTypes> GossipVerifiedBlob<T> {
pub fn new(
blob: SignedBlobSidecar<T::EthSpec>,
chain: &BeaconChain<T>,
) -> Result<Self, BlobError<T::EthSpec>> {
let blob_index = blob.message.index;
validate_blob_sidecar_for_gossip(blob, blob_index, chain)
}
pub fn id(&self) -> BlobIdentifier {
self.blob.id()
self.blob.message.id()
}
pub fn block_root(&self) -> Hash256 {
self.blob.block_root
self.blob.message.block_root
}
pub fn to_blob(self) -> Arc<BlobSidecar<T>> {
self.blob
pub fn to_blob(self) -> Arc<BlobSidecar<T::EthSpec>> {
self.blob.message
}
pub fn signed_blob(&self) -> SignedBlobSidecar<T::EthSpec> {
self.blob.clone()
}
pub fn slot(&self) -> Slot {
self.blob.slot
self.blob.message.slot
}
}
@@ -151,7 +166,7 @@ pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
signed_blob_sidecar: SignedBlobSidecar<T::EthSpec>,
subnet: u64,
chain: &BeaconChain<T>,
) -> Result<GossipVerifiedBlob<T::EthSpec>, BlobError<T::EthSpec>> {
) -> Result<GossipVerifiedBlob<T>, BlobError<T::EthSpec>> {
let blob_slot = signed_blob_sidecar.message.slot;
let blob_index = signed_blob_sidecar.message.index;
let block_parent_root = signed_blob_sidecar.message.block_parent_root;
@@ -366,7 +381,7 @@ pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
// Note: If this BlobSidecar goes on to fail full verification, we do not evict it from the seen_cache
// as alternate blob_sidecars for the same identifier can still be retrieved
// over rpc. Evicting them from this cache would allow faster propagation over gossip. So we allow
// retreieval of potentially valid blocks over rpc, but try to punish the proposer for signing
// retrieval of potentially valid blocks over rpc, but try to punish the proposer for signing
// invalid messages. Issue for more background
// https://github.com/ethereum/consensus-specs/issues/3261
if chain
@@ -383,7 +398,7 @@ pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
}
Ok(GossipVerifiedBlob {
blob: signed_blob_sidecar.message,
blob: signed_blob_sidecar,
})
}

View File

@@ -48,7 +48,10 @@
// returned alongside.
#![allow(clippy::result_large_err)]
use crate::blob_verification::{AsBlock, BlobError, BlockWrapper, MaybeAvailableBlock};
use crate::blob_verification::{
AsBlock, BlobError, BlockWrapper, GossipVerifiedBlob, GossipVerifiedBlobList,
MaybeAvailableBlock,
};
use crate::data_availability_checker::{
AvailabilityCheckError, AvailabilityPendingBlock, AvailableBlock,
};
@@ -79,6 +82,7 @@ use slog::{debug, error, warn, Logger};
use slot_clock::SlotClock;
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use state_processing::per_block_processing::{errors::IntoWithIndex, is_merge_transition_block};
use state_processing::{
block_signature_verifier::{BlockSignatureVerifier, Error as BlockSignatureVerifierError},
@@ -803,48 +807,65 @@ pub struct BlockImportData<E: EthSpec> {
pub consensus_context: ConsensusContext<E>,
}
pub trait IntoGossipVerifiedBlock<T: BeaconChainTypes>: Sized {
pub type GossipVerifiedBlockContents<T> =
(GossipVerifiedBlock<T>, Option<GossipVerifiedBlobList<T>>);
pub trait IntoGossipVerifiedBlockContents<T: BeaconChainTypes>: Sized {
fn into_gossip_verified_block(
self,
chain: &BeaconChain<T>,
) -> Result<GossipVerifiedBlock<T>, BlockError<T::EthSpec>>;
fn inner(&self) -> &SignedBeaconBlock<T::EthSpec>;
fn blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>>;
) -> Result<GossipVerifiedBlockContents<T>, BlockError<T::EthSpec>>;
fn inner_block(&self) -> &SignedBeaconBlock<T::EthSpec>;
fn inner_blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>>;
}
impl<T: BeaconChainTypes> IntoGossipVerifiedBlock<T>
for (
GossipVerifiedBlock<T>,
Option<SignedBlobSidecarList<T::EthSpec>>,
)
{
impl<T: BeaconChainTypes> IntoGossipVerifiedBlockContents<T> for GossipVerifiedBlockContents<T> {
fn into_gossip_verified_block(
self,
_chain: &BeaconChain<T>,
) -> Result<GossipVerifiedBlock<T>, BlockError<T::EthSpec>> {
Ok(self.0)
) -> Result<GossipVerifiedBlockContents<T>, BlockError<T::EthSpec>> {
Ok(self)
}
fn inner(&self) -> &SignedBeaconBlock<T::EthSpec> {
fn inner_block(&self) -> &SignedBeaconBlock<T::EthSpec> {
self.0.block.as_block()
}
fn blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>> {
self.1.clone()
fn inner_blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>> {
self.1.as_ref().map(|blobs| {
VariableList::from(
blobs
.into_iter()
.map(GossipVerifiedBlob::signed_blob)
.collect::<Vec<_>>(),
)
})
}
}
impl<T: BeaconChainTypes> IntoGossipVerifiedBlock<T> for SignedBlockContents<T::EthSpec> {
impl<T: BeaconChainTypes> IntoGossipVerifiedBlockContents<T> for SignedBlockContents<T::EthSpec> {
fn into_gossip_verified_block(
self,
chain: &BeaconChain<T>,
) -> Result<GossipVerifiedBlock<T>, BlockError<T::EthSpec>> {
GossipVerifiedBlock::new(self.deconstruct().into(), chain)
) -> Result<GossipVerifiedBlockContents<T>, BlockError<T::EthSpec>> {
let (block, blobs) = self.deconstruct();
let gossip_verified_block = GossipVerifiedBlock::new(Arc::new(block), chain)?;
let gossip_verified_blobs = blobs
.map(|blobs| {
Ok::<_, BlobError<T::EthSpec>>(VariableList::from(
blobs
.into_iter()
.map(|blob| GossipVerifiedBlob::new(blob, chain))
.collect::<Result<Vec<_>, BlobError<T::EthSpec>>>()?,
))
})
.transpose()?;
Ok((gossip_verified_block, gossip_verified_blobs))
}
fn inner(&self) -> &SignedBeaconBlock<T::EthSpec> {
fn inner_block(&self) -> &SignedBeaconBlock<T::EthSpec> {
self.signed_block()
}
fn blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>> {
fn inner_blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>> {
self.blobs_cloned()
}
}
@@ -887,10 +908,12 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
///
/// Returns an error if the block is invalid, or i8f the block was unable to be verified.
pub fn new(
block: BlockWrapper<T::EthSpec>,
block: Arc<SignedBeaconBlock<T::EthSpec>>,
chain: &BeaconChain<T>,
) -> Result<Self, BlockError<T::EthSpec>> {
let maybe_available = chain.data_availability_checker.check_availability(block)?;
let maybe_available = chain
.data_availability_checker
.check_availability(block.into())?;
// If the block is valid for gossip we don't supply it to the slasher here because
// we assume it will be transformed into a fully verified block. We *do* need to supply
// it to the slasher if an error occurs, because that's the end of this block's journey,

View File

@@ -205,7 +205,7 @@ impl<T: BeaconChainTypes> DataAvailabilityChecker<T> {
/// This should only accept gossip verified blobs, so we should not have to worry about dupes.
pub fn put_gossip_blob(
&self,
gossip_blob: GossipVerifiedBlob<T::EthSpec>,
gossip_blob: GossipVerifiedBlob<T>,
) -> Result<Availability<T::EthSpec>, AvailabilityCheckError> {
// Verify the KZG commitments.
let kzg_verified_blob = if let Some(kzg) = self.kzg.as_ref() {

View File

@@ -1075,7 +1075,7 @@ mod test {
log: Logger,
) -> (
AvailabilityPendingExecutedBlock<E>,
Vec<GossipVerifiedBlob<E>>,
Vec<GossipVerifiedBlob<BaseHarnessType<E, Hot, Cold>>>,
)
where
E: EthSpec,

View File

@@ -71,7 +71,7 @@ pub use beacon_fork_choice_store::{BeaconForkChoiceStore, Error as ForkChoiceSto
pub use block_verification::{
get_block_root, AvailabilityPendingExecutedBlock, BlockError, ExecutedBlock,
ExecutionPayloadError, ExecutionPendingBlock, GossipVerifiedBlock, IntoExecutionPendingBlock,
IntoGossipVerifiedBlock, PayloadVerificationOutcome, PayloadVerificationStatus,
IntoGossipVerifiedBlockContents, PayloadVerificationOutcome, PayloadVerificationStatus,
};
pub use canonical_head::{CachedHead, CanonicalHead, CanonicalHeadRwLock};
pub use eth1_chain::{Eth1Chain, Eth1ChainBackend};