Skip to main content

ethrex_crypto/
kzg.rs

1#[cfg(not(feature = "std"))]
2use alloc::string::String;
3
4// TODO: Currently, we cannot include the types crate independently of common because the crates are not yet split.
5// After issue #4596 ("Split types crate from common") is resolved, update this to import the types crate directly,
6// so that crypto/kzg.rs does not depend on common for type definitions.
7pub const BYTES_PER_FIELD_ELEMENT: usize = 32;
8pub const FIELD_ELEMENTS_PER_BLOB: usize = 4096;
9pub const BYTES_PER_BLOB: usize = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB;
10pub const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 2 * FIELD_ELEMENTS_PER_BLOB;
11pub const FIELD_ELEMENTS_PER_CELL: usize = 64;
12pub const BYTES_PER_CELL: usize = FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT;
13pub const CELLS_PER_EXT_BLOB: usize = FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL;
14
15// https://github.com/ethereum/c-kzg-4844?tab=readme-ov-file#precompute
16// For Risc0 we need this parameter to be 0.
17// For the rest we keep the value 8 due to optimizations.
18#[cfg(not(feature = "risc0"))]
19pub const KZG_PRECOMPUTE: u64 = 8;
20#[cfg(feature = "risc0")]
21pub const KZG_PRECOMPUTE: u64 = 0;
22
23type Bytes48 = [u8; 48];
24type Blob = [u8; BYTES_PER_BLOB];
25type Commitment = Bytes48;
26type Proof = Bytes48;
27
28/// Schedules the Ethereum trusted setup to load on a background thread so later KZG operations avoid the first-call cost.
29pub fn warm_up_trusted_setup() {
30    #[cfg(feature = "c-kzg")]
31    {
32        let _ = std::thread::Builder::new()
33            .name("kzg-warmup".into())
34            .spawn(|| {
35                std::hint::black_box(c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE));
36            });
37    }
38}
39
40#[derive(thiserror::Error, Debug)]
41pub enum KzgError {
42    #[cfg(feature = "c-kzg")]
43    #[error("c-kzg error: {0}")]
44    CKzg(#[from] c_kzg::Error),
45    #[cfg(feature = "kzg-rs")]
46    #[error("kzg-rs error: {0}")]
47    KzgRs(kzg_rs::KzgError),
48    #[cfg(not(feature = "c-kzg"))]
49    #[error("{0} is not supported without c-kzg feature enabled")]
50    NotSupportedWithoutCKZG(String),
51    #[error("unimplemented: {0}")]
52    Unimplemented(String),
53}
54
55#[cfg(feature = "kzg-rs")]
56impl From<kzg_rs::KzgError> for KzgError {
57    fn from(value: kzg_rs::KzgError) -> Self {
58        KzgError::KzgRs(value)
59    }
60}
61
62/// Verifies a KZG proof for blob committed data as defined by EIP-7594.
63#[allow(unused_variables)]
64pub fn verify_cell_kzg_proof_batch(
65    blobs: &[Blob],
66    commitments: &[Commitment],
67    cell_proof: &[Proof],
68) -> Result<bool, KzgError> {
69    #[cfg(not(feature = "c-kzg"))]
70    return Err(KzgError::NotSupportedWithoutCKZG(String::from(
71        "Cell proof verification",
72    )));
73    #[cfg(feature = "c-kzg")]
74    {
75        let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
76        let mut cells = Vec::new();
77        for blob in blobs {
78            let blob: c_kzg::Blob = (*blob).into();
79            let cells_blob = c_kzg_settings
80                .compute_cells(&blob)
81                .map_err(KzgError::CKzg)?;
82            cells.extend(*cells_blob);
83        }
84        c_kzg::KzgSettings::verify_cell_kzg_proof_batch(
85            c_kzg_settings,
86            &commitments
87                .iter()
88                .flat_map(|commitment| {
89                    std::iter::repeat_n((*commitment).into(), CELLS_PER_EXT_BLOB)
90                })
91                .collect::<Vec<_>>(),
92            &std::iter::repeat_n(0..CELLS_PER_EXT_BLOB as u64, blobs.len())
93                .flatten()
94                .collect::<Vec<_>>(),
95            &cells,
96            &cell_proof
97                .iter()
98                .map(|proof| (*proof).into())
99                .collect::<Vec<_>>(),
100        )
101        .map_err(KzgError::from)
102    }
103}
104
105/// Verifies a KZG proof for blob committed data, as defined by c-kzg-4844.
106pub fn verify_blob_kzg_proof(
107    blob: Blob,
108    commitment: Commitment,
109    proof: Proof,
110) -> Result<bool, KzgError> {
111    #[cfg(all(not(feature = "c-kzg"), not(feature = "kzg-rs")))]
112    {
113        let _blob = blob;
114        let _commitment = commitment;
115        let _proof = proof;
116        Err(KzgError::Unimplemented(
117            "One of features c-kzg or kzg-rs should be active".into(),
118        ))
119    }
120    #[cfg(all(not(feature = "c-kzg"), feature = "kzg-rs"))]
121    {
122        kzg_rs::KzgProof::verify_blob_kzg_proof(
123            kzg_rs::Blob(blob),
124            &kzg_rs::Bytes48(commitment),
125            &kzg_rs::Bytes48(proof),
126            &kzg_rs::get_kzg_settings(),
127        )
128        .map_err(KzgError::from)
129    }
130    #[cfg(feature = "c-kzg")]
131    {
132        let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
133        c_kzg_settings
134            .verify_blob_kzg_proof(&blob.into(), &commitment.into(), &proof.into())
135            .map_err(KzgError::from)
136    }
137}
138
139#[cfg(feature = "c-kzg")]
140pub fn verify_kzg_proof_batch(
141    blobs: &[Blob],
142    commitments: &[Commitment],
143    cell_proof: &[Proof],
144) -> Result<bool, KzgError> {
145    {
146        // perf note: c_kzg::Blob is repr C maybe a unsafe transmute improves perf if the collect were deemed costly
147        let blobs: Vec<_> = blobs.iter().map(|x| c_kzg::Blob::new(*x)).collect();
148        let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
149        c_kzg_settings
150            .verify_blob_kzg_proof_batch(
151                &blobs,
152                &commitments
153                    .iter()
154                    .map(|x| c_kzg::Bytes48::new(*x))
155                    .collect::<Vec<_>>(),
156                &cell_proof
157                    .iter()
158                    .map(|proof| (*proof).into())
159                    .collect::<Vec<_>>(),
160            )
161            .map_err(KzgError::from)
162    }
163}
164
165/// Verifies that p(z) = y given a commitment that corresponds to the polynomial p(x) and a KZG proof
166pub fn verify_kzg_proof(
167    commitment_bytes: [u8; 48],
168    z: [u8; 32],
169    y: [u8; 32],
170    proof_bytes: [u8; 48],
171) -> Result<bool, KzgError> {
172    #[cfg(all(not(feature = "c-kzg"), not(feature = "kzg-rs")))]
173    {
174        let _commitment_bytes = commitment_bytes;
175        let _z = z;
176        let _y = y;
177        let _proof_bytes = proof_bytes;
178        Err(KzgError::Unimplemented(
179            "One of features c-kzg or kzg-rs should be active".into(),
180        ))
181    }
182    #[cfg(all(not(feature = "c-kzg"), feature = "kzg-rs"))]
183    {
184        kzg_rs::KzgProof::verify_kzg_proof(
185            &kzg_rs::Bytes48(commitment_bytes),
186            &kzg_rs::Bytes32(z),
187            &kzg_rs::Bytes32(y),
188            &kzg_rs::Bytes48(proof_bytes),
189            &kzg_rs::get_kzg_settings(),
190        )
191        .map_err(KzgError::from)
192    }
193    #[cfg(feature = "c-kzg")]
194    {
195        let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
196        c_kzg_settings
197            .verify_kzg_proof(
198                &commitment_bytes.into(),
199                &z.into(),
200                &y.into(),
201                &proof_bytes.into(),
202            )
203            .map_err(KzgError::from)
204    }
205}
206
207#[cfg(feature = "c-kzg")]
208pub fn blob_to_kzg_commitment_and_proof(blob: &Blob) -> Result<(Commitment, Proof), KzgError> {
209    let blob: c_kzg::Blob = (*blob).into();
210
211    let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
212
213    let commitment = c_kzg::KzgSettings::blob_to_kzg_commitment(c_kzg_settings, &blob)?;
214
215    let commitment_bytes = commitment.to_bytes();
216    let proof = c_kzg_settings.compute_blob_kzg_proof(&blob, &commitment_bytes)?;
217
218    let proof_bytes = proof.to_bytes();
219
220    Ok((commitment_bytes.into_inner(), proof_bytes.into_inner()))
221}
222
223#[cfg(feature = "c-kzg")]
224pub fn blob_to_commitment_and_cell_proofs(
225    blob: &Blob,
226) -> Result<(Commitment, Vec<Proof>), KzgError> {
227    let c_kzg_settings = c_kzg::ethereum_kzg_settings(KZG_PRECOMPUTE);
228
229    let blob: c_kzg::Blob = (*blob).into();
230
231    let commitment = c_kzg::KzgSettings::blob_to_kzg_commitment(c_kzg_settings, &blob)?;
232
233    let commitment_bytes = commitment.to_bytes();
234
235    let (_cells, cell_proofs) = c_kzg_settings
236        .compute_cells_and_kzg_proofs(&blob)
237        .map_err(KzgError::CKzg)?;
238
239    let cell_proofs = cell_proofs.map(|p| p.to_bytes().into_inner());
240
241    Ok((commitment_bytes.into_inner(), cell_proofs.to_vec()))
242}