mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-08 01:05:47 +00:00
Add gossip verification conditions
This commit is contained in:
@@ -4,14 +4,16 @@ use std::sync::Arc;
|
|||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use ssz_types::VariableList;
|
use ssz_types::VariableList;
|
||||||
|
|
||||||
use crate::beacon_chain::{BeaconChain, BeaconChainTypes, MAXIMUM_GOSSIP_CLOCK_DISPARITY};
|
use crate::beacon_chain::{
|
||||||
use crate::block_verification::PayloadVerificationOutcome;
|
BeaconChain, BeaconChainTypes, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
|
||||||
use crate::{kzg_utils, BeaconChainError, BlockError};
|
VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT,
|
||||||
|
};
|
||||||
|
use crate::BeaconChainError;
|
||||||
use state_processing::per_block_processing::eip4844::eip4844::verify_kzg_commitments_against_transactions;
|
use state_processing::per_block_processing::eip4844::eip4844::verify_kzg_commitments_against_transactions;
|
||||||
use types::{
|
use types::{
|
||||||
BeaconBlockRef, BeaconStateError, BlobsSidecar, EthSpec, Hash256, KzgCommitment,
|
BeaconBlockRef, BeaconStateError, BlobsSidecar, EthSpec, Hash256, KzgCommitment,
|
||||||
SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
|
SignedBeaconBlock, SignedBeaconBlockAndBlobsSidecar, SignedBeaconBlockHeader,
|
||||||
Transactions,
|
SignedBlobSidecar, Slot, Transactions,
|
||||||
};
|
};
|
||||||
use types::{Epoch, ExecPayload};
|
use types::{Epoch, ExecPayload};
|
||||||
use types::blob_sidecar::BlobSidecar;
|
use types::blob_sidecar::BlobSidecar;
|
||||||
@@ -65,6 +67,52 @@ pub enum BlobError {
|
|||||||
UnavailableBlobs,
|
UnavailableBlobs,
|
||||||
/// Blobs provided for a pre-Eip4844 fork.
|
/// Blobs provided for a pre-Eip4844 fork.
|
||||||
InconsistentFork,
|
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),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BeaconChainError> for BlobError {
|
impl From<BeaconChainError> for BlobError {
|
||||||
@@ -79,34 +127,125 @@ impl From<BeaconStateError> for BlobError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_blob_for_gossip<T: BeaconChainTypes>(
|
pub fn validate_blob_sidecar_for_gossip<T: BeaconChainTypes>(
|
||||||
block_wrapper: BlockWrapper<T::EthSpec>,
|
blob_sidecar: SignedBlobSidecar<T::EthSpec>,
|
||||||
block_root: Hash256,
|
subnet: u64,
|
||||||
chain: &BeaconChain<T>,
|
chain: &BeaconChain<T>,
|
||||||
) -> Result<AvailabilityPendingBlock<T::EthSpec>, BlobError> {
|
) -> Result<(), BlobError> {
|
||||||
if let BlockWrapper::BlockAndBlobs(ref block, ref blobs_sidecar) = block_wrapper {
|
let blob_slot = blob_sidecar.message.slot;
|
||||||
let blob_slot = blobs_sidecar.beacon_block_slot;
|
let blob_index = blob_sidecar.message.index;
|
||||||
// Do not gossip or process blobs from future or past slots.
|
let block_root = 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
|
let latest_permissible_slot = chain
|
||||||
.slot_clock
|
.slot_clock
|
||||||
.now_with_future_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
|
.now_with_future_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
|
||||||
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
.ok_or(BeaconChainError::UnableToReadSlot)?;
|
||||||
if blob_slot > latest_permissible_slot {
|
if blob_slot > latest_permissible_slot {
|
||||||
return Err(BlobError::FutureSlot {
|
return Err(BlobError::FutureSlot {
|
||||||
message_slot: latest_permissible_slot,
|
message_slot: blob_slot,
|
||||||
latest_permissible_slot: blob_slot,
|
latest_permissible_slot,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if blob_slot != block.slot() {
|
// TODO(pawan): Verify not from a past slot?
|
||||||
return Err(BlobError::SlotMismatch {
|
|
||||||
|
// 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,
|
blob_slot,
|
||||||
block_slot: block.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 = 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)
|
||||||
|
{
|
||||||
|
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 = blob_sidecar.message.proposer_index;
|
||||||
|
if proposer_index != blob_proposer_index {
|
||||||
|
return Err(BlobError::ProposerIndexMismatch {
|
||||||
|
sidecar: blob_proposer_index,
|
||||||
|
local: proposer_index,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
block_wrapper.into_availablilty_pending_block(block_root, chain)
|
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 as usize)
|
||||||
|
.ok_or_else(|| BlobError::UnknownValidator(proposer_index as u64))?;
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
// TODO(pawan): potentially add to a seen cache at this point.
|
||||||
|
|
||||||
|
// 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?
|
||||||
|
|
||||||
|
if block_opt.is_none() {
|
||||||
|
return Err(BlobError::UnknownHeadBlock {
|
||||||
|
beacon_block_root: block_root,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_data_availability<T: BeaconChainTypes>(
|
pub fn verify_data_availability<T: BeaconChainTypes>(
|
||||||
|
|||||||
Reference in New Issue
Block a user