mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 21:34:46 +00:00
Rework block processing (#4092)
* introduce availability pending block * add intoavailableblock trait * small fixes * add 'gossip blob cache' and start to clean up processing and transition types * shard memory blob cache * Initial commit * Fix after rebase * Add gossip verification conditions * cache cleanup * general chaos * extended chaos * cargo fmt * more progress * more progress * tons of changes, just tryna compile * everything, everywhere, all at once * Reprocess an ExecutedBlock on unavailable blobs * Add sus gossip verification for blobs * Merge stuff * Remove reprocessing cache stuff * lint * Add a wrapper to allow construction of only valid `AvailableBlock`s * rename blob arc list to blob list * merge cleanuo * Revert "merge cleanuo" This reverts commit5e98326878. * Revert "Revert "merge cleanuo"" This reverts commit3a4009443a. * fix rpc methods * move beacon block and blob to eth2/types * rename gossip blob cache to data availability checker * lots of changes * fix some compilation issues * fix compilation issues * fix compilation issues * fix compilation issues * fix compilation issues * fix compilation issues * cargo fmt * use a common data structure for block import types * fix availability check on proposal import * refactor the blob cache and split the block wrapper into two types * add type conversion for signed block and block wrapper * fix beacon chain tests and do some renaming, add some comments * Partial processing (#4) * move beacon block and blob to eth2/types * rename gossip blob cache to data availability checker * lots of changes * fix some compilation issues * fix compilation issues * fix compilation issues * fix compilation issues * fix compilation issues * fix compilation issues * cargo fmt * use a common data structure for block import types * fix availability check on proposal import * refactor the blob cache and split the block wrapper into two types * add type conversion for signed block and block wrapper * fix beacon chain tests and do some renaming, add some comments * cargo update (#6) --------- Co-authored-by: realbigsean <sean@sigmaprime.io> Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
@@ -2,16 +2,20 @@ use derivative::Derivative;
|
||||
use slot_clock::SlotClock;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::beacon_chain::{BeaconChain, BeaconChainTypes, MAXIMUM_GOSSIP_CLOCK_DISPARITY};
|
||||
use crate::BeaconChainError;
|
||||
use state_processing::per_block_processing::eip4844::eip4844::verify_kzg_commitments_against_transactions;
|
||||
use types::signed_beacon_block::BlobReconstructionError;
|
||||
use types::{
|
||||
BeaconBlockRef, BeaconStateError, BlobsSidecar, EthSpec, Hash256, KzgCommitment,
|
||||
SignedBeaconBlock, SignedBeaconBlockAndBlobsSidecar, SignedBeaconBlockHeader, Slot,
|
||||
Transactions,
|
||||
use crate::beacon_chain::{
|
||||
BeaconChain, BeaconChainTypes, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
|
||||
VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT,
|
||||
};
|
||||
use crate::data_availability_checker::{
|
||||
AvailabilityCheckError, AvailabilityPendingBlock, AvailableBlock,
|
||||
};
|
||||
use crate::kzg_utils::{validate_blob, validate_blobs};
|
||||
use crate::BeaconChainError;
|
||||
use kzg::Kzg;
|
||||
use types::{
|
||||
BeaconBlockRef, BeaconStateError, BlobSidecar, BlobSidecarList, Epoch, EthSpec, Hash256,
|
||||
KzgCommitment, SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlobSidecar, Slot,
|
||||
};
|
||||
use types::{Epoch, ExecPayload};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlobError {
|
||||
@@ -62,15 +66,54 @@ pub enum BlobError {
|
||||
UnavailableBlobs,
|
||||
/// Blobs provided for a pre-Eip4844 fork.
|
||||
InconsistentFork,
|
||||
}
|
||||
|
||||
impl From<BlobReconstructionError> for BlobError {
|
||||
fn from(e: BlobReconstructionError) -> Self {
|
||||
match e {
|
||||
BlobReconstructionError::UnavailableBlobs => BlobError::UnavailableBlobs,
|
||||
BlobReconstructionError::InconsistentFork => BlobError::InconsistentFork,
|
||||
}
|
||||
}
|
||||
/// The `blobs_sidecar.message.beacon_block_root` block is unknown.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// The blob points to a block we have not yet imported. The blob cannot be imported
|
||||
/// into fork choice yet
|
||||
UnknownHeadBlock {
|
||||
beacon_block_root: Hash256,
|
||||
},
|
||||
|
||||
/// The `BlobSidecar` was gossiped over an incorrect subnet.
|
||||
InvalidSubnet {
|
||||
expected: u64,
|
||||
received: u64,
|
||||
},
|
||||
|
||||
/// The sidecar corresponds to a slot older than the finalized head slot.
|
||||
PastFinalizedSlot {
|
||||
blob_slot: Slot,
|
||||
finalized_slot: Slot,
|
||||
},
|
||||
|
||||
/// The proposer index specified in the sidecar does not match the locally computed
|
||||
/// proposer index.
|
||||
ProposerIndexMismatch {
|
||||
sidecar: usize,
|
||||
local: usize,
|
||||
},
|
||||
|
||||
ProposerSignatureInvalid,
|
||||
|
||||
/// A sidecar with same slot, beacon_block_root and proposer_index but different blob is received for
|
||||
/// the same blob index.
|
||||
RepeatSidecar {
|
||||
proposer: usize,
|
||||
slot: Slot,
|
||||
blob_index: usize,
|
||||
},
|
||||
|
||||
/// The proposal_index corresponding to blob.beacon_block_root is not known.
|
||||
///
|
||||
/// ## Peer scoring
|
||||
///
|
||||
/// The block is invalid and the peer is faulty.
|
||||
UnknownValidator(u64),
|
||||
|
||||
BlobCacheError(AvailabilityCheckError),
|
||||
}
|
||||
|
||||
impl From<BeaconChainError> for BlobError {
|
||||
@@ -85,302 +128,220 @@ impl From<BeaconStateError> for BlobError {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_blob_for_gossip<T: BeaconChainTypes>(
|
||||
block_wrapper: BlockWrapper<T::EthSpec>,
|
||||
block_root: Hash256,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<AvailableBlock<T::EthSpec>, BlobError> {
|
||||
if let BlockWrapper::BlockAndBlob(ref block, ref blobs_sidecar) = block_wrapper {
|
||||
let blob_slot = blobs_sidecar.beacon_block_slot;
|
||||
// Do not gossip or process blobs from future or past slots.
|
||||
let latest_permissible_slot = chain
|
||||
.slot_clock
|
||||
.now_with_future_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||
if blob_slot > latest_permissible_slot {
|
||||
return Err(BlobError::FutureSlot {
|
||||
message_slot: latest_permissible_slot,
|
||||
latest_permissible_slot: blob_slot,
|
||||
});
|
||||
}
|
||||
|
||||
if blob_slot != block.slot() {
|
||||
return Err(BlobError::SlotMismatch {
|
||||
blob_slot,
|
||||
block_slot: block.slot(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
block_wrapper.into_available_block(block_root, chain)
|
||||
/// A wrapper around a `BlobSidecar` that indicates it has been approved for re-gossiping on
|
||||
/// the p2p network.
|
||||
#[derive(Debug)]
|
||||
pub struct GossipVerifiedBlob<T: EthSpec> {
|
||||
blob: Arc<BlobSidecar<T>>,
|
||||
}
|
||||
|
||||
fn verify_data_availability<T: BeaconChainTypes>(
|
||||
_blob_sidecar: &BlobsSidecar<T::EthSpec>,
|
||||
kzg_commitments: &[KzgCommitment],
|
||||
transactions: &Transactions<T::EthSpec>,
|
||||
_block_slot: Slot,
|
||||
_block_root: Hash256,
|
||||
impl<T: EthSpec> GossipVerifiedBlob<T> {
|
||||
pub fn block_root(&self) -> Hash256 {
|
||||
self.blob.block_root
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
|
||||
signed_blob_sidecar: SignedBlobSidecar<T::EthSpec>,
|
||||
subnet: u64,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(), BlobError> {
|
||||
if verify_kzg_commitments_against_transactions::<T::EthSpec>(transactions, kzg_commitments)
|
||||
.is_err()
|
||||
) -> Result<GossipVerifiedBlob<T::EthSpec>, BlobError> {
|
||||
let blob_slot = signed_blob_sidecar.message.slot;
|
||||
let blob_index = signed_blob_sidecar.message.index;
|
||||
let block_root = signed_blob_sidecar.message.block_root;
|
||||
|
||||
// Verify that the blob_sidecar was received on the correct subnet.
|
||||
if blob_index != subnet {
|
||||
return Err(BlobError::InvalidSubnet {
|
||||
expected: blob_index,
|
||||
received: subnet,
|
||||
});
|
||||
}
|
||||
|
||||
// Verify that the sidecar is not from a future slot.
|
||||
let latest_permissible_slot = chain
|
||||
.slot_clock
|
||||
.now_with_future_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||
if blob_slot > latest_permissible_slot {
|
||||
return Err(BlobError::FutureSlot {
|
||||
message_slot: blob_slot,
|
||||
latest_permissible_slot,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(pawan): Verify not from a past slot?
|
||||
|
||||
// Verify that the sidecar slot is greater than the latest finalized slot
|
||||
let latest_finalized_slot = chain
|
||||
.head()
|
||||
.finalized_checkpoint()
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
if blob_slot <= latest_finalized_slot {
|
||||
return Err(BlobError::PastFinalizedSlot {
|
||||
blob_slot,
|
||||
finalized_slot: latest_finalized_slot,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(pawan): should we verify locally that the parent root is correct
|
||||
// or just use whatever the proposer gives us?
|
||||
let proposer_shuffling_root = signed_blob_sidecar.message.block_parent_root;
|
||||
|
||||
let (proposer_index, fork) = match chain
|
||||
.beacon_proposer_cache
|
||||
.lock()
|
||||
.get_slot::<T::EthSpec>(proposer_shuffling_root, blob_slot)
|
||||
{
|
||||
return Err(BlobError::TransactionCommitmentMismatch);
|
||||
}
|
||||
|
||||
// Validatate that the kzg proof is valid against the commitments and blobs
|
||||
let _kzg = chain
|
||||
.kzg
|
||||
.as_ref()
|
||||
.ok_or(BlobError::TrustedSetupNotInitialized)?;
|
||||
|
||||
todo!("use `kzg_utils::validate_blobs` once the function is updated")
|
||||
// if !kzg_utils::validate_blobs_sidecar(
|
||||
// kzg,
|
||||
// block_slot,
|
||||
// block_root,
|
||||
// kzg_commitments,
|
||||
// blob_sidecar,
|
||||
// )
|
||||
// .map_err(BlobError::KzgError)?
|
||||
// {
|
||||
// return Err(BlobError::InvalidKzgProof);
|
||||
// }
|
||||
// Ok(())
|
||||
}
|
||||
|
||||
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBeaconBlockAndBlobsSidecar`]. This makes no
|
||||
/// claims about data availability and should not be used in consensus. This struct is useful in
|
||||
/// networking when we want to send blocks around without consensus checks.
|
||||
#[derive(Clone, Debug, Derivative)]
|
||||
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
|
||||
pub enum BlockWrapper<E: EthSpec> {
|
||||
Block(Arc<SignedBeaconBlock<E>>),
|
||||
BlockAndBlob(Arc<SignedBeaconBlock<E>>, Arc<BlobsSidecar<E>>),
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BlockWrapper<E> {
|
||||
pub fn new(
|
||||
block: Arc<SignedBeaconBlock<E>>,
|
||||
blobs_sidecar: Option<Arc<BlobsSidecar<E>>>,
|
||||
) -> Self {
|
||||
if let Some(blobs_sidecar) = blobs_sidecar {
|
||||
BlockWrapper::BlockAndBlob(block, blobs_sidecar)
|
||||
} else {
|
||||
BlockWrapper::Block(block)
|
||||
Some(proposer) => (proposer.index, proposer.fork),
|
||||
None => {
|
||||
let state = &chain.canonical_head.cached_head().snapshot.beacon_state;
|
||||
(
|
||||
state.get_beacon_proposer_index(blob_slot, &chain.spec)?,
|
||||
state.fork(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let blob_proposer_index = signed_blob_sidecar.message.proposer_index;
|
||||
if proposer_index != blob_proposer_index as usize {
|
||||
return Err(BlobError::ProposerIndexMismatch {
|
||||
sidecar: blob_proposer_index as usize,
|
||||
local: proposer_index,
|
||||
});
|
||||
}
|
||||
|
||||
let signature_is_valid = {
|
||||
let pubkey_cache = chain
|
||||
.validator_pubkey_cache
|
||||
.try_read_for(VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT)
|
||||
.ok_or(BeaconChainError::ValidatorPubkeyCacheLockTimeout)
|
||||
.map_err(BlobError::BeaconChainError)?;
|
||||
|
||||
let pubkey = pubkey_cache
|
||||
.get(proposer_index)
|
||||
.ok_or_else(|| BlobError::UnknownValidator(proposer_index as u64))?;
|
||||
|
||||
signed_blob_sidecar.verify_signature(
|
||||
None,
|
||||
pubkey,
|
||||
&fork,
|
||||
chain.genesis_validators_root,
|
||||
&chain.spec,
|
||||
)
|
||||
};
|
||||
|
||||
if !signature_is_valid {
|
||||
return Err(BlobError::ProposerSignatureInvalid);
|
||||
}
|
||||
|
||||
// TODO(pawan): kzg validations.
|
||||
|
||||
// TODO(pawan): Check if other blobs for the same proposer index and blob index have been
|
||||
// received and drop if required.
|
||||
|
||||
// Verify if the corresponding block for this blob has been received.
|
||||
// Note: this should be the last gossip check so that we can forward the blob
|
||||
// over the gossip network even if we haven't received the corresponding block yet
|
||||
// as all other validations have passed.
|
||||
let block_opt = chain
|
||||
.canonical_head
|
||||
.fork_choice_read_lock()
|
||||
.get_block(&block_root)
|
||||
.or_else(|| chain.early_attester_cache.get_proto_block(block_root)); // TODO(pawan): should we be checking this cache?
|
||||
|
||||
// TODO(pawan): this may be redundant with the new `AvailabilityProcessingStatus::PendingBlock variant`
|
||||
if block_opt.is_none() {
|
||||
return Err(BlobError::UnknownHeadBlock {
|
||||
beacon_block_root: block_root,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(GossipVerifiedBlob {
|
||||
blob: signed_blob_sidecar.message,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct KzgVerifiedBlob<T: EthSpec> {
|
||||
blob: Arc<BlobSidecar<T>>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> KzgVerifiedBlob<T> {
|
||||
pub fn to_blob(self) -> Arc<BlobSidecar<T>> {
|
||||
self.blob
|
||||
}
|
||||
pub fn as_blob(&self) -> &BlobSidecar<T> {
|
||||
&self.blob
|
||||
}
|
||||
pub fn clone_blob(&self) -> Arc<BlobSidecar<T>> {
|
||||
self.blob.clone()
|
||||
}
|
||||
pub fn kzg_commitment(&self) -> KzgCommitment {
|
||||
self.blob.kzg_commitment
|
||||
}
|
||||
pub fn block_root(&self) -> Hash256 {
|
||||
self.blob.block_root
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<SignedBeaconBlock<E>> for BlockWrapper<E> {
|
||||
fn from(block: SignedBeaconBlock<E>) -> Self {
|
||||
BlockWrapper::Block(Arc::new(block))
|
||||
pub fn verify_kzg_for_blob<T: EthSpec>(
|
||||
blob: GossipVerifiedBlob<T>,
|
||||
kzg: &Kzg,
|
||||
) -> Result<KzgVerifiedBlob<T>, AvailabilityCheckError> {
|
||||
//TODO(sean) remove clone
|
||||
if validate_blob::<T>(
|
||||
kzg,
|
||||
blob.blob.blob.clone(),
|
||||
blob.blob.kzg_commitment,
|
||||
blob.blob.kzg_proof,
|
||||
)
|
||||
.map_err(AvailabilityCheckError::Kzg)?
|
||||
{
|
||||
Ok(KzgVerifiedBlob { blob: blob.blob })
|
||||
} else {
|
||||
Err(AvailabilityCheckError::KzgVerificationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<SignedBeaconBlockAndBlobsSidecar<E>> for BlockWrapper<E> {
|
||||
fn from(block: SignedBeaconBlockAndBlobsSidecar<E>) -> Self {
|
||||
let SignedBeaconBlockAndBlobsSidecar {
|
||||
beacon_block,
|
||||
blobs_sidecar,
|
||||
} = block;
|
||||
BlockWrapper::BlockAndBlob(beacon_block, blobs_sidecar)
|
||||
pub fn verify_kzg_for_blob_list<T: EthSpec>(
|
||||
blob_list: BlobSidecarList<T>,
|
||||
kzg: &Kzg,
|
||||
) -> Result<KzgVerifiedBlobList<T>, AvailabilityCheckError> {
|
||||
let (blobs, (commitments, proofs)): (Vec<_>, (Vec<_>, Vec<_>)) = blob_list
|
||||
.clone()
|
||||
.into_iter()
|
||||
//TODO(sean) remove clone
|
||||
.map(|blob| (blob.blob.clone(), (blob.kzg_commitment, blob.kzg_proof)))
|
||||
.unzip();
|
||||
if validate_blobs::<T>(
|
||||
kzg,
|
||||
commitments.as_slice(),
|
||||
blobs.as_slice(),
|
||||
proofs.as_slice(),
|
||||
)
|
||||
.map_err(AvailabilityCheckError::Kzg)?
|
||||
{
|
||||
Ok(blob_list
|
||||
.into_iter()
|
||||
.map(|blob| KzgVerifiedBlob { blob })
|
||||
.collect())
|
||||
} else {
|
||||
Err(AvailabilityCheckError::KzgVerificationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> From<Arc<SignedBeaconBlock<E>>> for BlockWrapper<E> {
|
||||
fn from(block: Arc<SignedBeaconBlock<E>>) -> Self {
|
||||
BlockWrapper::Block(block)
|
||||
}
|
||||
}
|
||||
pub type KzgVerifiedBlobList<T> = Vec<KzgVerifiedBlob<T>>;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DataAvailabilityCheckRequired {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
pub trait IntoAvailableBlock<T: BeaconChainTypes> {
|
||||
fn into_available_block(
|
||||
self,
|
||||
block_root: Hash256,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<AvailableBlock<T::EthSpec>, BlobError>;
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoAvailableBlock<T> for BlockWrapper<T::EthSpec> {
|
||||
fn into_available_block(
|
||||
self,
|
||||
block_root: Hash256,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<AvailableBlock<T::EthSpec>, BlobError> {
|
||||
let data_availability_boundary = chain.data_availability_boundary();
|
||||
let da_check_required =
|
||||
data_availability_boundary.map_or(DataAvailabilityCheckRequired::No, |boundary| {
|
||||
if self.slot().epoch(T::EthSpec::slots_per_epoch()) >= boundary {
|
||||
DataAvailabilityCheckRequired::Yes
|
||||
} else {
|
||||
DataAvailabilityCheckRequired::No
|
||||
}
|
||||
});
|
||||
match self {
|
||||
BlockWrapper::Block(block) => AvailableBlock::new(block, block_root, da_check_required),
|
||||
BlockWrapper::BlockAndBlob(block, blobs_sidecar) => {
|
||||
if matches!(da_check_required, DataAvailabilityCheckRequired::Yes) {
|
||||
let kzg_commitments = block
|
||||
.message()
|
||||
.body()
|
||||
.blob_kzg_commitments()
|
||||
.map_err(|_| BlobError::KzgCommitmentMissing)?;
|
||||
let transactions = block
|
||||
.message()
|
||||
.body()
|
||||
.execution_payload_eip4844()
|
||||
.map(|payload| payload.transactions())
|
||||
.map_err(|_| BlobError::TransactionsMissing)?
|
||||
.ok_or(BlobError::TransactionsMissing)?;
|
||||
verify_data_availability(
|
||||
&blobs_sidecar,
|
||||
kzg_commitments,
|
||||
transactions,
|
||||
block.slot(),
|
||||
block_root,
|
||||
chain,
|
||||
)?;
|
||||
}
|
||||
|
||||
AvailableBlock::new_with_blobs(block, blobs_sidecar, da_check_required)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBeaconBlockAndBlobsSidecar`]. An
|
||||
/// `AvailableBlock` has passed any required data availability checks and should be used in
|
||||
/// consensus. This newtype wraps `AvailableBlockInner` to ensure data availability checks
|
||||
/// cannot be circumvented on construction.
|
||||
#[derive(Clone, Debug, Derivative)]
|
||||
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
|
||||
pub struct AvailableBlock<E: EthSpec>(AvailableBlockInner<E>);
|
||||
|
||||
/// A wrapper over a [`SignedBeaconBlock`] or a [`SignedBeaconBlockAndBlobsSidecar`].
|
||||
#[derive(Clone, Debug, Derivative)]
|
||||
#[derivative(PartialEq, Hash(bound = "E: EthSpec"))]
|
||||
enum AvailableBlockInner<E: EthSpec> {
|
||||
Block(Arc<SignedBeaconBlock<E>>),
|
||||
BlockAndBlob(SignedBeaconBlockAndBlobsSidecar<E>),
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AvailableBlock<E> {
|
||||
pub fn new(
|
||||
beacon_block: Arc<SignedBeaconBlock<E>>,
|
||||
block_root: Hash256,
|
||||
da_check_required: DataAvailabilityCheckRequired,
|
||||
) -> Result<Self, BlobError> {
|
||||
match beacon_block.as_ref() {
|
||||
// No data availability check required prior to Eip4844.
|
||||
SignedBeaconBlock::Base(_)
|
||||
| SignedBeaconBlock::Altair(_)
|
||||
| SignedBeaconBlock::Capella(_)
|
||||
| SignedBeaconBlock::Merge(_) => {
|
||||
Ok(AvailableBlock(AvailableBlockInner::Block(beacon_block)))
|
||||
}
|
||||
SignedBeaconBlock::Eip4844(_) => {
|
||||
match da_check_required {
|
||||
DataAvailabilityCheckRequired::Yes => {
|
||||
// Attempt to reconstruct empty blobs here.
|
||||
let blobs_sidecar = beacon_block
|
||||
.reconstruct_empty_blobs(Some(block_root))
|
||||
.map(Arc::new)?;
|
||||
Ok(AvailableBlock(AvailableBlockInner::BlockAndBlob(
|
||||
SignedBeaconBlockAndBlobsSidecar {
|
||||
beacon_block,
|
||||
blobs_sidecar,
|
||||
},
|
||||
)))
|
||||
}
|
||||
DataAvailabilityCheckRequired::No => {
|
||||
Ok(AvailableBlock(AvailableBlockInner::Block(beacon_block)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is private because an `AvailableBlock` should be
|
||||
/// constructed via the `into_available_block` method.
|
||||
fn new_with_blobs(
|
||||
beacon_block: Arc<SignedBeaconBlock<E>>,
|
||||
blobs_sidecar: Arc<BlobsSidecar<E>>,
|
||||
da_check_required: DataAvailabilityCheckRequired,
|
||||
) -> Result<Self, BlobError> {
|
||||
match beacon_block.as_ref() {
|
||||
// This method shouldn't be called with a pre-Eip4844 block.
|
||||
SignedBeaconBlock::Base(_)
|
||||
| SignedBeaconBlock::Altair(_)
|
||||
| SignedBeaconBlock::Capella(_)
|
||||
| SignedBeaconBlock::Merge(_) => Err(BlobError::InconsistentFork),
|
||||
SignedBeaconBlock::Eip4844(_) => {
|
||||
match da_check_required {
|
||||
DataAvailabilityCheckRequired::Yes => Ok(AvailableBlock(
|
||||
AvailableBlockInner::BlockAndBlob(SignedBeaconBlockAndBlobsSidecar {
|
||||
beacon_block,
|
||||
blobs_sidecar,
|
||||
}),
|
||||
)),
|
||||
DataAvailabilityCheckRequired::No => {
|
||||
// Blobs were not verified so we drop them, we'll instead just pass around
|
||||
// an available `Eip4844` block without blobs.
|
||||
Ok(AvailableBlock(AvailableBlockInner::Block(beacon_block)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blobs(&self) -> Option<Arc<BlobsSidecar<E>>> {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(_) => None,
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
Some(block_sidecar_pair.blobs_sidecar.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deconstruct(self) -> (Arc<SignedBeaconBlock<E>>, Option<Arc<BlobsSidecar<E>>>) {
|
||||
match self.0 {
|
||||
AvailableBlockInner::Block(block) => (block, None),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
let SignedBeaconBlockAndBlobsSidecar {
|
||||
beacon_block,
|
||||
blobs_sidecar,
|
||||
} = block_sidecar_pair;
|
||||
(beacon_block, Some(blobs_sidecar))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoBlockWrapper<E: EthSpec>: AsBlock<E> {
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E>;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> IntoBlockWrapper<E> for BlockWrapper<E> {
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> IntoBlockWrapper<E> for AvailableBlock<E> {
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E> {
|
||||
let (block, blobs) = self.deconstruct();
|
||||
if let Some(blobs) = blobs {
|
||||
BlockWrapper::BlockAndBlob(block, blobs)
|
||||
} else {
|
||||
BlockWrapper::Block(block)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MaybeAvailableBlock<E: EthSpec> {
|
||||
/// This variant is fully available.
|
||||
/// i.e. for pre-eip4844 blocks, it contains a (`SignedBeaconBlock`, `Blobs::None`) and for
|
||||
/// post-4844 blocks, it contains a `SignedBeaconBlock` and a Blobs variant other than `Blobs::None`.
|
||||
Available(AvailableBlock<E>),
|
||||
/// This variant is not fully available and requires blobs to become fully available.
|
||||
AvailabilityPending(AvailabilityPendingBlock<E>),
|
||||
}
|
||||
|
||||
pub trait AsBlock<E: EthSpec> {
|
||||
@@ -393,193 +354,149 @@ pub trait AsBlock<E: EthSpec> {
|
||||
fn as_block(&self) -> &SignedBeaconBlock<E>;
|
||||
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>>;
|
||||
fn canonical_root(&self) -> Hash256;
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E>;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AsBlock<E> for MaybeAvailableBlock<E> {
|
||||
fn slot(&self) -> Slot {
|
||||
self.as_block().slot()
|
||||
}
|
||||
fn epoch(&self) -> Epoch {
|
||||
self.as_block().epoch()
|
||||
}
|
||||
fn parent_root(&self) -> Hash256 {
|
||||
self.as_block().parent_root()
|
||||
}
|
||||
fn state_root(&self) -> Hash256 {
|
||||
self.as_block().state_root()
|
||||
}
|
||||
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
|
||||
self.as_block().signed_block_header()
|
||||
}
|
||||
fn message(&self) -> BeaconBlockRef<E> {
|
||||
self.as_block().message()
|
||||
}
|
||||
fn as_block(&self) -> &SignedBeaconBlock<E> {
|
||||
match &self {
|
||||
MaybeAvailableBlock::Available(block) => block.as_block(),
|
||||
MaybeAvailableBlock::AvailabilityPending(block) => block.as_block(),
|
||||
}
|
||||
}
|
||||
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
|
||||
match &self {
|
||||
MaybeAvailableBlock::Available(block) => block.block_cloned(),
|
||||
MaybeAvailableBlock::AvailabilityPending(block) => block.block_cloned(),
|
||||
}
|
||||
}
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
self.as_block().canonical_root()
|
||||
}
|
||||
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E> {
|
||||
match self {
|
||||
MaybeAvailableBlock::Available(available_block) => available_block.into_block_wrapper(),
|
||||
MaybeAvailableBlock::AvailabilityPending(pending_block) => {
|
||||
BlockWrapper::Block(pending_block.to_block())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AsBlock<E> for &MaybeAvailableBlock<E> {
|
||||
fn slot(&self) -> Slot {
|
||||
self.as_block().slot()
|
||||
}
|
||||
fn epoch(&self) -> Epoch {
|
||||
self.as_block().epoch()
|
||||
}
|
||||
fn parent_root(&self) -> Hash256 {
|
||||
self.as_block().parent_root()
|
||||
}
|
||||
fn state_root(&self) -> Hash256 {
|
||||
self.as_block().state_root()
|
||||
}
|
||||
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
|
||||
self.as_block().signed_block_header()
|
||||
}
|
||||
fn message(&self) -> BeaconBlockRef<E> {
|
||||
self.as_block().message()
|
||||
}
|
||||
fn as_block(&self) -> &SignedBeaconBlock<E> {
|
||||
match &self {
|
||||
MaybeAvailableBlock::Available(block) => block.as_block(),
|
||||
MaybeAvailableBlock::AvailabilityPending(block) => block.as_block(),
|
||||
}
|
||||
}
|
||||
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
|
||||
match &self {
|
||||
MaybeAvailableBlock::Available(block) => block.block_cloned(),
|
||||
MaybeAvailableBlock::AvailabilityPending(block) => block.block_cloned(),
|
||||
}
|
||||
}
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
self.as_block().canonical_root()
|
||||
}
|
||||
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E> {
|
||||
self.clone().into_block_wrapper()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Derivative)]
|
||||
#[derivative(Hash(bound = "E: EthSpec"))]
|
||||
pub enum BlockWrapper<E: EthSpec> {
|
||||
Block(Arc<SignedBeaconBlock<E>>),
|
||||
BlockAndBlobs(Arc<SignedBeaconBlock<E>>, BlobSidecarList<E>),
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AsBlock<E> for BlockWrapper<E> {
|
||||
fn slot(&self) -> Slot {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.slot(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.slot(),
|
||||
}
|
||||
self.as_block().slot()
|
||||
}
|
||||
fn epoch(&self) -> Epoch {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.epoch(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.epoch(),
|
||||
}
|
||||
self.as_block().epoch()
|
||||
}
|
||||
fn parent_root(&self) -> Hash256 {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.parent_root(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.parent_root(),
|
||||
}
|
||||
self.as_block().parent_root()
|
||||
}
|
||||
fn state_root(&self) -> Hash256 {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.state_root(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.state_root(),
|
||||
}
|
||||
self.as_block().state_root()
|
||||
}
|
||||
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.signed_block_header(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.signed_block_header(),
|
||||
}
|
||||
self.as_block().signed_block_header()
|
||||
}
|
||||
fn message(&self) -> BeaconBlockRef<E> {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.message(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.message(),
|
||||
}
|
||||
self.as_block().message()
|
||||
}
|
||||
fn as_block(&self) -> &SignedBeaconBlock<E> {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block,
|
||||
BlockWrapper::BlockAndBlob(block, _) => block,
|
||||
BlockWrapper::BlockAndBlobs(block, _) => block,
|
||||
}
|
||||
}
|
||||
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.clone(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.clone(),
|
||||
BlockWrapper::BlockAndBlobs(block, _) => block.clone(),
|
||||
}
|
||||
}
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.canonical_root(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.canonical_root(),
|
||||
}
|
||||
self.as_block().canonical_root()
|
||||
}
|
||||
|
||||
fn into_block_wrapper(self) -> BlockWrapper<E> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AsBlock<E> for &BlockWrapper<E> {
|
||||
fn slot(&self) -> Slot {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.slot(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.slot(),
|
||||
}
|
||||
}
|
||||
fn epoch(&self) -> Epoch {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.epoch(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.epoch(),
|
||||
}
|
||||
}
|
||||
fn parent_root(&self) -> Hash256 {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.parent_root(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.parent_root(),
|
||||
}
|
||||
}
|
||||
fn state_root(&self) -> Hash256 {
|
||||
match self {
|
||||
BlockWrapper::Block(block) => block.state_root(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.state_root(),
|
||||
}
|
||||
}
|
||||
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.signed_block_header(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.signed_block_header(),
|
||||
}
|
||||
}
|
||||
fn message(&self) -> BeaconBlockRef<E> {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.message(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.message(),
|
||||
}
|
||||
}
|
||||
fn as_block(&self) -> &SignedBeaconBlock<E> {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block,
|
||||
BlockWrapper::BlockAndBlob(block, _) => block,
|
||||
}
|
||||
}
|
||||
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.clone(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.clone(),
|
||||
}
|
||||
}
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
match &self {
|
||||
BlockWrapper::Block(block) => block.canonical_root(),
|
||||
BlockWrapper::BlockAndBlob(block, _) => block.canonical_root(),
|
||||
}
|
||||
impl<E: EthSpec> From<Arc<SignedBeaconBlock<E>>> for BlockWrapper<E> {
|
||||
fn from(value: Arc<SignedBeaconBlock<E>>) -> Self {
|
||||
Self::Block(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> AsBlock<E> for AvailableBlock<E> {
|
||||
fn slot(&self) -> Slot {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.slot(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.slot()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn epoch(&self) -> Epoch {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.epoch(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.epoch()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn parent_root(&self) -> Hash256 {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.parent_root(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.parent_root()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn state_root(&self) -> Hash256 {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.state_root(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.state_root()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn signed_block_header(&self) -> SignedBeaconBlockHeader {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.signed_block_header(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.signed_block_header()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn message(&self) -> BeaconBlockRef<E> {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.message(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.message()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn as_block(&self) -> &SignedBeaconBlock<E> {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block,
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
&block_sidecar_pair.beacon_block
|
||||
}
|
||||
}
|
||||
}
|
||||
fn block_cloned(&self) -> Arc<SignedBeaconBlock<E>> {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.clone(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
fn canonical_root(&self) -> Hash256 {
|
||||
match &self.0 {
|
||||
AvailableBlockInner::Block(block) => block.canonical_root(),
|
||||
AvailableBlockInner::BlockAndBlob(block_sidecar_pair) => {
|
||||
block_sidecar_pair.beacon_block.canonical_root()
|
||||
}
|
||||
}
|
||||
impl<E: EthSpec> From<SignedBeaconBlock<E>> for BlockWrapper<E> {
|
||||
fn from(value: SignedBeaconBlock<E>) -> Self {
|
||||
Self::Block(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user