diff --git a/Cargo.lock b/Cargo.lock index 0d08c1a66..57c8b1591 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -711,7 +711,7 @@ dependencies = [ [[package]] name = "c-kzg" version = "0.1.0" -source = "git+https://github.com/pawanjay176/c-kzg-4844?rev=f4b0c2a84e7a90fa2e0e4e04e5d777be146c6e94#f4b0c2a84e7a90fa2e0e4e04e5d777be146c6e94" +source = "git+https://github.com/pawanjay176/c-kzg-4844?rev=cb3745d26b728ee526dc41912e3e1bc6f17a5eeb#cb3745d26b728ee526dc41912e3e1bc6f17a5eeb" dependencies = [ "hex", "libc", diff --git a/beacon_node/beacon_chain/src/blob_verification.rs b/beacon_node/beacon_chain/src/blob_verification.rs index ff313e19a..caa2cc819 100644 --- a/beacon_node/beacon_chain/src/blob_verification.rs +++ b/beacon_node/beacon_chain/src/blob_verification.rs @@ -111,11 +111,11 @@ pub fn validate_blob_for_gossip( // Verify that blobs are properly formatted //TODO: add the check while constructing a Blob type from bytes instead of after - for (i, blob) in blob_sidecar.blobs.iter().enumerate() { - if blob.iter().any(|b| *b >= *BLS_MODULUS) { - return Err(BlobError::BlobOutOfRange { blob_index: i }); - } - } + // for (i, blob) in blob_sidecar.blobs.iter().enumerate() { + // if blob.iter().any(|b| *b >= *BLS_MODULUS) { + // return Err(BlobError::BlobOutOfRange { blob_index: i }); + // } + // } // Verify that the KZG proof is a valid G1 point if PublicKey::deserialize(&blob_sidecar.kzg_aggregate_proof.0).is_err() { diff --git a/consensus/types/src/eth_spec.rs b/consensus/types/src/eth_spec.rs index 661484fde..745aa304d 100644 --- a/consensus/types/src/eth_spec.rs +++ b/consensus/types/src/eth_spec.rs @@ -3,7 +3,7 @@ use crate::*; use safe_arith::SafeArith; use serde_derive::{Deserialize, Serialize}; use ssz_types::typenum::{ - bit::B0, UInt, Unsigned, U0, U1024, U1048576, U1073741824, U1099511627776, U128, U16, + bit::B0, UInt, Unsigned, U0, U1024, U1048576, U1073741824, U1099511627776, U128, U131072, U16, U16777216, U2, U2048, U256, U32, U4, U4096, U512, U625, U64, U65536, U8, U8192, }; use std::fmt::{self, Debug}; @@ -105,6 +105,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + */ type MaxBlobsPerBlock: Unsigned + Clone + Sync + Send + Debug + PartialEq; type FieldElementsPerBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq; + type BytesPerFieldElement: Unsigned + Clone + Sync + Send + Debug + PartialEq; /* * Derived values (set these CAREFULLY) */ @@ -123,6 +124,11 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq + /// Must be set to `SyncCommitteeSize / SyncCommitteeSubnetCount`. type SyncSubcommitteeSize: Unsigned + Clone + Sync + Send + Debug + PartialEq; + /// The total length of a blob in bytes. + /// + /// Must be set to `BytesPerFieldElement * FieldElementsPerBlob`. + type BytesPerBlob: Unsigned + Clone + Sync + Send + Debug + PartialEq; + fn default_spec() -> ChainSpec; fn spec_name() -> EthSpecId; @@ -293,7 +299,9 @@ impl EthSpec for MainnetEthSpec { type MinGasLimit = U5000; type MaxExtraDataBytes = U32; type MaxBlobsPerBlock = U16; // 2**4 = 16 + type BytesPerFieldElement = U32; type FieldElementsPerBlob = U4096; + type BytesPerBlob = U131072; type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch @@ -347,7 +355,9 @@ impl EthSpec for MinimalEthSpec { MaxExtraDataBytes, MaxBlsToExecutionChanges, MaxBlobsPerBlock, - FieldElementsPerBlob + FieldElementsPerBlob, + BytesPerFieldElement, + BytesPerBlob }); fn default_spec() -> ChainSpec { @@ -396,6 +406,8 @@ impl EthSpec for GnosisEthSpec { type MaxWithdrawalsPerPayload = U16; type MaxBlobsPerBlock = U16; // 2**4 = 16 type FieldElementsPerBlob = U4096; + type BytesPerFieldElement = U32; + type BytesPerBlob = U131072; fn default_spec() -> ChainSpec { ChainSpec::gnosis() diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index c5faf5049..ab07c7a07 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -198,7 +198,7 @@ pub type Uint256 = ethereum_types::U256; pub type Address = H160; pub type ForkVersion = [u8; 4]; pub type BLSFieldElement = Uint256; -pub type Blob = FixedVector::FieldElementsPerBlob>; +pub type Blob = FixedVector::BytesPerBlob>; pub type VersionedHash = Hash256; pub use bls::{ diff --git a/crypto/kzg/Cargo.toml b/crypto/kzg/Cargo.toml index d861eb3a4..27944eddc 100644 --- a/crypto/kzg/Cargo.toml +++ b/crypto/kzg/Cargo.toml @@ -19,4 +19,4 @@ eth2_serde_utils = "0.1.1" hex = "0.4.2" eth2_hashing = "0.3.0" ethereum-types = "0.12.1" -c-kzg = {git = "https://github.com/pawanjay176/c-kzg-4844", rev = "f4b0c2a84e7a90fa2e0e4e04e5d777be146c6e94" } \ No newline at end of file +c-kzg = {git = "https://github.com/pawanjay176/c-kzg-4844", rev = "cb3745d26b728ee526dc41912e3e1bc6f17a5eeb" } diff --git a/crypto/kzg/src/lib.rs b/crypto/kzg/src/lib.rs index daf958c3a..0f9638546 100644 --- a/crypto/kzg/src/lib.rs +++ b/crypto/kzg/src/lib.rs @@ -1,30 +1,83 @@ mod kzg_commitment; mod kzg_proof; +pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof}; +use c_kzg::{Error as CKzgError, KZGSettings, BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB}; use std::path::PathBuf; -use c_kzg::{Error as CKzgError, KZGSettings}; +const BYTES_PER_BLOB: usize = FIELD_ELEMENTS_PER_BLOB * BYTES_PER_FIELD_ELEMENT; -pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof}; +/// The consensus type `Blob` is generic over EthSpec, so it cannot be imported +/// in this crate without creating a cyclic dependency between the kzg and consensus/types crates. +/// So need to use a Vec here unless we think of a smarter way of doing this +type Blob = [u8; BYTES_PER_BLOB]; #[derive(Debug)] pub enum Error { InvalidTrustedSetup(CKzgError), + InvalidKzgCommitment(CKzgError), + InvalidKzgProof(CKzgError), + KzgVerificationFailed(CKzgError), + EmptyBlobs, + EmptyKzgCommitments, + InvalidLength(String), + KzgProofComputationFailed(CKzgError), } pub struct Kzg { - _trusted_setup: KZGSettings, + trusted_setup: KZGSettings, } impl Kzg { pub fn new_from_file(file_path: PathBuf) -> Result { Ok(Self { - _trusted_setup: KZGSettings::load_trusted_setup(file_path) - .map_err(|e| Error::InvalidTrustedSetup(e))?, + trusted_setup: KZGSettings::load_trusted_setup(file_path) + .map_err(Error::InvalidTrustedSetup)?, }) } - pub fn verify_aggregate_kzg_proof() {} + pub fn compute_aggregate_kzg_proof(&self, blobs: &[Blob]) -> Result { + if blobs.len() == 0 { + return Err(Error::EmptyBlobs); + } + c_kzg::KZGProof::compute_aggregate_kzg_proof(blobs, &self.trusted_setup) + .map_err(Error::KzgProofComputationFailed) + .map(|proof| KzgProof(proof.to_bytes())) + } - pub fn blob_to_kzg_commitment() {} + pub fn verify_aggregate_kzg_proof( + &self, + blobs: &[Blob], + expected_kzg_commitments: &[KzgCommitment], + kzg_aggregated_proof: KzgProof, + ) -> Result { + if blobs.len() == 0 { + return Err(Error::EmptyBlobs); + } + if expected_kzg_commitments.len() == 0 { + return Err(Error::EmptyBlobs); + } + if blobs.len() != expected_kzg_commitments.len() { + return Err(Error::InvalidLength( + "blobs and expected_kzg_commitments should be of same size".to_string(), + )); + } + let commitments = expected_kzg_commitments + .into_iter() + .map(|comm| { + c_kzg::KZGCommitment::from_bytes(&comm.0).map_err(Error::InvalidKzgCommitment) + }) + .collect::, Error>>()?; + let proof = + c_kzg::KZGProof::from_bytes(&kzg_aggregated_proof.0).map_err(Error::InvalidKzgProof)?; + proof + .verify_aggregate_kzg_proof(blobs, &commitments, &self.trusted_setup) + .map_err(Error::InvalidKzgProof) + } + + pub fn blob_to_kzg_commitment(&self, blob: Blob) -> KzgCommitment { + KzgCommitment( + c_kzg::KZGCommitment::blob_to_kzg_commitment(blob, &self.trusted_setup).to_bytes(), + ) + } }