Remove c-kzg (#8930)

#7330


  Removes `c-kzg` from our `kzg` crate and rely fully on the `rust_eth_kzg` crate.

This removes the old `Blob` type entirely and instead handles `rust_eth_kzg::KzgBlobRef`s directly which allows us to avoid some extra stack allocations . Similarly, we make `Bytes32` and `Bytes48` type aliases rather than structs as this fits better with the new `rust_eth_kzg` API.


Co-Authored-By: Mac L <mjladson@pm.me>
This commit is contained in:
Mac L
2026-03-11 07:43:26 +02:00
committed by GitHub
parent 2bb79f43aa
commit 815040dc3c
14 changed files with 129 additions and 188 deletions

View File

@@ -1,4 +1,4 @@
use c_kzg::BYTES_PER_COMMITMENT;
use crate::{Bytes48, BYTES_PER_COMMITMENT};
use educe::Educe;
use ethereum_hashing::hash_fixed;
use serde::de::{Deserialize, Deserializer};
@@ -14,7 +14,7 @@ pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01;
#[derive(Educe, Clone, Copy, Encode, Decode)]
#[educe(PartialEq, Eq, Hash)]
#[ssz(struct_behaviour = "transparent")]
pub struct KzgCommitment(pub [u8; c_kzg::BYTES_PER_COMMITMENT]);
pub struct KzgCommitment(pub [u8; BYTES_PER_COMMITMENT]);
impl KzgCommitment {
pub fn calculate_versioned_hash(&self) -> Hash256 {
@@ -24,13 +24,13 @@ impl KzgCommitment {
}
pub fn empty_for_testing() -> Self {
KzgCommitment([0; c_kzg::BYTES_PER_COMMITMENT])
KzgCommitment([0; BYTES_PER_COMMITMENT])
}
}
impl From<KzgCommitment> for c_kzg::Bytes48 {
impl From<KzgCommitment> for Bytes48 {
fn from(value: KzgCommitment) -> Self {
value.0.into()
value.0
}
}

View File

@@ -1,4 +1,4 @@
use c_kzg::BYTES_PER_PROOF;
use crate::BYTES_PER_PROOF;
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use ssz_derive::{Decode, Encode};
@@ -11,12 +11,6 @@ use tree_hash::{PackedEncoding, TreeHash};
#[ssz(struct_behaviour = "transparent")]
pub struct KzgProof(pub [u8; BYTES_PER_PROOF]);
impl From<KzgProof> for c_kzg::Bytes48 {
fn from(value: KzgProof) -> Self {
value.0.into()
}
}
impl KzgProof {
/// Creates a valid proof using `G1_POINT_AT_INFINITY`.
pub fn empty() -> Self {

View File

@@ -12,11 +12,12 @@ pub use crate::{
trusted_setup::TrustedSetup,
};
pub use c_kzg::{
Blob, Bytes32, Bytes48, KzgSettings, BYTES_PER_BLOB, BYTES_PER_COMMITMENT,
BYTES_PER_FIELD_ELEMENT, BYTES_PER_PROOF, FIELD_ELEMENTS_PER_BLOB,
pub use rust_eth_kzg::constants::{
BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, FIELD_ELEMENTS_PER_BLOB,
};
pub const BYTES_PER_PROOF: usize = 48;
use crate::trusted_setup::load_trusted_setup;
use rayon::prelude::*;
pub use rust_eth_kzg::{
@@ -25,13 +26,6 @@ pub use rust_eth_kzg::{
};
use tracing::{instrument, Span};
/// Disables the fixed-base multi-scalar multiplication optimization for computing
/// cell KZG proofs, because `rust-eth-kzg` already handles the precomputation.
///
/// Details about `precompute` parameter can be found here:
/// <https://github.com/ethereum/c-kzg-4844/pull/545/files>
pub const NO_PRECOMPUTE: u64 = 0;
// Note: Both `NUMBER_OF_COLUMNS` and `CELLS_PER_EXT_BLOB` are preset values - however this
// is a constant in the KZG library - be aware that overriding `NUMBER_OF_COLUMNS` will break KZG
// operations.
@@ -39,14 +33,15 @@ pub type CellsAndKzgProofs = ([Cell; CELLS_PER_EXT_BLOB], [KzgProof; CELLS_PER_E
pub type KzgBlobRef<'a> = &'a [u8; BYTES_PER_BLOB];
type Bytes32 = [u8; 32];
type Bytes48 = [u8; 48];
#[derive(Debug)]
pub enum Error {
/// An error from initialising the trusted setup.
TrustedSetupError(String),
/// An error from the underlying kzg library.
Kzg(c_kzg::Error),
/// A prover/verifier error from the rust-eth-kzg library.
PeerDASKZG(rust_eth_kzg::Error),
/// An error from the rust-eth-kzg library.
Kzg(rust_eth_kzg::Error),
/// The kzg verification failed
KzgVerificationFailed,
/// Misc indexing error
@@ -57,38 +52,29 @@ pub enum Error {
DASContextUninitialized,
}
impl From<c_kzg::Error> for Error {
fn from(value: c_kzg::Error) -> Self {
impl From<rust_eth_kzg::Error> for Error {
fn from(value: rust_eth_kzg::Error) -> Self {
Error::Kzg(value)
}
}
/// A wrapper over a kzg library that holds the trusted setup parameters.
/// A wrapper over the rust-eth-kzg library that holds the trusted setup parameters.
#[derive(Debug)]
pub struct Kzg {
trusted_setup: KzgSettings,
context: DASContext,
}
impl Kzg {
pub fn new_from_trusted_setup_no_precomp(trusted_setup: &[u8]) -> Result<Self, Error> {
let (ckzg_trusted_setup, rkzg_trusted_setup) = load_trusted_setup(trusted_setup)?;
let rkzg_trusted_setup = load_trusted_setup(trusted_setup)?;
let context = DASContext::new(&rkzg_trusted_setup, rust_eth_kzg::UsePrecomp::No);
Ok(Self {
trusted_setup: KzgSettings::load_trusted_setup(
&ckzg_trusted_setup.g1_monomial(),
&ckzg_trusted_setup.g1_lagrange(),
&ckzg_trusted_setup.g2_monomial(),
NO_PRECOMPUTE,
)?,
context,
})
Ok(Self { context })
}
/// Load the kzg trusted setup parameters from a vec of G1 and G2 points.
pub fn new_from_trusted_setup(trusted_setup: &[u8]) -> Result<Self, Error> {
let (ckzg_trusted_setup, rkzg_trusted_setup) = load_trusted_setup(trusted_setup)?;
let rkzg_trusted_setup = load_trusted_setup(trusted_setup)?;
// It's not recommended to change the config parameter for precomputation as storage
// grows exponentially, but the speedup is exponential - after a while the speedup
@@ -100,15 +86,7 @@ impl Kzg {
},
);
Ok(Self {
trusted_setup: KzgSettings::load_trusted_setup(
&ckzg_trusted_setup.g1_monomial(),
&ckzg_trusted_setup.g1_lagrange(),
&ckzg_trusted_setup.g2_monomial(),
NO_PRECOMPUTE,
)?,
context,
})
Ok(Self { context })
}
fn context(&self) -> &DASContext {
@@ -118,34 +96,35 @@ impl Kzg {
/// Compute the kzg proof given a blob and its kzg commitment.
pub fn compute_blob_kzg_proof(
&self,
blob: &Blob,
blob: KzgBlobRef<'_>,
kzg_commitment: KzgCommitment,
) -> Result<KzgProof, Error> {
self.trusted_setup
.compute_blob_kzg_proof(blob, &kzg_commitment.into())
.map(|proof| KzgProof(proof.to_bytes().into_inner()))
.map_err(Into::into)
let proof = self
.context()
.compute_blob_kzg_proof(blob, &kzg_commitment.0)
.map_err(Error::Kzg)?;
Ok(KzgProof(proof))
}
/// Verify a kzg proof given the blob, kzg commitment and kzg proof.
pub fn verify_blob_kzg_proof(
&self,
blob: &Blob,
blob: KzgBlobRef<'_>,
kzg_commitment: KzgCommitment,
kzg_proof: KzgProof,
) -> Result<(), Error> {
if cfg!(feature = "fake_crypto") {
return Ok(());
}
if !self.trusted_setup.verify_blob_kzg_proof(
blob,
&kzg_commitment.into(),
&kzg_proof.into(),
)? {
Err(Error::KzgVerificationFailed)
} else {
Ok(())
}
self.context()
.verify_blob_kzg_proof(blob, &kzg_commitment.0, &kzg_proof.0)
.map_err(|e| {
if e.is_proof_invalid() {
Error::KzgVerificationFailed
} else {
Error::Kzg(e)
}
})
}
/// Verify a batch of blob commitment proof triplets.
@@ -154,52 +133,48 @@ impl Kzg {
/// TODO(pawan): test performance against a parallelized rayon impl.
pub fn verify_blob_kzg_proof_batch(
&self,
blobs: &[Blob],
blobs: &[KzgBlobRef<'_>],
kzg_commitments: &[KzgCommitment],
kzg_proofs: &[KzgProof],
) -> Result<(), Error> {
if cfg!(feature = "fake_crypto") {
return Ok(());
}
let commitments_bytes = kzg_commitments
.iter()
.map(|comm| Bytes48::from(*comm))
.collect::<Vec<_>>();
let blob_refs: Vec<&[u8; BYTES_PER_BLOB]> = blobs.to_vec();
let commitment_refs: Vec<&[u8; 48]> = kzg_commitments.iter().map(|c| &c.0).collect();
let proof_refs: Vec<&[u8; 48]> = kzg_proofs.iter().map(|p| &p.0).collect();
let proofs_bytes = kzg_proofs
.iter()
.map(|proof| Bytes48::from(*proof))
.collect::<Vec<_>>();
if !self.trusted_setup.verify_blob_kzg_proof_batch(
blobs,
&commitments_bytes,
&proofs_bytes,
)? {
Err(Error::KzgVerificationFailed)
} else {
Ok(())
}
self.context()
.verify_blob_kzg_proof_batch(blob_refs, commitment_refs, proof_refs)
.map_err(|e| {
if e.is_proof_invalid() {
Error::KzgVerificationFailed
} else {
Error::Kzg(e)
}
})
}
/// Converts a blob to a kzg commitment.
pub fn blob_to_kzg_commitment(&self, blob: &Blob) -> Result<KzgCommitment, Error> {
self.trusted_setup
pub fn blob_to_kzg_commitment(&self, blob: KzgBlobRef<'_>) -> Result<KzgCommitment, Error> {
let commitment = self
.context()
.blob_to_kzg_commitment(blob)
.map(|commitment| KzgCommitment(commitment.to_bytes().into_inner()))
.map_err(Into::into)
.map_err(Error::Kzg)?;
Ok(KzgCommitment(commitment))
}
/// Computes the kzg proof for a given `blob` and an evaluation point `z`
pub fn compute_kzg_proof(
&self,
blob: &Blob,
blob: KzgBlobRef<'_>,
z: &Bytes32,
) -> Result<(KzgProof, Bytes32), Error> {
self.trusted_setup
.compute_kzg_proof(blob, z)
.map(|(proof, y)| (KzgProof(proof.to_bytes().into_inner()), y))
.map_err(Into::into)
let (proof, y) = self
.context()
.compute_kzg_proof(blob, *z)
.map_err(Error::Kzg)?;
Ok((KzgProof(proof), y))
}
/// Verifies a `kzg_proof` for a `kzg_commitment` that evaluating a polynomial at `z` results in `y`
@@ -213,9 +188,14 @@ impl Kzg {
if cfg!(feature = "fake_crypto") {
return Ok(true);
}
self.trusted_setup
.verify_kzg_proof(&kzg_commitment.into(), z, y, &kzg_proof.into())
.map_err(Into::into)
match self
.context()
.verify_kzg_proof(&kzg_commitment.0, *z, *y, &kzg_proof.0)
{
Ok(()) => Ok(true),
Err(e) if e.is_proof_invalid() => Ok(false),
Err(e) => Err(Error::Kzg(e)),
}
}
/// Computes the cells and associated proofs for a given `blob`.
@@ -226,18 +206,15 @@ impl Kzg {
let (cells, proofs) = self
.context()
.compute_cells_and_kzg_proofs(blob)
.map_err(Error::PeerDASKZG)?;
.map_err(Error::Kzg)?;
// Convert the proof type to a c-kzg proof type
let c_kzg_proof = proofs.map(KzgProof);
Ok((cells, c_kzg_proof))
let kzg_proofs = proofs.map(KzgProof);
Ok((cells, kzg_proofs))
}
/// Computes the cells for a given `blob`.
pub fn compute_cells(&self, blob: KzgBlobRef<'_>) -> Result<[Cell; CELLS_PER_EXT_BLOB], Error> {
self.context()
.compute_cells(blob)
.map_err(Error::PeerDASKZG)
self.context().compute_cells(blob).map_err(Error::Kzg)
}
/// Verifies a batch of cell-proof-commitment triplets.
@@ -291,8 +268,8 @@ impl Kzg {
for (cell, proof, commitment) in &column_data {
cells.push(*cell);
proofs.push(proof.as_ref());
commitments.push(commitment.as_ref());
proofs.push(proof);
commitments.push(commitment);
}
// Create per-chunk tracing span for visualizing parallel processing.
@@ -319,7 +296,7 @@ impl Kzg {
Err(e) if e.is_proof_invalid() => {
Err((Some(column_index), Error::KzgVerificationFailed))
}
Err(e) => Err((Some(column_index), Error::PeerDASKZG(e))),
Err(e) => Err((Some(column_index), Error::Kzg(e))),
}
})
.collect::<Result<Vec<()>, (Option<u64>, Error)>>()?;
@@ -335,10 +312,9 @@ impl Kzg {
let (cells, proofs) = self
.context()
.recover_cells_and_kzg_proofs(cell_ids.to_vec(), cells.to_vec())
.map_err(Error::PeerDASKZG)?;
.map_err(Error::Kzg)?;
// Convert the proof type to a c-kzg proof type
let c_kzg_proof = proofs.map(KzgProof);
Ok((cells, c_kzg_proof))
let kzg_proofs = proofs.map(KzgProof);
Ok((cells, kzg_proofs))
}
}

View File

@@ -24,7 +24,7 @@ struct G1Point([u8; BYTES_PER_G1_POINT]);
struct G2Point([u8; BYTES_PER_G2_POINT]);
/// Contains the trusted setup parameters that are required to instantiate a
/// `c_kzg::KzgSettings` object.
/// `rust_eth_kzg::TrustedSetup` object.
///
/// The serialize/deserialize implementations are written according to
/// the format specified in the ethereum consensus specs trusted setup files.
@@ -155,19 +155,9 @@ fn strip_prefix(s: &str) -> &str {
}
}
/// Loads the trusted setup from JSON.
///
/// ## Note:
/// Currently we load both c-kzg and rust-eth-kzg trusted setup structs, because c-kzg is still being
/// used for 4844. Longer term we're planning to switch all KZG operations to the rust-eth-kzg
/// crate, and we'll be able to maintain a single trusted setup struct.
pub(crate) fn load_trusted_setup(
trusted_setup: &[u8],
) -> Result<(TrustedSetup, PeerDASTrustedSetup), Error> {
let ckzg_trusted_setup: TrustedSetup = serde_json::from_slice(trusted_setup)
.map_err(|e| Error::TrustedSetupError(format!("{e:?}")))?;
/// Loads the trusted setup from JSON bytes into a `rust_eth_kzg::TrustedSetup`.
pub(crate) fn load_trusted_setup(trusted_setup: &[u8]) -> Result<PeerDASTrustedSetup, Error> {
let trusted_setup_json = std::str::from_utf8(trusted_setup)
.map_err(|e| Error::TrustedSetupError(format!("{e:?}")))?;
let rkzg_trusted_setup = PeerDASTrustedSetup::from_json(trusted_setup_json);
Ok((ckzg_trusted_setup, rkzg_trusted_setup))
Ok(PeerDASTrustedSetup::from_json(trusted_setup_json))
}