Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3378,11 +3378,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
);
}

self.data_availability_checker.put_pre_execution_block(
block_root,
unverified_block.block_cloned(),
block_source,
)?;
// Gloas blocks dont need to be inserted into the DA cache
// they are always available.
if !unverified_block
.block()
.fork_name_unchecked()
.gloas_enabled()
{
self.data_availability_checker.put_pre_execution_block(
block_root,
unverified_block.block_cloned(),
block_source,
)?;
}

// Start the Prometheus timer.
let _full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES);
Expand Down
84 changes: 70 additions & 14 deletions beacon_node/beacon_chain/src/block_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
use crate::beacon_snapshot::PreProcessingSnapshot;
use crate::blob_verification::GossipBlobError;
use crate::block_verification_types::{AsBlock, BlockImportData, RpcBlock};
use crate::data_availability_checker::{AvailabilityCheckError, MaybeAvailableBlock};
use crate::data_availability_checker::{
AvailabilityCheckError, AvailableBlock, AvailableBlockData, MaybeAvailableBlock,
};
use crate::data_column_verification::GossipDataColumnError;
use crate::execution_payload::{
AllowOptimisticImport, NotifyExecutionLayer, PayloadNotifier,
Expand Down Expand Up @@ -334,6 +336,15 @@ pub enum BlockError {
max_blobs_at_epoch: usize,
block: usize,
},
/// The bid's parent_block_root does not match the block's parent_root.
///
/// ## Peer scoring
///
/// The block is invalid and the peer should be penalized.
BidParentRootMismatch {
bid_parent_root: Hash256,
block_parent_root: Hash256,
},
}

/// Which specific signature(s) are invalid in a SignedBeaconBlock
Expand Down Expand Up @@ -887,15 +898,15 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {

// Do not gossip blocks that claim to contain more blobs than the max allowed
// at the given block epoch.
if let Ok(commitments) = block.message().body().blob_kzg_commitments() {
if let Some(blob_kzg_commitments_len) = block.message().blob_kzg_commitments_len() {
let max_blobs_at_epoch = chain
.spec
.max_blobs_per_block(block.slot().epoch(T::EthSpec::slots_per_epoch()))
as usize;
if commitments.len() > max_blobs_at_epoch {
if blob_kzg_commitments_len > max_blobs_at_epoch {
return Err(BlockError::InvalidBlobCount {
max_blobs_at_epoch,
block: commitments.len(),
block: blob_kzg_commitments_len,
});
}
}
Expand Down Expand Up @@ -932,6 +943,24 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
let block_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
let (parent_block, block) =
verify_parent_block_is_known::<T>(&fork_choice_read_lock, block)?;

// [New in Gloas]: Verify bid.parent_block_root matches block.parent_root.
if let Ok(bid) = block.message().body().signed_execution_payload_bid()
&& bid.message.parent_block_root != block.message().parent_root()
{
return Err(BlockError::BidParentRootMismatch {
bid_parent_root: bid.message.parent_block_root,
block_parent_root: block.message().parent_root(),
});
}

// TODO(gloas) The following validation can only be completed once fork choice has been implemented:
// The block's parent execution payload (defined by bid.parent_block_hash) has been seen
// (via gossip or non-gossip sources) (a client MAY queue blocks for processing
// once the parent payload is retrieved). If execution_payload verification of block's execution
// payload parent by an execution node is complete, verify the block's execution payload
// parent (defined by bid.parent_block_hash) passes all validation.

drop(fork_choice_read_lock);

// Track the number of skip slots between the block and its parent.
Expand Down Expand Up @@ -1038,8 +1067,15 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
});
}

// Validate the block's execution_payload (if any).
validate_execution_payload_for_gossip(&parent_block, block.message(), chain)?;
// [New in Gloas]: Skip payload validation checks. The payload now arrives separetely
// via `ExecutionPayloadEnvelope`.
if !chain
.spec
.fork_name_at_slot::<T::EthSpec>(block.slot())
.gloas_enabled()
{
validate_execution_payload_for_gossip(&parent_block, block.message(), chain)?;
}

// Beacon API block_gossip events
if let Some(event_handler) = chain.event_handler.as_ref()
Expand Down Expand Up @@ -1211,15 +1247,35 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {

let result = info_span!("signature_verify").in_scope(|| signature_verifier.verify());
match result {
Ok(_) => Ok(Self {
block: MaybeAvailableBlock::AvailabilityPending {
Ok(_) => {
// gloas blocks are always available.
let maybe_available = if chain
.spec
.fork_name_at_slot::<T::EthSpec>(block.slot())
.gloas_enabled()
{
MaybeAvailableBlock::Available(
AvailableBlock::new(
block,
AvailableBlockData::NoData,
&chain.data_availability_checker,
chain.spec.clone(),
)
.map_err(BlockError::AvailabilityCheck)?,
)
} else {
MaybeAvailableBlock::AvailabilityPending {
block_root: from.block_root,
block,
}
};
Ok(Self {
block: maybe_available,
block_root: from.block_root,
block,
},
block_root: from.block_root,
parent: Some(parent),
consensus_context,
}),
parent: Some(parent),
consensus_context,
})
}
Err(_) => Err(BlockError::InvalidSignature(
InvalidSignature::BlockBodySignatures,
)),
Expand Down
11 changes: 10 additions & 1 deletion beacon_node/beacon_chain/src/execution_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
state: &BeaconState<T::EthSpec>,
notify_execution_layer: NotifyExecutionLayer,
) -> Result<Self, BlockError> {
let payload_verification_status = if is_execution_enabled(state, block.message().body()) {
let payload_verification_status = if block.fork_name_unchecked().gloas_enabled() {
// Gloas blocks don't contain an execution payload.
Some(PayloadVerificationStatus::Irrelevant)
} else if is_execution_enabled(state, block.message().body()) {
// Perform the initial stages of payload verification.
//
// We will duplicate these checks again during `per_block_processing`, however these
Expand Down Expand Up @@ -294,6 +297,12 @@ pub fn validate_execution_payload_for_gossip<T: BeaconChainTypes>(
block: BeaconBlockRef<'_, T::EthSpec>,
chain: &BeaconChain<T>,
) -> Result<(), BlockError> {
// Gloas blocks don't have an execution payload in the block body.
// Bid-related validations are handled in gossip block verification.
if block.fork_name_unchecked().gloas_enabled() {
return Ok(());
}

// Only apply this validation if this is a Bellatrix beacon block.
if let Ok(execution_payload) = block.body().execution_payload() {
// This logic should match `is_execution_enabled`. We use only the execution block hash of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,8 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {
| Err(e @ BlockError::ParentExecutionPayloadInvalid { .. })
| Err(e @ BlockError::KnownInvalidExecutionPayload(_))
| Err(e @ BlockError::GenesisBlock)
| Err(e @ BlockError::InvalidBlobCount { .. }) => {
| Err(e @ BlockError::InvalidBlobCount { .. })
| Err(e @ BlockError::BidParentRootMismatch { .. }) => {
warn!(error = %e, "Could not verify block for gossip. Rejecting the block");
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject);
self.gossip_penalize_peer(
Expand Down Expand Up @@ -1490,19 +1491,23 @@ impl<T: BeaconChainTypes> NetworkBeaconProcessor<T> {

// Block is gossip valid. Attempt to fetch blobs from the EL using versioned hashes derived
// from kzg commitments, without having to wait for all blobs to be sent from the peers.
let publish_blobs = true;
let self_clone = self.clone();
let block_clone = block.clone();
let current_span = Span::current();
self.executor.spawn(
async move {
self_clone
.fetch_engine_blobs_and_publish(block_clone, block_root, publish_blobs)
.await
}
.instrument(current_span),
"fetch_blobs_gossip",
);
// TODO(gloas) we'll want to use this same optimization, but we need to refactor the
// `fetch_and_process_engine_blobs` flow to support gloas.
if !block.fork_name_unchecked().gloas_enabled() {
let publish_blobs = true;
let self_clone = self.clone();
let block_clone = block.clone();
let current_span = Span::current();
self.executor.spawn(
async move {
self_clone
.fetch_engine_blobs_and_publish(block_clone, block_root, publish_blobs)
.await
}
.instrument(current_span),
"fetch_blobs_gossip",
);
}

let result = self
.chain
Expand Down
20 changes: 20 additions & 0 deletions consensus/types/src/block/beacon_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,26 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockRef<'a, E, Payl
pub fn execution_payload(&self) -> Result<Payload::Ref<'a>, BeaconStateError> {
self.body().execution_payload()
}

pub fn blob_kzg_commitments_len(&self) -> Option<usize> {
match self {
BeaconBlockRef::Base(_) => None,
BeaconBlockRef::Altair(_) => None,
BeaconBlockRef::Bellatrix(_) => None,
BeaconBlockRef::Capella(_) => None,
BeaconBlockRef::Deneb(block) => Some(block.body.blob_kzg_commitments.len()),
BeaconBlockRef::Electra(block) => Some(block.body.blob_kzg_commitments.len()),
BeaconBlockRef::Fulu(block) => Some(block.body.blob_kzg_commitments.len()),
BeaconBlockRef::Gloas(block) => Some(
block
.body
.signed_execution_payload_bid
.message
.blob_kzg_commitments
.len(),
),
}
}
}

impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockRefMut<'a, E, Payload> {
Expand Down