Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
58 changes: 55 additions & 3 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3041,7 +3041,29 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// In the case of (2), skipping the block is valid since we should never import it.
// However, we will potentially get a `ParentUnknown` on a later block. The sync
// protocol will need to ensure this is handled gracefully.
Err(BlockError::WouldRevertFinalizedSlot { .. }) => continue,
Err(BlockError::WouldRevertFinalizedSlot { .. }) => {
// For Gloas blocks, persist the envelope even though we're skipping
// the block. This is needed after checkpoint sync: the checkpoint
// block's envelope must be in the store so that `load_parent` can
// verify it when importing the first post-checkpoint block.
if let RangeSyncBlock::Gloas {
envelope: Some(ref available_envelope),
..
} = block
{
let (signed_envelope, _columns) = available_envelope.clone().deconstruct();
if let Err(e) = self
.store
.put_payload_envelope(&block_root, &signed_envelope)
{
return Err(Box::new(ChainSegmentResult::Failed {
imported_blocks,
error: BlockError::BeaconChainError(Box::new(e.into())),
}));
}
}
continue;
}
// The block has a known parent that does not descend from the finalized block.
// There is no need to process this block or any children.
Err(BlockError::NotFinalizedDescendant { block_parent_root }) => {
Expand Down Expand Up @@ -3150,11 +3172,41 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
};

// Import the blocks into the chain.
for signature_verified_block in signature_verified_blocks {
for (signature_verified_block, envelope) in signature_verified_blocks {
let block_root = signature_verified_block.block_root();
let block_slot = signature_verified_block.slot();

// For Gloas blocks, persist the envelope and notify fork choice
// before importing the block. The next block's `load_parent` will
// check for this envelope in the store.
if let Some(available_envelope) = envelope {
let (signed_envelope, _columns) = available_envelope.deconstruct();
if let Err(e) = self
.store
.put_payload_envelope(&block_root, &signed_envelope)
{
return ChainSegmentResult::Failed {
imported_blocks,
error: BlockError::BeaconChainError(Box::new(e.into())),
};
}
if let Err(e) = self
.canonical_head
.fork_choice_write_lock()
.on_valid_payload_envelope_received(block_root)
{
return ChainSegmentResult::Failed {
imported_blocks,
error: BlockError::BeaconChainError(Box::new(
BeaconChainError::ForkChoiceError(e),
)),
};
}
}

match self
.process_block(
signature_verified_block.block_root(),
block_root,
signature_verified_block,
notify_execution_layer,
BlockImportSource::RangeSync,
Expand Down
105 changes: 94 additions & 11 deletions beacon_node/beacon_chain/src/block_verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ use crate::execution_payload::{
};
use crate::kzg_utils::blobs_to_data_column_sidecars;
use crate::observed_block_producers::SeenBlock;
use crate::payload_envelope_verification::{AvailableEnvelope, EnvelopeError};
use crate::validator_monitor::HISTORIC_EPOCHS as VALIDATOR_MONITOR_HISTORIC_EPOCHS;
use crate::validator_pubkey_cache::ValidatorPubkeyCache;
use crate::{
Expand Down Expand Up @@ -320,6 +321,20 @@ pub enum BlockError {
bid_parent_root: Hash256,
block_parent_root: Hash256,
},
/// The child block is known but its parent execution payload envelope has not been received yet.
///
/// ## Peer scoring
///
/// It's unclear if this block is valid, but it cannot be fully verified without the parent's
/// execution payload envelope.
ParentEnvelopeUnknown { parent_root: Hash256 },
/// An error occurred while processing the execution payload envelope during range sync.
EnvelopeError(Box<EnvelopeError>),

PayloadEnvelopeError {
e: Box<EnvelopeError>,
penalize_peer: bool,
},
}

/// Which specific signature(s) are invalid in a SignedBeaconBlock
Expand Down Expand Up @@ -486,6 +501,36 @@ impl From<ArithError> for BlockError {
}
}

impl From<EnvelopeError> for BlockError {
fn from(e: EnvelopeError) -> Self {
let penalize_peer = match &e {
// REJECT per spec: peer sent invalid envelope data
EnvelopeError::BadSignature
| EnvelopeError::BuilderIndexMismatch { .. }
| EnvelopeError::BlockHashMismatch { .. }
| EnvelopeError::SlotMismatch { .. }
| EnvelopeError::IncorrectBlockProposer { .. } => true,
// IGNORE per spec: not the peer's fault
EnvelopeError::BlockRootUnknown { .. }
| EnvelopeError::PriorToFinalization { .. }
| EnvelopeError::UnknownValidator { .. } => false,
// Internal errors: not the peer's fault
EnvelopeError::BeaconChainError(_)
| EnvelopeError::BeaconStateError(_)
| EnvelopeError::BlockProcessingError(_)
| EnvelopeError::EnvelopeProcessingError(_)
| EnvelopeError::ExecutionPayloadError(_)
| EnvelopeError::BlockError(_)
| EnvelopeError::InternalError(_)
| EnvelopeError::OptimisticSyncNotSupported { .. } => false,
};
BlockError::PayloadEnvelopeError {
e: Box::new(e),
penalize_peer,
}
}
}

/// Stores information about verifying a payload against an execution engine.
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
pub struct PayloadVerificationOutcome {
Expand Down Expand Up @@ -583,10 +628,17 @@ pub(crate) fn process_block_slash_info<T: BeaconChainTypes, TErr: BlockBlobError
/// The given `chain_segment` must contain only blocks from the same epoch, otherwise an error
/// will be returned.
#[instrument(skip_all)]
#[allow(clippy::type_complexity)]
pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
mut chain_segment: Vec<(Hash256, RangeSyncBlock<T::EthSpec>)>,
chain: &BeaconChain<T>,
) -> Result<Vec<SignatureVerifiedBlock<T>>, BlockError> {
) -> Result<
Vec<(
SignatureVerifiedBlock<T>,
Option<Box<AvailableEnvelope<T::EthSpec>>>,
)>,
BlockError,
> {
if chain_segment.is_empty() {
return Ok(vec![]);
}
Expand Down Expand Up @@ -615,14 +667,29 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
let consensus_context =
ConsensusContext::new(block.slot()).set_current_block_root(block_root);

let available_block = block.into_available_block();
let (available_block, envelope) = match block {
RangeSyncBlock::Base(ab) => (ab, None),
RangeSyncBlock::Gloas { block, envelope } => {
let ab = AvailableBlock::new(
block,
AvailableBlockData::NoData,
&chain.data_availability_checker,
chain.spec.clone(),
)
.map_err(BlockError::AvailabilityCheck)?;
(ab, envelope)
}
};
available_blocks.push(available_block.clone());
signature_verified_blocks.push(SignatureVerifiedBlock {
block: MaybeAvailableBlock::Available(available_block),
block_root,
parent: None,
consensus_context,
});
signature_verified_blocks.push((
SignatureVerifiedBlock {
block: MaybeAvailableBlock::Available(available_block),
block_root,
parent: None,
consensus_context,
},
envelope,
));
}

chain
Expand All @@ -632,7 +699,7 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(
// verify signatures
let pubkey_cache = get_validator_pubkey_cache(chain)?;
let mut signature_verifier = get_signature_verifier(&state, &pubkey_cache, &chain.spec);
for svb in &mut signature_verified_blocks {
for (svb, _) in &mut signature_verified_blocks {
signature_verifier
.include_all_signatures(svb.block.as_block(), &mut svb.consensus_context)?;
}
Expand All @@ -643,7 +710,7 @@ pub fn signature_verify_chain_segment<T: BeaconChainTypes>(

drop(pubkey_cache);

if let Some(signature_verified_block) = signature_verified_blocks.first_mut() {
if let Some((signature_verified_block, _)) = signature_verified_blocks.first_mut() {
signature_verified_block.parent = Some(parent);
}

Expand Down Expand Up @@ -1191,7 +1258,7 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
let result = info_span!("signature_verify").in_scope(|| signature_verifier.verify());
match result {
Ok(_) => {
// gloas blocks are always available.
// Gloas blocks are always available — data arrives via the envelope.
let maybe_available = if chain
.spec
.fork_name_at_slot::<T::EthSpec>(block.slot())
Expand Down Expand Up @@ -1946,6 +2013,22 @@ fn load_parent<T: BeaconChainTypes, B: AsBlock<T::EthSpec>>(
BlockError::from(BeaconChainError::MissingBeaconBlock(block.parent_root()))
})?;

// For post-Gloas parent blocks, the execution payload arrives via the envelope.
// If the parent's execution payload envelope hasn't arrived yet,
// return an unknown parent error so the block gets sent to the
// reprocess queue.
if parent_block.slot() != 0
&& chain
.spec
.fork_name_at_slot::<T::EthSpec>(parent_block.slot())
.gloas_enabled()
{
let _envelope = chain
.store
.get_payload_envelope(&root)?
.ok_or(BlockError::ParentEnvelopeUnknown { parent_root: root })?;
}

// Load the parent block's state from the database, returning an error if it is not found.
// It is an error because if we know the parent block we should also know the parent state.
// Retrieve any state that is advanced through to at most `block.slot()`: this is
Expand Down
Loading
Loading