Merge branch 'deneb-free-blobs' into get-validator-block-and-blobs

This commit is contained in:
Jimmy Chen 2023-03-15 10:55:00 +11:00
commit e4608d44ea
No known key found for this signature in database
GPG Key ID: 7AAEE02659DCF690
6 changed files with 127 additions and 85 deletions

3
Cargo.lock generated
View File

@ -892,10 +892,11 @@ dependencies = [
[[package]] [[package]]
name = "c-kzg" name = "c-kzg"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/ethereum/c-kzg-4844?rev=69f6155d7524247be9d3f54ab3bfbe33a0345622#69f6155d7524247be9d3f54ab3bfbe33a0345622" source = "git+https://github.com/ethereum/c-kzg-4844?rev=549739fcb3aaec6fe5651e1912f05c604b45621b#549739fcb3aaec6fe5651e1912f05c604b45621b"
dependencies = [ dependencies = [
"hex", "hex",
"libc", "libc",
"serde",
] ]
[[package]] [[package]]

View File

@ -1,6 +1,8 @@
use kzg::{Error as KzgError, Kzg, BYTES_PER_BLOB}; use kzg::{Error as KzgError, Kzg, BYTES_PER_BLOB};
use types::{Blob, BlobsSidecar, EthSpec, Hash256, KzgCommitment, KzgProof, Slot}; use types::{Blob, EthSpec, KzgCommitment, KzgProof};
/// Converts a blob ssz List object to an array to be used with the kzg
/// crypto library.
fn ssz_blob_to_crypto_blob<T: EthSpec>(blob: Blob<T>) -> kzg::Blob { fn ssz_blob_to_crypto_blob<T: EthSpec>(blob: Blob<T>) -> kzg::Blob {
let blob_vec: Vec<u8> = blob.into(); let blob_vec: Vec<u8> = blob.into();
let mut arr = [0; BYTES_PER_BLOB]; let mut arr = [0; BYTES_PER_BLOB];
@ -8,47 +10,48 @@ fn ssz_blob_to_crypto_blob<T: EthSpec>(blob: Blob<T>) -> kzg::Blob {
arr.into() arr.into()
} }
pub fn validate_blobs_sidecar<T: EthSpec>( /// Validate a single blob-commitment-proof triplet from a `BlobSidecar`.
pub fn validate_blob<T: EthSpec>(
kzg: &Kzg, kzg: &Kzg,
slot: Slot, blob: Blob<T>,
beacon_block_root: Hash256, kzg_commitment: KzgCommitment,
expected_kzg_commitments: &[KzgCommitment], kzg_proof: KzgProof,
blobs_sidecar: &BlobsSidecar<T>,
) -> Result<bool, KzgError> { ) -> Result<bool, KzgError> {
if slot != blobs_sidecar.beacon_block_slot kzg.verify_blob_kzg_proof(
|| beacon_block_root != blobs_sidecar.beacon_block_root ssz_blob_to_crypto_blob::<T>(blob),
|| blobs_sidecar.blobs.len() != expected_kzg_commitments.len() kzg_commitment,
{ kzg_proof,
return Ok(false);
}
let blobs = blobs_sidecar
.blobs
.clone() // TODO(pawan): avoid this clone
.into_iter()
.map(|blob| ssz_blob_to_crypto_blob::<T>(blob))
.collect::<Vec<_>>();
kzg.verify_aggregate_kzg_proof(
&blobs,
expected_kzg_commitments,
blobs_sidecar.kzg_aggregated_proof,
) )
} }
pub fn compute_aggregate_kzg_proof<T: EthSpec>( /// Validate a batch of blob-commitment-proof triplets from multiple `BlobSidecars`.
pub fn validate_blobs<T: EthSpec>(
kzg: &Kzg, kzg: &Kzg,
expected_kzg_commitments: &[KzgCommitment],
blobs: &[Blob<T>], blobs: &[Blob<T>],
) -> Result<KzgProof, KzgError> { kzg_proofs: &[KzgProof],
) -> Result<bool, KzgError> {
let blobs = blobs let blobs = blobs
.iter() .iter()
.map(|blob| ssz_blob_to_crypto_blob::<T>(blob.clone())) // TODO(pawan): avoid this clone .map(|blob| ssz_blob_to_crypto_blob::<T>(blob.clone())) // Avoid this clone
.collect::<Vec<_>>(); .collect::<Vec<_>>();
kzg.compute_aggregate_kzg_proof(&blobs) kzg.verify_blob_kzg_proof_batch(&blobs, expected_kzg_commitments, kzg_proofs)
} }
pub fn blob_to_kzg_commitment<T: EthSpec>(kzg: &Kzg, blob: Blob<T>) -> KzgCommitment { /// Compute the kzg proof given an ssz blob and its kzg commitment.
let blob = ssz_blob_to_crypto_blob::<T>(blob); pub fn compute_blob_kzg_proof<T: EthSpec>(
kzg.blob_to_kzg_commitment(blob) kzg: &Kzg,
blob: Blob<T>,
kzg_commitment: KzgCommitment,
) -> Result<KzgProof, KzgError> {
kzg.compute_blob_kzg_proof(ssz_blob_to_crypto_blob::<T>(blob), kzg_commitment)
}
/// Compute the kzg commitment for a given blob.
pub fn blob_to_kzg_commitment<T: EthSpec>(
kzg: &Kzg,
blob: Blob<T>,
) -> Result<KzgCommitment, KzgError> {
kzg.blob_to_kzg_commitment(ssz_blob_to_crypto_blob::<T>(blob))
} }

View File

@ -16,7 +16,7 @@ serde_derive = "1.0.116"
eth2_serde_utils = "0.1.1" eth2_serde_utils = "0.1.1"
hex = "0.4.2" hex = "0.4.2"
eth2_hashing = "0.3.0" eth2_hashing = "0.3.0"
c-kzg = {git = "https://github.com/ethereum/c-kzg-4844", rev = "69f6155d7524247be9d3f54ab3bfbe33a0345622" } c-kzg = {git = "https://github.com/ethereum/c-kzg-4844", rev = "549739fcb3aaec6fe5651e1912f05c604b45621b" }
arbitrary = { version = "1.0", features = ["derive"], optional = true } arbitrary = { version = "1.0", features = ["derive"], optional = true }
[features] [features]

View File

@ -1,3 +1,4 @@
use c_kzg::{Bytes48, BYTES_PER_COMMITMENT};
use derivative::Derivative; use derivative::Derivative;
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
@ -7,12 +8,16 @@ use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use tree_hash::{PackedEncoding, TreeHash}; use tree_hash::{PackedEncoding, TreeHash};
const KZG_COMMITMENT_BYTES_LEN: usize = 48;
#[derive(Derivative, Clone, Encode, Decode)] #[derive(Derivative, Clone, Encode, Decode)]
#[derivative(PartialEq, Eq, Hash)] #[derivative(PartialEq, Eq, Hash)]
#[ssz(struct_behaviour = "transparent")] #[ssz(struct_behaviour = "transparent")]
pub struct KzgCommitment(pub [u8; KZG_COMMITMENT_BYTES_LEN]); pub struct KzgCommitment(pub [u8; BYTES_PER_COMMITMENT]);
impl From<KzgCommitment> for Bytes48 {
fn from(value: KzgCommitment) -> Self {
value.0.into()
}
}
impl Display for KzgCommitment { impl Display for KzgCommitment {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
@ -22,13 +27,13 @@ impl Display for KzgCommitment {
impl Default for KzgCommitment { impl Default for KzgCommitment {
fn default() -> Self { fn default() -> Self {
KzgCommitment([0; KZG_COMMITMENT_BYTES_LEN]) KzgCommitment([0; BYTES_PER_COMMITMENT])
} }
} }
impl TreeHash for KzgCommitment { impl TreeHash for KzgCommitment {
fn tree_hash_type() -> tree_hash::TreeHashType { fn tree_hash_type() -> tree_hash::TreeHashType {
<[u8; KZG_COMMITMENT_BYTES_LEN] as TreeHash>::tree_hash_type() <[u8; BYTES_PER_COMMITMENT] as TreeHash>::tree_hash_type()
} }
fn tree_hash_packed_encoding(&self) -> PackedEncoding { fn tree_hash_packed_encoding(&self) -> PackedEncoding {
@ -36,7 +41,7 @@ impl TreeHash for KzgCommitment {
} }
fn tree_hash_packing_factor() -> usize { fn tree_hash_packing_factor() -> usize {
<[u8; KZG_COMMITMENT_BYTES_LEN] as TreeHash>::tree_hash_packing_factor() <[u8; BYTES_PER_COMMITMENT] as TreeHash>::tree_hash_packing_factor()
} }
fn tree_hash_root(&self) -> tree_hash::Hash256 { fn tree_hash_root(&self) -> tree_hash::Hash256 {
@ -86,15 +91,15 @@ impl FromStr for KzgCommitment {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(stripped) = s.strip_prefix("0x") { if let Some(stripped) = s.strip_prefix("0x") {
let bytes = hex::decode(stripped).map_err(|e| e.to_string())?; let bytes = hex::decode(stripped).map_err(|e| e.to_string())?;
if bytes.len() == KZG_COMMITMENT_BYTES_LEN { if bytes.len() == BYTES_PER_COMMITMENT {
let mut kzg_commitment_bytes = [0; KZG_COMMITMENT_BYTES_LEN]; let mut kzg_commitment_bytes = [0; BYTES_PER_COMMITMENT];
kzg_commitment_bytes[..].copy_from_slice(&bytes); kzg_commitment_bytes[..].copy_from_slice(&bytes);
Ok(Self(kzg_commitment_bytes)) Ok(Self(kzg_commitment_bytes))
} else { } else {
Err(format!( Err(format!(
"InvalidByteLength: got {}, expected {}", "InvalidByteLength: got {}, expected {}",
bytes.len(), bytes.len(),
KZG_COMMITMENT_BYTES_LEN BYTES_PER_COMMITMENT
)) ))
} }
} else { } else {
@ -112,7 +117,7 @@ impl Debug for KzgCommitment {
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for KzgCommitment { impl arbitrary::Arbitrary<'_> for KzgCommitment {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut bytes = [0u8; KZG_COMMITMENT_BYTES_LEN]; let mut bytes = [0u8; BYTES_PER_COMMITMENT];
u.fill_buffer(&mut bytes)?; u.fill_buffer(&mut bytes)?;
Ok(KzgCommitment(bytes)) Ok(KzgCommitment(bytes))
} }

View File

@ -1,3 +1,4 @@
use c_kzg::{Bytes48, BYTES_PER_PROOF};
use serde::de::{Deserialize, Deserializer}; use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
@ -6,15 +7,19 @@ use std::fmt::Debug;
use std::str::FromStr; use std::str::FromStr;
use tree_hash::{PackedEncoding, TreeHash}; use tree_hash::{PackedEncoding, TreeHash};
const KZG_PROOF_BYTES_LEN: usize = 48;
#[derive(PartialEq, Hash, Clone, Copy, Encode, Decode)] #[derive(PartialEq, Hash, Clone, Copy, Encode, Decode)]
#[ssz(struct_behaviour = "transparent")] #[ssz(struct_behaviour = "transparent")]
pub struct KzgProof(pub [u8; KZG_PROOF_BYTES_LEN]); pub struct KzgProof(pub [u8; BYTES_PER_PROOF]);
impl From<KzgProof> for Bytes48 {
fn from(value: KzgProof) -> Self {
value.0.into()
}
}
impl KzgProof { impl KzgProof {
pub fn empty() -> Self { pub fn empty() -> Self {
let mut bytes = [0; KZG_PROOF_BYTES_LEN]; let mut bytes = [0; BYTES_PER_PROOF];
bytes[0] = 192; bytes[0] = 192;
Self(bytes) Self(bytes)
} }
@ -28,25 +33,25 @@ impl fmt::Display for KzgProof {
impl Default for KzgProof { impl Default for KzgProof {
fn default() -> Self { fn default() -> Self {
KzgProof([0; KZG_PROOF_BYTES_LEN]) KzgProof([0; BYTES_PER_PROOF])
} }
} }
impl From<[u8; KZG_PROOF_BYTES_LEN]> for KzgProof { impl From<[u8; BYTES_PER_PROOF]> for KzgProof {
fn from(bytes: [u8; KZG_PROOF_BYTES_LEN]) -> Self { fn from(bytes: [u8; BYTES_PER_PROOF]) -> Self {
Self(bytes) Self(bytes)
} }
} }
impl Into<[u8; KZG_PROOF_BYTES_LEN]> for KzgProof { impl Into<[u8; BYTES_PER_PROOF]> for KzgProof {
fn into(self) -> [u8; KZG_PROOF_BYTES_LEN] { fn into(self) -> [u8; BYTES_PER_PROOF] {
self.0 self.0
} }
} }
impl TreeHash for KzgProof { impl TreeHash for KzgProof {
fn tree_hash_type() -> tree_hash::TreeHashType { fn tree_hash_type() -> tree_hash::TreeHashType {
<[u8; KZG_PROOF_BYTES_LEN]>::tree_hash_type() <[u8; BYTES_PER_PROOF]>::tree_hash_type()
} }
fn tree_hash_packed_encoding(&self) -> PackedEncoding { fn tree_hash_packed_encoding(&self) -> PackedEncoding {
@ -54,7 +59,7 @@ impl TreeHash for KzgProof {
} }
fn tree_hash_packing_factor() -> usize { fn tree_hash_packing_factor() -> usize {
<[u8; KZG_PROOF_BYTES_LEN]>::tree_hash_packing_factor() <[u8; BYTES_PER_PROOF]>::tree_hash_packing_factor()
} }
fn tree_hash_root(&self) -> tree_hash::Hash256 { fn tree_hash_root(&self) -> tree_hash::Hash256 {
@ -104,15 +109,15 @@ impl FromStr for KzgProof {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(stripped) = s.strip_prefix("0x") { if let Some(stripped) = s.strip_prefix("0x") {
let bytes = hex::decode(stripped).map_err(|e| e.to_string())?; let bytes = hex::decode(stripped).map_err(|e| e.to_string())?;
if bytes.len() == KZG_PROOF_BYTES_LEN { if bytes.len() == BYTES_PER_PROOF {
let mut kzg_proof_bytes = [0; KZG_PROOF_BYTES_LEN]; let mut kzg_proof_bytes = [0; BYTES_PER_PROOF];
kzg_proof_bytes[..].copy_from_slice(&bytes); kzg_proof_bytes[..].copy_from_slice(&bytes);
Ok(Self(kzg_proof_bytes)) Ok(Self(kzg_proof_bytes))
} else { } else {
Err(format!( Err(format!(
"InvalidByteLength: got {}, expected {}", "InvalidByteLength: got {}, expected {}",
bytes.len(), bytes.len(),
KZG_PROOF_BYTES_LEN BYTES_PER_PROOF
)) ))
} }
} else { } else {
@ -130,7 +135,7 @@ impl Debug for KzgProof {
#[cfg(feature = "arbitrary")] #[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for KzgProof { impl arbitrary::Arbitrary<'_> for KzgProof {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let mut bytes = [0u8; KZG_PROOF_BYTES_LEN]; let mut bytes = [0u8; BYTES_PER_PROOF];
u.fill_buffer(&mut bytes)?; u.fill_buffer(&mut bytes)?;
Ok(KzgProof(bytes)) Ok(KzgProof(bytes))
} }

View File

@ -3,6 +3,7 @@ mod kzg_proof;
mod trusted_setup; mod trusted_setup;
pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof, trusted_setup::TrustedSetup}; pub use crate::{kzg_commitment::KzgCommitment, kzg_proof::KzgProof, trusted_setup::TrustedSetup};
use c_kzg::Bytes48;
pub use c_kzg::{ pub use c_kzg::{
Blob, Error as CKzgError, KZGSettings, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT, Blob, Error as CKzgError, KZGSettings, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT,
FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENTS_PER_BLOB,
@ -13,9 +14,9 @@ use std::path::PathBuf;
pub enum Error { pub enum Error {
InvalidTrustedSetup(CKzgError), InvalidTrustedSetup(CKzgError),
InvalidKzgProof(CKzgError), InvalidKzgProof(CKzgError),
InvalidLength(String), InvalidBytes(CKzgError),
KzgProofComputationFailed(CKzgError), KzgProofComputationFailed(CKzgError),
InvalidBlob(String), InvalidBlob(CKzgError),
} }
/// A wrapper over a kzg library that holds the trusted setup parameters. /// A wrapper over a kzg library that holds the trusted setup parameters.
@ -51,40 +52,67 @@ impl Kzg {
}) })
} }
/// Compute the aggregated kzg proof given an array of blobs. /// Compute the kzg proof given a blob and its kzg commitment.
pub fn compute_aggregate_kzg_proof(&self, blobs: &[Blob]) -> Result<KzgProof, Error> { pub fn compute_blob_kzg_proof(
c_kzg::KZGProof::compute_aggregate_kzg_proof(blobs, &self.trusted_setup) &self,
blob: Blob,
kzg_commitment: KzgCommitment,
) -> Result<KzgProof, Error> {
c_kzg::KZGProof::compute_blob_kzg_proof(blob, kzg_commitment.into(), &self.trusted_setup)
.map_err(Error::KzgProofComputationFailed) .map_err(Error::KzgProofComputationFailed)
.map(|proof| KzgProof(proof.to_bytes())) .map(|proof| KzgProof(proof.to_bytes().into_inner()))
} }
/// Verify an aggregate kzg proof given the blobs that generated the proof, the kzg commitments /// Verify a kzg proof given the blob, kzg commitment and kzg proof.
/// and the kzg proof. pub fn verify_blob_kzg_proof(
pub fn verify_aggregate_kzg_proof( &self,
blob: Blob,
kzg_commitment: KzgCommitment,
kzg_proof: KzgProof,
) -> Result<bool, Error> {
c_kzg::KZGProof::verify_blob_kzg_proof(
blob,
kzg_commitment.into(),
kzg_proof.into(),
&self.trusted_setup,
)
.map_err(Error::InvalidKzgProof)
}
/// Verify a batch of blob commitment proof triplets.
///
/// Note: This method is slightly faster than calling `Self::verify_blob_kzg_proof` in a loop sequentially.
/// TODO(pawan): test performance against a parallelized rayon impl.
pub fn verify_blob_kzg_proof_batch(
&self, &self,
blobs: &[Blob], blobs: &[Blob],
expected_kzg_commitments: &[KzgCommitment], kzg_commitments: &[KzgCommitment],
kzg_aggregated_proof: KzgProof, kzg_proofs: &[KzgProof],
) -> Result<bool, Error> { ) -> Result<bool, Error> {
if blobs.len() != expected_kzg_commitments.len() { let commitments_bytes = kzg_commitments
return Err(Error::InvalidLength(
"blobs and expected_kzg_commitments should be of same size".to_string(),
));
}
let commitments = expected_kzg_commitments
.iter() .iter()
.map(|comm| comm.0.into()) .map(|comm| Bytes48::from_bytes(&comm.0))
.collect::<Vec<c_kzg::KZGCommitment>>(); .collect::<Result<Vec<Bytes48>, _>>()
let proof: c_kzg::KZGProof = kzg_aggregated_proof.0.into(); .map_err(Error::InvalidBytes)?;
proof
.verify_aggregate_kzg_proof(blobs, &commitments, &self.trusted_setup) let proofs_bytes = kzg_proofs
.map_err(Error::InvalidKzgProof) .iter()
.map(|proof| Bytes48::from_bytes(&proof.0))
.collect::<Result<Vec<Bytes48>, _>>()
.map_err(Error::InvalidBytes)?;
c_kzg::KZGProof::verify_blob_kzg_proof_batch(
blobs,
&commitments_bytes,
&proofs_bytes,
&self.trusted_setup,
)
.map_err(Error::InvalidKzgProof)
} }
/// Converts a blob to a kzg commitment. /// Converts a blob to a kzg commitment.
pub fn blob_to_kzg_commitment(&self, blob: Blob) -> KzgCommitment { pub fn blob_to_kzg_commitment(&self, blob: Blob) -> Result<KzgCommitment, Error> {
KzgCommitment( c_kzg::KZGCommitment::blob_to_kzg_commitment(blob, &self.trusted_setup)
c_kzg::KZGCommitment::blob_to_kzg_commitment(blob, &self.trusted_setup).to_bytes(), .map_err(Error::InvalidBlob)
) .map(|com| KzgCommitment(com.to_bytes().into_inner()))
} }
} }