mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-26 17:23:41 +00:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user