mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 13:28:33 +00:00
Sidecar inclusion proof (#4900)
* Refactor BlobSidecar to new type * Fix some compile errors * Gossip verification compiles * Fix http api types take 1 * Fix another round of compile errors * Beacon node crate compiles * EF tests compile * Remove all blob signing from VC * fmt * Tests compile * Fix some tests * Fix more http tests * get compiling * Fix gossip conditions and tests * Add basic proof generation and verification * remove unnecessary ssz decode * add back build_sidecar * remove default at fork for blobs * fix beacon chain tests * get relase tests compiling * fix lints * fix existing spec tests * add new ef tests * fix gossip duplicate rule * lints * add back sidecar signature check in gossip * add finalized descendant check to blob sidecar gossip * fix error conversion * fix release tests * sidecar inclusion self review cleanup * Add proof verification and computation metrics * Remove accidentally committed file * Unify some block and blob errors; add slashing conditions for sidecars * Address review comment * Clean up re-org tests (#4957) * Address more review comments * Add Comments & Eliminate Unnecessary Clones * update names * Update beacon_node/beacon_chain/src/metrics.rs Co-authored-by: Jimmy Chen <jchen.tc@gmail.com> * Update beacon_node/network/src/network_beacon_processor/tests.rs Co-authored-by: Jimmy Chen <jchen.tc@gmail.com> * pr feedback * fix test compile * Sidecar Inclusion proof small refactor and updates (#4967) * Update some comments, variables and small cosmetic fixes. * Couple blobs and proofs into a tuple in `PayloadAndBlobs` for simplicity and safety. * Update function comment. * Update testing/ef_tests/src/cases/merkle_proof_validity.rs Co-authored-by: Jimmy Chen <jchen.tc@gmail.com> * Rename the block and blob wrapper types used in the beacon API interfaces. * make sure gossip invalid blobs are passed to the slasher (#4970) * Add blob headers to slasher before adding to DA checker * Replace Vec with HashSet in BlockQueue * fmt * Rename gindex -> index * Simplify gossip condition --------- Co-authored-by: realbigsean <seananderson33@gmail.com> Co-authored-by: realbigsean <sean@sigmaprime.io> Co-authored-by: Michael Sproul <michael@sigmaprime.io> Co-authored-by: Mark Mackey <mark@sigmaprime.io> Co-authored-by: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
@@ -70,7 +70,7 @@ use crate::{
|
||||
metrics, BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||
};
|
||||
use derivative::Derivative;
|
||||
use eth2::types::{EventKind, SignedBlockContents};
|
||||
use eth2::types::{EventKind, PublishBlockRequest};
|
||||
use execution_layer::PayloadStatus;
|
||||
pub use fork_choice::{AttestationFromBlock, PayloadVerificationStatus};
|
||||
use parking_lot::RwLockReadGuard;
|
||||
@@ -95,15 +95,15 @@ use std::fs;
|
||||
use std::io::Write;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use store::{Error as DBError, HotStateSummary, KeyValueStore, SignedBlobSidecarList, StoreOp};
|
||||
use store::{Error as DBError, HotStateSummary, KeyValueStore, StoreOp};
|
||||
use task_executor::JoinHandle;
|
||||
use tree_hash::TreeHash;
|
||||
use types::ExecPayload;
|
||||
use types::{
|
||||
BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, CloneConfig, Epoch, EthSpec,
|
||||
ExecutionBlockHash, Hash256, InconsistentFork, PublicKey, PublicKeyBytes, RelativeEpoch,
|
||||
SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
|
||||
};
|
||||
use types::{BlobSidecar, ExecPayload};
|
||||
|
||||
pub const POS_PANDA_BANNER: &str = r#"
|
||||
,,, ,,, ,,, ,,,
|
||||
@@ -507,7 +507,7 @@ pub enum BlockSlashInfo<TErr> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BlockSlashInfo<BlockError<E>> {
|
||||
pub fn from_early_error(header: SignedBeaconBlockHeader, e: BlockError<E>) -> Self {
|
||||
pub fn from_early_error_block(header: SignedBeaconBlockHeader, e: BlockError<E>) -> Self {
|
||||
match e {
|
||||
BlockError::ProposalSignatureInvalid => BlockSlashInfo::SignatureInvalid(e),
|
||||
// `InvalidSignature` could indicate any signature in the block, so we want
|
||||
@@ -517,17 +517,28 @@ impl<E: EthSpec> BlockSlashInfo<BlockError<E>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> BlockSlashInfo<GossipBlobError<E>> {
|
||||
pub fn from_early_error_blob(header: SignedBeaconBlockHeader, e: GossipBlobError<E>) -> Self {
|
||||
match e {
|
||||
GossipBlobError::ProposalSignatureInvalid => BlockSlashInfo::SignatureInvalid(e),
|
||||
// `InvalidSignature` could indicate any signature in the block, so we want
|
||||
// to recheck the proposer signature alone.
|
||||
_ => BlockSlashInfo::SignatureNotChecked(header, e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process invalid blocks to see if they are suitable for the slasher.
|
||||
///
|
||||
/// If no slasher is configured, this is a no-op.
|
||||
fn process_block_slash_info<T: BeaconChainTypes>(
|
||||
pub(crate) fn process_block_slash_info<T: BeaconChainTypes, TErr: BlockBlobError>(
|
||||
chain: &BeaconChain<T>,
|
||||
slash_info: BlockSlashInfo<BlockError<T::EthSpec>>,
|
||||
) -> BlockError<T::EthSpec> {
|
||||
slash_info: BlockSlashInfo<TErr>,
|
||||
) -> TErr {
|
||||
if let Some(slasher) = chain.slasher.as_ref() {
|
||||
let (verified_header, error) = match slash_info {
|
||||
BlockSlashInfo::SignatureNotChecked(header, e) => {
|
||||
if verify_header_signature(chain, &header).is_ok() {
|
||||
if verify_header_signature::<_, TErr>(chain, &header).is_ok() {
|
||||
(header, e)
|
||||
} else {
|
||||
return e;
|
||||
@@ -673,7 +684,6 @@ pub trait IntoGossipVerifiedBlockContents<T: BeaconChainTypes>: Sized {
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<GossipVerifiedBlockContents<T>, BlockContentsError<T::EthSpec>>;
|
||||
fn inner_block(&self) -> &SignedBeaconBlock<T::EthSpec>;
|
||||
fn inner_blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>>;
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoGossipVerifiedBlockContents<T> for GossipVerifiedBlockContents<T> {
|
||||
@@ -686,45 +696,40 @@ impl<T: BeaconChainTypes> IntoGossipVerifiedBlockContents<T> for GossipVerifiedB
|
||||
fn inner_block(&self) -> &SignedBeaconBlock<T::EthSpec> {
|
||||
self.0.block.as_block()
|
||||
}
|
||||
fn inner_blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>> {
|
||||
self.1.as_ref().map(|blobs| {
|
||||
VariableList::from(
|
||||
blobs
|
||||
.into_iter()
|
||||
.map(GossipVerifiedBlob::signed_blob)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes> IntoGossipVerifiedBlockContents<T> for SignedBlockContents<T::EthSpec> {
|
||||
impl<T: BeaconChainTypes> IntoGossipVerifiedBlockContents<T> for PublishBlockRequest<T::EthSpec> {
|
||||
fn into_gossip_verified_block(
|
||||
self,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<GossipVerifiedBlockContents<T>, BlockContentsError<T::EthSpec>> {
|
||||
let (block, blobs) = self.deconstruct();
|
||||
let gossip_verified_block = GossipVerifiedBlock::new(Arc::new(block), chain)?;
|
||||
|
||||
let gossip_verified_blobs = blobs
|
||||
.map(|blobs| {
|
||||
Ok::<_, GossipBlobError<T::EthSpec>>(VariableList::from(
|
||||
blobs
|
||||
.into_iter()
|
||||
.map(|blob| GossipVerifiedBlob::new(blob, chain))
|
||||
.collect::<Result<Vec<_>, GossipBlobError<T::EthSpec>>>()?,
|
||||
))
|
||||
.map(|(kzg_proofs, blobs)| {
|
||||
let mut gossip_verified_blobs = vec![];
|
||||
for (i, (kzg_proof, blob)) in kzg_proofs.iter().zip(blobs).enumerate() {
|
||||
let _timer =
|
||||
metrics::start_timer(&metrics::BLOB_SIDECAR_INCLUSION_PROOF_COMPUTATION);
|
||||
let blob = BlobSidecar::new(i, blob, &block, *kzg_proof)
|
||||
.map_err(BlockContentsError::SidecarError)?;
|
||||
drop(_timer);
|
||||
let gossip_verified_blob =
|
||||
GossipVerifiedBlob::new(Arc::new(blob), i as u64, chain)?;
|
||||
gossip_verified_blobs.push(gossip_verified_blob);
|
||||
}
|
||||
let gossip_verified_blobs = VariableList::from(gossip_verified_blobs);
|
||||
Ok::<_, BlockContentsError<T::EthSpec>>(gossip_verified_blobs)
|
||||
})
|
||||
.transpose()?;
|
||||
let gossip_verified_block = GossipVerifiedBlock::new(Arc::new(block), chain)?;
|
||||
|
||||
Ok((gossip_verified_block, gossip_verified_blobs))
|
||||
}
|
||||
|
||||
fn inner_block(&self) -> &SignedBeaconBlock<T::EthSpec> {
|
||||
self.signed_block()
|
||||
}
|
||||
|
||||
fn inner_blobs(&self) -> Option<SignedBlobSidecarList<T::EthSpec>> {
|
||||
self.blobs_cloned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implemented on types that can be converted into a `ExecutionPendingBlock`.
|
||||
@@ -745,7 +750,9 @@ pub trait IntoExecutionPendingBlock<T: BeaconChainTypes>: Sized {
|
||||
}
|
||||
execution_pending
|
||||
})
|
||||
.map_err(|slash_info| process_block_slash_info(chain, slash_info))
|
||||
.map_err(|slash_info| {
|
||||
process_block_slash_info::<_, BlockError<T::EthSpec>>(chain, slash_info)
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert the block to fully-verified form while producing data to aid checking slashability.
|
||||
@@ -774,7 +781,10 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
|
||||
// and it could be a repeat proposal (a likely cause for slashing!).
|
||||
let header = block.signed_block_header();
|
||||
Self::new_without_slasher_checks(block, chain).map_err(|e| {
|
||||
process_block_slash_info(chain, BlockSlashInfo::from_early_error(header, e))
|
||||
process_block_slash_info::<_, BlockError<T::EthSpec>>(
|
||||
chain,
|
||||
BlockSlashInfo::from_early_error_block(header, e),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1055,7 +1065,8 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Self, BlockSlashInfo<BlockError<T::EthSpec>>> {
|
||||
let header = block.signed_block_header();
|
||||
Self::new(block, block_root, chain).map_err(|e| BlockSlashInfo::from_early_error(header, e))
|
||||
Self::new(block, block_root, chain)
|
||||
.map_err(|e| BlockSlashInfo::from_early_error_block(header, e))
|
||||
}
|
||||
|
||||
/// Finishes signature verification on the provided `GossipVerifedBlock`. Does not re-verify
|
||||
@@ -1109,7 +1120,7 @@ impl<T: BeaconChainTypes> SignatureVerifiedBlock<T> {
|
||||
) -> Result<Self, BlockSlashInfo<BlockError<T::EthSpec>>> {
|
||||
let header = from.block.signed_block_header();
|
||||
Self::from_gossip_verified_block(from, chain)
|
||||
.map_err(|e| BlockSlashInfo::from_early_error(header, e))
|
||||
.map_err(|e| BlockSlashInfo::from_early_error_block(header, e))
|
||||
}
|
||||
|
||||
pub fn block_root(&self) -> Hash256 {
|
||||
@@ -1908,28 +1919,45 @@ fn load_parent<T: BeaconChainTypes, B: AsBlock<T::EthSpec>>(
|
||||
result
|
||||
}
|
||||
|
||||
/// This trait is used to unify `BlockError` and `BlobError` so
|
||||
/// `cheap_state_advance_to_obtain_committees` can be re-used in gossip blob validation.
|
||||
pub trait CheapStateAdvanceError: From<BeaconStateError> + From<BeaconChainError> + Debug {
|
||||
/// This trait is used to unify `BlockError` and `GossipBlobError`.
|
||||
pub trait BlockBlobError: From<BeaconStateError> + From<BeaconChainError> + Debug {
|
||||
fn not_later_than_parent_error(block_slot: Slot, state_slot: Slot) -> Self;
|
||||
fn unknown_validator_error(validator_index: u64) -> Self;
|
||||
fn proposer_signature_invalid() -> Self;
|
||||
}
|
||||
|
||||
impl<E: EthSpec> CheapStateAdvanceError for BlockError<E> {
|
||||
impl<E: EthSpec> BlockBlobError for BlockError<E> {
|
||||
fn not_later_than_parent_error(block_slot: Slot, parent_slot: Slot) -> Self {
|
||||
BlockError::BlockIsNotLaterThanParent {
|
||||
block_slot,
|
||||
parent_slot,
|
||||
}
|
||||
}
|
||||
|
||||
fn unknown_validator_error(validator_index: u64) -> Self {
|
||||
BlockError::UnknownValidator(validator_index)
|
||||
}
|
||||
|
||||
fn proposer_signature_invalid() -> Self {
|
||||
BlockError::ProposalSignatureInvalid
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> CheapStateAdvanceError for GossipBlobError<E> {
|
||||
impl<E: EthSpec> BlockBlobError for GossipBlobError<E> {
|
||||
fn not_later_than_parent_error(blob_slot: Slot, parent_slot: Slot) -> Self {
|
||||
GossipBlobError::BlobIsNotLaterThanParent {
|
||||
blob_slot,
|
||||
parent_slot,
|
||||
}
|
||||
}
|
||||
|
||||
fn unknown_validator_error(validator_index: u64) -> Self {
|
||||
GossipBlobError::UnknownValidator(validator_index)
|
||||
}
|
||||
|
||||
fn proposer_signature_invalid() -> Self {
|
||||
GossipBlobError::ProposalSignatureInvalid
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a cheap (time-efficient) state advancement so the committees and proposer shuffling for
|
||||
@@ -1943,7 +1971,7 @@ impl<E: EthSpec> CheapStateAdvanceError for GossipBlobError<E> {
|
||||
/// and `Cow::Borrowed(state)` will be returned. Otherwise, the state will be cloned, cheaply
|
||||
/// advanced and then returned as a `Cow::Owned`. The end result is that the given `state` is never
|
||||
/// mutated to be invalid (in fact, it is never changed beyond a simple committee cache build).
|
||||
pub fn cheap_state_advance_to_obtain_committees<'a, E: EthSpec, Err: CheapStateAdvanceError>(
|
||||
pub fn cheap_state_advance_to_obtain_committees<'a, E: EthSpec, Err: BlockBlobError>(
|
||||
state: &'a mut BeaconState<E>,
|
||||
state_root_opt: Option<Hash256>,
|
||||
block_slot: Slot,
|
||||
@@ -1979,12 +2007,11 @@ pub fn cheap_state_advance_to_obtain_committees<'a, E: EthSpec, Err: CheapStateA
|
||||
/// Obtains a read-locked `ValidatorPubkeyCache` from the `chain`.
|
||||
pub fn get_validator_pubkey_cache<T: BeaconChainTypes>(
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<RwLockReadGuard<ValidatorPubkeyCache<T>>, BlockError<T::EthSpec>> {
|
||||
) -> Result<RwLockReadGuard<ValidatorPubkeyCache<T>>, BeaconChainError> {
|
||||
chain
|
||||
.validator_pubkey_cache
|
||||
.try_read_for(VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT)
|
||||
.ok_or(BeaconChainError::ValidatorPubkeyCacheLockTimeout)
|
||||
.map_err(BlockError::BeaconChainError)
|
||||
}
|
||||
|
||||
/// Produces an _empty_ `BlockSignatureVerifier`.
|
||||
@@ -2025,14 +2052,14 @@ fn get_signature_verifier<'a, T: BeaconChainTypes>(
|
||||
/// Verify that `header` was signed with a valid signature from its proposer.
|
||||
///
|
||||
/// Return `Ok(())` if the signature is valid, and an `Err` otherwise.
|
||||
fn verify_header_signature<T: BeaconChainTypes>(
|
||||
fn verify_header_signature<T: BeaconChainTypes, Err: BlockBlobError>(
|
||||
chain: &BeaconChain<T>,
|
||||
header: &SignedBeaconBlockHeader,
|
||||
) -> Result<(), BlockError<T::EthSpec>> {
|
||||
) -> Result<(), Err> {
|
||||
let proposer_pubkey = get_validator_pubkey_cache(chain)?
|
||||
.get(header.message.proposer_index as usize)
|
||||
.cloned()
|
||||
.ok_or(BlockError::UnknownValidator(header.message.proposer_index))?;
|
||||
.ok_or(Err::unknown_validator_error(header.message.proposer_index))?;
|
||||
let head_fork = chain.canonical_head.cached_head().head_fork();
|
||||
|
||||
if header.verify_signature::<T::EthSpec>(
|
||||
@@ -2043,7 +2070,7 @@ fn verify_header_signature<T: BeaconChainTypes>(
|
||||
) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BlockError::ProposalSignatureInvalid)
|
||||
Err(Err::proposer_signature_invalid())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user