Add a wrapper to allow construction of only valid AvailableBlocks

This commit is contained in:
Pawan Dhananjay
2023-03-21 18:11:51 +05:30
parent 0958ce610f
commit ecfa9e7555
5 changed files with 177 additions and 95 deletions

View File

@@ -2714,7 +2714,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|chain| { |chain| {
chain chain
.data_availability_checker .data_availability_checker
.check_block_availability(executed_block) .check_block_availability(executed_block, |epoch| {
chain.block_needs_da_check(epoch)
})
}, },
count_unrealized, count_unrealized,
) )
@@ -2846,7 +2848,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} }
}; };
let slot = available_block.block.slot(); let slot = available_block.slot();
// import // import
let chain = self.clone(); let chain = self.clone();
@@ -2925,7 +2927,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// ----------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------
let current_slot = self.slot()?; let current_slot = self.slot()?;
let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch()); let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch());
let block = signed_block.block.message(); let block = signed_block.message();
let post_exec_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_POST_EXEC_PROCESSING); let post_exec_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_POST_EXEC_PROCESSING);
// Check against weak subjectivity checkpoint. // Check against weak subjectivity checkpoint.
@@ -2969,7 +2971,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
)?; )?;
// TODO(pawan): fix this atrocity // TODO(pawan): fix this atrocity
let signed_block = signed_block.into_available_block().unwrap(); let signed_block = signed_block.into_available_block().unwrap();
let block = signed_block.block.message(); let block = signed_block.message();
// Register the new block with the fork choice service. // Register the new block with the fork choice service.
{ {
@@ -6169,6 +6171,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}) })
} }
/// Returns true if the given epoch lies within the da boundary and false otherwise.
pub fn block_needs_da_check(&self, block_epoch: Epoch) -> bool {
self.data_availability_boundary()
.map_or(false, |da_epoch| block_epoch >= da_epoch)
}
/// The epoch that is a data availability boundary, or the latest finalized epoch. /// The epoch that is a data availability boundary, or the latest finalized epoch.
/// `None` if the `Eip4844` fork is disabled. /// `None` if the `Eip4844` fork is disabled.
pub fn finalized_data_availability_boundary(&self) -> Option<Epoch> { pub fn finalized_data_availability_boundary(&self) -> Option<Epoch> {

View File

@@ -322,15 +322,78 @@ impl<T: BeaconChainTypes> IntoAvailableBlock<T> for BlockWrapper<T::EthSpec> {
} }
#[derive(Clone, Debug, PartialEq, Derivative)] #[derive(Clone, Debug, PartialEq, Derivative)]
#[derivative(Hash(bound = "T: EthSpec"))] #[derivative(Hash(bound = "E: EthSpec"))]
pub struct AvailableBlock<T: EthSpec> { pub struct AvailableBlock<E: EthSpec>(AvailableBlockInner<E>);
pub block: Arc<SignedBeaconBlock<T>>,
pub blobs: VerifiedBlobs<T>, #[derive(Clone, Debug, PartialEq, Derivative)]
#[derivative(Hash(bound = "E: EthSpec"))]
struct AvailableBlockInner<E: EthSpec> {
block: Arc<SignedBeaconBlock<E>>,
blobs: VerifiedBlobs<E>,
} }
impl<T: EthSpec> AvailableBlock<T> { impl<E: EthSpec> AvailableBlock<E> {
pub fn blobs(&self) -> Option<BlobSidecarArcList<T>> { pub fn new(
match &self.blobs { block: Arc<SignedBeaconBlock<E>>,
blobs: Vec<Arc<BlobSidecar<E>>>,
da_check: impl FnOnce(Epoch) -> bool,
) -> Result<Self, String> {
if let Ok(block_kzg_commitments) = block.message().body().blob_kzg_commitments() {
if blobs.is_empty() && block_kzg_commitments.is_empty() {
return Ok(Self(AvailableBlockInner {
block,
blobs: VerifiedBlobs::EmptyBlobs,
}));
}
if blobs.is_empty() {
if da_check(block.epoch()) {
return Ok(Self(AvailableBlockInner {
block,
blobs: VerifiedBlobs::NotRequired,
}));
} else {
return Err("Block within DA boundary but no blobs provided".to_string());
}
}
if blobs.len() != block_kzg_commitments.len() {
return Err(format!(
"Block commitments and blobs length must be the same.
Block commitments len: {}, blobs length: {}",
block_kzg_commitments.len(),
blobs.len()
));
}
for (block_commitment, blob) in block_kzg_commitments.iter().zip(blobs.iter()) {
if *block_commitment != blob.kzg_commitment {
return Err(format!(
"Invalid input. Blob and block commitment mismatch at index {}",
blob.index
));
}
}
Ok(Self(AvailableBlockInner {
block,
blobs: VerifiedBlobs::Available(blobs.into()),
}))
}
// This is a pre 4844 block
else {
Ok(Self(AvailableBlockInner {
block,
blobs: VerifiedBlobs::PreEip4844,
}))
}
}
pub fn block(&self) -> &SignedBeaconBlock<E> {
&self.0.block
}
pub fn blobs(&self) -> Option<BlobSidecarArcList<E>> {
match &self.0.blobs {
VerifiedBlobs::EmptyBlobs | VerifiedBlobs::NotRequired | VerifiedBlobs::PreEip4844 => { VerifiedBlobs::EmptyBlobs | VerifiedBlobs::NotRequired | VerifiedBlobs::PreEip4844 => {
None None
} }
@@ -338,12 +401,12 @@ impl<T: EthSpec> AvailableBlock<T> {
} }
} }
pub fn deconstruct(self) -> (Arc<SignedBeaconBlock<T>>, Option<BlobSidecarArcList<T>>) { pub fn deconstruct(self) -> (Arc<SignedBeaconBlock<E>>, Option<BlobSidecarArcList<E>>) {
match self.blobs { match self.0.blobs {
VerifiedBlobs::EmptyBlobs | VerifiedBlobs::NotRequired | VerifiedBlobs::PreEip4844 => { VerifiedBlobs::EmptyBlobs | VerifiedBlobs::NotRequired | VerifiedBlobs::PreEip4844 => {
(self.block, None) (self.0.block, None)
} }
VerifiedBlobs::Available(blobs) => (self.block, Some(blobs)), VerifiedBlobs::Available(blobs) => (self.0.block, Some(blobs)),
} }
} }
} }
@@ -394,117 +457,113 @@ impl<E: EthSpec> BlockWrapper<E> {
} }
} }
impl<E: EthSpec> AsBlock<E> for AvailableBlock<E> {
fn slot(&self) -> Slot {
self.0.block.slot()
}
fn epoch(&self) -> Epoch {
self.0.block.epoch()
}
fn parent_root(&self) -> Hash256 {
self.0.block.parent_root()
}
fn state_root(&self) -> Hash256 {
self.0.block.state_root()
}
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
self.0.block.signed_block_header()
}
fn message(&self) -> BeaconBlockRef<E> {
self.0.block.message()
}
fn as_block(&self) -> &SignedBeaconBlock<E> {
&self.0.block
}
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
self.0.block.clone()
}
fn canonical_root(&self) -> Hash256 {
self.0.block.canonical_root()
}
}
impl<E: EthSpec> AsBlock<E> for BlockWrapper<E> { impl<E: EthSpec> AsBlock<E> for BlockWrapper<E> {
fn slot(&self) -> Slot { fn slot(&self) -> Slot {
match self { self.as_block().slot()
BlockWrapper::Available(block) => block.block.slot(),
BlockWrapper::AvailabilityPending(block) => block.slot(),
}
} }
fn epoch(&self) -> Epoch { fn epoch(&self) -> Epoch {
match self { self.as_block().epoch()
BlockWrapper::Available(block) => block.block.epoch(),
BlockWrapper::AvailabilityPending(block) => block.epoch(),
}
} }
fn parent_root(&self) -> Hash256 { fn parent_root(&self) -> Hash256 {
match self { self.as_block().parent_root()
BlockWrapper::Available(block) => block.block.parent_root(),
BlockWrapper::AvailabilityPending(block) => block.parent_root(),
}
} }
fn state_root(&self) -> Hash256 { fn state_root(&self) -> Hash256 {
match self { self.as_block().state_root()
BlockWrapper::Available(block) => block.block.state_root(),
BlockWrapper::AvailabilityPending(block) => block.state_root(),
}
} }
fn signed_block_header(&self) -> SignedBeaconBlockHeader { fn signed_block_header(&self) -> SignedBeaconBlockHeader {
match &self { self.as_block().signed_block_header()
BlockWrapper::Available(block) => block.block.signed_block_header(),
BlockWrapper::AvailabilityPending(block) => block.signed_block_header(),
}
} }
fn message(&self) -> BeaconBlockRef<E> { fn message(&self) -> BeaconBlockRef<E> {
match &self { self.as_block().message()
BlockWrapper::Available(block) => block.block.message(),
BlockWrapper::AvailabilityPending(block) => block.message(),
}
} }
fn as_block(&self) -> &SignedBeaconBlock<E> { fn as_block(&self) -> &SignedBeaconBlock<E> {
match &self { match &self {
BlockWrapper::Available(block) => &block.block, BlockWrapper::Available(block) => &block.0.block,
BlockWrapper::AvailabilityPending(block) => block, BlockWrapper::AvailabilityPending(block) => block,
} }
} }
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> { fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
match &self { match &self {
BlockWrapper::Available(block) => block.block.clone(), BlockWrapper::Available(block) => block.0.block.clone(),
BlockWrapper::AvailabilityPending(block) => block.clone(), BlockWrapper::AvailabilityPending(block) => block.clone(),
} }
} }
fn canonical_root(&self) -> Hash256 { fn canonical_root(&self) -> Hash256 {
match &self { self.as_block().canonical_root()
BlockWrapper::Available(block) => block.block.canonical_root(),
BlockWrapper::AvailabilityPending(block) => block.canonical_root(),
}
} }
} }
impl<E: EthSpec> AsBlock<E> for &BlockWrapper<E> { impl<E: EthSpec> AsBlock<E> for &BlockWrapper<E> {
fn slot(&self) -> Slot { fn slot(&self) -> Slot {
match self { self.as_block().slot()
BlockWrapper::Available(block) => block.block.slot(),
BlockWrapper::AvailabilityPending(block) => block.slot(),
}
} }
fn epoch(&self) -> Epoch { fn epoch(&self) -> Epoch {
match self { self.as_block().epoch()
BlockWrapper::Available(block) => block.block.epoch(),
BlockWrapper::AvailabilityPending(block) => block.epoch(),
}
} }
fn parent_root(&self) -> Hash256 { fn parent_root(&self) -> Hash256 {
match self { self.as_block().parent_root()
BlockWrapper::Available(block) => block.block.parent_root(),
BlockWrapper::AvailabilityPending(block) => block.parent_root(),
}
} }
fn state_root(&self) -> Hash256 { fn state_root(&self) -> Hash256 {
match self { self.as_block().state_root()
BlockWrapper::Available(block) => block.block.state_root(),
BlockWrapper::AvailabilityPending(block) => block.state_root(),
}
} }
fn signed_block_header(&self) -> SignedBeaconBlockHeader { fn signed_block_header(&self) -> SignedBeaconBlockHeader {
match &self { self.as_block().signed_block_header()
BlockWrapper::Available(block) => block.block.signed_block_header(),
BlockWrapper::AvailabilityPending(block) => block.signed_block_header(),
}
} }
fn message(&self) -> BeaconBlockRef<E> { fn message(&self) -> BeaconBlockRef<E> {
match &self { self.as_block().message()
BlockWrapper::Available(block) => block.block.message(),
BlockWrapper::AvailabilityPending(block) => block.message(),
}
} }
fn as_block(&self) -> &SignedBeaconBlock<E> { fn as_block(&self) -> &SignedBeaconBlock<E> {
match &self { match &self {
BlockWrapper::Available(block) => &block.block, BlockWrapper::Available(block) => &block.0.block,
BlockWrapper::AvailabilityPending(block) => block, BlockWrapper::AvailabilityPending(block) => block,
} }
} }
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> { fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
match &self { match &self {
BlockWrapper::Available(block) => block.block.clone(), BlockWrapper::Available(block) => block.0.block.clone(),
BlockWrapper::AvailabilityPending(block) => block.clone(), BlockWrapper::AvailabilityPending(block) => block.clone(),
} }
} }
fn canonical_root(&self) -> Hash256 { fn canonical_root(&self) -> Hash256 {
match &self { self.as_block().canonical_root()
BlockWrapper::Available(block) => block.block.canonical_root(),
BlockWrapper::AvailabilityPending(block) => block.canonical_root(),
}
} }
} }

View File

@@ -93,9 +93,9 @@ use task_executor::JoinHandle;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::ExecPayload; use types::ExecPayload;
use types::{ use types::{
BeaconBlockRef, BeaconState, BeaconStateError, BlindedPayload, ChainSpec, CloneConfig, Epoch, BeaconBlockRef, BeaconState, BeaconStateError, BlindedPayload, BlobSidecar, ChainSpec,
EthSpec, ExecutionBlockHash, Hash256, InconsistentFork, PublicKey, PublicKeyBytes, CloneConfig, Epoch, EthSpec, ExecutionBlockHash, Hash256, InconsistentFork, PublicKey,
RelativeEpoch, SignedBeaconBlock, SignedBeaconBlockHeader, Slot, PublicKeyBytes, RelativeEpoch, SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
}; };
pub const POS_PANDA_BANNER: &str = r#" pub const POS_PANDA_BANNER: &str = r#"
@@ -1171,16 +1171,30 @@ impl<T: BeaconChainTypes> IntoExecutionPendingBlock<T> for BlockWrapper<T::EthSp
match self { match self {
BlockWrapper::AvailabilityPending(block) => block BlockWrapper::AvailabilityPending(block) => block
.into_execution_pending_block_slashable(block_root, chain, notify_execution_layer), .into_execution_pending_block_slashable(block_root, chain, notify_execution_layer),
BlockWrapper::Available(AvailableBlock { block, blobs }) => { BlockWrapper::Available(available_block) => {
let (block, blobs) = available_block.deconstruct();
let mut execution_pending_block = block.into_execution_pending_block_slashable( let mut execution_pending_block = block.into_execution_pending_block_slashable(
block_root, block_root,
chain, chain,
notify_execution_layer, notify_execution_layer,
)?; )?;
let block = execution_pending_block.block.block_cloned(); let block = execution_pending_block.block.block_cloned();
let blobs: Vec<Arc<BlobSidecar<_>>> = match blobs {
Some(blob_list) => blob_list.into(),
None => vec![],
};
let available_execution_pending_block = let available_execution_pending_block =
BlockWrapper::Available(AvailableBlock { block, blobs }); AvailableBlock::new(block, blobs, |epoch| chain.block_needs_da_check(epoch))
execution_pending_block.block = available_execution_pending_block; .map_err(|e| {
BlockSlashInfo::SignatureValid(
execution_pending_block.block.signed_block_header(),
BlockError::AvailabilityCheck(
AvailabilityCheckError::InvalidBlockOrBlob(e),
),
)
})?;
execution_pending_block.block = available_execution_pending_block.into();
Ok(execution_pending_block) Ok(execution_pending_block)
} }
} }

View File

@@ -1,21 +1,22 @@
use crate::blob_verification::{AsBlock, AvailableBlock, BlockWrapper, VerifiedBlobs}; use crate::blob_verification::{AsBlock, AvailableBlock, BlockWrapper};
use crate::block_verification::ExecutedBlock; use crate::block_verification::ExecutedBlock;
use crate::kzg_utils::validate_blob; use crate::kzg_utils::validate_blob;
use kzg::Error as KzgError; use kzg::Error as KzgError;
use kzg::Kzg; use kzg::Kzg;
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use ssz_types::{Error, VariableList}; use ssz_types::Error;
use std::collections::hash_map::Entry; use std::collections::hash_map::Entry;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use types::blob_sidecar::{BlobIdentifier, BlobSidecar}; use types::blob_sidecar::{BlobIdentifier, BlobSidecar};
use types::{EthSpec, Hash256}; use types::{Epoch, EthSpec, Hash256};
#[derive(Debug)] #[derive(Debug)]
pub enum AvailabilityCheckError { pub enum AvailabilityCheckError {
DuplicateBlob(Hash256), DuplicateBlob(Hash256),
Kzg(KzgError), Kzg(KzgError),
SszTypes(ssz_types::Error), SszTypes(ssz_types::Error),
InvalidBlockOrBlob(String),
} }
impl From<ssz_types::Error> for AvailabilityCheckError { impl From<ssz_types::Error> for AvailabilityCheckError {
@@ -131,6 +132,7 @@ impl<T: EthSpec> DataAvailabilityChecker<T> {
pub fn check_block_availability( pub fn check_block_availability(
&self, &self,
executed_block: ExecutedBlock<T>, executed_block: ExecutedBlock<T>,
da_check_fn: impl FnOnce(Epoch) -> bool,
) -> Result<Availability<T>, AvailabilityCheckError> { ) -> Result<Availability<T>, AvailabilityCheckError> {
let block_clone = executed_block.block.clone(); let block_clone = executed_block.block.clone();
@@ -166,12 +168,10 @@ impl<T: EthSpec> DataAvailabilityChecker<T> {
payload_verification_outcome, payload_verification_outcome,
} = executed_block; } = executed_block;
let available_block = BlockWrapper::Available(AvailableBlock { let available_block =
block, AvailableBlock::new(block, removed.verified_blobs, da_check_fn)
blobs: VerifiedBlobs::Available(VariableList::new( .map_err(AvailabilityCheckError::InvalidBlockOrBlob)?
removed.verified_blobs, .into();
)?),
});
let available_executed = ExecutedBlock { let available_executed = ExecutedBlock {
block: available_block, block: available_block,

View File

@@ -1,4 +1,5 @@
use crate::metrics; use crate::metrics;
use beacon_chain::blob_verification::AsBlock;
use beacon_chain::blob_verification::BlockWrapper; use beacon_chain::blob_verification::BlockWrapper;
use beacon_chain::validator_monitor::{get_block_delay_ms, timestamp_now}; use beacon_chain::validator_monitor::{get_block_delay_ms, timestamp_now};
use beacon_chain::{AvailabilityProcessingStatus, NotifyExecutionLayer}; use beacon_chain::{AvailabilityProcessingStatus, NotifyExecutionLayer};
@@ -110,14 +111,14 @@ pub async fn publish_block<T: BeaconChainTypes>(
"Valid block from HTTP API"; "Valid block from HTTP API";
"block_delay" => ?delay, "block_delay" => ?delay,
"root" => format!("{}", root), "root" => format!("{}", root),
"proposer_index" => available_block.block.message().proposer_index(), "proposer_index" => available_block.message().proposer_index(),
"slot" => available_block.block.slot(), "slot" => available_block.slot(),
); );
// Notify the validator monitor. // Notify the validator monitor.
chain.validator_monitor.read().register_api_block( chain.validator_monitor.read().register_api_block(
seen_timestamp, seen_timestamp,
available_block.block.message(), available_block.message(),
root, root,
&chain.slot_clock, &chain.slot_clock,
); );
@@ -133,7 +134,7 @@ pub async fn publish_block<T: BeaconChainTypes>(
late_block_logging( late_block_logging(
&chain, &chain,
seen_timestamp, seen_timestamp,
available_block.block.message(), available_block.message(),
root, root,
"local", "local",
&log, &log,
@@ -165,7 +166,7 @@ pub async fn publish_block<T: BeaconChainTypes>(
log, log,
"Block from HTTP API already known"; "Block from HTTP API already known";
"block" => ?block_root, "block" => ?block_root,
"slot" => available_block.block.slot(), "slot" => available_block.slot(),
); );
Ok(()) Ok(())
} }