From 02a88f07049c7f6d5de91542050f0b1d0006af69 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Wed, 15 Mar 2023 15:15:46 +1100 Subject: [PATCH] Add KZG proof and blob validation --- beacon_node/beacon_chain/src/beacon_chain.rs | 82 +++++++++++++++---- beacon_node/beacon_chain/src/errors.rs | 1 + beacon_node/beacon_chain/src/kzg_utils.rs | 5 +- .../lighthouse_network/src/types/topics.rs | 1 - beacon_node/store/src/hot_cold_store.rs | 4 +- crypto/kzg/src/kzg_commitment.rs | 2 +- 6 files changed, 71 insertions(+), 24 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 277194105..57c685325 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -73,6 +73,7 @@ use fork_choice::{ use futures::channel::mpsc::Sender; use itertools::process_results; use itertools::Itertools; +use kzg::Kzg; use operation_pool::{AttestationRef, OperationPool, PersistedOperationPool, ReceivedPreCapella}; use parking_lot::{Mutex, RwLock}; use proto_array::{CountUnrealizedFull, DoNotReOrg, ProposerHeadError}; @@ -107,6 +108,7 @@ use store::{ use task_executor::{ShutdownReason, TaskExecutor}; use tree_hash::TreeHash; use types::beacon_state::CloneConfig; +use types::blobs_sidecar::KzgCommitments; use types::consts::eip4844::MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS; use types::consts::merge::INTERVALS_PER_SLOT; use types::*; @@ -4760,39 +4762,60 @@ impl BeaconChain { .as_ref() .ok_or(BlockProductionError::TrustedSetupNotInitialized)?; let beacon_block_root = block.canonical_root(); - let expected_kzg_commitments = block.body().blob_kzg_commitments().map_err(|_| { - BlockProductionError::InvalidBlockVariant( - "EIP4844 block does not contain kzg commitments".to_string(), - ) - })?; + let expected_kzg_commitments: &KzgCommitments = + block.body().blob_kzg_commitments().map_err(|_| { + BlockProductionError::InvalidBlockVariant( + "EIP4844 block does not contain kzg commitments".to_string(), + ) + })?; + + if expected_kzg_commitments.len() != blobs.len() { + return Err(BlockProductionError::MissingKzgCommitment(format!( + "Missing KZG commitment for slot {}. Expected {}, got: {}", + slot, + blobs.len(), + expected_kzg_commitments.len() + ))); + } + + let kzg_proofs = + Self::compute_blob_kzg_proofs(kzg, &blobs, expected_kzg_commitments, slot)?; + + kzg_utils::validate_blobs::( + kzg, + expected_kzg_commitments, + &blobs, + &kzg_proofs, + ) + .map_err(BlockProductionError::KzgError)?; let blob_sidecars = VariableList::from( blobs .into_iter() .enumerate() .map(|(blob_index, blob)| { - BlobSidecar { + let kzg_commitment = expected_kzg_commitments + .get(blob_index) + .expect("KZG commitment should exist for blob"); + + let kzg_proof = kzg_proofs + .get(blob_index) + .expect("KZG proof should exist for blob"); + + Ok(BlobSidecar { block_root: beacon_block_root, index: blob_index as u64, slot, block_parent_root: block.parent_root(), proposer_index, blob, - kzg_commitment: expected_kzg_commitments[blob_index].clone(), - kzg_proof: Default::default(), // TODO: compute KZG proof - } + kzg_commitment: *kzg_commitment, + kzg_proof: *kzg_proof, + }) }) - .collect::>>(), + .collect::>, BlockProductionError>>()?, ); - // TODO: validate blobs - // kzg_utils::validate_blobs_sidecar( - // kzg, - // slot, - // beacon_block_root, - // expected_kzg_commitments, - // &blobs_sidecar, - // ).map_err(BlockProductionError::KzgError)?; self.blob_cache.put(beacon_block_root, blob_sidecars); } @@ -4809,6 +4832,29 @@ impl BeaconChain { Ok((block, state)) } + fn compute_blob_kzg_proofs( + kzg: &Arc, + blobs: &Blobs<::EthSpec>, + expected_kzg_commitments: &KzgCommitments<::EthSpec>, + slot: Slot, + ) -> Result, BlockProductionError> { + blobs + .iter() + .enumerate() + .map(|(blob_index, blob)| { + let kzg_commitment = expected_kzg_commitments.get(blob_index).ok_or( + BlockProductionError::MissingKzgCommitment(format!( + "Missing KZG commitment for slot {} blob index {}", + slot, blob_index + )), + )?; + + kzg_utils::compute_blob_kzg_proof::(kzg, blob, kzg_commitment.clone()) + .map_err(BlockProductionError::KzgError) + }) + .collect::, BlockProductionError>>() + } + /// This method must be called whenever an execution engine indicates that a payload is /// invalid. /// diff --git a/beacon_node/beacon_chain/src/errors.rs b/beacon_node/beacon_chain/src/errors.rs index 8f9aa0293..911fe1d5b 100644 --- a/beacon_node/beacon_chain/src/errors.rs +++ b/beacon_node/beacon_chain/src/errors.rs @@ -273,6 +273,7 @@ pub enum BlockProductionError { ShuttingDown, MissingSyncAggregate, MissingExecutionPayload, + MissingKzgCommitment(String), TokioJoin(tokio::task::JoinError), BeaconChain(BeaconChainError), InvalidPayloadFork, diff --git a/beacon_node/beacon_chain/src/kzg_utils.rs b/beacon_node/beacon_chain/src/kzg_utils.rs index 3536420a1..19788a1b4 100644 --- a/beacon_node/beacon_chain/src/kzg_utils.rs +++ b/beacon_node/beacon_chain/src/kzg_utils.rs @@ -42,10 +42,11 @@ pub fn validate_blobs( /// Compute the kzg proof given an ssz blob and its kzg commitment. pub fn compute_blob_kzg_proof( kzg: &Kzg, - blob: Blob, + blob: &Blob, kzg_commitment: KzgCommitment, ) -> Result { - kzg.compute_blob_kzg_proof(ssz_blob_to_crypto_blob::(blob), kzg_commitment) + // Avoid this blob clone + kzg.compute_blob_kzg_proof(ssz_blob_to_crypto_blob::(blob.clone()), kzg_commitment) } /// Compute the kzg commitment for a given blob. diff --git a/beacon_node/lighthouse_network/src/types/topics.rs b/beacon_node/lighthouse_network/src/types/topics.rs index 88764fb95..d9deaaf05 100644 --- a/beacon_node/lighthouse_network/src/types/topics.rs +++ b/beacon_node/lighthouse_network/src/types/topics.rs @@ -314,7 +314,6 @@ mod tests { VoluntaryExit, ProposerSlashing, AttesterSlashing, - BeaconBlocksAndBlobsSidecar, ] .iter() { diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 9762a0f59..60e2f7759 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -259,7 +259,7 @@ impl HotColdDB, LevelDB> { db.blobs_db = Some(LevelDB::open(path.as_path())?); } } - let blob_info = blob_info.unwrap_or(db.get_blob_info()); + let blob_info = blob_info.unwrap_or_else(|| db.get_blob_info()); db.compare_and_set_blob_info_with_write(blob_info, new_blob_info)?; info!( db.log, @@ -1899,7 +1899,7 @@ impl, Cold: ItemStore> HotColdDB let blob_info = self.get_blob_info(); let oldest_blob_slot = blob_info .oldest_blob_slot - .unwrap_or(eip4844_fork.start_slot(E::slots_per_epoch())); + .unwrap_or_else(|| eip4844_fork.start_slot(E::slots_per_epoch())); // The last entirely pruned epoch, blobs sidecar pruning may have stopped early in the // middle of an epoch otherwise the oldest blob slot is a start slot. diff --git a/crypto/kzg/src/kzg_commitment.rs b/crypto/kzg/src/kzg_commitment.rs index 3a18fe8b5..0f2725b75 100644 --- a/crypto/kzg/src/kzg_commitment.rs +++ b/crypto/kzg/src/kzg_commitment.rs @@ -8,7 +8,7 @@ use std::fmt::{Debug, Display, Formatter}; use std::str::FromStr; use tree_hash::{PackedEncoding, TreeHash}; -#[derive(Derivative, Clone, Encode, Decode)] +#[derive(Derivative, Clone, Copy, Encode, Decode)] #[derivative(PartialEq, Eq, Hash)] #[ssz(struct_behaviour = "transparent")] pub struct KzgCommitment(pub [u8; BYTES_PER_COMMITMENT]);