From c5881649fe3a5d17f5b4bc3f2a0243316c523b6a Mon Sep 17 00:00:00 2001 From: sirse Date: Wed, 27 Nov 2024 17:49:37 +0200 Subject: [PATCH] Refactor DAS implementation to support various trusted setup sizes / cell sizes --- blst/benches/eip_7594.rs | 25 +- blst/benches/fk_20.rs | 32 +- blst/benches/kzg.rs | 24 +- blst/src/data_availability_sampling.rs | 4 +- blst/src/eip_4844.rs | 89 ++- blst/src/eip_7594.rs | 305 +------- blst/src/types/kzg_settings.rs | 151 +++- blst/src/utils.rs | 96 +-- blst/tests/eip_7594.rs | 128 ++-- blst/tests/fk20_proofs.rs | 86 +-- blst/tests/kzg_proofs.rs | 42 +- kzg-bench/src/benches/das.rs | 6 +- kzg-bench/src/benches/eip_7594.rs | 96 +-- kzg-bench/src/benches/fk20.rs | 79 +- kzg-bench/src/benches/kzg.rs | 54 +- kzg-bench/src/tests/c_bindings.rs | 69 +- kzg-bench/src/tests/das.rs | 6 +- kzg-bench/src/tests/eip_7594.rs | 114 +-- kzg-bench/src/tests/fk20_proofs.rs | 285 +++---- kzg-bench/src/tests/kzg_proofs.rs | 118 ++- kzg/src/das.rs | 895 ++++++++++++++++++++++ kzg/src/eip_4844.rs | 110 +-- kzg/src/eip_7594.rs | 985 ------------------------- kzg/src/eth/c_bindings.rs | 441 +++++++++++ kzg/src/eth/mod.rs | 26 + kzg/src/fk20_proof.rs | 90 --- kzg/src/lib.rs | 15 +- run-c-kzg-4844-benches.sh | 16 +- 28 files changed, 2033 insertions(+), 2354 deletions(-) create mode 100644 kzg/src/das.rs delete mode 100644 kzg/src/eip_7594.rs create mode 100644 kzg/src/eth/c_bindings.rs create mode 100644 kzg/src/eth/mod.rs delete mode 100644 kzg/src/fk20_proof.rs diff --git a/blst/benches/eip_7594.rs b/blst/benches/eip_7594.rs index e699dce55..70ac24db1 100644 --- a/blst/benches/eip_7594.rs +++ b/blst/benches/eip_7594.rs @@ -1,33 +1,14 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use kzg::{ - eip_4844::{blob_to_kzg_commitment_rust, bytes_to_blob}, - eip_7594::{ - compute_cells_and_kzg_proofs, recover_cells_and_kzg_proofs, verify_cell_kzg_proof_batch, - }, -}; +use kzg::eip_4844::{blob_to_kzg_commitment_rust, bytes_to_blob}; use kzg_bench::benches::eip_7594::bench_eip_7594; -use rust_kzg_blst::{ - eip_4844::load_trusted_setup_filename_rust, - types::{ - fft_settings::FsFFTSettings, - fp::FsFp, - fr::FsFr, - g1::{FsG1, FsG1Affine}, - g2::FsG2, - kzg_settings::FsKZGSettings, - poly::FsPoly, - }, -}; +use rust_kzg_blst::{eip_4844::load_trusted_setup_filename_rust, eip_7594::BlstBackend}; fn bench_eip_7594_(c: &mut Criterion) { - bench_eip_7594::( + bench_eip_7594::( c, &load_trusted_setup_filename_rust, &bytes_to_blob, &blob_to_kzg_commitment_rust, - &compute_cells_and_kzg_proofs, - &recover_cells_and_kzg_proofs, - &verify_cell_kzg_proof_batch, ); } diff --git a/blst/benches/fk_20.rs b/blst/benches/fk_20.rs index e7586d37d..04dfe044a 100644 --- a/blst/benches/fk_20.rs +++ b/blst/benches/fk_20.rs @@ -1,43 +1,17 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::fk20::{bench_fk_multi_da, bench_fk_single_da}; -use rust_kzg_blst::types::fft_settings::FsFFTSettings; +use rust_kzg_blst::eip_7594::BlstBackend; use rust_kzg_blst::types::fk20_multi_settings::FsFK20MultiSettings; use rust_kzg_blst::types::fk20_single_settings::FsFK20SingleSettings; -use rust_kzg_blst::types::fp::FsFp; -use rust_kzg_blst::types::fr::FsFr; -use rust_kzg_blst::types::g1::{FsG1, FsG1Affine}; -use rust_kzg_blst::types::g2::FsG2; -use rust_kzg_blst::types::kzg_settings::FsKZGSettings; -use rust_kzg_blst::types::poly::FsPoly; use rust_kzg_blst::utils::generate_trusted_setup; fn bench_fk_single_da_(c: &mut Criterion) { - bench_fk_single_da::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20SingleSettings, - FsFp, - FsG1Affine, - >(c, &generate_trusted_setup) + bench_fk_single_da::(c, &generate_trusted_setup) } fn bench_fk_multi_da_(c: &mut Criterion) { - bench_fk_multi_da::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20MultiSettings, - FsFp, - FsG1Affine, - >(c, &generate_trusted_setup) + bench_fk_multi_da::(c, &generate_trusted_setup) } criterion_group! { diff --git a/blst/benches/kzg.rs b/blst/benches/kzg.rs index 05f5af89f..62f20e45b 100644 --- a/blst/benches/kzg.rs +++ b/blst/benches/kzg.rs @@ -1,32 +1,14 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::kzg::{bench_commit_to_poly, bench_compute_proof_single}; -use rust_kzg_blst::types::fft_settings::FsFFTSettings; -use rust_kzg_blst::types::fp::FsFp; -use rust_kzg_blst::types::fr::FsFr; -use rust_kzg_blst::types::g1::{FsG1, FsG1Affine}; -use rust_kzg_blst::types::g2::FsG2; -use rust_kzg_blst::types::kzg_settings::FsKZGSettings; -use rust_kzg_blst::types::poly::FsPoly; +use rust_kzg_blst::eip_7594::BlstBackend; use rust_kzg_blst::utils::generate_trusted_setup; fn bench_commit_to_poly_(c: &mut Criterion) { - bench_commit_to_poly::( - c, - &generate_trusted_setup, - ) + bench_commit_to_poly::(c, &generate_trusted_setup) } fn bench_compute_proof_single_(c: &mut Criterion) { - bench_compute_proof_single::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFp, - FsG1Affine, - >(c, &generate_trusted_setup) + bench_compute_proof_single::(c, &generate_trusted_setup) } criterion_group! { diff --git a/blst/src/data_availability_sampling.rs b/blst/src/data_availability_sampling.rs index 56a772026..4ee26fc86 100644 --- a/blst/src/data_availability_sampling.rs +++ b/blst/src/data_availability_sampling.rs @@ -4,7 +4,7 @@ use alloc::string::String; use alloc::vec::Vec; use core::cmp::Ordering; -use kzg::{Fr, DAS}; +use kzg::{DASExtension, Fr}; use crate::types::fft_settings::FsFFTSettings; use crate::types::fr::FsFr; @@ -71,7 +71,7 @@ impl FsFFTSettings { } } -impl DAS for FsFFTSettings { +impl DASExtension for FsFFTSettings { /// Polynomial extension for data availability sampling. Given values of even indices, produce values of odd indices. /// FFTSettings must hold at least 2 times the roots of provided evens. /// The resulting odd indices make the right half of the coefficients of the inverse FFT of the combined indices zero. diff --git a/blst/src/eip_4844.rs b/blst/src/eip_4844.rs index 8383826a3..da8bef5a1 100644 --- a/blst/src/eip_4844.rs +++ b/blst/src/eip_4844.rs @@ -6,8 +6,13 @@ use core::ptr; use kzg::eip_4844::{ blob_to_kzg_commitment_rust, compute_blob_kzg_proof_rust, compute_kzg_proof_rust, load_trusted_setup_rust, verify_blob_kzg_proof_batch_rust, verify_blob_kzg_proof_rust, - verify_kzg_proof_rust, FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB, + verify_kzg_proof_rust, BYTES_PER_G1, FIELD_ELEMENTS_PER_BLOB, TRUSTED_SETUP_NUM_G1_POINTS, + TRUSTED_SETUP_NUM_G2_POINTS, +}; +use kzg::eth::c_bindings::{ + Blob, Bytes32, Bytes48, CKZGSettings, CKzgRet, KZGCommitment, KZGProof, }; +use kzg::eth::{FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB}; use kzg::{cfg_into_iter, Fr, G1}; #[cfg(feature = "std")] use libc::FILE; @@ -19,18 +24,12 @@ use std::io::Read; #[cfg(feature = "std")] use kzg::eip_4844::load_trusted_setup_string; -use kzg::eip_4844::{ - Blob, Bytes32, Bytes48, CKZGSettings, KZGCommitment, KZGProof, BYTES_PER_G1, C_KZG_RET, - C_KZG_RET_BADARGS, C_KZG_RET_OK, FIELD_ELEMENTS_PER_BLOB, TRUSTED_SETUP_NUM_G1_POINTS, - TRUSTED_SETUP_NUM_G2_POINTS, -}; - use crate::types::fr::FsFr; use crate::types::g1::FsG1; +use crate::types::kzg_settings::FsKZGSettings; use crate::utils::{ - deserialize_blob, handle_ckzg_badargs, kzg_settings_to_c, kzg_settings_to_rust, - PRECOMPUTATION_TABLES, + deserialize_blob, handle_ckzg_badargs, kzg_settings_to_c, PRECOMPUTATION_TABLES, }; #[cfg(feature = "parallel")] @@ -56,18 +55,18 @@ pub unsafe extern "C" fn blob_to_kzg_commitment( out: *mut KZGCommitment, blob: *const Blob, s: &CKZGSettings, -) -> C_KZG_RET { +) -> CKzgRet { if TRUSTED_SETUP_NUM_G1_POINTS == 0 { // FIXME: load_trusted_setup should set this value, but if not, it fails TRUSTED_SETUP_NUM_G1_POINTS = FIELD_ELEMENTS_PER_BLOB }; let deserialized_blob = handle_ckzg_badargs!(deserialize_blob(blob)); - let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let settings: FsKZGSettings = handle_ckzg_badargs!(s.try_into()); let tmp = handle_ckzg_badargs!(blob_to_kzg_commitment_rust(&deserialized_blob, &settings)); (*out).bytes = tmp.to_bytes(); - C_KZG_RET_OK + CKzgRet::Ok } /// # Safety @@ -81,7 +80,7 @@ pub unsafe extern "C" fn load_trusted_setup( g2_monomial_bytes: *const u8, num_g2_monomial_bytes: u64, _precompute: u64, -) -> C_KZG_RET { +) -> CKzgRet { *out = CKZGSettings { brp_roots_of_unity: ptr::null_mut(), roots_of_unity: ptr::null_mut(), @@ -113,7 +112,7 @@ pub unsafe extern "C" fn load_trusted_setup( PRECOMPUTATION_TABLES.save_precomputation(settings.precomputation.take(), &c_settings); *out = c_settings; - C_KZG_RET_OK + CKzgRet::Ok } /// # Safety @@ -122,7 +121,7 @@ pub unsafe extern "C" fn load_trusted_setup( pub unsafe extern "C" fn load_trusted_setup_file( out: *mut CKZGSettings, in_: *mut FILE, -) -> C_KZG_RET { +) -> CKzgRet { *out = CKZGSettings { brp_roots_of_unity: ptr::null_mut(), roots_of_unity: ptr::null_mut(), @@ -146,7 +145,7 @@ pub unsafe extern "C" fn load_trusted_setup_file( // Helps pass the Java test "shouldThrowExceptionOnIncorrectTrustedSetupFromFile", // as well as 5 others that pass only if this one passes (likely because Java doesn't // deallocate its KZGSettings pointer when no exception is thrown). - return C_KZG_RET_BADARGS; + return CKzgRet::BadArgs; } let mut settings = handle_ckzg_badargs!(load_trusted_setup_rust( &g1_monomial_bytes, @@ -160,7 +159,7 @@ pub unsafe extern "C" fn load_trusted_setup_file( *out = c_settings; - C_KZG_RET_OK + CKzgRet::Ok } /// # Safety @@ -170,14 +169,14 @@ pub unsafe extern "C" fn compute_blob_kzg_proof( blob: *const Blob, commitment_bytes: *const Bytes48, s: &CKZGSettings, -) -> C_KZG_RET { +) -> CKzgRet { let deserialized_blob = match deserialize_blob(blob) { Ok(value) => value, Err(err) => return err, }; let commitment_g1 = handle_ckzg_badargs!(FsG1::from_bytes(&(*commitment_bytes).bytes)); - let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let settings: FsKZGSettings = handle_ckzg_badargs!(s.try_into()); let proof = handle_ckzg_badargs!(compute_blob_kzg_proof_rust( &deserialized_blob, &commitment_g1, @@ -185,7 +184,7 @@ pub unsafe extern "C" fn compute_blob_kzg_proof( )); (*out).bytes = proof.to_bytes(); - C_KZG_RET_OK + CKzgRet::Ok } /// # Safety @@ -270,13 +269,13 @@ pub unsafe extern "C" fn verify_kzg_proof( y_bytes: *const Bytes32, proof_bytes: *const Bytes48, s: &CKZGSettings, -) -> C_KZG_RET { +) -> CKzgRet { let frz = handle_ckzg_badargs!(FsFr::from_bytes(&(*z_bytes).bytes)); let fry = handle_ckzg_badargs!(FsFr::from_bytes(&(*y_bytes).bytes)); let g1commitment = handle_ckzg_badargs!(FsG1::from_bytes(&(*commitment_bytes).bytes)); let g1proof = handle_ckzg_badargs!(FsG1::from_bytes(&(*proof_bytes).bytes)); - let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let settings: FsKZGSettings = handle_ckzg_badargs!(s.try_into()); let result = handle_ckzg_badargs!(verify_kzg_proof_rust( &g1commitment, @@ -287,7 +286,7 @@ pub unsafe extern "C" fn verify_kzg_proof( )); *ok = result; - C_KZG_RET_OK + CKzgRet::Ok } /// # Safety @@ -298,11 +297,11 @@ pub unsafe extern "C" fn verify_blob_kzg_proof( commitment_bytes: *const Bytes48, proof_bytes: *const Bytes48, s: &CKZGSettings, -) -> C_KZG_RET { +) -> CKzgRet { let deserialized_blob = handle_ckzg_badargs!(deserialize_blob(blob)); let commitment_g1 = handle_ckzg_badargs!(FsG1::from_bytes(&(*commitment_bytes).bytes)); let proof_g1 = handle_ckzg_badargs!(FsG1::from_bytes(&(*proof_bytes).bytes)); - let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let settings: FsKZGSettings = handle_ckzg_badargs!(s.try_into()); let result = handle_ckzg_badargs!(verify_blob_kzg_proof_rust( &deserialized_blob, @@ -312,7 +311,7 @@ pub unsafe extern "C" fn verify_blob_kzg_proof( )); *ok = result; - C_KZG_RET_OK + CKzgRet::Ok } /// # Safety @@ -324,31 +323,29 @@ pub unsafe extern "C" fn verify_blob_kzg_proof_batch( proofs_bytes: *const Bytes48, n: usize, s: &CKZGSettings, -) -> C_KZG_RET { +) -> CKzgRet { let raw_blobs = core::slice::from_raw_parts(blobs, n); let raw_commitments = core::slice::from_raw_parts(commitments_bytes, n); let raw_proofs = core::slice::from_raw_parts(proofs_bytes, n); - let deserialized_blobs: Result>, C_KZG_RET> = cfg_into_iter!(raw_blobs) - .map(|raw_blob| deserialize_blob(raw_blob).map_err(|_| C_KZG_RET_BADARGS)) + let deserialized_blobs: Result>, CKzgRet> = cfg_into_iter!(raw_blobs) + .map(|raw_blob| deserialize_blob(raw_blob).map_err(|_| CKzgRet::BadArgs)) .collect(); - let commitments_g1: Result, C_KZG_RET> = cfg_into_iter!(raw_commitments) - .map(|raw_commitment| { - FsG1::from_bytes(&raw_commitment.bytes).map_err(|_| C_KZG_RET_BADARGS) - }) + let commitments_g1: Result, CKzgRet> = cfg_into_iter!(raw_commitments) + .map(|raw_commitment| FsG1::from_bytes(&raw_commitment.bytes).map_err(|_| CKzgRet::BadArgs)) .collect(); - let proofs_g1: Result, C_KZG_RET> = cfg_into_iter!(raw_proofs) - .map(|raw_proof| FsG1::from_bytes(&raw_proof.bytes).map_err(|_| C_KZG_RET_BADARGS)) + let proofs_g1: Result, CKzgRet> = cfg_into_iter!(raw_proofs) + .map(|raw_proof| FsG1::from_bytes(&raw_proof.bytes).map_err(|_| CKzgRet::BadArgs)) .collect(); if let (Ok(blobs), Ok(commitments), Ok(proofs)) = (deserialized_blobs, commitments_g1, proofs_g1) { - let settings = match kzg_settings_to_rust(s) { + let settings: FsKZGSettings = match s.try_into() { Ok(value) => value, - Err(_) => return C_KZG_RET_BADARGS, + Err(_) => return CKzgRet::BadArgs, }; let result = @@ -356,13 +353,13 @@ pub unsafe extern "C" fn verify_blob_kzg_proof_batch( if let Ok(result) = result { *ok = result; - C_KZG_RET_OK + CKzgRet::Ok } else { - C_KZG_RET_BADARGS + CKzgRet::BadArgs } } else { *ok = false; - C_KZG_RET_BADARGS + CKzgRet::BadArgs } } @@ -374,7 +371,7 @@ pub unsafe extern "C" fn compute_kzg_proof( blob: *const Blob, z_bytes: *const Bytes32, s: &CKZGSettings, -) -> C_KZG_RET { +) -> CKzgRet { let deserialized_blob = match deserialize_blob(blob) { Ok(value) => value, Err(err) => return err, @@ -382,21 +379,21 @@ pub unsafe extern "C" fn compute_kzg_proof( let frz = match FsFr::from_bytes(&(*z_bytes).bytes) { Ok(value) => value, - Err(_) => return C_KZG_RET_BADARGS, + Err(_) => return CKzgRet::BadArgs, }; - let settings = match kzg_settings_to_rust(s) { + let settings: FsKZGSettings = match s.try_into() { Ok(value) => value, - Err(_) => return C_KZG_RET_BADARGS, + Err(_) => return CKzgRet::BadArgs, }; let (proof_out_tmp, fry_tmp) = match compute_kzg_proof_rust(&deserialized_blob, &frz, &settings) { Ok(value) => value, - Err(_) => return C_KZG_RET_BADARGS, + Err(_) => return CKzgRet::BadArgs, }; (*proof_out).bytes = proof_out_tmp.to_bytes(); (*y_out).bytes = fry_tmp.to_bytes(); - C_KZG_RET_OK + CKzgRet::Ok } diff --git a/blst/src/eip_7594.rs b/blst/src/eip_7594.rs index 3caabac64..2df8b46f6 100644 --- a/blst/src/eip_7594.rs +++ b/blst/src/eip_7594.rs @@ -1,284 +1,29 @@ extern crate alloc; -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; - -use crate::{ - types::g1::FsG1, - utils::{deserialize_blob, kzg_settings_to_rust}, -}; -use kzg::{ - eip_4844::{ - Blob, Bytes48, CKZGSettings, Cell, KZGProof, BYTES_PER_FIELD_ELEMENT, CELLS_PER_EXT_BLOB, - C_KZG_RET, C_KZG_RET_BADARGS, C_KZG_RET_OK, FIELD_ELEMENTS_PER_CELL, - }, - Fr, G1, -}; - -use crate::types::{fr::FsFr, kzg_settings::FsKZGSettings}; - -pub fn compute_cells_and_kzg_proofs_rust( - cells: Option<&mut [[FsFr; FIELD_ELEMENTS_PER_CELL]]>, - proofs: Option<&mut [FsG1]>, - blob: &[FsFr], - s: &FsKZGSettings, -) -> Result<(), String> { - kzg::eip_7594::compute_cells_and_kzg_proofs(cells, proofs, blob, s) -} - -pub fn recover_cells_and_kzg_proofs_rust( - recovered_cells: &mut [[FsFr; FIELD_ELEMENTS_PER_CELL]], - recovered_proofs: Option<&mut [FsG1]>, - cell_indicies: &[usize], - cells: &[[FsFr; FIELD_ELEMENTS_PER_CELL]], - s: &FsKZGSettings, -) -> Result<(), String> { - kzg::eip_7594::recover_cells_and_kzg_proofs( - recovered_cells, - recovered_proofs, - cell_indicies, - cells, - s, - ) +use kzg::EcBackend; + +use crate::types::fft_settings::FsFFTSettings; +use crate::types::fp::FsFp; +use crate::types::g1::FsG1; +use crate::types::g1::FsG1Affine; +use crate::types::g2::FsG2; +use crate::types::kzg_settings::FsKZGSettings; +use crate::types::poly::FsPoly; +use kzg::c_bindings_eip7594; + +use crate::types::fr::FsFr; + +pub struct BlstBackend; + +impl EcBackend for BlstBackend { + type Fr = FsFr; + type G1Fp = FsFp; + type G1Affine = FsG1Affine; + type G1 = FsG1; + type G2 = FsG2; + type Poly = FsPoly; + type FFTSettings = FsFFTSettings; + type KZGSettings = FsKZGSettings; } -pub fn verify_cell_kzg_proof_batch_rust( - commitments: &[FsG1], - cell_indices: &[usize], - cells: &[[FsFr; FIELD_ELEMENTS_PER_CELL]], - proofs: &[FsG1], - s: &FsKZGSettings, -) -> Result { - kzg::eip_7594::verify_cell_kzg_proof_batch(commitments, cell_indices, cells, proofs, s) -} - -/// # Safety -#[no_mangle] -pub unsafe extern "C" fn compute_cells_and_kzg_proofs( - cells: *mut Cell, - proofs: *mut KZGProof, - blob: *const Blob, - settings: *const CKZGSettings, -) -> C_KZG_RET { - unsafe fn inner( - cells: *mut Cell, - proofs: *mut KZGProof, - blob: *const Blob, - settings: *const CKZGSettings, - ) -> Result<(), String> { - let mut cells_rs = if cells.is_null() { - None - } else { - Some(vec![ - [FsFr::default(); FIELD_ELEMENTS_PER_CELL]; - CELLS_PER_EXT_BLOB - ]) - }; - let mut proofs_rs = if proofs.is_null() { - None - } else { - Some(vec![FsG1::default(); CELLS_PER_EXT_BLOB]) - }; - - let blob = deserialize_blob(blob).map_err(|_| "Invalid blob".to_string())?; - let settings = kzg_settings_to_rust(&*settings)?; - - compute_cells_and_kzg_proofs_rust( - cells_rs.as_deref_mut(), - proofs_rs.as_deref_mut(), - &blob, - &settings, - )?; - - if let Some(cells_rs) = cells_rs { - let cells = core::slice::from_raw_parts_mut(cells, CELLS_PER_EXT_BLOB); - for (cell_index, cell) in cells_rs.iter().enumerate() { - for (fr_index, fr) in cell.iter().enumerate() { - cells[cell_index].bytes[(fr_index * BYTES_PER_FIELD_ELEMENT) - ..((fr_index + 1) * BYTES_PER_FIELD_ELEMENT)] - .copy_from_slice(&fr.to_bytes()); - } - } - } - - if let Some(proofs_rs) = proofs_rs { - let proofs = core::slice::from_raw_parts_mut(proofs, CELLS_PER_EXT_BLOB); - for (proof_index, proof) in proofs_rs.iter().enumerate() { - proofs[proof_index].bytes.copy_from_slice(&proof.to_bytes()); - } - } - - Ok(()) - } - - match inner(cells, proofs, blob, settings) { - Ok(()) => C_KZG_RET_OK, - Err(_) => C_KZG_RET_BADARGS, - } -} - -/// # Safety -#[no_mangle] -pub unsafe extern "C" fn recover_cells_and_kzg_proofs( - recovered_cells: *mut Cell, - recovered_proofs: *mut KZGProof, - cell_indices: *const u64, - cells: *const Cell, - num_cells: u64, - s: *const CKZGSettings, -) -> C_KZG_RET { - unsafe fn inner( - recovered_cells: *mut Cell, - recovered_proofs: *mut KZGProof, - cell_indices: *const u64, - cells: *const Cell, - num_cells: u64, - s: *const CKZGSettings, - ) -> Result<(), String> { - let mut recovered_cells_rs = - vec![[FsFr::default(); FIELD_ELEMENTS_PER_CELL]; CELLS_PER_EXT_BLOB]; - - let mut recovered_proofs_rs = if recovered_proofs.is_null() { - None - } else { - Some(vec![FsG1::default(); CELLS_PER_EXT_BLOB]) - }; - - let cell_indicies = core::slice::from_raw_parts(cell_indices, num_cells as usize) - .iter() - .map(|it| *it as usize) - .collect::>(); - let cells = core::slice::from_raw_parts(cells, num_cells as usize) - .iter() - .map(|it| -> Result<[FsFr; FIELD_ELEMENTS_PER_CELL], String> { - it.bytes - .chunks(BYTES_PER_FIELD_ELEMENT) - .map(FsFr::from_bytes) - .collect::, String>>() - .and_then(|frs| { - frs.try_into() - .map_err(|_| "Invalid field element count per cell".to_string()) - }) - }) - .collect::, String>>()?; - let settings = kzg_settings_to_rust(&*s)?; - - recover_cells_and_kzg_proofs_rust( - &mut recovered_cells_rs, - recovered_proofs_rs.as_deref_mut(), - &cell_indicies, - &cells, - &settings, - )?; - - let recovered_cells = core::slice::from_raw_parts_mut(recovered_cells, CELLS_PER_EXT_BLOB); - for (cell_c, cell_rs) in recovered_cells.iter_mut().zip(recovered_cells_rs.iter()) { - cell_c.bytes.copy_from_slice( - &cell_rs - .iter() - .flat_map(|fr| fr.to_bytes()) - .collect::>(), - ); - } - - if let Some(recovered_proofs_rs) = recovered_proofs_rs { - let recovered_proofs = - core::slice::from_raw_parts_mut(recovered_proofs, CELLS_PER_EXT_BLOB); - - for (proof_c, proof_rs) in recovered_proofs.iter_mut().zip(recovered_proofs_rs.iter()) { - proof_c.bytes = proof_rs.to_bytes(); - } - } - - Ok(()) - } - - match inner( - recovered_cells, - recovered_proofs, - cell_indices, - cells, - num_cells, - s, - ) { - Ok(()) => C_KZG_RET_OK, - Err(_) => C_KZG_RET_BADARGS, - } -} - -/// # Safety -#[no_mangle] -pub unsafe extern "C" fn verify_cell_kzg_proof_batch( - ok: *mut bool, - commitments_bytes: *const Bytes48, - cell_indices: *const u64, - cells: *const Cell, - proofs_bytes: *const Bytes48, - num_cells: u64, - s: *const CKZGSettings, -) -> C_KZG_RET { - unsafe fn inner( - ok: *mut bool, - commitments_bytes: *const Bytes48, - cell_indices: *const u64, - cells: *const Cell, - proofs_bytes: *const Bytes48, - num_cells: u64, - s: *const CKZGSettings, - ) -> Result<(), String> { - let commitments = core::slice::from_raw_parts(commitments_bytes, num_cells as usize) - .iter() - .map(|bytes| FsG1::from_bytes(&bytes.bytes)) - .collect::, String>>()?; - - let cell_indices = core::slice::from_raw_parts(cell_indices, num_cells as usize) - .iter() - .map(|it| *it as usize) - .collect::>(); - - let cells = core::slice::from_raw_parts(cells, num_cells as usize) - .iter() - .map(|it| -> Result<[FsFr; FIELD_ELEMENTS_PER_CELL], String> { - it.bytes - .chunks(BYTES_PER_FIELD_ELEMENT) - .map(FsFr::from_bytes) - .collect::, String>>() - .and_then(|frs| { - frs.try_into() - .map_err(|_| "Invalid field element count per cell".to_string()) - }) - }) - .collect::, String>>()?; - - let proofs = core::slice::from_raw_parts(proofs_bytes, num_cells as usize) - .iter() - .map(|bytes| FsG1::from_bytes(&bytes.bytes)) - .collect::, String>>()?; - - let settings = kzg_settings_to_rust(&*s)?; - - *ok = verify_cell_kzg_proof_batch_rust( - &commitments, - &cell_indices, - &cells, - &proofs, - &settings, - )?; - - Ok(()) - } - - match inner( - ok, - commitments_bytes, - cell_indices, - cells, - proofs_bytes, - num_cells, - s, - ) { - Ok(()) => C_KZG_RET_OK, - Err(_) => C_KZG_RET_BADARGS, - } -} +c_bindings_eip7594!(BlstBackend); diff --git a/blst/src/types/kzg_settings.rs b/blst/src/types/kzg_settings.rs index ad65205df..460744917 100644 --- a/blst/src/types/kzg_settings.rs +++ b/blst/src/types/kzg_settings.rs @@ -4,12 +4,10 @@ use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::{vec, vec::Vec}; -use kzg::eip_4844::{ - FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB, - TRUSTED_SETUP_NUM_G2_POINTS, -}; +use kzg::eth::c_bindings::CKZGSettings; +use kzg::eth::{self, FIELD_ELEMENTS_PER_EXT_BLOB}; use kzg::msm::precompute::{precompute, PrecomputationTable}; -use kzg::{FFTFr, FFTSettings, Fr, G1Mul, G2Mul, KZGSettings, Poly, G1, G2}; +use kzg::{FFTFr, FFTSettings, Fr, G1Mul, G2Mul, KZGSettings, Poly, Preset, G1, G2}; use crate::consts::{G1_GENERATOR, G2_GENERATOR}; use crate::fft_g1::fft_g1_fast; @@ -19,6 +17,7 @@ use crate::types::fr::FsFr; use crate::types::g1::FsG1; use crate::types::g2::FsG2; use crate::types::poly::FsPoly; +use crate::utils::PRECOMPUTATION_TABLES; use super::fp::FsFp; use super::g1::FsG1Affine; @@ -33,28 +32,30 @@ pub struct FsKZGSettings { pub x_ext_fft_columns: Vec>, } -fn g1_fft(output: &mut [FsG1], input: &[FsG1], s: &FsFFTSettings) -> Result<(), String> { - // g1_t *out, const g1_t *in, size_t n, const KZGSettings *s - +fn g1_fft(output: &mut [FsG1], input: &[FsG1], s: &FsFFTSettings) -> Result<(), String> { /* Ensure the length is valid */ - if input.len() > FIELD_ELEMENTS_PER_EXT_BLOB || !input.len().is_power_of_two() { + if input.len() > P::FIELD_ELEMENTS_PER_EXT_BLOB || !input.len().is_power_of_two() { return Err("Invalid input size".to_string()); } - let roots_stride = FIELD_ELEMENTS_PER_EXT_BLOB / input.len(); + let roots_stride = P::FIELD_ELEMENTS_PER_EXT_BLOB / input.len(); fft_g1_fast(output, input, 1, &s.roots_of_unity, roots_stride); Ok(()) } -fn toeplitz_part_1(output: &mut [FsG1], x: &[FsG1], s: &FsFFTSettings) -> Result<(), String> { +fn toeplitz_part_1( + output: &mut [FsG1], + x: &[FsG1], + s: &FsFFTSettings, +) -> Result<(), String> { let n = x.len(); let n2 = n * 2; let mut x_ext = vec![FsG1::identity(); n2]; x_ext[..n].copy_from_slice(x); - g1_fft(output, &x_ext, s)?; + g1_fft::

(output, &x_ext, s)?; Ok(()) } @@ -66,14 +67,27 @@ impl KZGSettings for g2_monomial: &[FsG2], fft_settings: &FsFFTSettings, ) -> Result { - if g1_monomial.len() != FIELD_ELEMENTS_PER_BLOB - || g1_lagrange_brp.len() != FIELD_ELEMENTS_PER_BLOB - || g2_monomial.len() != TRUSTED_SETUP_NUM_G2_POINTS + Self::new_for_preset::<{ eth::FIELD_ELEMENTS_PER_CELL }, eth::Mainnet>( + g1_monomial, + g1_lagrange_brp, + g2_monomial, + fft_settings, + ) + } + + fn new_for_preset( + g1_monomial: &[FsG1], + g1_lagrange_brp: &[FsG1], + g2_monomial: &[FsG2], + fft_settings: &FsFFTSettings, + ) -> Result { + if g1_monomial.len() != P::FIELD_ELEMENTS_PER_BLOB + || g1_lagrange_brp.len() != P::FIELD_ELEMENTS_PER_BLOB { return Err("Length does not match FIELD_ELEMENTS_PER_BLOB".to_string()); } - let n = FIELD_ELEMENTS_PER_EXT_BLOB / 2; + let n = P::FIELD_ELEMENTS_PER_EXT_BLOB / 2; let k = n / FIELD_ELEMENTS_PER_CELL; let k2 = 2 * k; @@ -89,32 +103,13 @@ impl KZGSettings for } x[k - 1] = FsG1::identity(); - toeplitz_part_1(&mut points, &x, fft_settings)?; + toeplitz_part_1::

(&mut points, &x, fft_settings)?; for row in 0..k2 { x_ext_fft_columns[row][offset] = points[row]; } } - // for (size_t offset = 0; offset < FIELD_ELEMENTS_PER_CELL; offset++) { - // /* Compute x, sections of the g1 values */ - // size_t start = n - FIELD_ELEMENTS_PER_CELL - 1 - offset; - // for (size_t i = 0; i < k - 1; i++) { - // size_t j = start - i * FIELD_ELEMENTS_PER_CELL; - // x[i] = s->g1_values_monomial[j]; - // } - // x[k - 1] = G1_IDENTITY; - - // /* Compute points, the fft of an extended x */ - // ret = toeplitz_part_1(points, x, k, s); - // if (ret != C_KZG_OK) goto out; - - // /* Reorganize from rows into columns */ - // for (size_t row = 0; row < k2; row++) { - // s->x_ext_fft_columns[row][offset] = points[row]; - // } - // } - Ok(Self { g1_values_monomial: g1_monomial.to_vec(), g1_values_lagrange_brp: g1_lagrange_brp.to_vec(), @@ -314,3 +309,87 @@ impl KZGSettings for &self.x_ext_fft_columns[index] } } + +impl<'a> TryFrom<&'a CKZGSettings> for FsKZGSettings { + type Error = String; + + fn try_from(settings: &'a CKZGSettings) -> Result { + let roots_of_unity = unsafe { + core::slice::from_raw_parts(settings.roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB + 1) + .iter() + .map(|r| FsFr(*r)) + .collect::>() + }; + + let brp_roots_of_unity = unsafe { + core::slice::from_raw_parts(settings.brp_roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB) + .iter() + .map(|r| FsFr(*r)) + .collect::>() + }; + + let reverse_roots_of_unity = unsafe { + core::slice::from_raw_parts( + settings.reverse_roots_of_unity, + FIELD_ELEMENTS_PER_EXT_BLOB + 1, + ) + .iter() + .map(|r| FsFr(*r)) + .collect::>() + }; + + let fft_settings = FsFFTSettings { + max_width: FIELD_ELEMENTS_PER_EXT_BLOB, + root_of_unity: roots_of_unity[1], + roots_of_unity, + brp_roots_of_unity, + reverse_roots_of_unity, + }; + + Ok(FsKZGSettings { + fs: fft_settings, + g1_values_monomial: unsafe { + core::slice::from_raw_parts( + settings.g1_values_monomial, + eth::FIELD_ELEMENTS_PER_BLOB, + ) + } + .iter() + .map(|r| FsG1(*r)) + .collect::>(), + g1_values_lagrange_brp: unsafe { + core::slice::from_raw_parts( + settings.g1_values_lagrange_brp, + eth::FIELD_ELEMENTS_PER_BLOB, + ) + } + .iter() + .map(|r| FsG1(*r)) + .collect::>(), + g2_values_monomial: unsafe { + core::slice::from_raw_parts( + settings.g2_values_monomial, + eth::TRUSTED_SETUP_NUM_G2_POINTS, + ) + } + .iter() + .map(|r| FsG2(*r)) + .collect::>(), + x_ext_fft_columns: unsafe { + core::slice::from_raw_parts( + settings.x_ext_fft_columns, + 2 * ((FIELD_ELEMENTS_PER_EXT_BLOB / 2) / eth::FIELD_ELEMENTS_PER_CELL), + ) + } + .iter() + .map(|it| { + unsafe { core::slice::from_raw_parts(*it, eth::FIELD_ELEMENTS_PER_CELL) } + .iter() + .map(|it| FsG1(*it)) + .collect::>() + }) + .collect::>(), + precomputation: unsafe { PRECOMPUTATION_TABLES.get_precomputation(settings) }, + }) + } +} diff --git a/blst/src/utils.rs b/blst/src/utils.rs index b1263c55b..1605c6c2b 100644 --- a/blst/src/utils.rs +++ b/blst/src/utils.rs @@ -1,18 +1,13 @@ extern crate alloc; use alloc::boxed::Box; -use alloc::string::String; use alloc::vec::Vec; -use kzg::eip_4844::{ - hash_to_bls_field, Blob, CKZGSettings, PrecomputationTableManager, BYTES_PER_FIELD_ELEMENT, - C_KZG_RET, C_KZG_RET_BADARGS, FIELD_ELEMENTS_PER_BLOB, FIELD_ELEMENTS_PER_CELL, - FIELD_ELEMENTS_PER_EXT_BLOB, TRUSTED_SETUP_NUM_G2_POINTS, -}; +use kzg::eip_4844::{hash_to_bls_field, PrecomputationTableManager, BYTES_PER_FIELD_ELEMENT}; +use kzg::eth::c_bindings::{Blob, CKZGSettings, CKzgRet}; use kzg::{Fr, G1Mul, G2Mul}; use crate::consts::{G1_GENERATOR, G2_GENERATOR}; -use crate::types::fft_settings::FsFFTSettings; use crate::types::fp::FsFp; use crate::types::fr::FsFr; use crate::types::g1::{FsG1, FsG1Affine}; @@ -32,7 +27,7 @@ pub fn generate_trusted_setup( for _ in 0..n { s1.push(G1_GENERATOR.mul(&s_pow)); - s2.push(G1_GENERATOR); // TODO: this should be lagrange form + s2.push(G1_GENERATOR.mul(&s_pow)); // TODO: this should be lagrange form s3.push(G2_GENERATOR.mul(&s_pow)); s_pow = s_pow.mul(&s); @@ -41,7 +36,7 @@ pub fn generate_trusted_setup( (s1, s2, s3) } -pub(crate) unsafe fn deserialize_blob(blob: *const Blob) -> Result, C_KZG_RET> { +pub(crate) unsafe fn deserialize_blob(blob: *const Blob) -> Result, CKzgRet> { (*blob) .bytes .chunks(BYTES_PER_FIELD_ELEMENT) @@ -51,61 +46,23 @@ pub(crate) unsafe fn deserialize_blob(blob: *const Blob) -> Result, C_ if let Ok(result) = FsFr::from_bytes(&bytes) { Ok(result) } else { - Err(C_KZG_RET_BADARGS) + Err(CKzgRet::BadArgs) } }) - .collect::, C_KZG_RET>>() + .collect::, CKzgRet>>() } macro_rules! handle_ckzg_badargs { ($x: expr) => { match $x { Ok(value) => value, - Err(_) => return kzg::eip_4844::C_KZG_RET_BADARGS, + Err(_) => return kzg::eth::c_bindings::CKzgRet::BadArgs, } }; } pub(crate) use handle_ckzg_badargs; -pub(crate) fn fft_settings_to_rust( - c_settings: *const CKZGSettings, -) -> Result { - let settings = unsafe { &*c_settings }; - - let roots_of_unity = unsafe { - core::slice::from_raw_parts(settings.roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB + 1) - .iter() - .map(|r| FsFr(*r)) - .collect::>() - }; - - let brp_roots_of_unity = unsafe { - core::slice::from_raw_parts(settings.brp_roots_of_unity, FIELD_ELEMENTS_PER_EXT_BLOB) - .iter() - .map(|r| FsFr(*r)) - .collect::>() - }; - - let reverse_roots_of_unity = unsafe { - core::slice::from_raw_parts( - settings.reverse_roots_of_unity, - FIELD_ELEMENTS_PER_EXT_BLOB + 1, - ) - .iter() - .map(|r| FsFr(*r)) - .collect::>() - }; - - Ok(FsFFTSettings { - max_width: FIELD_ELEMENTS_PER_EXT_BLOB, - root_of_unity: roots_of_unity[1], - roots_of_unity, - brp_roots_of_unity, - reverse_roots_of_unity, - }) -} - pub(crate) static mut PRECOMPUTATION_TABLES: PrecomputationTableManager< FsFr, FsG1, @@ -113,45 +70,6 @@ pub(crate) static mut PRECOMPUTATION_TABLES: PrecomputationTableManager< FsG1Affine, > = PrecomputationTableManager::new(); -pub(crate) fn kzg_settings_to_rust(c_settings: &CKZGSettings) -> Result { - Ok(FsKZGSettings { - fs: fft_settings_to_rust(c_settings)?, - g1_values_monomial: unsafe { - core::slice::from_raw_parts(c_settings.g1_values_monomial, FIELD_ELEMENTS_PER_BLOB) - } - .iter() - .map(|r| FsG1(*r)) - .collect::>(), - g1_values_lagrange_brp: unsafe { - core::slice::from_raw_parts(c_settings.g1_values_lagrange_brp, FIELD_ELEMENTS_PER_BLOB) - } - .iter() - .map(|r| FsG1(*r)) - .collect::>(), - g2_values_monomial: unsafe { - core::slice::from_raw_parts(c_settings.g2_values_monomial, TRUSTED_SETUP_NUM_G2_POINTS) - } - .iter() - .map(|r| FsG2(*r)) - .collect::>(), - x_ext_fft_columns: unsafe { - core::slice::from_raw_parts( - c_settings.x_ext_fft_columns, - 2 * ((FIELD_ELEMENTS_PER_EXT_BLOB / 2) / FIELD_ELEMENTS_PER_CELL), - ) - } - .iter() - .map(|it| { - unsafe { core::slice::from_raw_parts(*it, FIELD_ELEMENTS_PER_CELL) } - .iter() - .map(|it| FsG1(*it)) - .collect::>() - }) - .collect::>(), - precomputation: unsafe { PRECOMPUTATION_TABLES.get_precomputation(c_settings) }, - }) -} - pub(crate) fn kzg_settings_to_c(rust_settings: &FsKZGSettings) -> CKZGSettings { CKZGSettings { roots_of_unity: Box::leak( diff --git a/blst/tests/eip_7594.rs b/blst/tests/eip_7594.rs index 412ff3d97..cb139bb23 100644 --- a/blst/tests/eip_7594.rs +++ b/blst/tests/eip_7594.rs @@ -1,7 +1,8 @@ #[cfg(test)] mod tests { - use kzg::eip_4844::{ - blob_to_kzg_commitment_rust, bytes_to_blob, CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_CELL, + use kzg::{ + eip_4844::{blob_to_kzg_commitment_rust, bytes_to_blob}, + eth, DAS, }; use kzg_bench::tests::{ eip_4844::generate_random_blob_bytes, @@ -13,53 +14,26 @@ mod tests { }; use rust_kzg_blst::{ eip_4844::load_trusted_setup_filename_rust, - eip_7594::{ - compute_cells_and_kzg_proofs_rust, recover_cells_and_kzg_proofs_rust, - verify_cell_kzg_proof_batch_rust, - }, - types::{ - fft_settings::FsFFTSettings, - fp::FsFp, - fr::FsFr, - g1::{FsG1, FsG1Affine}, - g2::FsG2, - kzg_settings::FsKZGSettings, - poly::FsPoly, - }, + eip_7594::BlstBackend, + types::{fr::FsFr, g1::FsG1, kzg_settings::FsKZGSettings}, }; #[test] pub fn test_vectors_compute_cells_and_kzg_proofs_() { - test_vectors_compute_cells_and_kzg_proofs::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFp, - FsG1Affine, - >( + test_vectors_compute_cells_and_kzg_proofs::( &load_trusted_setup_filename_rust, - &compute_cells_and_kzg_proofs_rust, &bytes_to_blob, ); } #[test] pub fn test_vectors_recover_cells_and_kzg_proofs_() { - test_vectors_recover_cells_and_kzg_proofs( - &load_trusted_setup_filename_rust, - &recover_cells_and_kzg_proofs_rust, - ); + test_vectors_recover_cells_and_kzg_proofs::(&load_trusted_setup_filename_rust); } #[test] pub fn test_vectors_verify_cell_kzg_proof_batch_() { - test_vectors_verify_cell_kzg_proof_batch( - &load_trusted_setup_filename_rust, - &verify_cell_kzg_proof_batch_rust, - ); + test_vectors_verify_cell_kzg_proof_batch::(&load_trusted_setup_filename_rust); } #[test] @@ -69,50 +43,46 @@ mod tests { /* Get a random blob */ let blob_bytes = generate_random_blob_bytes(&mut rng); - let blob = bytes_to_blob(&blob_bytes).unwrap(); + let blob: Vec = bytes_to_blob(&blob_bytes).unwrap(); - let mut cells = - vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| FsFr::default()); - CELLS_PER_EXT_BLOB - ]; - let mut proofs = vec![FsG1::default(); CELLS_PER_EXT_BLOB]; + let mut cells: Vec<[FsFr; eth::FIELD_ELEMENTS_PER_CELL]> = + vec![core::array::from_fn(|_| FsFr::default()); eth::CELLS_PER_EXT_BLOB]; + let mut proofs = vec![FsG1::default(); eth::CELLS_PER_EXT_BLOB]; /* Get the cells and proofs */ - let mut result = compute_cells_and_kzg_proofs_rust( - Some(&mut cells), - Some(&mut proofs), - &blob, - &settings, + let mut result = >::compute_cells_and_kzg_proofs( + &settings, Some(&mut cells), Some(&mut proofs), &blob ); assert!(result.is_ok()); - let cell_indices: Vec = (0..).step_by(2).take(CELLS_PER_EXT_BLOB / 2).collect(); - let mut partial_cells = - vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| FsFr::default()); - CELLS_PER_EXT_BLOB / 2 - ]; + let cell_indices: Vec = (0..).step_by(2).take(eth::CELLS_PER_EXT_BLOB / 2).collect(); + let mut partial_cells: Vec<[FsFr; eth::FIELD_ELEMENTS_PER_CELL]> = + vec![core::array::from_fn(|_| FsFr::default()); eth::CELLS_PER_EXT_BLOB / 2]; /* Erase half of the cells */ - for i in 0..(CELLS_PER_EXT_BLOB / 2) { + for i in 0..(eth::CELLS_PER_EXT_BLOB / 2) { partial_cells[i] = cells[cell_indices[i]]; } - let mut recovered_cells = - vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| FsFr::default()); - CELLS_PER_EXT_BLOB - ]; - let mut recovered_proofs = vec![FsG1::default(); CELLS_PER_EXT_BLOB]; + let mut recovered_cells: Vec<[FsFr; eth::FIELD_ELEMENTS_PER_CELL]> = + vec![core::array::from_fn(|_| FsFr::default()); eth::CELLS_PER_EXT_BLOB]; + let mut recovered_proofs = vec![FsG1::default(); eth::CELLS_PER_EXT_BLOB]; /* Reconstruct with half of the cells */ - result = recover_cells_and_kzg_proofs_rust( + result = >::recover_cells_and_kzg_proofs( + &settings, &mut recovered_cells, Some(&mut recovered_proofs), &cell_indices, &partial_cells, - &settings, ); assert!(result.is_ok()); @@ -135,34 +105,32 @@ mod tests { assert!(commitment_result.is_ok()); let commitment = commitment_result.unwrap(); - let mut cells = - vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| FsFr::default()); - CELLS_PER_EXT_BLOB - ]; - let mut proofs = vec![FsG1::default(); CELLS_PER_EXT_BLOB]; + let mut cells: Vec<[FsFr; eth::FIELD_ELEMENTS_PER_CELL]> = + vec![core::array::from_fn(|_| FsFr::default()); eth::CELLS_PER_EXT_BLOB]; + let mut proofs = vec![FsG1::default(); eth::CELLS_PER_EXT_BLOB]; /* Compute cells and proofs */ - let result = compute_cells_and_kzg_proofs_rust( - Some(&mut cells), - Some(&mut proofs), - &blob, - &settings, + let result = >::compute_cells_and_kzg_proofs( + &settings, Some(&mut cells), Some(&mut proofs), &blob ); assert!(result.is_ok()); /* Initialize list of commitments & cell indices */ - let commitments = vec![commitment; CELLS_PER_EXT_BLOB]; + let commitments = vec![commitment; eth::CELLS_PER_EXT_BLOB]; - let cell_indices: Vec = (0..).step_by(1).take(CELLS_PER_EXT_BLOB).collect(); + let cell_indices: Vec = (0..).step_by(1).take(eth::CELLS_PER_EXT_BLOB).collect(); /* Verify all the proofs */ - let verify_result = verify_cell_kzg_proof_batch_rust( - &commitments, - &cell_indices, - &cells, - &proofs, - &settings, + let verify_result = >::verify_cell_kzg_proof_batch( + &settings, &commitments, &cell_indices, &cells, &proofs ); assert!(verify_result.is_ok()); } diff --git a/blst/tests/fk20_proofs.rs b/blst/tests/fk20_proofs.rs index efec6a4d6..ac637fad7 100644 --- a/blst/tests/fk20_proofs.rs +++ b/blst/tests/fk20_proofs.rs @@ -1,110 +1,38 @@ #[cfg(test)] mod tests { use kzg_bench::tests::fk20_proofs::*; - use rust_kzg_blst::types::fft_settings::FsFFTSettings; + use rust_kzg_blst::eip_7594::BlstBackend; use rust_kzg_blst::types::fk20_multi_settings::FsFK20MultiSettings; use rust_kzg_blst::types::fk20_single_settings::FsFK20SingleSettings; - use rust_kzg_blst::types::fp::FsFp; - use rust_kzg_blst::types::fr::FsFr; - use rust_kzg_blst::types::g1::{FsG1, FsG1Affine}; - use rust_kzg_blst::types::g2::FsG2; - use rust_kzg_blst::types::kzg_settings::FsKZGSettings; - use rust_kzg_blst::types::poly::FsPoly; use rust_kzg_blst::utils::generate_trusted_setup; - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn test_fk_single() { - fk_single::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20SingleSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + fk_single::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn test_fk_single_strided() { - fk_single_strided::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20SingleSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + fk_single_strided::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn test_fk_multi_settings() { - fk_multi_settings::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20MultiSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + fk_multi_settings::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn test_fk_multi_chunk_len_1_512() { - fk_multi_chunk_len_1_512::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20MultiSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + fk_multi_chunk_len_1_512::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn test_fk_multi_chunk_len_16_512() { - fk_multi_chunk_len_16_512::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20MultiSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + fk_multi_chunk_len_16_512::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn test_fk_multi_chunk_len_16_16() { - fk_multi_chunk_len_16_16::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFK20MultiSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + fk_multi_chunk_len_16_16::(&generate_trusted_setup); } } diff --git a/blst/tests/kzg_proofs.rs b/blst/tests/kzg_proofs.rs index b70777b97..077a0c2ff 100644 --- a/blst/tests/kzg_proofs.rs +++ b/blst/tests/kzg_proofs.rs @@ -9,59 +9,29 @@ mod tests { commit_to_nil_poly, commit_to_too_long_poly_returns_err, proof_multi, proof_single, }; - use rust_kzg_blst::types::fft_settings::FsFFTSettings; - use rust_kzg_blst::types::fp::FsFp; - use rust_kzg_blst::types::fr::FsFr; - use rust_kzg_blst::types::g1::{FsG1, FsG1Affine}; + use rust_kzg_blst::eip_7594::BlstBackend; + use rust_kzg_blst::types::g1::FsG1; use rust_kzg_blst::types::g2::FsG2; - use rust_kzg_blst::types::kzg_settings::FsKZGSettings; - use rust_kzg_blst::types::poly::FsPoly; use rust_kzg_blst::utils::generate_trusted_setup; - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] pub fn test_proof_single() { - proof_single::( - &generate_trusted_setup, - ); + proof_single::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] pub fn test_commit_to_nil_poly() { - commit_to_nil_poly::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + commit_to_nil_poly::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] pub fn test_commit_to_too_long_poly() { - commit_to_too_long_poly_returns_err::< - FsFr, - FsG1, - FsG2, - FsPoly, - FsFFTSettings, - FsKZGSettings, - FsFp, - FsG1Affine, - >(&generate_trusted_setup); + commit_to_too_long_poly_returns_err::(&generate_trusted_setup); } - #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] pub fn test_proof_multi() { - proof_multi::( - &generate_trusted_setup, - ); + proof_multi::(&generate_trusted_setup); } // This aims at showing that the use of the blst::Pairing engine in pairings_verify diff --git a/kzg-bench/src/benches/das.rs b/kzg-bench/src/benches/das.rs index 129216255..3003d3731 100644 --- a/kzg-bench/src/benches/das.rs +++ b/kzg-bench/src/benches/das.rs @@ -1,9 +1,11 @@ use criterion::Criterion; -use kzg::{FFTSettings, Fr, DAS}; +use kzg::{DASExtension, FFTSettings, Fr}; const BENCH_SCALE: usize = 15; -pub fn bench_das_extension + DAS>(c: &mut Criterion) { +pub fn bench_das_extension + DASExtension>( + c: &mut Criterion, +) { let fft_settings = TFFTSettings::new(BENCH_SCALE).unwrap(); let data: Vec = vec![TFr::rand(); fft_settings.get_max_width() / 2]; let id = format!("bench_DAS_extension scale: '{}'", BENCH_SCALE); diff --git a/kzg-bench/src/benches/eip_7594.rs b/kzg-bench/src/benches/eip_7594.rs index df10f78c5..9b467c7a7 100644 --- a/kzg-bench/src/benches/eip_7594.rs +++ b/kzg-bench/src/benches/eip_7594.rs @@ -2,10 +2,9 @@ use std::path::PathBuf; use crate::tests::eip_4844::generate_random_blob_bytes; use criterion::{BenchmarkId, Criterion}; -use kzg::eip_4844::{ - BYTES_PER_BLOB, CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_CELL, TRUSTED_SETUP_PATH, -}; -use kzg::{FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, KZGSettings, Poly, G1, G2}; +use kzg::eip_4844::TRUSTED_SETUP_PATH; +use kzg::eth::{self, BYTES_PER_BLOB, CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_CELL}; +use kzg::{EcBackend, DAS}; pub fn get_partial_cells(cells: &[T], m: usize) -> (Vec, Vec) { let mut cell_indices = Vec::new(); @@ -23,40 +22,11 @@ pub fn get_partial_cells(cells: &[T], m: usize) -> (Vec, Vec #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] -pub fn bench_eip_7594< - TFr: Fr + std::fmt::Debug, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( +pub fn bench_eip_7594( c: &mut Criterion, - load_trusted_setup: &dyn Fn(&str) -> Result, - bytes_to_blob: &dyn Fn(&[u8]) -> Result, String>, - blob_to_kzg_commitment: &dyn Fn(&[TFr], &TKZGSettings) -> Result, - compute_cells_and_kzg_proofs: &dyn Fn( - Option<&mut [[TFr; FIELD_ELEMENTS_PER_CELL]]>, - Option<&mut [TG1]>, - &[TFr], - &TKZGSettings, - ) -> Result<(), String>, - recover_cells_and_kzg_proofs: &dyn Fn( - &mut [[TFr; FIELD_ELEMENTS_PER_CELL]], - Option<&mut [TG1]>, - &[usize], - &[[TFr; FIELD_ELEMENTS_PER_CELL]], - &TKZGSettings, - ) -> Result<(), String>, - verify_cell_kzg_proof_batch: &dyn Fn( - &[TG1], - &[usize], - &[[TFr; FIELD_ELEMENTS_PER_CELL]], - &[TG1], - &TKZGSettings, - ) -> Result, + load_trusted_setup: &dyn Fn(&str) -> Result, + bytes_to_blob: &dyn Fn(&[u8]) -> Result, String>, + blob_to_kzg_commitment: &dyn Fn(&[B::Fr], &B::KZGSettings) -> Result, ) { let ts = load_trusted_setup( PathBuf::from(env!("CARGO_MANIFEST_DIR")) @@ -80,13 +50,13 @@ pub fn bench_eip_7594< for blob in blobs.iter() { let mut cells = vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| TFr::default()); + core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| B::Fr::default()); CELLS_PER_EXT_BLOB ]; - let mut proofs = vec![TG1::default(); CELLS_PER_EXT_BLOB]; + let mut proofs = vec![B::G1::default(); CELLS_PER_EXT_BLOB]; let blob = bytes_to_blob(blob).unwrap(); - compute_cells_and_kzg_proofs(Some(&mut cells), Some(&mut proofs), &blob, &ts).unwrap(); + >::compute_cells_and_kzg_proofs(&ts, Some(&mut cells), Some(&mut proofs), &blob).unwrap(); blob_cells.push(cells); blob_cell_proofs.push(proofs); blob_commitments.push(blob_to_kzg_commitment(&blob, &ts).unwrap()); @@ -103,12 +73,12 @@ pub fn bench_eip_7594< let mut recv_cells = vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| TFr::default()); + core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| B::Fr::default()); CELLS_PER_EXT_BLOB ]; - let mut recv_proofs = vec![TG1::default(); CELLS_PER_EXT_BLOB]; + let mut recv_proofs = vec![B::G1::default(); CELLS_PER_EXT_BLOB]; - compute_cells_and_kzg_proofs(Some(&mut recv_cells), Some(&mut recv_proofs), &blob, &ts) + >::compute_cells_and_kzg_proofs(&ts, Some(&mut recv_cells), Some(&mut recv_proofs), &blob) .unwrap(); }); }); @@ -121,20 +91,20 @@ pub fn bench_eip_7594< group.bench_function(BenchmarkId::from_parameter(percent_missing), |b| { b.iter(|| { let mut recv_cells = vec![ - vec![TFr::default(); FIELD_ELEMENTS_PER_CELL] + vec![B::Fr::default(); FIELD_ELEMENTS_PER_CELL] .try_into() .unwrap(); CELLS_PER_EXT_BLOB ]; - let mut recv_proofs = vec![TG1::default(); CELLS_PER_EXT_BLOB]; + let mut recv_proofs = vec![B::G1::default(); CELLS_PER_EXT_BLOB]; - recover_cells_and_kzg_proofs( + >::recover_cells_and_kzg_proofs( + &ts, &mut recv_cells, Some(&mut recv_proofs), &cell_indices, &partial_cells, - &ts, ) .unwrap(); }); @@ -150,20 +120,20 @@ pub fn bench_eip_7594< group.bench_function(BenchmarkId::from_parameter(i), |b| { b.iter(|| { let mut recv_cells = vec![ - vec![TFr::default(); FIELD_ELEMENTS_PER_CELL] + vec![B::Fr::default(); FIELD_ELEMENTS_PER_CELL] .try_into() .unwrap(); CELLS_PER_EXT_BLOB ]; - let mut recv_proofs = vec![TG1::default(); CELLS_PER_EXT_BLOB]; + let mut recv_proofs = vec![B::G1::default(); CELLS_PER_EXT_BLOB]; - recover_cells_and_kzg_proofs( + >::recover_cells_and_kzg_proofs( + &ts, &mut recv_cells, Some(&mut recv_proofs), &cell_indices, &partial_cells, - &ts, ) .unwrap(); }); @@ -187,12 +157,16 @@ pub fn bench_eip_7594< } b.iter(|| { - let result = verify_cell_kzg_proof_batch( + let result = >::verify_cell_kzg_proof_batch( + &ts, &cell_commitments, &cell_indices, &cells, &cell_proofs, - &ts, ) .unwrap(); assert!(result); @@ -217,12 +191,16 @@ pub fn bench_eip_7594< } b.iter(|| { - let result = verify_cell_kzg_proof_batch( + let result = >::verify_cell_kzg_proof_batch( + &ts, &cell_commitments, &cell_indices, &cells, &cell_proofs, - &ts, ) .unwrap(); assert!(result); @@ -249,12 +227,16 @@ pub fn bench_eip_7594< } b.iter(|| { - let result = verify_cell_kzg_proof_batch( + let result = >::verify_cell_kzg_proof_batch( + &ts, &cell_commitments, &cell_indices, &cells, &cell_proofs, - &ts, ) .unwrap(); assert!(result); diff --git a/kzg-bench/src/benches/fk20.rs b/kzg-bench/src/benches/fk20.rs index 422b2e556..98ee2e0e4 100644 --- a/kzg-bench/src/benches/fk20.rs +++ b/kzg-bench/src/benches/fk20.rs @@ -1,8 +1,7 @@ use criterion::Criterion; use kzg::{ common_utils::{is_power_of_two, log2_pow2}, - FFTFr, FFTSettings, FK20MultiSettings, FK20SingleSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, - KZGSettings, Poly, G1, G2, + EcBackend, FFTSettings, FK20MultiSettings, FK20SingleSettings, Fr, KZGSettings, Poly, Preset, }; use rand::{thread_rng, RngCore}; @@ -13,20 +12,30 @@ pub const SECRET: [u8; 32usize] = [ const BENCH_SCALE: usize = 14; +struct TestPreset1; + +impl Preset for TestPreset1 { + const FIELD_ELEMENTS_PER_BLOB: usize = 16385; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 32770; + const CELLS_PER_EXT_BLOB: usize = 2048; +} + #[allow(clippy::type_complexity)] pub fn bench_fk_single_da< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TFK20SingleSettings: FK20SingleSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20SingleSettings: FK20SingleSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( c: &mut Criterion, - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { let mut rng = thread_rng(); let coeffs: Vec = vec![rng.next_u64(); 1 << (BENCH_SCALE - 1)]; @@ -35,15 +44,15 @@ pub fn bench_fk_single_da< let secrets_len = n_len + 1; assert!(n_len >= 2 * poly_len); - let mut p = TPoly::new(poly_len); + let mut p = B::Poly::new(poly_len); for (i, &coeff) in coeffs.iter().enumerate() { - p.set_coeff_at(i, &TFr::from_u64(coeff)); + p.set_coeff_at(i, &B::Fr::from_u64(coeff)); } // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(BENCH_SCALE).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(BENCH_SCALE).unwrap(); + let ks = B::KZGSettings::new_for_preset::<16, TestPreset1>(&s1, &s2, &s3, &fs).unwrap(); let fk = TFK20SingleSettings::new(&ks, 2 * poly_len).unwrap(); // Commit to the polynomial @@ -54,20 +63,30 @@ pub fn bench_fk_single_da< c.bench_function(&id, |b| b.iter(|| fk.data_availability(&p).unwrap())); } +struct TestPreset2; + +impl Preset for TestPreset2 { + const FIELD_ELEMENTS_PER_BLOB: usize = 32768; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 65536; + const CELLS_PER_EXT_BLOB: usize = 4096; +} + #[allow(clippy::type_complexity)] pub fn bench_fk_multi_da< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings + FFTFr, - TKZGSettings: KZGSettings, - TFK20MultiSettings: FK20MultiSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20MultiSettings: FK20MultiSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( c: &mut Criterion, - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { let n = 1 << BENCH_SCALE; let chunk_len = 16; @@ -84,12 +103,12 @@ pub fn bench_fk_multi_da< // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(width).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(width).unwrap(); + let ks = B::KZGSettings::new_for_preset::<16, TestPreset2>(&s1, &s2, &s3, &fs).unwrap(); let fk = TFK20MultiSettings::new(&ks, secrets_len, chunk_len).unwrap(); // Create a test polynomial of size n that's independent of chunk_len - let mut p = TPoly::new(n); + let mut p = B::Poly::new(n); for i in 0..chunk_count { for j in 0..chunk_len { let p_index = i * chunk_len + j; @@ -102,7 +121,7 @@ pub fn bench_fk_multi_da< if v_index == 5 { v += tmp * tmp; } - p.set_coeff_at(p_index, &TFr::from_u64(v)); + p.set_coeff_at(p_index, &B::Fr::from_u64(v)); if v_index == 12 { p.set_coeff_at(p_index, &p.get_coeff_at(p_index).negate()); } diff --git a/kzg-bench/src/benches/kzg.rs b/kzg-bench/src/benches/kzg.rs index cbcf47919..23eee4938 100644 --- a/kzg-bench/src/benches/kzg.rs +++ b/kzg-bench/src/benches/kzg.rs @@ -1,5 +1,5 @@ use criterion::Criterion; -use kzg::{FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, KZGSettings, Poly, G1, G2}; +use kzg::{EcBackend, FFTSettings, Fr, KZGSettings, Poly, Preset}; pub const SECRET: [u8; 32usize] = [ 0xa4, 0x73, 0x31, 0x95, 0x28, 0xc8, 0xb6, 0xea, 0x4d, 0x08, 0xcc, 0x53, 0x18, 0x00, 0x00, 0x00, @@ -8,54 +8,44 @@ pub const SECRET: [u8; 32usize] = [ const BENCH_SCALE: usize = 15; +struct TestPreset; + +impl Preset for TestPreset { + const FIELD_ELEMENTS_PER_BLOB: usize = 32768; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 65536; + const CELLS_PER_EXT_BLOB: usize = 512; +} + #[allow(clippy::type_complexity)] -pub fn bench_commit_to_poly< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( +pub fn bench_commit_to_poly( c: &mut Criterion, - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { - let fs = TFFTSettings::new(BENCH_SCALE).unwrap(); + let fs = B::FFTSettings::new(BENCH_SCALE).unwrap(); let (s1, s2, s3) = generate_trusted_setup(fs.get_max_width(), SECRET); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); - let mut poly = TPoly::new(fs.get_max_width()); + let ks = B::KZGSettings::new_for_preset::<64, TestPreset>(&s1, &s2, &s3, &fs).unwrap(); + let mut poly = B::Poly::new(fs.get_max_width()); for i in 0..fs.get_max_width() { - poly.set_coeff_at(i, &TFr::rand()); + poly.set_coeff_at(i, &B::Fr::rand()); } let id = format!("bench_commit_to_poly scale: '{}'", BENCH_SCALE); c.bench_function(&id, |b| b.iter(|| ks.commit_to_poly(&poly).unwrap())); } #[allow(clippy::type_complexity)] -pub fn bench_compute_proof_single< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( +pub fn bench_compute_proof_single( c: &mut Criterion, - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { - let fs = TFFTSettings::new(BENCH_SCALE).unwrap(); + let fs = B::FFTSettings::new(BENCH_SCALE).unwrap(); let (s1, s2, s3) = generate_trusted_setup(fs.get_max_width(), SECRET); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); - let mut poly = TPoly::new(fs.get_max_width()); + let ks = B::KZGSettings::new_for_preset::<64, TestPreset>(&s1, &s2, &s3, &fs).unwrap(); + let mut poly = B::Poly::new(fs.get_max_width()); for i in 0..fs.get_max_width() { - poly.set_coeff_at(i, &TFr::rand()); + poly.set_coeff_at(i, &B::Fr::rand()); } let id = format!("bench_compute_proof_single scale: '{}'", BENCH_SCALE); c.bench_function(&id, |b| { - b.iter(|| ks.compute_proof_single(&poly, &TFr::rand()).unwrap()) + b.iter(|| ks.compute_proof_single(&poly, &B::Fr::rand()).unwrap()) }); } diff --git a/kzg-bench/src/tests/c_bindings.rs b/kzg-bench/src/tests/c_bindings.rs index e8372e5d9..c5cd2d135 100644 --- a/kzg-bench/src/tests/c_bindings.rs +++ b/kzg-bench/src/tests/c_bindings.rs @@ -6,10 +6,13 @@ use std::{ ptr::null_mut, }; -use kzg::eip_4844::{ - load_trusted_setup_string, Blob, Bytes48, CKZGSettings, KZGCommitment, KZGProof, - BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, BYTES_PER_G1, BYTES_PER_G2, BYTES_PER_PROOF, - C_KZG_RET, C_KZG_RET_BADARGS, C_KZG_RET_OK, +use kzg::eth::c_bindings::{Blob, Bytes48, CKZGSettings, KZGCommitment, KZGProof}; +use kzg::{ + eip_4844::{ + load_trusted_setup_string, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, BYTES_PER_G1, + BYTES_PER_G2, BYTES_PER_PROOF, + }, + eth::c_bindings::CKzgRet, }; use libc::FILE; @@ -35,7 +38,7 @@ fn get_ckzg_settings( load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) -> CKZGSettings { let mut c_settings = CKZGSettings { g1_values_lagrange_brp: null_mut(), @@ -65,7 +68,7 @@ fn get_ckzg_settings( libc::fclose(file); } - assert_ne!(out, C_KZG_RET_BADARGS); + assert_ne!(out, CKzgRet::BadArgs); c_settings } @@ -75,11 +78,11 @@ pub fn blob_to_kzg_commitment_invalid_blob_test( out: *mut KZGCommitment, blob: *const Blob, s: &CKZGSettings, - ) -> C_KZG_RET, + ) -> CKzgRet, load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let settings = get_ckzg_settings(load_trusted_setup_file); @@ -101,7 +104,7 @@ pub fn blob_to_kzg_commitment_invalid_blob_test( let output = unsafe { blob_to_kzg_commitment(&mut commitment, &blob, &settings) }; - assert_eq!(output, C_KZG_RET_BADARGS) + assert_eq!(output, CKzgRet::BadArgs) } pub fn load_trusted_setup_invalid_g1_byte_length_test( @@ -114,7 +117,7 @@ pub fn load_trusted_setup_invalid_g1_byte_length_test( *const u8, u64, u64, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let mut file = File::open(get_trusted_setup_path()).unwrap(); let mut contents = String::new(); @@ -151,7 +154,7 @@ pub fn load_trusted_setup_invalid_g1_byte_length_test( ) }; - assert_eq!(status, C_KZG_RET_BADARGS) + assert_eq!(status, CKzgRet::BadArgs) } pub fn load_trusted_setup_invalid_g1_point_test( @@ -164,7 +167,7 @@ pub fn load_trusted_setup_invalid_g1_point_test( *const u8, u64, u64, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let mut file = File::open(get_trusted_setup_path()).unwrap(); let mut contents = String::new(); @@ -200,7 +203,7 @@ pub fn load_trusted_setup_invalid_g1_point_test( ) }; - assert_eq!(status, C_KZG_RET_BADARGS) + assert_eq!(status, CKzgRet::BadArgs) } pub fn load_trusted_setup_invalid_g2_byte_length_test( @@ -213,7 +216,7 @@ pub fn load_trusted_setup_invalid_g2_byte_length_test( *const u8, u64, u64, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let mut file = File::open(get_trusted_setup_path()).unwrap(); let mut contents = String::new(); @@ -250,7 +253,7 @@ pub fn load_trusted_setup_invalid_g2_byte_length_test( ) }; - assert_eq!(status, C_KZG_RET_BADARGS) + assert_eq!(status, CKzgRet::BadArgs) } pub fn load_trusted_setup_invalid_g2_point_test( @@ -263,7 +266,7 @@ pub fn load_trusted_setup_invalid_g2_point_test( *const u8, u64, u64, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let mut file = File::open(get_trusted_setup_path()).unwrap(); let mut contents = String::new(); @@ -299,7 +302,7 @@ pub fn load_trusted_setup_invalid_g2_point_test( ) }; - assert_eq!(status, C_KZG_RET_BADARGS) + assert_eq!(status, CKzgRet::BadArgs) } pub fn load_trusted_setup_invalid_form_test( @@ -312,7 +315,7 @@ pub fn load_trusted_setup_invalid_form_test( *const u8, u64, u64, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let mut file = File::open(get_trusted_setup_fixture_path("old")).unwrap(); let mut contents = String::new(); @@ -346,14 +349,14 @@ pub fn load_trusted_setup_invalid_form_test( ) }; - assert_eq!(status, C_KZG_RET_BADARGS) + assert_eq!(status, CKzgRet::BadArgs) } pub fn load_trusted_setup_file_invalid_format_test( load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { struct Fixture { name: String, @@ -433,7 +436,7 @@ pub fn load_trusted_setup_file_invalid_format_test( } assert!( - output == C_KZG_RET_BADARGS, + output == CKzgRet::BadArgs, "{}, fixture: {file_path}", fixture.message ); @@ -444,7 +447,7 @@ pub fn load_trusted_setup_file_valid_format_test( load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { struct Fixture { name: String, @@ -494,7 +497,7 @@ pub fn load_trusted_setup_file_valid_format_test( } assert!( - output == C_KZG_RET_OK, + output == CKzgRet::Ok, "{}, fixture: {file_path}", fixture.message ); @@ -533,7 +536,7 @@ pub fn free_trusted_setup_set_all_values_to_null_test( load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let mut settings = get_ckzg_settings(load_trusted_setup_file); @@ -562,11 +565,11 @@ pub fn compute_blob_kzg_proof_invalid_blob_test( blob: *const Blob, commitment_bytes: *const Bytes48, s: &CKZGSettings, - ) -> C_KZG_RET, + ) -> CKzgRet, load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let settings = get_ckzg_settings(load_trusted_setup_file); @@ -592,7 +595,7 @@ pub fn compute_blob_kzg_proof_invalid_blob_test( let out = unsafe { compute_blob_kzg_proof(&mut out, &blob, &commitment, &settings) }; - assert_eq!(out, C_KZG_RET_BADARGS); + assert_eq!(out, CKzgRet::BadArgs); } pub fn compute_blob_kzg_proof_commitment_is_point_at_infinity_test( @@ -601,11 +604,11 @@ pub fn compute_blob_kzg_proof_commitment_is_point_at_infinity_test( blob: *const Blob, commitment_bytes: *const Bytes48, s: &CKZGSettings, - ) -> C_KZG_RET, + ) -> CKzgRet, load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let settings = get_ckzg_settings(load_trusted_setup_file); @@ -626,7 +629,7 @@ pub fn compute_blob_kzg_proof_commitment_is_point_at_infinity_test( let out = unsafe { compute_blob_kzg_proof(&mut out, &blob, &commitment, &settings) }; - assert_eq!(out, C_KZG_RET_OK); + assert_eq!(out, CKzgRet::Ok); } pub fn compute_blob_kzg_proof_zero_input_test( @@ -635,11 +638,11 @@ pub fn compute_blob_kzg_proof_zero_input_test( blob: *const Blob, commitment_bytes: *const Bytes48, s: &CKZGSettings, - ) -> C_KZG_RET, + ) -> CKzgRet, load_trusted_setup_file: unsafe extern "C" fn( out: *mut CKZGSettings, in_: *mut FILE, - ) -> C_KZG_RET, + ) -> CKzgRet, ) { let settings = get_ckzg_settings(load_trusted_setup_file); @@ -659,5 +662,5 @@ pub fn compute_blob_kzg_proof_zero_input_test( let out = unsafe { compute_blob_kzg_proof(&mut out, &blob, &commitment, &settings) }; - assert_eq!(out, C_KZG_RET_OK); + assert_eq!(out, CKzgRet::Ok); } diff --git a/kzg-bench/src/tests/das.rs b/kzg-bench/src/tests/das.rs index 986a407d7..8a2509e40 100644 --- a/kzg-bench/src/tests/das.rs +++ b/kzg-bench/src/tests/das.rs @@ -1,7 +1,7 @@ -use kzg::{FFTFr, FFTSettings, Fr, DAS}; +use kzg::{DASExtension, FFTFr, FFTSettings, Fr}; /// Check if DAS FFT creates odds that match precomputed values -pub fn das_extension_test_known + DAS>() { +pub fn das_extension_test_known + DASExtension>() { #[rustfmt::skip] let expected_u: [[u64; 4]; 8] = [ [0xa0c43757db972d7d, 0x79d15a1e0677962c, 0xf678865c0c95fa6a, 0x4e85fd4814f96825, ], @@ -35,7 +35,7 @@ pub fn das_extension_test_known + DAS + DAS + FFTFr, + TFFTSettings: FFTSettings + DASExtension + FFTFr, >() { let max_scale: usize = 15; diff --git a/kzg-bench/src/tests/eip_7594.rs b/kzg-bench/src/tests/eip_7594.rs index 7703db3c6..80ca343d9 100644 --- a/kzg-bench/src/tests/eip_7594.rs +++ b/kzg-bench/src/tests/eip_7594.rs @@ -2,11 +2,8 @@ use super::utils::{get_manifest_dir, get_trusted_setup_path}; use crate::test_vectors::{ compute_cells_and_kzg_proofs, recover_cells_and_kzg_proofs, verify_cell_kzg_proof_batch, }; -use kzg::{ - eip_4844::{BYTES_PER_FIELD_ELEMENT, CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_CELL}, - FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, KZGSettings, Poly, G1, G2, -}; -use std::{fmt::Debug, fs, path::PathBuf}; +use kzg::{eth, EcBackend, Fr, DAS, G1}; +use std::{fs, path::PathBuf}; const COMPUTE_CELLS_AND_KZG_PROOFS_TEST_VECTORS: &str = "src/test_vectors/compute_cells_and_kzg_proofs/*/*/*"; @@ -16,24 +13,9 @@ const VERIFY_CELL_KZG_PROOF_BATCH_TEST_VECTORS: &str = "src/test_vectors/verify_cell_kzg_proof_batch/*/*/*"; #[allow(clippy::type_complexity)] -pub fn test_vectors_compute_cells_and_kzg_proofs< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - load_trusted_setup: &dyn Fn(&str) -> Result, - compute_cells_and_kzg_proofs: &dyn Fn( - Option<&mut [[TFr; FIELD_ELEMENTS_PER_CELL]]>, - Option<&mut [TG1]>, - &[TFr], - &TKZGSettings, - ) -> Result<(), String>, - bytes_to_blob: &dyn Fn(&[u8]) -> Result, String>, +pub fn test_vectors_compute_cells_and_kzg_proofs( + load_trusted_setup: &dyn Fn(&str) -> Result, + bytes_to_blob: &dyn Fn(&[u8]) -> Result, String>, ) { let settings = load_trusted_setup(get_trusted_setup_path().as_str()).unwrap(); let test_files: Vec = glob::glob(&format!( @@ -58,18 +40,19 @@ pub fn test_vectors_compute_cells_and_kzg_proofs< } }; - let mut recv_cells = - vec![ - core::array::from_fn::<_, FIELD_ELEMENTS_PER_CELL, _>(|_| TFr::default()); - CELLS_PER_EXT_BLOB - ]; - let mut recv_proofs = vec![TG1::default(); CELLS_PER_EXT_BLOB]; + let mut recv_cells = vec![ + core::array::from_fn::<_, { eth::FIELD_ELEMENTS_PER_CELL }, _>( + |_| B::Fr::default() + ); + eth::CELLS_PER_EXT_BLOB + ]; + let mut recv_proofs = vec![B::G1::default(); eth::CELLS_PER_EXT_BLOB]; - match compute_cells_and_kzg_proofs( + match >::compute_cells_and_kzg_proofs( + &settings, Some(&mut recv_cells), Some(&mut recv_proofs), &blob, - &settings, ) { Err(_) => assert!(test.get_output().is_none()), Ok(()) => { @@ -100,24 +83,8 @@ pub fn test_vectors_compute_cells_and_kzg_proofs< } #[allow(clippy::type_complexity)] -pub fn test_vectors_recover_cells_and_kzg_proofs< - TFr: Fr + Debug, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - load_trusted_setup: &dyn Fn(&str) -> Result, - recover_cells_and_kzg_proofs: &dyn Fn( - &mut [[TFr; FIELD_ELEMENTS_PER_CELL]], - Option<&mut [TG1]>, - &[usize], - &[[TFr; FIELD_ELEMENTS_PER_CELL]], - &TKZGSettings, - ) -> Result<(), String>, +pub fn test_vectors_recover_cells_and_kzg_proofs( + load_trusted_setup: &dyn Fn(&str) -> Result, ) { let settings = load_trusted_setup(get_trusted_setup_path().as_str()).unwrap(); let test_files: Vec = glob::glob(&format!( @@ -141,8 +108,8 @@ pub fn test_vectors_recover_cells_and_kzg_proofs< .iter() .map(|bytes| { match bytes - .chunks(BYTES_PER_FIELD_ELEMENT) - .map(|bytes| TFr::from_bytes(bytes)) + .chunks(eth::BYTES_PER_FIELD_ELEMENT) + .map(B::Fr::from_bytes) .collect::, String>>() { Ok(value) => value @@ -171,20 +138,21 @@ pub fn test_vectors_recover_cells_and_kzg_proofs< }; let mut recv_cells = vec![ - vec![TFr::default(); FIELD_ELEMENTS_PER_CELL] + vec![B::Fr::default(); eth::FIELD_ELEMENTS_PER_CELL] .try_into() - .unwrap(); - CELLS_PER_EXT_BLOB + .map_err(|_| ()) + .expect("Failed to create output cells"); + eth::CELLS_PER_EXT_BLOB ]; - let mut recv_proofs = vec![TG1::default(); CELLS_PER_EXT_BLOB]; + let mut recv_proofs = vec![B::G1::default(); eth::CELLS_PER_EXT_BLOB]; - match recover_cells_and_kzg_proofs( + match >::recover_cells_and_kzg_proofs( + &settings, &mut recv_cells, Some(&mut recv_proofs), &test.input.get_cell_indices().unwrap().iter().map(|it| (*it).into()).collect::>(), &cells, - &settings, ) { Err(err) => assert!(test.get_output().is_none(), "Should correctly recover cells, but failed with error {err:?}, for test vector {test_file:?}"), Ok(()) => { @@ -219,24 +187,8 @@ pub fn test_vectors_recover_cells_and_kzg_proofs< } #[allow(clippy::type_complexity)] -pub fn test_vectors_verify_cell_kzg_proof_batch< - TFr: Fr + Debug, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - load_trusted_setup: &dyn Fn(&str) -> Result, - verify_cell_kzg_proof_batch: &dyn Fn( - &[TG1], - &[usize], - &[[TFr; FIELD_ELEMENTS_PER_CELL]], - &[TG1], - &TKZGSettings, - ) -> Result, +pub fn test_vectors_verify_cell_kzg_proof_batch( + load_trusted_setup: &dyn Fn(&str) -> Result, ) { let settings = load_trusted_setup(get_trusted_setup_path().as_str()).unwrap(); let test_files: Vec = glob::glob(&format!( @@ -260,8 +212,8 @@ pub fn test_vectors_verify_cell_kzg_proof_batch< .iter() .map(|bytes| { match bytes - .chunks(BYTES_PER_FIELD_ELEMENT) - .map(|bytes| TFr::from_bytes(bytes)) + .chunks(eth::BYTES_PER_FIELD_ELEMENT) + .map(B::Fr::from_bytes) .collect::, String>>() { Ok(value) => value @@ -294,7 +246,7 @@ pub fn test_vectors_verify_cell_kzg_proof_batch< .get_commitment_bytes() .unwrap() .iter() - .map(|bytes| TG1::from_bytes(bytes)) + .map(|bytes| B::G1::from_bytes(bytes)) .collect::, _>>() { Ok(v) => v, @@ -319,7 +271,7 @@ pub fn test_vectors_verify_cell_kzg_proof_batch< .get_proof_bytes() .unwrap() .iter() - .map(|bytes| TG1::from_bytes(bytes)) + .map(|bytes| B::G1::from_bytes(bytes)) .collect::, _>>() { Ok(v) => v, @@ -341,12 +293,12 @@ pub fn test_vectors_verify_cell_kzg_proof_batch< let cell_indices = test.input.get_cell_indices().unwrap(); - match verify_cell_kzg_proof_batch( + match >::verify_cell_kzg_proof_batch( + &settings, &commitments, &cell_indices, &cells, &proofs, - &settings, ) { Err(err) => assert!(test.get_output().is_none(), "Should correctly verify cells, but failed with error {err:?}, for test vector {test_file:?}"), Ok(value) => { diff --git a/kzg-bench/src/tests/fk20_proofs.rs b/kzg-bench/src/tests/fk20_proofs.rs index 4b92c381d..79975a1a5 100644 --- a/kzg-bench/src/tests/fk20_proofs.rs +++ b/kzg-bench/src/tests/fk20_proofs.rs @@ -1,7 +1,7 @@ use kzg::{ common_utils::{is_power_of_two, log2_pow2, reverse_bit_order, reverse_bits_limited}, - FFTFr, FFTSettings, FK20MultiSettings, FK20SingleSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, - KZGSettings, Poly, G1, G2, + EcBackend, FFTFr, FFTSettings, FK20MultiSettings, FK20SingleSettings, Fr, KZGSettings, Poly, + Preset, }; pub const SECRET: [u8; 32usize] = [ @@ -9,19 +9,29 @@ pub const SECRET: [u8; 32usize] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; +struct TestPreset1; + +impl Preset for TestPreset1 { + const FIELD_ELEMENTS_PER_BLOB: usize = 33; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 66; + const CELLS_PER_EXT_BLOB: usize = 16; +} + #[allow(clippy::type_complexity)] pub fn fk_single< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TFK20SingleSettings: FK20SingleSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20SingleSettings: FK20SingleSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { let coeffs: Vec = vec![1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13]; let poly_len: usize = coeffs.len(); @@ -30,15 +40,15 @@ pub fn fk_single< let secrets_len = n_len + 1; assert!(n_len >= 2 * poly_len); - let mut p = TPoly::new(poly_len); + let mut p = B::Poly::new(poly_len); for (i, &coeff) in coeffs.iter().enumerate() { - p.set_coeff_at(i, &TFr::from_u64(coeff)); + p.set_coeff_at(i, &B::Fr::from_u64(coeff)); } // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(n).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(n).unwrap(); + let ks = B::KZGSettings::new_for_preset::<4, TestPreset1>(&s1, &s2, &s3, &fs).unwrap(); let fk = TFK20SingleSettings::new(&ks, 2 * poly_len).unwrap(); // Commit to the polynomial @@ -53,7 +63,7 @@ pub fn fk_single< for i in 0..(2 * poly_len) { let x = fs.get_roots_of_unity_at(i); let y = p.eval(&x); - let proof = &all_proofs[reverse_bits_limited(2 * poly_len - 1, i)]; + let proof = &all_proofs[reverse_bits_limited(2 * poly_len, i)]; assert!(ks.check_proof_single(&commitment, proof, &x, &y).unwrap()); } @@ -70,19 +80,29 @@ pub fn fk_single< } } +struct TestPreset2; + +impl Preset for TestPreset2 { + const FIELD_ELEMENTS_PER_BLOB: usize = 257; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 514; + const CELLS_PER_EXT_BLOB: usize = 32; +} + #[allow(clippy::type_complexity)] pub fn fk_single_strided< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TFK20SingleSettings: FK20SingleSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20SingleSettings: FK20SingleSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { let coeffs: Vec = vec![1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13]; let poly_len: usize = coeffs.len(); @@ -92,15 +112,15 @@ pub fn fk_single_strided< let secrets_len = n_len + 1; assert!(n_len >= 2 * poly_len); - let mut p = TPoly::new(poly_len); + let mut p = B::Poly::new(poly_len); for (i, &coeff) in coeffs.iter().enumerate() { - p.set_coeff_at(i, &TFr::from_u64(coeff)); + p.set_coeff_at(i, &B::Fr::from_u64(coeff)); } // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(n).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(n).unwrap(); + let ks = B::KZGSettings::new_for_preset::<16, TestPreset2>(&s1, &s2, &s3, &fs).unwrap(); let fk = TFK20SingleSettings::new(&ks, 2 * poly_len).unwrap(); // Commit to the polynomial @@ -113,51 +133,59 @@ pub fn fk_single_strided< for i in 0..(2 * poly_len) { let x = fs.get_roots_of_unity_at(i * stride); let y = p.eval(&x); - let proof = &all_proofs[reverse_bits_limited(2 * poly_len - 1, i)]; + let proof = &all_proofs[reverse_bits_limited(2 * poly_len, i)]; assert!(ks.check_proof_single(&commitment, proof, &x, &y).unwrap()); } } #[allow(clippy::type_complexity)] pub fn fk_multi_settings< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TFK20MultiSettings: FK20MultiSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20MultiSettings: FK20MultiSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { let n: usize = 5; let secrets_len: usize = 33; // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(n).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(n).unwrap(); + let ks = B::KZGSettings::new_for_preset::<4, TestPreset1>(&s1, &s2, &s3, &fs).unwrap(); let _fk = TFK20MultiSettings::new(&ks, 32, 4).unwrap(); } #[allow(clippy::type_complexity)] fn fk_multi_case< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings + FFTFr, - TKZGSettings: KZGSettings, - TFK20MultiSettings: FK20MultiSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + const FIELD_ELEMENTS_PER_CELL: usize, + B: EcBackend, + P: Preset, + TFK20MultiSettings: FK20MultiSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - chunk_len: usize, - n: usize, - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), -) { + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), +) where + B::FFTSettings: FFTFr, +{ + let chunk_len = FIELD_ELEMENTS_PER_CELL; + let n = P::FIELD_ELEMENTS_PER_BLOB / 2; let vv: Vec = vec![1, 2, 3, 4, 7, 8, 9, 10, 13, 14, 1, 15, 1, 1000, 134, 33]; assert!(is_power_of_two(n)); @@ -171,12 +199,13 @@ fn fk_multi_case< // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(width).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(width).unwrap(); + let ks = + B::KZGSettings::new_for_preset::(&s1, &s2, &s3, &fs).unwrap(); let fk = TFK20MultiSettings::new(&ks, n * 2, chunk_len).unwrap(); // Create a test polynomial of size n that's independent of chunk_len - let mut p = TPoly::new(n); + let mut p = B::Poly::new(n); for i in 0..chunk_count { for j in 0..chunk_len { let p_index = i * chunk_len + j; @@ -189,7 +218,7 @@ fn fk_multi_case< if v_index == 5 { v += tmp * tmp; } - p.set_coeff_at(p_index, &TFr::from_u64(v)); + p.set_coeff_at(p_index, &B::Fr::from_u64(v)); if v_index == 12 { p.set_coeff_at(p_index, &p.get_coeff_at(p_index).negate()); } @@ -206,7 +235,7 @@ fn fk_multi_case< let all_proofs = fk.data_availability(&p).unwrap(); // Now actually extend the polynomial with zeros - let mut extended_coeffs = vec![TFr::zero(); 2 * n]; + let mut extended_coeffs = vec![B::Fr::zero(); 2 * n]; for (i, extended_coeff) in extended_coeffs.iter_mut().enumerate().take(n) { *extended_coeff = p.get_coeff_at(i); } @@ -214,11 +243,11 @@ fn fk_multi_case< reverse_bit_order(&mut extended_coeffs_fft).unwrap(); // Verify the proofs - let mut ys = vec![TFr::default(); chunk_len]; - let mut ys2 = vec![TFr::default(); chunk_len]; + let mut ys = vec![B::Fr::default(); chunk_len]; + let mut ys2 = vec![B::Fr::default(); chunk_len]; let domain_stride = fs.get_max_width() / (2 * n); for pos in 0..(2 * chunk_count) { - let domain_pos = reverse_bits_limited(chunk_count, pos); + let domain_pos = reverse_bits_limited(2 * chunk_count, pos); let x = fs.get_roots_of_unity_at(domain_pos * domain_stride); // The ys from the extended coeffients @@ -247,83 +276,83 @@ fn fk_multi_case< } } +struct TestPreset3; + +impl Preset for TestPreset3 { + const FIELD_ELEMENTS_PER_BLOB: usize = 1024; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 2048; + const CELLS_PER_EXT_BLOB: usize = 1024; +} + #[allow(clippy::type_complexity)] pub fn fk_multi_chunk_len_1_512< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings + FFTFr, - TKZGSettings: KZGSettings, - TFK20MultiSettings: FK20MultiSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20MultiSettings: FK20MultiSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { - fk_multi_case::< - TFr, - TG1, - TG2, - TPoly, - TFFTSettings, - TKZGSettings, - TFK20MultiSettings, - TG1Fp, - TG1Affine, - >(1, 512, &generate_trusted_setup); + fk_multi_case::<1, B, TestPreset3, TFK20MultiSettings>(&generate_trusted_setup); +} + +struct TestPreset4; + +impl Preset for TestPreset4 { + const FIELD_ELEMENTS_PER_BLOB: usize = 1024; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 2048; + const CELLS_PER_EXT_BLOB: usize = 32; } #[allow(clippy::type_complexity)] pub fn fk_multi_chunk_len_16_512< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings + FFTFr, - TKZGSettings: KZGSettings, - TFK20MultiSettings: FK20MultiSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20MultiSettings: FK20MultiSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { - fk_multi_case::< - TFr, - TG1, - TG2, - TPoly, - TFFTSettings, - TKZGSettings, - TFK20MultiSettings, - TG1Fp, - TG1Affine, - >(16, 512, &generate_trusted_setup); + fk_multi_case::<16, B, TestPreset4, TFK20MultiSettings>(&generate_trusted_setup); +} + +struct TestPreset5; + +impl Preset for TestPreset5 { + const FIELD_ELEMENTS_PER_BLOB: usize = 32; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 64; + const CELLS_PER_EXT_BLOB: usize = 4; } #[allow(clippy::type_complexity)] pub fn fk_multi_chunk_len_16_16< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings + FFTFr, - TKZGSettings: KZGSettings, - TFK20MultiSettings: FK20MultiSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, + B: EcBackend, + TFK20MultiSettings: FK20MultiSettings< + B::Fr, + B::G1, + B::G2, + B::FFTSettings, + B::Poly, + B::KZGSettings, + B::G1Fp, + B::G1Affine, + >, >( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { - fk_multi_case::< - TFr, - TG1, - TG2, - TPoly, - TFFTSettings, - TKZGSettings, - TFK20MultiSettings, - TG1Fp, - TG1Affine, - >(16, 16, &generate_trusted_setup); + fk_multi_case::<16, B, TestPreset5, TFK20MultiSettings>(&generate_trusted_setup); } diff --git a/kzg-bench/src/tests/kzg_proofs.rs b/kzg-bench/src/tests/kzg_proofs.rs index cc7fd2e55..e92321317 100644 --- a/kzg-bench/src/tests/kzg_proofs.rs +++ b/kzg-bench/src/tests/kzg_proofs.rs @@ -1,23 +1,24 @@ -use kzg::{FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, KZGSettings, Poly, G1, G2}; +use kzg::{ + EcBackend, FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, KZGSettings, Poly, Preset, G1, G2, +}; pub const SECRET: [u8; 32usize] = [ 0xa4, 0x73, 0x31, 0x95, 0x28, 0xc8, 0xb6, 0xea, 0x4d, 0x08, 0xcc, 0x53, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; +struct TestPreset1; + +impl Preset for TestPreset1 { + const FIELD_ELEMENTS_PER_BLOB: usize = 17; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 34; + const CELLS_PER_EXT_BLOB: usize = 1; +} + /// Check that both FFT implementations produce the same results #[allow(clippy::type_complexity)] -pub fn proof_single< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), +pub fn proof_single( + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { // Our polynomial: degree 15, 16 coefficients let coeffs = [1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13]; @@ -25,18 +26,18 @@ pub fn proof_single< let secrets_len = poly_len + 1; // Create the polynomial - let mut p = TPoly::new(poly_len); + let mut p = B::Poly::new(poly_len); for (x, &coeff) in coeffs.iter().enumerate() { - p.set_coeff_at(x, &TFr::from_u64(coeff)); + p.set_coeff_at(x, &B::Fr::from_u64(coeff)); } // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(4).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(4).unwrap(); + let ks = B::KZGSettings::new_for_preset::<7, TestPreset1>(&s1, &s2, &s3, &fs).unwrap(); // Compute the proof for x = 25 - let x = TFr::from_u64(25); + let x = B::Fr::from_u64(25); let commitment = ks.commit_to_poly(&p).unwrap(); let proof = ks.compute_proof_single(&p, &x).unwrap(); let mut value = p.eval(&x); @@ -47,36 +48,35 @@ pub fn proof_single< .unwrap()); // Change the value and check that the proof fails - value = value.add(&TFr::one()); + value = value.add(&B::Fr::one()); assert!(!ks .check_proof_single(&commitment, &proof, &x, &value) .unwrap()); } +struct TestPreset2; + +impl Preset for TestPreset2 { + const FIELD_ELEMENTS_PER_BLOB: usize = 16; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 32; + const CELLS_PER_EXT_BLOB: usize = 4; +} + #[allow(clippy::type_complexity)] -pub fn commit_to_nil_poly< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), +pub fn commit_to_nil_poly( + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { { let secrets_len = 16; // Initialise the (arbitrary) secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(4).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(4).unwrap(); + let ks = B::KZGSettings::new_for_preset::<8, TestPreset2>(&s1, &s2, &s3, &fs).unwrap(); - let a = TPoly::new(0); + let a = B::Poly::new(0); let result = ks.commit_to_poly(&a).unwrap(); - assert!(result.equals(&TG1::default())); + assert!(result.equals(&B::G1::default())); } } @@ -111,44 +111,26 @@ pub fn commit_to_too_long_poly< // Instead of panicking, commit should return an err #[allow(clippy::type_complexity)] -pub fn commit_to_too_long_poly_returns_err< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), +pub fn commit_to_too_long_poly_returns_err( + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { let secrets_len = 16; let poly_len = 32; // poly is longer than secrets! // Initialise the (arbitrary) secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs = TFFTSettings::new(4).unwrap(); - let ks = TKZGSettings::new(&s1, &s2, &s3, &fs).unwrap(); + let fs = B::FFTSettings::new(4).unwrap(); + let ks = B::KZGSettings::new_for_preset::<8, TestPreset2>(&s1, &s2, &s3, &fs).unwrap(); - let a = TPoly::new(poly_len); + let a = B::Poly::new(poly_len); let _result = ks.commit_to_poly(&a); assert!(_result.is_err()); } //It was not verified that this test works, use with caution #[allow(clippy::type_complexity)] -pub fn proof_multi< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TFFTSettings: FFTSettings, - TKZGSettings: KZGSettings, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), +pub fn proof_multi( + generate_trusted_setup: &dyn Fn(usize, [u8; 32usize]) -> (Vec, Vec, Vec), ) { // Our polynomial: degree 15, 16 coefficients let coeffs = [1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13]; @@ -157,7 +139,7 @@ pub fn proof_multi< // Compute proof at 2^coset_scale points let coset_scale = 3; let coset_len = 1 << coset_scale; - let mut y: Vec = Vec::new(); + let mut y: Vec = Vec::new(); let secrets_len = if poly_len > coset_len { poly_len + 1 @@ -166,29 +148,29 @@ pub fn proof_multi< }; // Create the polynomial - let mut p = TPoly::new(poly_len); + let mut p = B::Poly::new(poly_len); for (x, &coeff) in coeffs.iter().enumerate() { - p.set_coeff_at(x, &TFr::from_u64(coeff)); + p.set_coeff_at(x, &B::Fr::from_u64(coeff)); } // Initialise the secrets and data structures let (s1, s2, s3) = generate_trusted_setup(secrets_len, SECRET); - let fs1 = TFFTSettings::new(4).unwrap(); - let ks1 = TKZGSettings::new(&s1, &s2, &s3, &fs1).unwrap(); + let fs1 = B::FFTSettings::new(4).unwrap(); + let ks1 = B::KZGSettings::new_for_preset::<7, TestPreset1>(&s1, &s2, &s3, &fs1).unwrap(); // Commit to the polynomial let commitment = ks1.commit_to_poly(&p).unwrap(); - let fs2 = TFFTSettings::new(coset_scale).unwrap(); - let ks2 = TKZGSettings::new(&s1, &s2, &s3, &fs2).unwrap(); + let fs2 = B::FFTSettings::new(coset_scale).unwrap(); + let ks2 = B::KZGSettings::new_for_preset::<7, TestPreset1>(&s1, &s2, &s3, &fs2).unwrap(); // Compute proof at the points [x * root_i] 0 <= i < coset_len - let x = TFr::from_u64(5431); + let x = B::Fr::from_u64(5431); let proof = ks2.compute_proof_multi(&p, &x, coset_len).unwrap(); // y_i is the value of the polynomial at each x_i for i in 0..coset_len { - let tmp = TFr::mul(&x, &ks2.get_roots_of_unity_at(i)); + let tmp = B::Fr::mul(&x, &ks2.get_roots_of_unity_at(i)); y.push(p.eval(&tmp)); } @@ -199,7 +181,7 @@ pub fn proof_multi< assert!(result); // Change a value and check that the proof fails - let temp = TFr::add(&y[4], &TFr::one()); + let temp = B::Fr::add(&y[4], &B::Fr::one()); let _temp = std::mem::replace(&mut y[4], temp); let result = ks2 .check_proof_multi(&commitment, &proof, &x, &y, coset_len) diff --git a/kzg/src/das.rs b/kzg/src/das.rs new file mode 100644 index 000000000..dae8305e5 --- /dev/null +++ b/kzg/src/das.rs @@ -0,0 +1,895 @@ +use core::fmt::Debug; + +#[cfg(feature = "parallel")] +use rayon::prelude::*; + +use crate::alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; + +use crate::{ + common_utils::{reverse_bit_order, reverse_bits_limited}, + eip_4844::{ + blob_to_polynomial, compute_powers, hash, hash_to_bls_field, BYTES_PER_COMMITMENT, + BYTES_PER_FIELD_ELEMENT, BYTES_PER_PROOF, + }, + eth::CELLS_PER_EXT_BLOB, + FFTFr, FFTSettings, Fr, G1Affine, G1Fp, G1LinComb, KZGSettings, PairingVerify, Poly, FFTG1, G1, + G2, +}; + +pub const RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN: [u8; 16] = *b"RCKZGCBATCH__V1_"; + +macro_rules! cfg_iter_mut { + ($collection:expr) => {{ + #[cfg(feature = "parallel")] + { + $collection.par_iter_mut() + } + #[cfg(not(feature = "parallel"))] + { + $collection.iter_mut() + } + }}; +} + +macro_rules! cfg_iter { + ($collection:expr) => {{ + #[cfg(feature = "parallel")] + { + $collection.par_iter() + } + #[cfg(not(feature = "parallel"))] + { + $collection.iter() + } + }}; +} + +pub trait EcBackend { + type Fr: Fr + Debug + Send; + type G1Fp: G1Fp; + type G1Affine: G1Affine; + type G1: G1 + + G1LinComb + + PairingVerify; + type G2: G2; + type Poly: Poly; + type FFTSettings: FFTSettings + FFTFr + FFTG1; + type KZGSettings: KZGSettings< + Self::Fr, + Self::G1, + Self::G2, + Self::FFTSettings, + Self::Poly, + Self::G1Fp, + Self::G1Affine, + >; +} + +pub trait Preset { + const FIELD_ELEMENTS_PER_BLOB: usize; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize; + const CELLS_PER_EXT_BLOB: usize; +} + +fn deduplicate_commitments( + commitments: &mut [TG1], + indicies: &mut [usize], + count: &mut usize, +) { + if *count == 0 { + return; + } + + indicies[0] = 0; + let mut new_count = 1; + + for i in 1..*count { + let mut exist = false; + for j in 0..new_count { + if commitments[i] == commitments[j] { + indicies[i] = j; + exist = true; + break; + } + } + + if !exist { + commitments[new_count] = commitments[i].clone(); + indicies[i] = new_count; + new_count += 1; + } + } +} + +/** + * This is a precomputed map of cell index to reverse-bits-limited cell index. + * + * for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) + * printf("%#04llx,\n", reverse_bits_limited(CELLS_PER_EXT_BLOB, i)); + * + * Because of the way our evaluation domain is defined, we can use CELL_INDICES_RBL to find the + * coset factor of a cell. In particular, for cell i, its coset factor is + * roots_of_unity[CELLS_INDICES_RBL[i]]. + */ +const CELL_INDICES_RBL: [usize; 128] = [ + 0x00, 0x40, 0x20, 0x60, 0x10, 0x50, 0x30, 0x70, 0x08, 0x48, 0x28, 0x68, 0x18, 0x58, 0x38, 0x78, + 0x04, 0x44, 0x24, 0x64, 0x14, 0x54, 0x34, 0x74, 0x0c, 0x4c, 0x2c, 0x6c, 0x1c, 0x5c, 0x3c, 0x7c, + 0x02, 0x42, 0x22, 0x62, 0x12, 0x52, 0x32, 0x72, 0x0a, 0x4a, 0x2a, 0x6a, 0x1a, 0x5a, 0x3a, 0x7a, + 0x06, 0x46, 0x26, 0x66, 0x16, 0x56, 0x36, 0x76, 0x0e, 0x4e, 0x2e, 0x6e, 0x1e, 0x5e, 0x3e, 0x7e, + 0x01, 0x41, 0x21, 0x61, 0x11, 0x51, 0x31, 0x71, 0x09, 0x49, 0x29, 0x69, 0x19, 0x59, 0x39, 0x79, + 0x05, 0x45, 0x25, 0x65, 0x15, 0x55, 0x35, 0x75, 0x0d, 0x4d, 0x2d, 0x6d, 0x1d, 0x5d, 0x3d, 0x7d, + 0x03, 0x43, 0x23, 0x63, 0x13, 0x53, 0x33, 0x73, 0x0b, 0x4b, 0x2b, 0x6b, 0x1b, 0x5b, 0x3b, 0x7b, + 0x07, 0x47, 0x27, 0x67, 0x17, 0x57, 0x37, 0x77, 0x0f, 0x4f, 0x2f, 0x6f, 0x1f, 0x5f, 0x3f, 0x7f, +]; + +pub trait DAS { + fn kzg_settings(&self) -> &B::KZGSettings; + + fn recover_cells_and_kzg_proofs( + &self, + recovered_cells: &mut [[B::Fr; FIELD_ELEMENTS_PER_CELL]], + recovered_proofs: Option<&mut [B::G1]>, + cell_indices: &[usize], + cells: &[[B::Fr; FIELD_ELEMENTS_PER_CELL]], + ) -> Result<(), String> { + if recovered_cells.len() != P::CELLS_PER_EXT_BLOB + || recovered_proofs + .as_ref() + .is_some_and(|it| it.len() != P::CELLS_PER_EXT_BLOB) + { + return Err("Invalid output array length".to_string()); + } + + if cells.len() != cell_indices.len() { + return Err( + "Cell indicies mismatch - cells length must be equal to cell indicies length" + .to_string(), + ); + } + + if cells.len() > P::CELLS_PER_EXT_BLOB { + return Err("Cell length cannot be larger than CELLS_PER_EXT_BLOB".to_string()); + } + + if cells.len() < P::CELLS_PER_EXT_BLOB / 2 { + return Err( + "Impossible to recover - cells length cannot be less than CELLS_PER_EXT_BLOB / 2" + .to_string(), + ); + } + + for cell_index in cell_indices { + if *cell_index >= P::CELLS_PER_EXT_BLOB { + return Err("Cell index cannot be larger than CELLS_PER_EXT_BLOB".to_string()); + } + } + + for cell in recovered_cells.iter_mut() { + for fr in cell { + *fr = B::Fr::null(); + } + } + + for i in 0..cells.len() { + let index = cell_indices[i]; + + if recovered_cells[index] + .as_ref() + .iter() + .any(|cell| !cell.is_null()) + { + return Err("Invalid output cell".to_string()); + } + + recovered_cells[index] = cells[i].clone(); + } + + let fft_settings = self.kzg_settings().get_fft_settings(); + + if cells.len() != P::CELLS_PER_EXT_BLOB { + recover_cells::( + recovered_cells.as_flattened_mut(), + cell_indices, + fft_settings, + )?; + } + + #[allow(clippy::redundant_slicing)] + let recovered_cells = &recovered_cells[..]; + + if let Some(recovered_proofs) = recovered_proofs { + let mut poly = vec![B::Fr::default(); P::FIELD_ELEMENTS_PER_EXT_BLOB]; + poly.clone_from_slice(recovered_cells.as_flattened()); + poly_lagrange_to_monomial::(&mut poly, fft_settings)?; + + let res = compute_fk20_proofs::( + &poly, + P::FIELD_ELEMENTS_PER_BLOB, + fft_settings, + self.kzg_settings(), + )?; + recovered_proofs.clone_from_slice(&res); + + reverse_bit_order(recovered_proofs)?; + } + + Ok(()) + } + + fn compute_cells_and_kzg_proofs( + &self, + cells: Option<&mut [[B::Fr; FIELD_ELEMENTS_PER_CELL]]>, + proofs: Option<&mut [B::G1]>, + blob: &[B::Fr], + ) -> Result<(), String> { + if cells.is_none() && proofs.is_none() { + return Err("Both cells & proofs cannot be none".to_string()); + } + + let poly = blob_to_polynomial::(blob)?; + + let mut poly_monomial = vec![B::Fr::zero(); P::FIELD_ELEMENTS_PER_EXT_BLOB]; + poly_monomial[0..P::FIELD_ELEMENTS_PER_BLOB].clone_from_slice(poly.get_coeffs()); + + let fft_settings = self.kzg_settings().get_fft_settings(); + poly_lagrange_to_monomial::( + &mut poly_monomial[..P::FIELD_ELEMENTS_PER_BLOB], + fft_settings, + )?; + + // compute cells + if let Some(cells) = cells { + cells + .as_flattened_mut() + .clone_from_slice(&fft_settings.fft_fr(&poly_monomial, false)?); + + reverse_bit_order(cells.as_flattened_mut())?; + }; + + // compute proofs + if let Some(proofs) = proofs { + let result = compute_fk20_proofs::( + &poly_monomial, + P::FIELD_ELEMENTS_PER_BLOB, + fft_settings, + self.kzg_settings(), + )?; + proofs.clone_from_slice(&result); + reverse_bit_order(proofs)?; + } + + Ok(()) + } + + fn verify_cell_kzg_proof_batch( + &self, + commitments: &[B::G1], + cell_indices: &[usize], + cells: &[[B::Fr; FIELD_ELEMENTS_PER_CELL]], + proofs: &[B::G1], + ) -> Result { + if cells.len() != cell_indices.len() { + return Err("Cell count mismatch".to_string()); + } + + if commitments.len() != cells.len() { + return Err("Commitment count mismatch".to_string()); + } + + if proofs.len() != cells.len() { + return Err("Proof count mismatch".to_string()); + } + + if cells.is_empty() { + return Ok(true); + } + + if cfg_iter!(cell_indices).any(|&cell_index| cell_index >= CELLS_PER_EXT_BLOB) { + return Err("Invalid cell index".to_string()); + } + + if cfg_iter!(proofs).any(|proof| !proof.is_valid()) { + return Err("Proof is not valid".to_string()); + } + + let mut new_count = commitments.len(); + let mut unique_commitments = commitments.to_vec(); + let mut commitment_indices = vec![0usize; cells.len()]; + deduplicate_commitments( + &mut unique_commitments, + &mut commitment_indices, + &mut new_count, + ); + + if cfg_iter!(unique_commitments).any(|commitment| !commitment.is_valid()) { + return Err("Commitment is not valid".to_string()); + } + + let fft_settings = self.kzg_settings().get_fft_settings(); + + let unique_commitments = &unique_commitments[0..new_count]; + + let r_powers = + compute_r_powers_for_verify_cell_kzg_proof_batch::( + unique_commitments, + &commitment_indices, + cell_indices, + cells, + proofs, + )?; + + let proof_lincomb = B::G1::g1_lincomb(proofs, &r_powers, cells.len(), None); + + let final_g1_sum = compute_weighted_sum_of_commitments::( + unique_commitments, + &commitment_indices, + &r_powers, + ); + + let interpolation_poly_commit = + compute_commitment_to_aggregated_interpolation_poly::( + &r_powers, + cell_indices, + cells, + fft_settings, + self.kzg_settings().get_g1_monomial(), + )?; + + let final_g1_sum = final_g1_sum.sub(&interpolation_poly_commit); + + let weighted_sum_of_proofs = computed_weighted_sum_of_proofs::< + FIELD_ELEMENTS_PER_CELL, + B, + P, + >(proofs, &r_powers, cell_indices, fft_settings)?; + + let final_g1_sum = final_g1_sum.add(&weighted_sum_of_proofs); + + let power_of_s = &self.kzg_settings().get_g2_monomial()[FIELD_ELEMENTS_PER_CELL]; + + Ok(B::G1::verify( + &final_g1_sum, + &B::G2::generator(), + &proof_lincomb, + power_of_s, + )) + } +} + +fn shift_poly(poly: &mut [B::Fr], shift_factor: &B::Fr) { + let mut factor_power = B::Fr::one(); + for coeff in poly.iter_mut().skip(1) { + factor_power = factor_power.mul(shift_factor); + *coeff = coeff.mul(&factor_power); + } +} + +fn coset_fft( + mut input: Vec, + fft_settings: &B::FFTSettings, +) -> Result, String> { + if input.is_empty() { + return Err("Invalid input length".to_string()); + } + + // TODO: move 7 to constant + shift_poly::(&mut input, &B::Fr::from_u64(7)); + + fft_settings.fft_fr(&input, false) +} + +fn coset_ifft( + input: &[B::Fr], + fft_settings: &B::FFTSettings, +) -> Result, String> { + if input.is_empty() { + return Err("Invalid input length".to_string()); + } + + let mut output = fft_settings.fft_fr(input, true)?; + + // TODO: move 1/7 to constant + shift_poly::(&mut output, &B::Fr::one().div(&B::Fr::from_u64(7))?); + + Ok(output) +} + +fn compute_vanishing_polynomial_from_roots( + roots: &[B::Fr], +) -> Result, String> { + if roots.is_empty() { + return Err("Roots cannot be empty".to_string()); + } + + let mut poly = Vec::new(); + poly.push(roots[0].negate()); + + for i in 1..roots.len() { + let neg_root = roots[i].negate(); + + poly.push(neg_root.add(&poly[i - 1])); + + for j in (1..i).rev() { + poly[j] = poly[j].mul(&neg_root).add(&poly[j - 1]); + } + poly[0] = poly[0].mul(&neg_root); + } + + poly.push(B::Fr::one()); + + Ok(poly) +} + +fn vanishing_polynomial_for_missing_cells< + const FIELD_ELEMENTS_PER_CELL: usize, + B: EcBackend, + P: Preset, +>( + missing_cell_indicies: &[usize], + fft_settings: &B::FFTSettings, +) -> Result, String> { + if missing_cell_indicies.is_empty() || missing_cell_indicies.len() >= P::CELLS_PER_EXT_BLOB { + return Err("Invalid missing cell indicies count".to_string()); + } + + let stride = P::FIELD_ELEMENTS_PER_EXT_BLOB / P::CELLS_PER_EXT_BLOB; + + let roots = missing_cell_indicies + .iter() + .map(|i| fft_settings.get_roots_of_unity_at(*i * stride)) + .collect::>(); + + let short_vanishing_poly = compute_vanishing_polynomial_from_roots::(&roots)?; + + let mut vanishing_poly = vec![B::Fr::zero(); P::FIELD_ELEMENTS_PER_EXT_BLOB]; + + for (i, coeff) in short_vanishing_poly.into_iter().enumerate() { + vanishing_poly[i * FIELD_ELEMENTS_PER_CELL] = coeff + } + + Ok(vanishing_poly) +} + +fn recover_cells( + output: &mut [B::Fr], + cell_indicies: &[usize], + fft_settings: &B::FFTSettings, +) -> Result<(), String> { + let mut missing_cell_indicies = Vec::new(); + + let mut cells_brp = output.to_vec(); + reverse_bit_order(&mut cells_brp)?; + + for i in 0..P::CELLS_PER_EXT_BLOB { + if !cell_indicies.contains(&i) { + missing_cell_indicies.push(reverse_bits_limited(P::CELLS_PER_EXT_BLOB, i)); + } + } + + let missing_cell_indicies = &missing_cell_indicies[..]; + + if missing_cell_indicies.len() > P::CELLS_PER_EXT_BLOB / 2 { + return Err("Not enough cells".to_string()); + } + + let vanishing_poly_coeff = vanishing_polynomial_for_missing_cells::< + FIELD_ELEMENTS_PER_CELL, + B, + P, + >(missing_cell_indicies, fft_settings)?; + + let vanishing_poly_eval = fft_settings.fft_fr(&vanishing_poly_coeff, false)?; + + let mut extended_evaluation_times_zero = Vec::with_capacity(P::FIELD_ELEMENTS_PER_EXT_BLOB); + + for i in 0..P::FIELD_ELEMENTS_PER_EXT_BLOB { + if cells_brp[i].is_null() { + extended_evaluation_times_zero.push(B::Fr::zero()); + } else { + extended_evaluation_times_zero.push(cells_brp[i].mul(&vanishing_poly_eval[i])); + } + } + + let extended_evaluation_times_zero_coeffs = + fft_settings.fft_fr(&extended_evaluation_times_zero, true)?; + let mut extended_evaluations_over_coset = + coset_fft::(extended_evaluation_times_zero_coeffs, fft_settings)?; + + let vanishing_poly_over_coset = coset_fft::(vanishing_poly_coeff, fft_settings)?; + + for i in 0..P::FIELD_ELEMENTS_PER_EXT_BLOB { + extended_evaluations_over_coset[i] = + extended_evaluations_over_coset[i].div(&vanishing_poly_over_coset[i])?; + } + + let reconstructed_poly_coeff = coset_ifft::(&extended_evaluations_over_coset, fft_settings)?; + + let out = fft_settings.fft_fr(&reconstructed_poly_coeff, false)?; + output.clone_from_slice(&out); + + reverse_bit_order(output)?; + + Ok(()) +} + +fn poly_lagrange_to_monomial( + lagrange_poly: &mut [B::Fr], + fft_settings: &B::FFTSettings, +) -> Result<(), String> { + let mut poly = lagrange_poly.to_vec(); + + reverse_bit_order(&mut poly)?; + + lagrange_poly.clone_from_slice(&fft_settings.fft_fr(&poly, true)?); + + Ok(()) +} + +fn toeplitz_coeffs_stride( + out: &mut [B::Fr], + input: &[B::Fr], + n: usize, + offset: usize, + stride: usize, +) -> Result<(), String> { + if stride == 0 { + return Err("Stride cannot be zero".to_string()); + } + + let k = n / stride; + let k2 = k * 2; + + out[0] = input[n - 1 - offset].clone(); + { + let mut i = 1; + while i <= k + 1 && i < k2 { + out[i] = B::Fr::zero(); + i += 1; + } + }; + + { + let mut i = k + 2; + let mut j = 2 * stride - offset - 1; + while i < k2 { + out[i] = input[j].clone(); + i += 1; + j += stride; + } + }; + + Ok(()) +} + +fn compute_fk20_proofs( + poly: &[B::Fr], + n: usize, + fft_settings: &B::FFTSettings, + kzg_settings: &B::KZGSettings, +) -> Result, String> { + let k = n / FIELD_ELEMENTS_PER_CELL; + let k2 = k * 2; + + let mut coeffs = vec![vec![B::Fr::default(); k]; k2]; + let mut h_ext_fft = vec![B::G1::identity(); k2]; + let mut toeplitz_coeffs = vec![B::Fr::default(); k2]; + let mut toeplitz_coeffs_fft = vec![B::Fr::default(); k2]; + + for i in 0..FIELD_ELEMENTS_PER_CELL { + toeplitz_coeffs_stride::(&mut toeplitz_coeffs, poly, n, i, FIELD_ELEMENTS_PER_CELL)?; + toeplitz_coeffs_fft.clone_from_slice(&fft_settings.fft_fr(&toeplitz_coeffs, false)?); + for j in 0..k2 { + coeffs[j][i] = toeplitz_coeffs_fft[j].clone(); + } + } + + for i in 0..k2 { + h_ext_fft[i] = B::G1::g1_lincomb( + kzg_settings.get_x_ext_fft_column(i), + &coeffs[i], + FIELD_ELEMENTS_PER_CELL, + None, + ); + } + + let mut h = fft_settings.fft_g1(&h_ext_fft, true)?; + + cfg_iter_mut!(h) + .take(k2) + .skip(k) + .for_each(|h| *h = B::G1::identity()); + + fft_settings.fft_g1(&h, false) +} + +fn compute_r_powers_for_verify_cell_kzg_proof_batch< + const FIELD_ELEMENTS_PER_CELL: usize, + B: EcBackend, +>( + commitments: &[B::G1], + commitment_indices: &[usize], + cell_indices: &[usize], + cells: &[[B::Fr; FIELD_ELEMENTS_PER_CELL]], + proofs: &[B::G1], +) -> Result, String> { + if commitment_indices.len() != cells.len() + || cell_indices.len() != cells.len() + || proofs.len() != cells.len() + { + return Err("Cell count mismatch".to_string()); + } + + // TODO: challenge generation probably also has to be in preset + + let input_size = RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN.len() + + size_of::() + + size_of::() + + size_of::() + // probably, BYTES_PER_COMMITMENT should be in backend trait - + // currently impossible due to encoded commitment length in G1 trait + + (commitments.len() * BYTES_PER_COMMITMENT) + + (cells.len() * size_of::()) + + (cells.len() * size_of::()) + + (cells.len() * (FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT)) + + (cells.len() * BYTES_PER_PROOF); + + let mut bytes = vec![0; input_size]; + bytes[..16].copy_from_slice(&RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN); + bytes[16..24].copy_from_slice(&(FIELD_ELEMENTS_PER_CELL as u64).to_be_bytes()); + bytes[24..32].copy_from_slice(&(commitments.len() as u64).to_be_bytes()); + bytes[32..40].copy_from_slice(&(cells.len() as u64).to_be_bytes()); + + let mut offset = 40; + for commitment in commitments { + bytes[offset..(offset + BYTES_PER_COMMITMENT)].copy_from_slice(&commitment.to_bytes()); + offset += BYTES_PER_COMMITMENT; + } + + for i in 0..cells.len() { + bytes[offset..(offset + 8)].copy_from_slice(&(commitment_indices[i] as u64).to_be_bytes()); + offset += 8; + + bytes[offset..(offset + 8)].copy_from_slice(&(cell_indices[i] as u64).to_be_bytes()); + offset += 8; + + bytes[offset..(offset + (FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT))] + .copy_from_slice( + &cells[i] + .as_ref() + .iter() + .flat_map(|fr| fr.to_bytes()) + .collect::>(), + ); + offset += FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT; + + bytes[offset..(offset + BYTES_PER_PROOF)].copy_from_slice(&(proofs[i].to_bytes())); + offset += BYTES_PER_PROOF; + } + + let bytes = &bytes[..]; + + if offset != input_size { + return Err("Failed to create challenge - invalid length".to_string()); + } + + // hash function (as well as whole algo above I guess?) should be in Preset (or backend, not clear for now) + let eval_challenge = hash(bytes); + let r = hash_to_bls_field(&eval_challenge); + + Ok(compute_powers(&r, cells.len())) +} + +fn compute_weighted_sum_of_commitments( + commitments: &[B::G1], + commitment_indices: &[usize], + r_powers: &[B::Fr], +) -> B::G1 { + let mut commitment_weights = vec![B::Fr::zero(); commitments.len()]; + + #[cfg(feature = "parallel")] + { + let num_threads = rayon::current_num_threads(); + let chunk_size = (r_powers.len() + num_threads - 1) / num_threads; + + let intermediate_weights: Vec<_> = r_powers + .par_chunks(chunk_size) + .zip(commitment_indices.par_chunks(chunk_size)) + .map(|(r_chunk, idx_chunk)| { + let mut local_weights = vec![B::Fr::zero(); commitments.len()]; + for (r_power, &index) in r_chunk.iter().zip(idx_chunk.iter()) { + local_weights[index] = local_weights[index].add(r_power); + } + local_weights + }) + .collect(); + + for local_weights in intermediate_weights { + for (i, weight) in local_weights.into_iter().enumerate() { + commitment_weights[i] = commitment_weights[i].add(&weight); + } + } + } + + #[cfg(not(feature = "parallel"))] + { + for i in 0..r_powers.len() { + commitment_weights[commitment_indices[i]] = + commitment_weights[commitment_indices[i]].add(&r_powers[i]); + } + } + + B::G1::g1_lincomb(commitments, &commitment_weights, commitments.len(), None) +} + +fn get_inv_coset_shift_for_cell( + cell_index: usize, + fft_settings: &B::FFTSettings, +) -> Result { + /* + * Get the cell index in reverse-bit order. + * This index points to this cell's coset factor h_k in the roots_of_unity array. + */ + let cell_index_rbl = CELL_INDICES_RBL[cell_index]; + + /* + * Observe that for every element in roots_of_unity, we can find its inverse by + * accessing its reflected element. + * + * For example, consider a multiplicative subgroup with eight elements: + * roots = {w^0, w^1, w^2, ... w^7, w^0} + * For a root of unity in roots[i], we can find its inverse in roots[-i]. + */ + if cell_index_rbl > P::FIELD_ELEMENTS_PER_EXT_BLOB { + return Err("Invalid cell index".to_string()); + } + let inv_coset_factor_idx = P::FIELD_ELEMENTS_PER_EXT_BLOB - cell_index_rbl; + + /* Get h_k^{-1} using the index */ + if inv_coset_factor_idx > P::FIELD_ELEMENTS_PER_EXT_BLOB { + return Err("Invalid cell index".to_string()); + } + + Ok(fft_settings.get_roots_of_unity_at(inv_coset_factor_idx)) +} + +fn compute_commitment_to_aggregated_interpolation_poly< + const FIELD_ELEMENTS_PER_CELL: usize, + B: EcBackend, + P: Preset, +>( + r_powers: &[B::Fr], + cell_indices: &[usize], + cells: &[[B::Fr; FIELD_ELEMENTS_PER_CELL]], + fft_settings: &B::FFTSettings, + g1_monomial: &[B::G1], +) -> Result { + let mut aggregated_column_cells = + vec![B::Fr::zero(); P::CELLS_PER_EXT_BLOB * FIELD_ELEMENTS_PER_CELL]; + + for (cell_index, column_index) in cell_indices.iter().enumerate() { + for fr_index in 0..FIELD_ELEMENTS_PER_CELL { + let original_fr = cells[cell_index].as_ref()[fr_index].clone(); + + let scaled_fr = original_fr.mul(&r_powers[cell_index]); + + let array_index = column_index * FIELD_ELEMENTS_PER_CELL + fr_index; + aggregated_column_cells[array_index] = + aggregated_column_cells[array_index].add(&scaled_fr); + } + } + + let mut is_cell_used = vec![false; P::CELLS_PER_EXT_BLOB]; + + for cell_index in cell_indices { + is_cell_used[*cell_index] = true; + } + + let mut aggregated_interpolation_poly = vec![B::Fr::zero(); FIELD_ELEMENTS_PER_CELL]; + for (i, is_cell_used) in is_cell_used.iter().enumerate() { + if !is_cell_used { + continue; + } + + let index = i * FIELD_ELEMENTS_PER_CELL; + + reverse_bit_order(&mut aggregated_column_cells[index..(index + FIELD_ELEMENTS_PER_CELL)])?; + + let mut column_interpolation_poly = fft_settings.fft_fr( + &aggregated_column_cells[index..(index + FIELD_ELEMENTS_PER_CELL)], + true, + )?; + + let inv_coset_factor = + get_inv_coset_shift_for_cell::(i, fft_settings)?; + + shift_poly::(&mut column_interpolation_poly, &inv_coset_factor); + + for k in 0..FIELD_ELEMENTS_PER_CELL { + aggregated_interpolation_poly[k] = + aggregated_interpolation_poly[k].add(&column_interpolation_poly[k]); + } + } + + // TODO: maybe pass precomputation here? + Ok(B::G1::g1_lincomb( + g1_monomial, + &aggregated_interpolation_poly, + FIELD_ELEMENTS_PER_CELL, + None, + )) +} + +fn get_coset_shift_pow_for_cell( + cell_index: usize, + fft_settings: &B::FFTSettings, +) -> Result { + /* + * Get the cell index in reverse-bit order. + * This index points to this cell's coset factor h_k in the roots_of_unity array. + */ + let cell_idx_rbl = CELL_INDICES_RBL[cell_index]; + + /* + * Get the index to h_k^n in the roots_of_unity array. + * + * Multiplying the index of h_k by n, effectively raises h_k to the n-th power, + * because advancing in the roots_of_unity array corresponds to increasing exponents. + */ + let h_k_pow_idx = cell_idx_rbl * FIELD_ELEMENTS_PER_CELL; + + if h_k_pow_idx > P::FIELD_ELEMENTS_PER_EXT_BLOB { + return Err("Invalid cell index".to_string()); + } + + /* Get h_k^n using the index */ + Ok(fft_settings.get_roots_of_unity_at(h_k_pow_idx)) +} + +fn computed_weighted_sum_of_proofs< + const FIELD_ELEMENTS_PER_CELL: usize, + B: EcBackend, + P: Preset, +>( + proofs: &[B::G1], + r_powers: &[B::Fr], + cell_indices: &[usize], + fft_settings: &B::FFTSettings, +) -> Result { + let num_cells = proofs.len(); + + if r_powers.len() != num_cells || cell_indices.len() != num_cells { + return Err("Length mismatch".to_string()); + } + + let mut weighted_powers_of_r = Vec::with_capacity(num_cells); + for i in 0..num_cells { + let h_k_pow = get_coset_shift_pow_for_cell::( + cell_indices[i], + fft_settings, + )?; + + weighted_powers_of_r.push(r_powers[i].mul(&h_k_pow)); + } + + Ok(B::G1::g1_lincomb( + proofs, + &weighted_powers_of_r, + num_cells, + None, + )) +} + +/* + * Automatically implement DAS for all backends + */ +impl + DAS for B::KZGSettings +{ + fn kzg_settings(&self) -> &::KZGSettings { + self + } +} diff --git a/kzg/src/eip_4844.rs b/kzg/src/eip_4844.rs index fac0e0e36..b0ce04a87 100644 --- a/kzg/src/eip_4844.rs +++ b/kzg/src/eip_4844.rs @@ -2,6 +2,8 @@ extern crate alloc; use crate::common_utils::reverse_bit_order; +use crate::eth::c_bindings::CKZGSettings; +use crate::eth::FIELD_ELEMENTS_PER_EXT_BLOB; use crate::msm::precompute::PrecomputationTable; use crate::G1Affine; use crate::G1Fp; @@ -15,9 +17,7 @@ use alloc::string::ToString; use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; -use blst::blst_p1_affine; pub use blst::{blst_fr, blst_p1, blst_p2}; -use core::ffi::c_uint; use core::hash::Hash; use core::hash::Hasher; use sha2::{Digest, Sha256}; @@ -57,114 +57,8 @@ pub const RANDOM_CHALLENGE_KZG_BATCH_DOMAIN: [u8; 16] = [ ]; // "RCKZGBATCH___V1_" ////////////////////////////// Constant values for EIP-7594 ////////////////////////////// -pub const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 2 * FIELD_ELEMENTS_PER_BLOB; -pub const FIELD_ELEMENTS_PER_CELL: usize = 64; -pub const BYTES_PER_CELL: usize = FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT; -pub const CELLS_PER_EXT_BLOB: usize = FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL; -pub const RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN: [u8; 16] = *b"RCKZGCBATCH__V1_"; - ////////////////////////////// C API for EIP-4844 ////////////////////////////// -pub type C_KZG_RET = c_uint; - -pub const C_KZG_RET_OK: C_KZG_RET = 0; -pub const C_KZG_RET_BADARGS: C_KZG_RET = 1; -pub const C_KZG_RET_ERROR: C_KZG_RET = 2; -pub const C_KZG_RET_MALLOC: C_KZG_RET = 3; - -#[repr(C)] -pub struct Bytes32 { - pub bytes: [u8; 32], -} - -#[repr(C)] -pub struct Bytes48 { - pub bytes: [u8; 48], -} - -#[repr(C)] -pub struct BLSFieldElement { - pub bytes: [u8; BYTES_PER_FIELD_ELEMENT], -} - -#[repr(C)] -pub struct Blob { - pub bytes: [u8; BYTES_PER_BLOB], -} - -#[repr(C)] -pub struct KZGCommitment { - pub bytes: [u8; BYTES_PER_COMMITMENT], -} - -#[repr(C)] -pub struct KZGProof { - pub bytes: [u8; BYTES_PER_PROOF], -} - -#[repr(C)] -pub struct CKZGSettings { - /** - * Roots of unity for the subgroup of size `FIELD_ELEMENTS_PER_EXT_BLOB`. - * - * The array contains `FIELD_ELEMENTS_PER_EXT_BLOB + 1` elements. - * The array starts and ends with Fr::one(). - */ - pub roots_of_unity: *mut blst_fr, - /** - * Roots of unity for the subgroup of size `FIELD_ELEMENTS_PER_EXT_BLOB` in bit-reversed order. - * - * This array is derived by applying a bit-reversal permutation to `roots_of_unity` - * excluding the last element. Essentially: - * `brp_roots_of_unity = bit_reversal_permutation(roots_of_unity[:-1])` - * - * The array contains `FIELD_ELEMENTS_PER_EXT_BLOB` elements. - */ - pub brp_roots_of_unity: *mut blst_fr, - /** - * Roots of unity for the subgroup of size `FIELD_ELEMENTS_PER_EXT_BLOB` in reversed order. - * - * It is the reversed version of `roots_of_unity`. Essentially: - * `reverse_roots_of_unity = reverse(roots_of_unity)` - * - * This array is primarily used in FFTs. - * The array contains `FIELD_ELEMENTS_PER_EXT_BLOB + 1` elements. - * The array starts and ends with Fr::one(). - */ - pub reverse_roots_of_unity: *mut blst_fr, - /** - * G1 group elements from the trusted setup in monomial form. - * The array contains `NUM_G1_POINTS = FIELD_ELEMENTS_PER_BLOB` elements. - */ - pub g1_values_monomial: *mut blst_p1, - /** - * G1 group elements from the trusted setup in Lagrange form and bit-reversed order. - * The array contains `NUM_G1_POINTS = FIELD_ELEMENTS_PER_BLOB` elements. - */ - pub g1_values_lagrange_brp: *mut blst_p1, - /** - * G2 group elements from the trusted setup in monomial form. - * The array contains `NUM_G2_POINTS` elements. - */ - pub g2_values_monomial: *mut blst_p2, - /** Data used during FK20 proof generation. */ - pub x_ext_fft_columns: *mut *mut blst_p1, - /** The precomputed tables for fixed-base MSM. */ - pub tables: *mut *mut blst_p1_affine, - /** The window size for the fixed-base MSM. */ - pub wbits: usize, - /** The scratch size for the fixed-base MSM. */ - pub scratch_size: usize, -} - -#[repr(C)] -pub struct Cell { - pub bytes: [u8; BYTES_PER_CELL], -} - -#[repr(C)] -pub struct CellIndex(u64); - pub struct PrecomputationTableManager where TFr: Fr, diff --git a/kzg/src/eip_7594.rs b/kzg/src/eip_7594.rs deleted file mode 100644 index 317905af6..000000000 --- a/kzg/src/eip_7594.rs +++ /dev/null @@ -1,985 +0,0 @@ -////////////////////////////// Trait based implementations of functions for EIP-7594 ////////////////////////////// - -extern crate alloc; - -use alloc::string::{String, ToString}; -use alloc::vec; -use alloc::vec::Vec; - -use crate::{ - common_utils::{reverse_bit_order, reverse_bits_limited}, - eip_4844::{ - blob_to_polynomial, compute_powers, hash, hash_to_bls_field, BYTES_PER_CELL, - BYTES_PER_COMMITMENT, BYTES_PER_PROOF, CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_BLOB, - FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB, - RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN, - }, - G1Mul, KZGSettings, G2, -}; -use crate::{ - FFTFr, FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1LinComb, PairingVerify, Poly, FFTG1, G1, -}; - -#[cfg(feature = "parallel")] -use rayon::prelude::*; - -fn fr_ifft>( - output: &mut [TFr], - input: &[TFr], - s: &TFFTSettings, -) -> Result<(), String> { - output.clone_from_slice(&s.fft_fr(input, true)?); - - Ok(()) -} - -fn fr_fft>( - output: &mut [TFr], - input: &[TFr], - s: &TFFTSettings, -) -> Result<(), String> { - output.clone_from_slice(&s.fft_fr(input, false)?); - - Ok(()) -} - -fn poly_lagrange_to_monomial>( - output: &mut [TFr], - largrange_poly: &[TFr], - s: &TFFTSettings, -) -> Result<(), String> { - let mut poly = largrange_poly.to_vec(); - - reverse_bit_order(&mut poly)?; - - fr_ifft(output, &poly, s)?; - - Ok(()) -} - -fn toeplitz_coeffs_stride( - out: &mut [TFr], - input: &[TFr], - n: usize, - offset: usize, - stride: usize, -) -> Result<(), String> { - if stride == 0 { - return Err("Stride cannot be zero".to_string()); - } - - let k = n / stride; - let k2 = k * 2; - - out[0] = input[n - 1 - offset].clone(); - { - let mut i = 1; - while i <= k + 1 && i < k2 { - out[i] = TFr::zero(); - i += 1; - } - }; - - { - let mut i = k + 2; - let mut j = 2 * stride - offset - 1; - while i < k2 { - out[i] = input[j].clone(); - i += 1; - j += stride; - } - }; - - Ok(()) -} - -fn g1_ifft>( - out: &mut [TG1], - input: &[TG1], - s: &TFFTSettings, -) -> Result<(), String> { - if input.len() > FIELD_ELEMENTS_PER_EXT_BLOB || !input.len().is_power_of_two() { - return Err("Invalid input length".to_string()); - } - - out.clone_from_slice(&s.fft_g1(input, true)?); - - Ok(()) -} - -fn g1_fft>( - out: &mut [TG1], - input: &[TG1], - s: &TFFTSettings, -) -> Result<(), String> { - if input.len() > FIELD_ELEMENTS_PER_EXT_BLOB || !input.len().is_power_of_two() { - return Err("Invalid input length".to_string()); - } - - out.clone_from_slice(&s.fft_g1(input, false)?); - - Ok(()) -} - -macro_rules! cfg_iter_mut { - ($collection:expr) => { - { - #[cfg(feature = "parallel")] - { - $collection.par_iter_mut() - } - #[cfg(not(feature = "parallel"))] - { - $collection.iter_mut() - } - } - }; -} - -fn compute_fk20_proofs< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp + G1LinComb, - TG2: G2, - TFFTSettings: FFTSettings + FFTG1 + FFTFr, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings+Sync, ->( - proofs: &mut [TG1], - poly: &[TFr], - n: usize, - s: &TKZGSettings, -) -> Result<(), String> { - let k = n / FIELD_ELEMENTS_PER_CELL; - let k2 = k * 2; - - let mut coeffs = vec![vec![TFr::default(); k]; k2]; - let mut h_ext_fft = vec![TG1::identity(); k2]; - let mut toeplitz_coeffs = vec![TFr::default(); k2]; - let mut toeplitz_coeffs_fft = vec![TFr::default(); k2]; - - for i in 0..FIELD_ELEMENTS_PER_CELL { - toeplitz_coeffs_stride(&mut toeplitz_coeffs, poly, n, i, FIELD_ELEMENTS_PER_CELL)?; - fr_fft( - &mut toeplitz_coeffs_fft, - &toeplitz_coeffs, - s.get_fft_settings(), - )?; - for j in 0..k2 { - coeffs[j][i] = toeplitz_coeffs_fft[j].clone(); - } - } - - for i in 0..k2 { - h_ext_fft[i] = TG1::g1_lincomb( - s.get_x_ext_fft_column(i), - &coeffs[i], - FIELD_ELEMENTS_PER_CELL, - None, - ); - } - - let mut h = vec![TG1::identity(); k2]; - g1_ifft(&mut h, &h_ext_fft, s.get_fft_settings())?; - - cfg_iter_mut!(h).take(k2).skip(k).for_each(|h_elem| { - *h_elem = TG1::identity(); - }); - - g1_fft(proofs, &h, s.get_fft_settings())?; - - Ok(()) -} - -pub fn compute_cells_and_kzg_proofs< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp + G1LinComb, - TG2: G2, - TFFTSettings: FFTSettings + FFTG1 + FFTFr, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings+Sync, ->( - cells: Option<&mut [[TFr; FIELD_ELEMENTS_PER_CELL]]>, - proofs: Option<&mut [TG1]>, - blob: &[TFr], - s: &TKZGSettings, -) -> Result<(), String> { - if cells.is_none() && proofs.is_none() { - return Err("Both cells & proofs cannot be none".to_string()); - } - - let poly = blob_to_polynomial::(blob)?; - - let mut poly_monomial = vec![TFr::zero(); FIELD_ELEMENTS_PER_EXT_BLOB]; - - poly_lagrange_to_monomial( - &mut poly_monomial[..FIELD_ELEMENTS_PER_BLOB], - poly.get_coeffs(), - s.get_fft_settings(), - )?; - - // compute cells - if let Some(cells) = cells { - fr_fft( - cells.as_flattened_mut(), - &poly_monomial, - s.get_fft_settings(), - )?; - - reverse_bit_order(cells.as_flattened_mut())?; - }; - - // compute proofs - if let Some(proofs) = proofs { - compute_fk20_proofs(proofs, &poly_monomial, FIELD_ELEMENTS_PER_BLOB, s)?; - reverse_bit_order(proofs)?; - } - - Ok(()) -} - -fn compute_vanishing_polynomial_from_roots(roots: &[TFr]) -> Result, String> { - if roots.is_empty() { - return Err("Roots cannot be empty".to_string()); - } - - let mut poly = Vec::new(); - poly.push(roots[0].negate()); - - for i in 1..roots.len() { - let neg_root = roots[i].negate(); - - poly.push(neg_root.add(&poly[i - 1])); - - for j in (1..i).rev() { - poly[j] = poly[j].mul(&neg_root).add(&poly[j - 1]); - } - poly[0] = poly[0].mul(&neg_root); - } - - poly.push(TFr::one()); - - Ok(poly) -} - -fn vanishing_polynomial_for_missing_cells< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TFFTSettings: FFTSettings, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - missing_cell_indicies: &[usize], - s: &TKZGSettings, -) -> Result, String> { - if missing_cell_indicies.is_empty() || missing_cell_indicies.len() >= CELLS_PER_EXT_BLOB { - return Err("Invalid missing cell indicies count".to_string()); - } - - const STRIDE: usize = FIELD_ELEMENTS_PER_EXT_BLOB / CELLS_PER_EXT_BLOB; - - let roots = missing_cell_indicies - .iter() - .map(|i| s.get_roots_of_unity_at(*i * STRIDE)) - .collect::>(); - - let short_vanishing_poly = compute_vanishing_polynomial_from_roots(&roots)?; - - let mut vanishing_poly = vec![TFr::zero(); FIELD_ELEMENTS_PER_EXT_BLOB]; - - for i in 0..short_vanishing_poly.len() { - vanishing_poly[i * FIELD_ELEMENTS_PER_CELL] = short_vanishing_poly[i].clone(); - } - - Ok(vanishing_poly) -} - -fn shift_poly(poly: &mut [TFr], shift_factor: &TFr) { - let mut factor_power = TFr::one(); - for coeff in poly.iter_mut().skip(1) { - factor_power = factor_power.mul(shift_factor); - *coeff = coeff.mul(&factor_power); - } -} - -fn coset_fft + FFTFr>( - input: &[TFr], - s: &TFFTSettings, -) -> Result, String> { - if input.is_empty() { - return Err("Invalid input length".to_string()); - } - - let mut in_shifted = input.to_vec(); - // TODO: move 7 to constant - shift_poly(&mut in_shifted, &TFr::from_u64(7)); - - let mut output = vec![TFr::default(); input.len()]; - fr_fft(&mut output, &in_shifted, s)?; - - Ok(output) -} - -fn coset_ifft>( - input: &[TFr], - s: &TFFTSettings, -) -> Result, String> { - if input.is_empty() { - return Err("Invalid input length".to_string()); - } - - let mut output = vec![TFr::default(); input.len()]; - fr_ifft(&mut output, input, s)?; - - shift_poly(&mut output, &TFr::one().div(&TFr::from_u64(7))?); - - Ok(output) -} - -fn recover_cells< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TFFTSettings: FFTSettings + FFTFr + FFTG1, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - output: &mut [TFr], - cell_indicies: &[usize], - s: &TKZGSettings, -) -> Result<(), String> { - let mut missing_cell_indicies = Vec::new(); - - let mut cells_brp = output.to_vec(); - reverse_bit_order(&mut cells_brp)?; - - for i in 0..CELLS_PER_EXT_BLOB { - if !cell_indicies.contains(&i) { - missing_cell_indicies.push(reverse_bits_limited(CELLS_PER_EXT_BLOB, i)); - } - } - - let missing_cell_indicies = &missing_cell_indicies[..]; - - if missing_cell_indicies.len() > CELLS_PER_EXT_BLOB / 2 { - return Err("Not enough cells".to_string()); - } - - let vanishing_poly_coeff = vanishing_polynomial_for_missing_cells(missing_cell_indicies, s)?; - let mut vanishing_poly_eval = vec![TFr::default(); FIELD_ELEMENTS_PER_EXT_BLOB]; - - fr_fft( - &mut vanishing_poly_eval, - &vanishing_poly_coeff, - s.get_fft_settings(), - )?; - - let mut extended_evaluation_times_zero = Vec::with_capacity(FIELD_ELEMENTS_PER_EXT_BLOB); - - for i in 0..FIELD_ELEMENTS_PER_EXT_BLOB { - if cells_brp[i].is_null() { - extended_evaluation_times_zero.push(TFr::zero()); - } else { - extended_evaluation_times_zero.push(cells_brp[i].mul(&vanishing_poly_eval[i])); - } - } - - let mut extended_evaluation_times_zero_coeffs = - vec![TFr::default(); FIELD_ELEMENTS_PER_EXT_BLOB]; - fr_ifft( - &mut extended_evaluation_times_zero_coeffs, - &extended_evaluation_times_zero, - s.get_fft_settings(), - )?; - - let mut extended_evaluations_over_coset = - coset_fft(&extended_evaluation_times_zero_coeffs, s.get_fft_settings())?; - - let vanishing_poly_over_coset = coset_fft(&vanishing_poly_coeff, s.get_fft_settings())?; - - for i in 0..FIELD_ELEMENTS_PER_EXT_BLOB { - extended_evaluations_over_coset[i] = - extended_evaluations_over_coset[i].div(&vanishing_poly_over_coset[i])?; - } - - let reconstructed_poly_coeff = - coset_ifft(&extended_evaluations_over_coset, s.get_fft_settings())?; - - fr_fft(output, &reconstructed_poly_coeff, s.get_fft_settings())?; - - reverse_bit_order(output)?; - - Ok(()) -} - -pub fn recover_cells_and_kzg_proofs< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp + G1LinComb, - TG2: G2, - TFFTSettings: FFTSettings + FFTFr + FFTG1, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings+Sync, ->( - recovered_cells: &mut [[TFr; FIELD_ELEMENTS_PER_CELL]], - recovered_proofs: Option<&mut [TG1]>, - cell_indicies: &[usize], - cells: &[[TFr; FIELD_ELEMENTS_PER_CELL]], - s: &TKZGSettings, -) -> Result<(), String> { - if recovered_cells.len() != CELLS_PER_EXT_BLOB - || recovered_proofs - .as_ref() - .is_some_and(|it| it.len() != CELLS_PER_EXT_BLOB) - { - return Err("Invalid output array length".to_string()); - } - - if cells.len() != cell_indicies.len() { - return Err( - "Cell indicies mismatch - cells length must be equal to cell indicies length" - .to_string(), - ); - } - - if cells.len() > CELLS_PER_EXT_BLOB { - return Err("Cell length cannot be larger than CELLS_PER_EXT_BLOB".to_string()); - } - - if cells.len() < CELLS_PER_EXT_BLOB / 2 { - return Err( - "Impossible to recover - cells length cannot be less than CELLS_PER_EXT_BLOB / 2" - .to_string(), - ); - } - - for cell_index in cell_indicies { - if *cell_index >= CELLS_PER_EXT_BLOB { - return Err("Cell index cannot be larger than CELLS_PER_EXT_BLOB".to_string()); - } - } - - for cell in recovered_cells.iter_mut() { - for fr in cell { - *fr = TFr::null(); - } - } - - for i in 0..cells.len() { - let index = cell_indicies[i]; - - for j in 0..FIELD_ELEMENTS_PER_CELL { - if !recovered_cells[index][j].is_null() { - return Err("Invalid output cell".to_string()); - } - } - - recovered_cells[index] = cells[i].clone(); - } - - if cells.len() != CELLS_PER_EXT_BLOB { - recover_cells(recovered_cells.as_flattened_mut(), cell_indicies, s)?; - } - - #[allow(clippy::redundant_slicing)] - let recovered_cells = &recovered_cells[..]; - - if let Some(recovered_proofs) = recovered_proofs { - let mut poly = vec![TFr::default(); FIELD_ELEMENTS_PER_EXT_BLOB]; - - poly_lagrange_to_monomial( - &mut poly, - recovered_cells.as_flattened(), - s.get_fft_settings(), - )?; - - compute_fk20_proofs(recovered_proofs, &poly, FIELD_ELEMENTS_PER_BLOB, s)?; - - reverse_bit_order(recovered_proofs)?; - } - - Ok(()) -} - -fn deduplicate_commitments( - commitments: &mut [TG1], - indicies: &mut [usize], - count: &mut usize, -) { - if *count == 0 { - return; - } - - indicies[0] = 0; - let mut new_count = 1; - - for i in 1..*count { - let mut exist = false; - for j in 0..new_count { - if commitments[i] == commitments[j] { - indicies[i] = j; - exist = true; - break; - } - } - - if !exist { - commitments[new_count] = commitments[i].clone(); - indicies[i] = new_count; - new_count += 1; - } - } -} - -fn compute_r_powers_for_verify_cell_kzg_proof_batch( - commitments: &[TG1], - commitment_indices: &[usize], - cell_indices: &[usize], - cells: &[[TFr; FIELD_ELEMENTS_PER_CELL]], - proofs: &[TG1], -) -> Result, String> { - if commitment_indices.len() != cells.len() - || cell_indices.len() != cells.len() - || proofs.len() != cells.len() - { - return Err("Cell count mismatch".to_string()); - } - - let input_size = RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN.len() - + size_of::() - + size_of::() - + size_of::() - + (commitments.len() * BYTES_PER_COMMITMENT) - + (cells.len() * size_of::()) - + (cells.len() * size_of::()) - + (cells.len() * BYTES_PER_CELL) - + (cells.len() * BYTES_PER_PROOF); - - let mut bytes = vec![0; input_size]; - bytes[..16].copy_from_slice(&RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN); - bytes[16..24].copy_from_slice(&(FIELD_ELEMENTS_PER_CELL as u64).to_be_bytes()); - bytes[24..32].copy_from_slice(&(commitments.len() as u64).to_be_bytes()); - bytes[32..40].copy_from_slice(&(cells.len() as u64).to_be_bytes()); - - let mut offset = 40; - for commitment in commitments { - bytes[offset..(offset + BYTES_PER_COMMITMENT)].copy_from_slice(&commitment.to_bytes()); - offset += BYTES_PER_COMMITMENT; - } - - for i in 0..cells.len() { - bytes[offset..(offset + 8)].copy_from_slice(&(commitment_indices[i] as u64).to_be_bytes()); - offset += 8; - - bytes[offset..(offset + 8)].copy_from_slice(&(cell_indices[i] as u64).to_be_bytes()); - offset += 8; - - bytes[offset..(offset + BYTES_PER_CELL)].copy_from_slice( - &cells[i] - .iter() - .flat_map(|fr| fr.to_bytes()) - .collect::>(), - ); - offset += BYTES_PER_CELL; - - bytes[offset..(offset + BYTES_PER_PROOF)].copy_from_slice(&(proofs[i].to_bytes())); - offset += BYTES_PER_PROOF; - } - - let bytes = &bytes[..]; - - if offset != input_size { - return Err("Failed to create challenge - invalid length".to_string()); - } - - let eval_challenge = hash(bytes); - let r = hash_to_bls_field(&eval_challenge); - - Ok(compute_powers(&r, cells.len())) -} - -fn compute_weighted_sum_of_commitments< - TG1: G1 + G1LinComb, - TFr: Fr + Send + Sync, //added + Send + Sync, - TG1Fp: G1Fp, - TG1Affine: G1Affine, ->( - commitments: &[TG1], - commitment_indices: &[usize], - r_powers: &[TFr], -) -> TG1 { - let mut commitment_weights = vec![TFr::zero(); commitments.len()]; - - #[cfg(feature = "parallel")] - { - let num_threads = rayon::current_num_threads(); - let chunk_size = (r_powers.len()+num_threads-1) / num_threads; - - let intermediate_weights: Vec<_> = r_powers - .par_chunks(chunk_size) - .zip(commitment_indices.par_chunks(chunk_size)) - .map(|(r_chunk, idx_chunk)| { - let mut local_weights = vec![TFr::zero(); commitments.len()]; - for (r_power, &index) in r_chunk.iter().zip(idx_chunk.iter()) { - local_weights[index] = local_weights[index].add(r_power); - } - local_weights - }) - .collect(); - - for local_weights in intermediate_weights { - for (i, weight) in local_weights.into_iter().enumerate() { - commitment_weights[i] = commitment_weights[i].add(&weight); - } - } - } - - #[cfg(not(feature = "parallel"))] - { - for i in 0..r_powers.len() { - commitment_weights[commitment_indices[i]] = - commitment_weights[commitment_indices[i]].add(&r_powers[i]); - } - } - - TG1::g1_lincomb(commitments, &commitment_weights, commitments.len(), None) -} - -/** - * This is a precomputed map of cell index to reverse-bits-limited cell index. - * - * for (size_t i = 0; i < CELLS_PER_EXT_BLOB; i++) - * printf("%#04llx,\n", reverse_bits_limited(CELLS_PER_EXT_BLOB, i)); - * - * Because of the way our evaluation domain is defined, we can use CELL_INDICES_RBL to find the - * coset factor of a cell. In particular, for cell i, its coset factor is - * roots_of_unity[CELLS_INDICES_RBL[i]]. - */ -const CELL_INDICES_RBL: [usize; CELLS_PER_EXT_BLOB] = [ - 0x00, 0x40, 0x20, 0x60, 0x10, 0x50, 0x30, 0x70, 0x08, 0x48, 0x28, 0x68, 0x18, 0x58, 0x38, 0x78, - 0x04, 0x44, 0x24, 0x64, 0x14, 0x54, 0x34, 0x74, 0x0c, 0x4c, 0x2c, 0x6c, 0x1c, 0x5c, 0x3c, 0x7c, - 0x02, 0x42, 0x22, 0x62, 0x12, 0x52, 0x32, 0x72, 0x0a, 0x4a, 0x2a, 0x6a, 0x1a, 0x5a, 0x3a, 0x7a, - 0x06, 0x46, 0x26, 0x66, 0x16, 0x56, 0x36, 0x76, 0x0e, 0x4e, 0x2e, 0x6e, 0x1e, 0x5e, 0x3e, 0x7e, - 0x01, 0x41, 0x21, 0x61, 0x11, 0x51, 0x31, 0x71, 0x09, 0x49, 0x29, 0x69, 0x19, 0x59, 0x39, 0x79, - 0x05, 0x45, 0x25, 0x65, 0x15, 0x55, 0x35, 0x75, 0x0d, 0x4d, 0x2d, 0x6d, 0x1d, 0x5d, 0x3d, 0x7d, - 0x03, 0x43, 0x23, 0x63, 0x13, 0x53, 0x33, 0x73, 0x0b, 0x4b, 0x2b, 0x6b, 0x1b, 0x5b, 0x3b, 0x7b, - 0x07, 0x47, 0x27, 0x67, 0x17, 0x57, 0x37, 0x77, 0x0f, 0x4f, 0x2f, 0x6f, 0x1f, 0x5f, 0x3f, 0x7f, -]; - -fn get_coset_shift_pow_for_cell< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TFFTSettings: FFTSettings, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - cell_index: usize, - settings: &TKZGSettings, -) -> Result { - /* - * Get the cell index in reverse-bit order. - * This index points to this cell's coset factor h_k in the roots_of_unity array. - */ - let cell_idx_rbl = CELL_INDICES_RBL[cell_index]; - - /* - * Get the index to h_k^n in the roots_of_unity array. - * - * Multiplying the index of h_k by n, effectively raises h_k to the n-th power, - * because advancing in the roots_of_unity array corresponds to increasing exponents. - */ - let h_k_pow_idx = cell_idx_rbl * FIELD_ELEMENTS_PER_CELL; - - if h_k_pow_idx > FIELD_ELEMENTS_PER_EXT_BLOB { - return Err("Invalid cell index".to_string()); - } - - /* Get h_k^n using the index */ - Ok(settings.get_roots_of_unity_at(h_k_pow_idx)) -} - -fn get_inv_coset_shift_for_cell< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TFFTSettings: FFTSettings, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - cell_index: usize, - settings: &TKZGSettings, -) -> Result { - /* - * Get the cell index in reverse-bit order. - * This index points to this cell's coset factor h_k in the roots_of_unity array. - */ - let cell_index_rbl = CELL_INDICES_RBL[cell_index]; - - /* - * Observe that for every element in roots_of_unity, we can find its inverse by - * accessing its reflected element. - * - * For example, consider a multiplicative subgroup with eight elements: - * roots = {w^0, w^1, w^2, ... w^7, w^0} - * For a root of unity in roots[i], we can find its inverse in roots[-i]. - */ - if cell_index_rbl > FIELD_ELEMENTS_PER_EXT_BLOB { - return Err("Invalid cell index".to_string()); - } - let inv_coset_factor_idx = FIELD_ELEMENTS_PER_EXT_BLOB - cell_index_rbl; - - /* Get h_k^{-1} using the index */ - if inv_coset_factor_idx > FIELD_ELEMENTS_PER_EXT_BLOB { - return Err("Invalid cell index".to_string()); - } - - Ok(settings.get_roots_of_unity_at(inv_coset_factor_idx)) -} - -fn compute_commitment_to_aggregated_interpolation_poly< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp + G1LinComb, - TG2: G2, - TFFTSettings: FFTSettings + FFTFr + FFTG1, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - r_powers: &[TFr], - cell_indices: &[usize], - cells: &[[TFr; FIELD_ELEMENTS_PER_CELL]], - s: &TKZGSettings, -) -> Result { - let mut aggregated_column_cells = - vec![TFr::zero(); CELLS_PER_EXT_BLOB * FIELD_ELEMENTS_PER_CELL]; - - for (cell_index, column_index) in cell_indices.iter().enumerate() { - for fr_index in 0..FIELD_ELEMENTS_PER_CELL { - let original_fr = cells[cell_index][fr_index].clone(); - - let scaled_fr = original_fr.mul(&r_powers[cell_index]); - - let array_index = column_index * FIELD_ELEMENTS_PER_CELL + fr_index; - aggregated_column_cells[array_index] = - aggregated_column_cells[array_index].add(&scaled_fr); - } - } - - let mut is_cell_used = [false; CELLS_PER_EXT_BLOB]; - - for cell_index in cell_indices { - is_cell_used[*cell_index] = true; - } - - let mut aggregated_interpolation_poly = vec![TFr::zero(); FIELD_ELEMENTS_PER_CELL]; - let mut column_interpolation_poly = vec![TFr::default(); FIELD_ELEMENTS_PER_CELL]; - for (i, is_cell_used) in is_cell_used.iter().enumerate() { - if !is_cell_used { - continue; - } - - let index = i * FIELD_ELEMENTS_PER_CELL; - - reverse_bit_order(&mut aggregated_column_cells[index..(index + FIELD_ELEMENTS_PER_CELL)])?; - - fr_ifft( - &mut column_interpolation_poly, - &aggregated_column_cells[index..(index + FIELD_ELEMENTS_PER_CELL)], - s.get_fft_settings(), - )?; - - let inv_coset_factor = get_inv_coset_shift_for_cell(i, s)?; - - shift_poly(&mut column_interpolation_poly, &inv_coset_factor); - - for k in 0..FIELD_ELEMENTS_PER_CELL { - aggregated_interpolation_poly[k] = - aggregated_interpolation_poly[k].add(&column_interpolation_poly[k]); - } - } - - // TODO: maybe pass precomputation here? - Ok(TG1::g1_lincomb( - s.get_g1_monomial(), - &aggregated_interpolation_poly, - FIELD_ELEMENTS_PER_CELL, - None, - )) -} - -fn computed_weighted_sum_of_proofs< - TFr: Fr, - TG1: G1 + G1Mul + G1GetFp + G1LinComb, - TG2: G2, - TFFTSettings: FFTSettings, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - proofs: &[TG1], - r_powers: &[TFr], - cell_indices: &[usize], - s: &TKZGSettings, -) -> Result { - let num_cells = proofs.len(); - - if r_powers.len() != num_cells || cell_indices.len() != num_cells { - return Err("Length mismatch".to_string()); - } - - let mut weighted_powers_of_r = Vec::with_capacity(num_cells); - for i in 0..num_cells { - let h_k_pow = get_coset_shift_pow_for_cell(cell_indices[i], s)?; - - weighted_powers_of_r.push(r_powers[i].mul(&h_k_pow)); - } - - Ok(TG1::g1_lincomb( - proofs, - &weighted_powers_of_r, - num_cells, - None, - )) -} - -pub fn verify_cell_kzg_proof_batch< - TFr: Fr + Send + Sync, - TG1: G1 + G1Mul + G1GetFp + G1LinComb + PairingVerify, - TG2: G2, - TFFTSettings: FFTSettings + FFTFr + FFTG1, - TPoly: Poly, - TG1Fp: G1Fp, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, ->( - commitments: &[TG1], - cell_indices: &[usize], - cells: &[[TFr; FIELD_ELEMENTS_PER_CELL]], - proofs: &[TG1], - s: &TKZGSettings, -) -> Result { - if cells.len() != cell_indices.len() { - return Err("Cell count mismatch".to_string()); - } - - if commitments.len() != cells.len() { - return Err("Commitment count mismatch".to_string()); - } - - if proofs.len() != cells.len() { - return Err("Proof count mismatch".to_string()); - } - - if cells.is_empty() { - return Ok(true); - } - - #[cfg(feature = "parallel")] - { - let cell_index_errors = cell_indices - .par_iter() - .any(|&cell_index| cell_index >= CELLS_PER_EXT_BLOB); - if cell_index_errors { - return Err("Invalid cell index".to_string()); - } - - let proof_errors = proofs.par_iter().any(|proof| !proof.is_valid()); - if proof_errors { - return Err("Proof is not valid".to_string()); - } - } - - #[cfg(not(feature = "parallel"))] - { - for cell_index in cell_indices { - if *cell_index >= CELLS_PER_EXT_BLOB { - return Err("Invalid cell index".to_string()); - } - } - - for proof in proofs { - if !proof.is_valid() { - return Err("Proof is not valid".to_string()); - } - } - } - - - let mut new_count = commitments.len(); - let mut unique_commitments = commitments.to_vec(); - let mut commitment_indices = vec![0usize; cells.len()]; - deduplicate_commitments( - &mut unique_commitments, - &mut commitment_indices, - &mut new_count, - ); - - #[cfg(feature = "parallel")] - { - let unique_commitment_errors = unique_commitments - .par_iter() - .any(|commitment| !commitment.is_valid()); - if unique_commitment_errors { - return Err("Commitment is not valid".to_string()); - } - } - - #[cfg(not(feature = "parallel"))] - { - for commitment in unique_commitments.iter() { - if !commitment.is_valid() { - return Err("Commitment is not valid".to_string()); - } - } - } - - let unique_commitments = &unique_commitments[0..new_count]; - - let r_powers = compute_r_powers_for_verify_cell_kzg_proof_batch( - unique_commitments, - &commitment_indices, - cell_indices, - cells, - proofs, - )?; - - let proof_lincomb = TG1::g1_lincomb(proofs, &r_powers, cells.len(), None); - - let final_g1_sum = - compute_weighted_sum_of_commitments(unique_commitments, &commitment_indices, &r_powers); - - let interpolation_poly_commit = - compute_commitment_to_aggregated_interpolation_poly(&r_powers, cell_indices, cells, s)?; - - let final_g1_sum = final_g1_sum.sub(&interpolation_poly_commit); - - let weighted_sum_of_proofs = - computed_weighted_sum_of_proofs(proofs, &r_powers, cell_indices, s)?; - - let final_g1_sum = final_g1_sum.add(&weighted_sum_of_proofs); - - let power_of_s = &s.get_g2_monomial()[FIELD_ELEMENTS_PER_CELL]; - - Ok(TG1::verify( - &final_g1_sum, - &TG2::generator(), - &proof_lincomb, - power_of_s, - )) -} diff --git a/kzg/src/eth/c_bindings.rs b/kzg/src/eth/c_bindings.rs new file mode 100644 index 000000000..ce34ffa60 --- /dev/null +++ b/kzg/src/eth/c_bindings.rs @@ -0,0 +1,441 @@ +use blst::{blst_fr, blst_p1, blst_p1_affine, blst_p2}; + +use crate::{ + eth::{CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_CELL}, + EcBackend, Fr, DAS, G1, +}; + +use super::{ + Mainnet, BYTES_PER_BLOB, BYTES_PER_CELL, BYTES_PER_COMMITMENT, BYTES_PER_FIELD_ELEMENT, + BYTES_PER_PROOF, +}; + +use crate::alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; + +#[repr(C)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum CKzgRet { + Ok = 0, + BadArgs = 1, + Error = 2, + Malloc = 3, +} + +#[repr(C)] +pub struct Bytes32 { + pub bytes: [u8; 32], +} + +#[repr(C)] +pub struct Bytes48 { + pub bytes: [u8; 48], +} + +#[repr(C)] +pub struct BLSFieldElement { + pub bytes: [u8; BYTES_PER_FIELD_ELEMENT], +} + +#[repr(C)] +pub struct Blob { + pub bytes: [u8; BYTES_PER_BLOB], +} + +#[repr(C)] +pub struct KZGCommitment { + pub bytes: [u8; BYTES_PER_COMMITMENT], +} + +#[repr(C)] +pub struct KZGProof { + pub bytes: [u8; BYTES_PER_PROOF], +} + +#[repr(C)] +pub struct CKZGSettings { + /** + * Roots of unity for the subgroup of size `FIELD_ELEMENTS_PER_EXT_BLOB`. + * + * The array contains `FIELD_ELEMENTS_PER_EXT_BLOB + 1` elements. + * The array starts and ends with Fr::one(). + */ + pub roots_of_unity: *mut blst_fr, + /** + * Roots of unity for the subgroup of size `FIELD_ELEMENTS_PER_EXT_BLOB` in bit-reversed order. + * + * This array is derived by applying a bit-reversal permutation to `roots_of_unity` + * excluding the last element. Essentially: + * `brp_roots_of_unity = bit_reversal_permutation(roots_of_unity[:-1])` + * + * The array contains `FIELD_ELEMENTS_PER_EXT_BLOB` elements. + */ + pub brp_roots_of_unity: *mut blst_fr, + /** + * Roots of unity for the subgroup of size `FIELD_ELEMENTS_PER_EXT_BLOB` in reversed order. + * + * It is the reversed version of `roots_of_unity`. Essentially: + * `reverse_roots_of_unity = reverse(roots_of_unity)` + * + * This array is primarily used in FFTs. + * The array contains `FIELD_ELEMENTS_PER_EXT_BLOB + 1` elements. + * The array starts and ends with Fr::one(). + */ + pub reverse_roots_of_unity: *mut blst_fr, + /** + * G1 group elements from the trusted setup in monomial form. + * The array contains `NUM_G1_POINTS = FIELD_ELEMENTS_PER_BLOB` elements. + */ + pub g1_values_monomial: *mut blst_p1, + /** + * G1 group elements from the trusted setup in Lagrange form and bit-reversed order. + * The array contains `NUM_G1_POINTS = FIELD_ELEMENTS_PER_BLOB` elements. + */ + pub g1_values_lagrange_brp: *mut blst_p1, + /** + * G2 group elements from the trusted setup in monomial form. + * The array contains `NUM_G2_POINTS` elements. + */ + pub g2_values_monomial: *mut blst_p2, + /** Data used during FK20 proof generation. */ + pub x_ext_fft_columns: *mut *mut blst_p1, + /** The precomputed tables for fixed-base MSM. */ + pub tables: *mut *mut blst_p1_affine, + /** The window size for the fixed-base MSM. */ + pub wbits: usize, + /** The scratch size for the fixed-base MSM. */ + pub scratch_size: usize, +} + +#[repr(C)] +pub struct Cell { + pub bytes: [u8; BYTES_PER_CELL], +} + +unsafe fn deserialize_blob( + blob: *const Blob, +) -> core::result::Result, CKzgRet> { + (*blob) + .bytes + .chunks(BYTES_PER_FIELD_ELEMENT) + .map(|chunk| { + let mut bytes = [0u8; BYTES_PER_FIELD_ELEMENT]; + bytes.copy_from_slice(chunk); + if let Ok(result) = B::Fr::from_bytes(&bytes) { + Ok(result) + } else { + Err(CKzgRet::BadArgs) + } + }) + .collect::, _>>() +} + +/// # Safety +pub unsafe fn compute_cells_and_kzg_proofs< + B: EcBackend, + D: DAS + for<'a> TryFrom<&'a CKZGSettings, Error = String>, +>( + cells: *mut Cell, + proofs: *mut KZGProof, + blob: *const Blob, + settings: *const CKZGSettings, +) -> CKzgRet { + unsafe fn inner< + B: EcBackend, + D: DAS + + for<'a> TryFrom<&'a CKZGSettings, Error = String>, + >( + cells: *mut Cell, + proofs: *mut KZGProof, + blob: *const Blob, + settings: *const CKZGSettings, + ) -> Result<(), String> { + let mut cells_rs: Option> = if cells.is_null() { + None + } else { + Some(vec![ + core::array::from_fn(|_| B::Fr::default()); + CELLS_PER_EXT_BLOB + ]) + }; + let mut proofs_rs = if proofs.is_null() { + None + } else { + Some(vec![B::G1::default(); CELLS_PER_EXT_BLOB]) + }; + + let blob = deserialize_blob::(blob).map_err(|_| "Invalid blob".to_string())?; + let settings: D = (&*settings).try_into()?; + + settings.compute_cells_and_kzg_proofs( + cells_rs.as_deref_mut(), + proofs_rs.as_deref_mut(), + &blob, + )?; + + if let Some(cells_rs) = cells_rs { + let cells = core::slice::from_raw_parts_mut(cells, CELLS_PER_EXT_BLOB); + for (cell_index, cell) in cells_rs.iter().enumerate() { + for (fr_index, fr) in cell.iter().enumerate() { + cells[cell_index].bytes[(fr_index * BYTES_PER_FIELD_ELEMENT) + ..((fr_index + 1) * BYTES_PER_FIELD_ELEMENT)] + .copy_from_slice(&fr.to_bytes()); + } + } + } + + if let Some(proofs_rs) = proofs_rs { + let proofs = core::slice::from_raw_parts_mut(proofs, CELLS_PER_EXT_BLOB); + for (proof_index, proof) in proofs_rs.iter().enumerate() { + proofs[proof_index].bytes.copy_from_slice(&proof.to_bytes()); + } + } + + Ok(()) + } + + match inner::(cells, proofs, blob, settings) { + Ok(()) => CKzgRet::Ok, + Err(_) => CKzgRet::BadArgs, + } +} + +/// # Safety +pub unsafe fn recover_cells_and_kzg_proofs< + B: EcBackend, + D: DAS + for<'a> TryFrom<&'a CKZGSettings, Error = String>, +>( + recovered_cells: *mut Cell, + recovered_proofs: *mut KZGProof, + cell_indices: *const u64, + cells: *const Cell, + num_cells: u64, + s: *const CKZGSettings, +) -> CKzgRet { + unsafe fn inner< + B: EcBackend, + D: DAS + + for<'a> TryFrom<&'a CKZGSettings, Error = String>, + >( + recovered_cells: *mut Cell, + recovered_proofs: *mut KZGProof, + cell_indices: *const u64, + cells: *const Cell, + num_cells: u64, + s: *const CKZGSettings, + ) -> Result<(), String> { + let mut recovered_cells_rs: Vec<[B::Fr; FIELD_ELEMENTS_PER_CELL]> = + vec![core::array::from_fn(|_| B::Fr::default()); CELLS_PER_EXT_BLOB]; + + let mut recovered_proofs_rs = if recovered_proofs.is_null() { + None + } else { + Some(vec![B::G1::default(); CELLS_PER_EXT_BLOB]) + }; + + let cell_indicies = core::slice::from_raw_parts(cell_indices, num_cells as usize) + .iter() + .map(|it| *it as usize) + .collect::>(); + let cells = core::slice::from_raw_parts(cells, num_cells as usize) + .iter() + .map(|it| -> Result<[B::Fr; FIELD_ELEMENTS_PER_CELL], String> { + it.bytes + .chunks(BYTES_PER_FIELD_ELEMENT) + .map(B::Fr::from_bytes) + .collect::, String>>() + .and_then(|frs| { + frs.try_into() + .map_err(|_| "Invalid field element count per cell".to_string()) + }) + }) + .collect::, String>>()?; + let settings: D = (&*s).try_into()?; + + settings.recover_cells_and_kzg_proofs( + &mut recovered_cells_rs, + recovered_proofs_rs.as_deref_mut(), + &cell_indicies, + &cells, + )?; + + let recovered_cells = core::slice::from_raw_parts_mut(recovered_cells, CELLS_PER_EXT_BLOB); + for (cell_c, cell_rs) in recovered_cells.iter_mut().zip(recovered_cells_rs.iter()) { + cell_c.bytes.copy_from_slice( + &cell_rs + .iter() + .flat_map(|fr| fr.to_bytes()) + .collect::>(), + ); + } + + if let Some(recovered_proofs_rs) = recovered_proofs_rs { + let recovered_proofs = + core::slice::from_raw_parts_mut(recovered_proofs, CELLS_PER_EXT_BLOB); + + for (proof_c, proof_rs) in recovered_proofs.iter_mut().zip(recovered_proofs_rs.iter()) { + proof_c.bytes = proof_rs.to_bytes(); + } + } + + Ok(()) + } + + match inner::( + recovered_cells, + recovered_proofs, + cell_indices, + cells, + num_cells, + s, + ) { + Ok(()) => CKzgRet::Ok, + Err(_) => CKzgRet::BadArgs, + } +} + +/// # Safety +pub unsafe fn verify_cell_kzg_proof_batch< + B: EcBackend, + D: DAS + for<'a> TryFrom<&'a CKZGSettings, Error = String>, +>( + ok: *mut bool, + commitments_bytes: *const Bytes48, + cell_indices: *const u64, + cells: *const Cell, + proofs_bytes: *const Bytes48, + num_cells: u64, + s: *const CKZGSettings, +) -> CKzgRet { + unsafe fn inner< + B: EcBackend, + D: DAS + + for<'a> TryFrom<&'a CKZGSettings, Error = String>, + >( + ok: *mut bool, + commitments_bytes: *const Bytes48, + cell_indices: *const u64, + cells: *const Cell, + proofs_bytes: *const Bytes48, + num_cells: u64, + s: *const CKZGSettings, + ) -> Result<(), String> { + let commitments = core::slice::from_raw_parts(commitments_bytes, num_cells as usize) + .iter() + .map(|bytes| B::G1::from_bytes(&bytes.bytes)) + .collect::, String>>()?; + + let cell_indices = core::slice::from_raw_parts(cell_indices, num_cells as usize) + .iter() + .map(|it| *it as usize) + .collect::>(); + + let cells = core::slice::from_raw_parts(cells, num_cells as usize) + .iter() + .map(|it| -> Result<[B::Fr; FIELD_ELEMENTS_PER_CELL], String> { + it.bytes + .chunks(BYTES_PER_FIELD_ELEMENT) + .map(B::Fr::from_bytes) + .collect::, String>>() + .and_then(|frs| { + frs.try_into() + .map_err(|_| "Invalid field element count per cell".to_string()) + }) + }) + .collect::, String>>()?; + + let proofs = core::slice::from_raw_parts(proofs_bytes, num_cells as usize) + .iter() + .map(|bytes| B::G1::from_bytes(&bytes.bytes)) + .collect::, String>>()?; + + let settings: D = (&*s).try_into()?; + + *ok = settings.verify_cell_kzg_proof_batch(&commitments, &cell_indices, &cells, &proofs)?; + + Ok(()) + } + + match inner::( + ok, + commitments_bytes, + cell_indices, + cells, + proofs_bytes, + num_cells, + s, + ) { + Ok(()) => CKzgRet::Ok, + Err(_) => CKzgRet::BadArgs, + } +} + +#[macro_export] +macro_rules! c_bindings_eip7594 { + ($backend:ty) => { + /// # Safety + #[no_mangle] + pub unsafe extern "C" fn compute_cells_and_kzg_proofs( + cells: *mut kzg::eth::c_bindings::Cell, + proofs: *mut kzg::eth::c_bindings::KZGProof, + blob: *const kzg::eth::c_bindings::Blob, + settings: *const kzg::eth::c_bindings::CKZGSettings, + ) -> kzg::eth::c_bindings::CKzgRet { + kzg::eth::c_bindings::compute_cells_and_kzg_proofs::< + $backend, + <$backend as kzg::EcBackend>::KZGSettings, + >(cells, proofs, blob, settings) + } + + /// # Safety + #[no_mangle] + pub unsafe extern "C" fn recover_cells_and_kzg_proofs( + recovered_cells: *mut kzg::eth::c_bindings::Cell, + recovered_proofs: *mut kzg::eth::c_bindings::KZGProof, + cell_indices: *const u64, + cells: *const kzg::eth::c_bindings::Cell, + num_cells: u64, + s: *const kzg::eth::c_bindings::CKZGSettings, + ) -> kzg::eth::c_bindings::CKzgRet { + kzg::eth::c_bindings::recover_cells_and_kzg_proofs::< + $backend, + <$backend as kzg::EcBackend>::KZGSettings, + >( + recovered_cells, + recovered_proofs, + cell_indices, + cells, + num_cells, + s, + ) + } + + /// # Safety + #[no_mangle] + pub unsafe extern "C" fn verify_cell_kzg_proof_batch( + ok: *mut bool, + commitments_bytes: *const kzg::eth::c_bindings::Bytes48, + cell_indices: *const u64, + cells: *const kzg::eth::c_bindings::Cell, + proofs_bytes: *const kzg::eth::c_bindings::Bytes48, + num_cells: u64, + s: *const kzg::eth::c_bindings::CKZGSettings, + ) -> kzg::eth::c_bindings::CKzgRet { + kzg::eth::c_bindings::verify_cell_kzg_proof_batch::< + $backend, + <$backend as kzg::EcBackend>::KZGSettings, + >( + ok, + commitments_bytes, + cell_indices, + cells, + proofs_bytes, + num_cells, + s, + ) + } + }; +} diff --git a/kzg/src/eth/mod.rs b/kzg/src/eth/mod.rs new file mode 100644 index 000000000..5f43fc343 --- /dev/null +++ b/kzg/src/eth/mod.rs @@ -0,0 +1,26 @@ +use crate::Preset; + +pub mod c_bindings; + +pub const FIELD_ELEMENTS_PER_BLOB: usize = 4096; +pub const BYTES_PER_G1: usize = 48; +pub const BYTES_PER_G2: usize = 96; +pub const BYTES_PER_BLOB: usize = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB; +pub const BYTES_PER_FIELD_ELEMENT: usize = 32; +pub const BYTES_PER_PROOF: usize = 48; +pub const BYTES_PER_COMMITMENT: usize = 48; +pub const FIELD_ELEMENTS_PER_EXT_BLOB: usize = 2 * FIELD_ELEMENTS_PER_BLOB; +pub const FIELD_ELEMENTS_PER_CELL: usize = 64; +pub const BYTES_PER_CELL: usize = FIELD_ELEMENTS_PER_CELL * BYTES_PER_FIELD_ELEMENT; +pub const CELLS_PER_EXT_BLOB: usize = FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL; +pub const RANDOM_CHALLENGE_KZG_CELL_BATCH_DOMAIN: [u8; 16] = *b"RCKZGCBATCH__V1_"; +pub const TRUSTED_SETUP_NUM_G1_POINTS: usize = 4096; +pub const TRUSTED_SETUP_NUM_G2_POINTS: usize = 65; + +pub struct Mainnet; + +impl Preset for Mainnet { + const FIELD_ELEMENTS_PER_BLOB: usize = FIELD_ELEMENTS_PER_BLOB; + const FIELD_ELEMENTS_PER_EXT_BLOB: usize = FIELD_ELEMENTS_PER_BLOB * 2; + const CELLS_PER_EXT_BLOB: usize = FIELD_ELEMENTS_PER_EXT_BLOB / FIELD_ELEMENTS_PER_CELL; +} diff --git a/kzg/src/fk20_proof.rs b/kzg/src/fk20_proof.rs deleted file mode 100644 index 65f52e234..000000000 --- a/kzg/src/fk20_proof.rs +++ /dev/null @@ -1,90 +0,0 @@ -// use crate::consts::G1_IDENTITY; -// use crate::eip_4844::FIELD_ELEMENTS_PER_CELL; -// use crate::kzg_types::{ArkFr as BlstFr, ArkG1}; -// use crate::utils::PolyData; -// use crate::utils::PolyData; -// use crate::{FFTFr, G1Mul, KZGSettings, G1}; -// use kzg::{FFTFr, FFTSetings, G1Mul, G1}; - -use crate::{ - eip_4844::FIELD_ELEMENTS_PER_CELL, FFTFr, FFTSettings, Fr, G1Affine, G1Fp, G1GetFp, G1Mul, - KZGSettings, Poly, G1, G2, -}; - -fn compute_fk20_proofs< - TFr: Fr, - TFFTFr: FFTFr, - TG1Fp: G1Fp, - TG1: G1 + G1Mul + G1GetFp, - TG2: G2, - TPoly: Poly, - TG1Affine: G1Affine, - TKZGSettings: KZGSettings, - TFFTSettings: FFTSettings, ->( - p: &TPoly, - n: usize, - s: &TKZGSettings, -) -> Result, String> { - let k = n / FIELD_ELEMENTS_PER_CELL; - let k2 = k * 2; - - let mut toeplitz_coeff = vec![TG1::default(); k2]; - let mut h = vec![TG1::identity(); k2]; - let mut h_ext_fft = vec![TG1::identity(); k2]; - - for i in 0..FIELD_ELEMENTS_PER_CELL { - toeplitz_coeffs_stride(p, &mut toeplitz_coeff, n, FIELD_ELEMENTS_PER_CELL)?; - s.get_fft_settings().fft_fr(&toeplitz_coeff, false)?; - for j in 0..k2 { - h_ext_fft[j] = h_ext_fft[j].add_or_dbl(&s.x_ext_fft[j].mul(&toeplitz_coeff[j])); - } - } - - s.get_fft_settings().fft_g1(&h_ext_fft, false)?; - - for i in h.iter_mut().take(k) { - *i = h_ext_fft[i.len() - 1]; - } - for i in h.iter_mut().take(k2).skip(k) { - *i = TG1::identity(); - } - - s.get_fft_settings().fft_g1(h.as_mut_slice(), false)?; - - Ok(h) -} - -fn toeplitz_coeffs_stride( - poly: &PolyData, - offset: usize, - stride: usize, - outlen: usize, -) -> Result { - let n = poly.len(); - - if stride == 0 { - return Err(String::from("stride must be greater than 0")); - } - - let k = n / stride; - let k2 = k * 2; - - if outlen < k2 { - return Err(String::from("outlen must be equal or greater than k2")); - } - - let mut out = PolyData::new(outlen); - out.set_coeff_at(0, &poly.coeffs[n - 1 - offset]); - let mut i = 1; - while i <= (k + 1) && i < k2 { - out.set_coeff_at(i, &BlstFr::zero()); - i += 1; - } - let mut j = 2 * stride - offset - 1; - for i in (k + 2)..k2 { - out.set_coeff_at(i, &poly.coeffs[j]); - j += stride; - } - Ok(out) -} diff --git a/kzg/src/lib.rs b/kzg/src/lib.rs index b22297cc9..d55602446 100644 --- a/kzg/src/lib.rs +++ b/kzg/src/lib.rs @@ -8,11 +8,13 @@ use core::fmt::Debug; use msm::precompute::PrecomputationTable; pub mod common_utils; +mod das; pub mod eip_4844; -pub mod eip_7594; -// pub mod fk20_proof; +pub mod eth; pub mod msm; +pub use das::{EcBackend, Preset, DAS}; + pub trait Fr: Default + Clone + PartialEq + Sync { fn null() -> Self; @@ -384,7 +386,7 @@ pub trait FFTG1 { fn fft_g1(&self, data: &[Coeff], inverse: bool) -> Result, String>; } -pub trait DAS { +pub trait DASExtension { fn das_fft_extension(&self, evens: &[Coeff]) -> Result, String>; } @@ -519,6 +521,13 @@ pub trait KZGSettings< fs: &Fs, ) -> Result; + fn new_for_preset( + g1_monomial: &[Coeff2], + g1_lagrange_brp: &[Coeff2], + g2_monomial: &[Coeff3], + fs: &Fs, + ) -> Result; + fn commit_to_poly(&self, p: &Polynomial) -> Result; fn compute_proof_single(&self, p: &Polynomial, x: &Coeff1) -> Result; diff --git a/run-c-kzg-4844-benches.sh b/run-c-kzg-4844-benches.sh index 890a3b653..d071f87a9 100755 --- a/run-c-kzg-4844-benches.sh +++ b/run-c-kzg-4844-benches.sh @@ -45,8 +45,6 @@ else cargo rustc --release --crate-type=staticlib fi -mv ../target/release/librust_kzg_$backend.a ../target/release/rust_kzg_$backend.a - ###################### cloning c-kzg-4844 ###################### print_msg "Cloning c-kzg-4844" @@ -55,25 +53,15 @@ cd c-kzg-4844 || exit git -c advice.detachedHead=false checkout "$C_KZG_4844_GIT_HASH" git submodule update --init +mv ../../target/release/librust_kzg_$backend.a ./lib + ###################### rust benchmarks ###################### print_msg "Patching rust binding" git apply < ../rust.patch -cd bindings/rust || exit print_msg "Running rust benchmarks" cargo bench -cd ../.. - -###################### java benchmarks ###################### - -print_msg "Patching java binding" -git apply < ../java.patch -cd bindings/java || exit - -print_msg "Running java benchmarks" -make build benchmark -cd ../.. ###################### go benchmarks ######################