Add KZG proof and blob validation

This commit is contained in:
Jimmy Chen 2023-03-15 15:15:46 +11:00
parent 62627d984c
commit 02a88f0704
No known key found for this signature in database
GPG Key ID: 7AAEE02659DCF690
6 changed files with 71 additions and 24 deletions

View File

@ -73,6 +73,7 @@ use fork_choice::{
use futures::channel::mpsc::Sender; use futures::channel::mpsc::Sender;
use itertools::process_results; use itertools::process_results;
use itertools::Itertools; use itertools::Itertools;
use kzg::Kzg;
use operation_pool::{AttestationRef, OperationPool, PersistedOperationPool, ReceivedPreCapella}; use operation_pool::{AttestationRef, OperationPool, PersistedOperationPool, ReceivedPreCapella};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use proto_array::{CountUnrealizedFull, DoNotReOrg, ProposerHeadError}; use proto_array::{CountUnrealizedFull, DoNotReOrg, ProposerHeadError};
@ -107,6 +108,7 @@ use store::{
use task_executor::{ShutdownReason, TaskExecutor}; use task_executor::{ShutdownReason, TaskExecutor};
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::beacon_state::CloneConfig; use types::beacon_state::CloneConfig;
use types::blobs_sidecar::KzgCommitments;
use types::consts::eip4844::MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS; use types::consts::eip4844::MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS;
use types::consts::merge::INTERVALS_PER_SLOT; use types::consts::merge::INTERVALS_PER_SLOT;
use types::*; use types::*;
@ -4760,39 +4762,60 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.as_ref() .as_ref()
.ok_or(BlockProductionError::TrustedSetupNotInitialized)?; .ok_or(BlockProductionError::TrustedSetupNotInitialized)?;
let beacon_block_root = block.canonical_root(); let beacon_block_root = block.canonical_root();
let expected_kzg_commitments = block.body().blob_kzg_commitments().map_err(|_| { let expected_kzg_commitments: &KzgCommitments<T::EthSpec> =
BlockProductionError::InvalidBlockVariant( block.body().blob_kzg_commitments().map_err(|_| {
"EIP4844 block does not contain kzg commitments".to_string(), 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::<T::EthSpec>(
kzg,
expected_kzg_commitments,
&blobs,
&kzg_proofs,
)
.map_err(BlockProductionError::KzgError)?;
let blob_sidecars = VariableList::from( let blob_sidecars = VariableList::from(
blobs blobs
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(blob_index, blob)| { .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, block_root: beacon_block_root,
index: blob_index as u64, index: blob_index as u64,
slot, slot,
block_parent_root: block.parent_root(), block_parent_root: block.parent_root(),
proposer_index, proposer_index,
blob, blob,
kzg_commitment: expected_kzg_commitments[blob_index].clone(), kzg_commitment: *kzg_commitment,
kzg_proof: Default::default(), // TODO: compute KZG proof kzg_proof: *kzg_proof,
} })
}) })
.collect::<Vec<BlobSidecar<T::EthSpec>>>(), .collect::<Result<Vec<BlobSidecar<T::EthSpec>>, 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); self.blob_cache.put(beacon_block_root, blob_sidecars);
} }
@ -4809,6 +4832,29 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok((block, state)) Ok((block, state))
} }
fn compute_blob_kzg_proofs(
kzg: &Arc<Kzg>,
blobs: &Blobs<<T as BeaconChainTypes>::EthSpec>,
expected_kzg_commitments: &KzgCommitments<<T as BeaconChainTypes>::EthSpec>,
slot: Slot,
) -> Result<Vec<KzgProof>, 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::<T::EthSpec>(kzg, blob, kzg_commitment.clone())
.map_err(BlockProductionError::KzgError)
})
.collect::<Result<Vec<KzgProof>, BlockProductionError>>()
}
/// This method must be called whenever an execution engine indicates that a payload is /// This method must be called whenever an execution engine indicates that a payload is
/// invalid. /// invalid.
/// ///

View File

@ -273,6 +273,7 @@ pub enum BlockProductionError {
ShuttingDown, ShuttingDown,
MissingSyncAggregate, MissingSyncAggregate,
MissingExecutionPayload, MissingExecutionPayload,
MissingKzgCommitment(String),
TokioJoin(tokio::task::JoinError), TokioJoin(tokio::task::JoinError),
BeaconChain(BeaconChainError), BeaconChain(BeaconChainError),
InvalidPayloadFork, InvalidPayloadFork,

View File

@ -42,10 +42,11 @@ pub fn validate_blobs<T: EthSpec>(
/// Compute the kzg proof given an ssz blob and its kzg commitment. /// Compute the kzg proof given an ssz blob and its kzg commitment.
pub fn compute_blob_kzg_proof<T: EthSpec>( pub fn compute_blob_kzg_proof<T: EthSpec>(
kzg: &Kzg, kzg: &Kzg,
blob: Blob<T>, blob: &Blob<T>,
kzg_commitment: KzgCommitment, kzg_commitment: KzgCommitment,
) -> Result<KzgProof, KzgError> { ) -> Result<KzgProof, KzgError> {
kzg.compute_blob_kzg_proof(ssz_blob_to_crypto_blob::<T>(blob), kzg_commitment) // Avoid this blob clone
kzg.compute_blob_kzg_proof(ssz_blob_to_crypto_blob::<T>(blob.clone()), kzg_commitment)
} }
/// Compute the kzg commitment for a given blob. /// Compute the kzg commitment for a given blob.

View File

@ -314,7 +314,6 @@ mod tests {
VoluntaryExit, VoluntaryExit,
ProposerSlashing, ProposerSlashing,
AttesterSlashing, AttesterSlashing,
BeaconBlocksAndBlobsSidecar,
] ]
.iter() .iter()
{ {

View File

@ -259,7 +259,7 @@ impl<E: EthSpec> HotColdDB<E, LevelDB<E>, LevelDB<E>> {
db.blobs_db = Some(LevelDB::open(path.as_path())?); 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)?; db.compare_and_set_blob_info_with_write(blob_info, new_blob_info)?;
info!( info!(
db.log, db.log,
@ -1899,7 +1899,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
let blob_info = self.get_blob_info(); let blob_info = self.get_blob_info();
let oldest_blob_slot = blob_info let oldest_blob_slot = blob_info
.oldest_blob_slot .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 // 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. // middle of an epoch otherwise the oldest blob slot is a start slot.

View File

@ -8,7 +8,7 @@ use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use tree_hash::{PackedEncoding, TreeHash}; use tree_hash::{PackedEncoding, TreeHash};
#[derive(Derivative, Clone, Encode, Decode)] #[derive(Derivative, Clone, Copy, Encode, Decode)]
#[derivative(PartialEq, Eq, Hash)] #[derivative(PartialEq, Eq, Hash)]
#[ssz(struct_behaviour = "transparent")] #[ssz(struct_behaviour = "transparent")]
pub struct KzgCommitment(pub [u8; BYTES_PER_COMMITMENT]); pub struct KzgCommitment(pub [u8; BYTES_PER_COMMITMENT]);