mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-05 13:54:36 +00:00
block verification changes
This commit is contained in:
@@ -57,7 +57,9 @@ use crate::observed_block_producers::ObservedBlockProducers;
|
||||
use crate::observed_data_sidecars::ObservedDataSidecars;
|
||||
use crate::observed_operations::{ObservationOutcome, ObservedOperations};
|
||||
use crate::observed_slashable::ObservedSlashable;
|
||||
use crate::payload_envelope_verification::{EnvelopeError, ExecutedEnvelope, ExecutionPendingEnvelope};
|
||||
use crate::payload_envelope_verification::{
|
||||
EnvelopeError, ExecutedEnvelope, ExecutionPendingEnvelope,
|
||||
};
|
||||
use crate::pending_payload_envelopes::PendingPayloadEnvelopes;
|
||||
use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||
use crate::persisted_custody::persist_custody_context;
|
||||
@@ -3399,11 +3401,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
);
|
||||
}
|
||||
|
||||
self.data_availability_checker.put_pre_execution_block(
|
||||
block_root,
|
||||
unverified_block.block_cloned(),
|
||||
block_source,
|
||||
)?;
|
||||
// GLOAS blocks don't need DA checking - they are always available from the
|
||||
// block's perspective. Skip inserting into the DA cache.
|
||||
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);
|
||||
|
||||
@@ -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,
|
||||
@@ -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 penalised.
|
||||
BidParentRootMismatch {
|
||||
bid_parent_root: Hash256,
|
||||
block_parent_root: Hash256,
|
||||
},
|
||||
}
|
||||
|
||||
/// Which specific signature(s) are invalid in a SignedBeaconBlock
|
||||
@@ -888,7 +899,19 @@ 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() {
|
||||
// GLOAS: check bid's commitments; pre-GLOAS: check body's commitments.
|
||||
if let Ok(bid) = block.message().body().signed_execution_payload_bid() {
|
||||
let max_blobs_at_epoch = chain
|
||||
.spec
|
||||
.max_blobs_per_block(block.slot().epoch(T::EthSpec::slots_per_epoch()))
|
||||
as usize;
|
||||
if bid.message.blob_kzg_commitments.len() > max_blobs_at_epoch {
|
||||
return Err(BlockError::InvalidBlobCount {
|
||||
max_blobs_at_epoch,
|
||||
block: bid.message.blob_kzg_commitments.len(),
|
||||
});
|
||||
}
|
||||
} else if let Ok(commitments) = block.message().body().blob_kzg_commitments() {
|
||||
let max_blobs_at_epoch = chain
|
||||
.spec
|
||||
.max_blobs_per_block(block.slot().epoch(T::EthSpec::slots_per_epoch()))
|
||||
@@ -933,6 +956,32 @@ 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)?;
|
||||
|
||||
// GLOAS: Verify bid.parent_block_root matches block.parent_root.
|
||||
if let Ok(bid) = block.message().body().signed_execution_payload_bid() {
|
||||
if 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(),
|
||||
});
|
||||
}
|
||||
|
||||
// GLOAS: Check if the execution payload parent (bid.parent_block_hash) has been
|
||||
// verified by the EL. If verified and found invalid, reject.
|
||||
if let Some(beacon_root) = fork_choice_read_lock
|
||||
.proto_array()
|
||||
.execution_block_hash_to_beacon_block_root(&bid.message.parent_block_hash)
|
||||
{
|
||||
if let Some(parent_payload_block) = fork_choice_read_lock.get_block(&beacon_root) {
|
||||
if parent_payload_block.execution_status.is_invalid() {
|
||||
return Err(BlockError::ParentExecutionPayloadInvalid {
|
||||
parent_root: beacon_root,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drop(fork_choice_read_lock);
|
||||
|
||||
// Track the number of skip slots between the block and its parent.
|
||||
@@ -1212,15 +1261,32 @@ 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 "data available" from the block's perspective
|
||||
// (the execution payload arrives separately via the payload envelope).
|
||||
let maybe_available = if block.fork_name_unchecked().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,
|
||||
)),
|
||||
|
||||
@@ -62,7 +62,11 @@ 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 carry an execution payload in the block body.
|
||||
// Execution verification happens via the payload envelope pipeline.
|
||||
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
|
||||
@@ -304,6 +308,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
|
||||
|
||||
@@ -2,7 +2,10 @@ use std::sync::Arc;
|
||||
|
||||
use educe::Educe;
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::{VerifySignatures, envelope_processing::{VerifyStateRoot, process_execution_payload_envelope}};
|
||||
use state_processing::{
|
||||
VerifySignatures,
|
||||
envelope_processing::{VerifyStateRoot, process_execution_payload_envelope},
|
||||
};
|
||||
use tracing::{Span, debug};
|
||||
use types::{
|
||||
EthSpec, SignedBeaconBlock, SignedExecutionPayloadEnvelope,
|
||||
|
||||
@@ -1359,7 +1359,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(
|
||||
@@ -1493,19 +1494,22 @@ 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",
|
||||
);
|
||||
// GLOAS blocks don't carry blobs; the execution payload arrives separately.
|
||||
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
|
||||
|
||||
@@ -643,10 +643,7 @@ where
|
||||
|
||||
/// Register that a valid execution payload envelope has been received for `block_root`,
|
||||
/// updating the node's `payload_status` from PENDING to FULL.
|
||||
pub fn on_execution_payload(
|
||||
&mut self,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), Error<T::Error>> {
|
||||
pub fn on_execution_payload(&mut self, block_root: Hash256) -> Result<(), Error<T::Error>> {
|
||||
self.proto_array
|
||||
.on_execution_payload(block_root)
|
||||
.map_err(Error::FailedToProcessValidExecutionPayload)
|
||||
|
||||
@@ -383,10 +383,7 @@ impl ProtoArray {
|
||||
/// Updates the node's `payload_status` from `PENDING` to `FULL`.
|
||||
///
|
||||
/// Returns an error if the block is unknown to fork choice.
|
||||
pub fn on_execution_payload(
|
||||
&mut self,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), Error> {
|
||||
pub fn on_execution_payload(&mut self, block_root: Hash256) -> Result<(), Error> {
|
||||
let index = self
|
||||
.indices
|
||||
.get(&block_root)
|
||||
|
||||
@@ -491,10 +491,7 @@ impl ProtoArrayForkChoice {
|
||||
/// Register that a valid execution payload envelope has been received for `block_root`.
|
||||
///
|
||||
/// See `ProtoArray::on_execution_payload` for documentation.
|
||||
pub fn on_execution_payload(
|
||||
&mut self,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), String> {
|
||||
pub fn on_execution_payload(&mut self, block_root: Hash256) -> Result<(), String> {
|
||||
self.proto_array
|
||||
.on_execution_payload(block_root)
|
||||
.map_err(|e| format!("on_execution_payload error: {:?}", e))
|
||||
@@ -899,6 +896,16 @@ impl ProtoArrayForkChoice {
|
||||
Some(block.execution_status)
|
||||
}
|
||||
|
||||
/// Returns the first *beacon block root* which contains an execution payload with the given
|
||||
/// `block_hash`, if any.
|
||||
pub fn execution_block_hash_to_beacon_block_root(
|
||||
&self,
|
||||
block_hash: &ExecutionBlockHash,
|
||||
) -> Option<Hash256> {
|
||||
self.proto_array
|
||||
.execution_block_hash_to_beacon_block_root(block_hash)
|
||||
}
|
||||
|
||||
/// Returns the weight of a given block.
|
||||
pub fn get_weight(&self, block_root: &Hash256) -> Option<u64> {
|
||||
let block_index = self.proto_array.indices.get(block_root)?;
|
||||
|
||||
Reference in New Issue
Block a user