diff --git a/Cargo.lock b/Cargo.lock index 29faf3d5b..dfe841574 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1470,6 +1470,7 @@ dependencies = [ "rayon", "rust-kzg-arkworks3-sppark", "rust-kzg-arkworks3-sppark-wlc", + "smallvec", ] [[package]] diff --git a/arkworks3/Cargo.toml b/arkworks3/Cargo.toml index 4a7ce6bc1..050b51e54 100644 --- a/arkworks3/Cargo.toml +++ b/arkworks3/Cargo.toml @@ -16,6 +16,7 @@ hex = "0.4.3" rand = { version = "0.8.5", optional = true } libc = { version = "0.2.148", default-features = false } rayon = { version = "1.8.0", optional = true } +smallvec = { version = "1.11.1", features = ["const_generics"] } rust-kzg-arkworks3-sppark = { path = "../arkworks3-sppark", version = "0.1.0", optional = true } rust-kzg-arkworks3-sppark-wlc = { path = "../arkworks3-sppark-wlc", version = "0.1.0", optional = true } diff --git a/arkworks3/benches/das.rs b/arkworks3/benches/das.rs index d0daeccd8..e5e4eece4 100644 --- a/arkworks3/benches/das.rs +++ b/arkworks3/benches/das.rs @@ -1,6 +1,6 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::das::bench_das_extension; -use rust_kzg_arkworks3::kzg_proofs::FFTSettings; +use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; fn bench_das_extension_(c: &mut Criterion) { diff --git a/arkworks3/benches/eip_4844.rs b/arkworks3/benches/eip_4844.rs index 4f6e8000c..d03dd56e0 100644 --- a/arkworks3/benches/eip_4844.rs +++ b/arkworks3/benches/eip_4844.rs @@ -6,7 +6,7 @@ use kzg::eip_4844::{ }; use kzg_bench::benches::eip_4844::bench_eip_4844; use rust_kzg_arkworks3::eip_4844::load_trusted_setup_filename_rust; -use rust_kzg_arkworks3::kzg_proofs::{FFTSettings, KZGSettings}; +use rust_kzg_arkworks3::kzg_proofs::{LFFTSettings as FFTSettings, LKZGSettings as KZGSettings}; use rust_kzg_arkworks3::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2}; use rust_kzg_arkworks3::utils::PolyData; diff --git a/arkworks3/benches/fft.rs b/arkworks3/benches/fft.rs index 26d1b4ed0..3ca428554 100644 --- a/arkworks3/benches/fft.rs +++ b/arkworks3/benches/fft.rs @@ -1,6 +1,6 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::fft::{bench_fft_fr, bench_fft_g1}; -use rust_kzg_arkworks3::kzg_proofs::FFTSettings; +use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::{ArkFr, ArkG1}; fn bench_fft_fr_(c: &mut Criterion) { diff --git a/arkworks3/benches/fk_20.rs b/arkworks3/benches/fk_20.rs index 3f9a6fa21..8e548bcd7 100644 --- a/arkworks3/benches/fk_20.rs +++ b/arkworks3/benches/fk_20.rs @@ -1,7 +1,9 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::fk20::{bench_fk_multi_da, bench_fk_single_da}; use rust_kzg_arkworks3::fk20_proofs::{KzgFK20MultiSettings, KzgFK20SingleSettings}; -use rust_kzg_arkworks3::kzg_proofs::{generate_trusted_setup, FFTSettings, KZGSettings}; +use rust_kzg_arkworks3::kzg_proofs::{ + generate_trusted_setup, LFFTSettings as FFTSettings, LKZGSettings as KZGSettings, +}; use rust_kzg_arkworks3::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2}; use rust_kzg_arkworks3::utils::PolyData; diff --git a/arkworks3/benches/kzg.rs b/arkworks3/benches/kzg.rs index f90bc381b..a6f80ff63 100644 --- a/arkworks3/benches/kzg.rs +++ b/arkworks3/benches/kzg.rs @@ -1,7 +1,9 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::kzg::{bench_commit_to_poly, bench_compute_proof_single}; -use rust_kzg_arkworks3::kzg_proofs::{generate_trusted_setup, FFTSettings, KZGSettings}; +use rust_kzg_arkworks3::kzg_proofs::{ + generate_trusted_setup, LFFTSettings as FFTSettings, LKZGSettings as KZGSettings, +}; use rust_kzg_arkworks3::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2}; use rust_kzg_arkworks3::utils::PolyData; diff --git a/arkworks3/benches/recover.rs b/arkworks3/benches/recover.rs index 7e031ccc5..62172c430 100644 --- a/arkworks3/benches/recover.rs +++ b/arkworks3/benches/recover.rs @@ -1,7 +1,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::recover::bench_recover; -use rust_kzg_arkworks3::kzg_proofs::FFTSettings; +use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; use rust_kzg_arkworks3::utils::PolyData; diff --git a/arkworks3/benches/zero_poly.rs b/arkworks3/benches/zero_poly.rs index b987af86e..07e274cd6 100644 --- a/arkworks3/benches/zero_poly.rs +++ b/arkworks3/benches/zero_poly.rs @@ -1,7 +1,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; use kzg_bench::benches::zero_poly::bench_zero_poly; -use rust_kzg_arkworks3::kzg_proofs::FFTSettings; +use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; use rust_kzg_arkworks3::utils::PolyData; diff --git a/arkworks3/src/das.rs b/arkworks3/src/das.rs index 9754cacad..61bfbe0f4 100644 --- a/arkworks3/src/das.rs +++ b/arkworks3/src/das.rs @@ -1,87 +1,91 @@ -use crate::kzg_proofs::FFTSettings; +use crate::kzg_proofs::LFFTSettings; use crate::kzg_types::ArkFr as BlstFr; use kzg::{Fr, DAS}; use std::cmp::Ordering; -impl FFTSettings { - fn das_fft_extension_stride(&self, ab: &mut [BlstFr], stride: usize) { - match ab.len().cmp(&2_usize) { - Ordering::Less => {} - Ordering::Greater => { - let half = ab.len(); - let halfhalf = half / 2; +impl LFFTSettings { + pub fn das_fft_extension_stride(&self, evens: &mut [BlstFr], stride: usize) { + match evens.len().cmp(&2) { + Ordering::Less => { + return; + } + Ordering::Equal => { + let x = evens[0].add(&evens[1]); + let y = evens[0].sub(&evens[1]); + let y_times_root = y.mul(&self.roots_of_unity[stride]); - for i in 0..halfhalf { - let tmp1 = ab[i].add(&ab[halfhalf + i]); - let tmp2 = ab[i].sub(&ab[halfhalf + i]); - ab[halfhalf + i] = tmp2.mul(&self.reverse_roots_of_unity[i * 2 * stride]); - ab[i] = tmp1; - } + evens[0] = x.add(&y_times_root); + evens[1] = x.sub(&y_times_root); - #[cfg(feature = "parallel")] - { - if ab.len() > 32 { - let (lo, hi) = ab.split_at_mut(halfhalf); - rayon::join( - || self.das_fft_extension_stride(hi, stride * 2), - || self.das_fft_extension_stride(lo, stride * 2), - ); - } else { - self.das_fft_extension_stride(&mut ab[..halfhalf], stride * 2); - self.das_fft_extension_stride(&mut ab[halfhalf..], stride * 2); - } - } - #[cfg(not(feature = "parallel"))] - { - self.das_fft_extension_stride(&mut ab[..halfhalf], stride * 2); - self.das_fft_extension_stride(&mut ab[halfhalf..], stride * 2); - } - for i in 0..halfhalf { - let x = ab[i]; - let y = ab[halfhalf + i]; - let y_times_root = y.mul(&self.expanded_roots_of_unity[(1 + 2 * i) * stride]); - ab[i] = x.add(&y_times_root); - ab[halfhalf + i] = x.sub(&y_times_root); - } + return; } - Ordering::Equal => { - let x = ab[0].add(&ab[1]); - let y = ab[0].sub(&ab[1]); - let tmp = y.mul(&self.expanded_roots_of_unity[stride]); + Ordering::Greater => {} + } + + let half: usize = evens.len() / 2; + for i in 0..half { + let tmp1 = evens[i].add(&evens[half + i]); + let tmp2 = evens[i].sub(&evens[half + i]); + evens[half + i] = tmp2.mul(&self.reverse_roots_of_unity[i * 2 * stride]); - ab[0] = x.add(&tmp); - ab[1] = x.sub(&tmp); + evens[i] = tmp1; + } + + #[cfg(feature = "parallel")] + { + if evens.len() > 32 { + let (lo, hi) = evens.split_at_mut(half); + rayon::join( + || self.das_fft_extension_stride(hi, stride * 2), + || self.das_fft_extension_stride(lo, stride * 2), + ); + } else { + // Recurse + self.das_fft_extension_stride(&mut evens[..half], stride * 2); + self.das_fft_extension_stride(&mut evens[half..], stride * 2); } } - } -} -impl DAS for FFTSettings { - fn das_fft_extension(&self, vals: &[BlstFr]) -> Result, String> { - if vals.is_empty() { - return Err(String::from("vals can not be empty")); + #[cfg(not(feature = "parallel"))] + { + // Recurse + self.das_fft_extension_stride(&mut evens[..half], stride * 2); + self.das_fft_extension_stride(&mut evens[half..], stride * 2); } - if !vals.len().is_power_of_two() { - return Err(String::from("vals lenght has to be power of 2")); + + for i in 0..half { + let x = evens[i]; + let y = evens[half + i]; + let y_times_root: BlstFr = y.mul(&self.roots_of_unity[(1 + 2 * i) * stride]); + + evens[i] = x.add(&y_times_root); + evens[half + i] = x.sub(&y_times_root); } - if vals.len() * 2 > self.max_width { + } +} + +impl DAS for LFFTSettings { + fn das_fft_extension(&self, evens: &[BlstFr]) -> Result, String> { + if evens.is_empty() { + return Err(String::from("A non-zero list ab expected")); + } else if !evens.len().is_power_of_two() { + return Err(String::from("A list with power-of-two length expected")); + } else if evens.len() * 2 > self.max_width { return Err(String::from( - "vals lenght * 2 has to equal or less than FFTSetings max width", + "Supplied list is longer than the available max width", )); } - let mut vals = vals.to_vec(); - let stride = self.max_width / (vals.len() * 2); - - self.das_fft_extension_stride(&mut vals, stride); + // In case more roots are provided with fft_settings, use a larger stride + let stride = self.max_width / (evens.len() * 2); + let mut odds = evens.to_vec(); + self.das_fft_extension_stride(&mut odds, stride); - let invlen = BlstFr::from_u64(vals.len() as u64); - let invlen = invlen.inverse(); - - for val in &mut vals { - val.fr *= invlen.fr - } + // TODO: explain why each odd member is multiplied by euclidean inverse of length + let mut inv_len = BlstFr::from_u64(odds.len() as u64); + inv_len = inv_len.eucl_inverse(); + let odds = odds.iter().map(|f| f.mul(&inv_len)).collect(); - Ok(vals) + Ok(odds) } } diff --git a/arkworks3/src/eip_4844.rs b/arkworks3/src/eip_4844.rs index fb8c62284..216dd0169 100644 --- a/arkworks3/src/eip_4844.rs +++ b/arkworks3/src/eip_4844.rs @@ -1,19 +1,16 @@ extern crate alloc; -use crate::kzg_proofs::{FFTSettings, KZGSettings}; -use crate::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2}; -use blst::{blst_fr, blst_p1, blst_p2}; -use kzg::common_utils::reverse_bit_order; +use crate::kzg_types::{ArkFr, ArkG1}; +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, Blob, Bytes32, Bytes48, CKZGSettings, KZGCommitment, KZGProof, - PrecomputationTableManager, BYTES_PER_FIELD_ELEMENT, BYTES_PER_G1, BYTES_PER_G2, C_KZG_RET, - C_KZG_RET_BADARGS, C_KZG_RET_OK, FIELD_ELEMENTS_PER_BLOB, TRUSTED_SETUP_NUM_G1_POINTS, + BYTES_PER_G1, C_KZG_RET, C_KZG_RET_BADARGS, C_KZG_RET_OK, FIELD_ELEMENTS_PER_BLOB, + FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB, TRUSTED_SETUP_NUM_G1_POINTS, TRUSTED_SETUP_NUM_G2_POINTS, }; use kzg::{cfg_into_iter, Fr, G1}; -use std::ptr::null_mut; #[cfg(feature = "std")] use libc::FILE; @@ -22,131 +19,29 @@ use std::fs::File; #[cfg(feature = "std")] use std::io::Read; +use crate::utils::{ + deserialize_blob, handle_ckzg_badargs, kzg_settings_to_c, kzg_settings_to_rust, + PRECOMPUTATION_TABLES, +}; + #[cfg(feature = "parallel")] use rayon::prelude::*; #[cfg(feature = "std")] use kzg::eip_4844::load_trusted_setup_string; -static mut PRECOMPUTATION_TABLES: PrecomputationTableManager = - PrecomputationTableManager::new(); - #[cfg(feature = "std")] -pub fn load_trusted_setup_filename_rust(filepath: &str) -> Result { +pub fn load_trusted_setup_filename_rust( + filepath: &str, +) -> Result { let mut file = File::open(filepath).map_err(|_| "Unable to open file".to_string())?; let mut contents = String::new(); file.read_to_string(&mut contents) .map_err(|_| "Unable to read file".to_string())?; - let (g1_bytes, g2_bytes) = load_trusted_setup_string(&contents)?; - load_trusted_setup_rust(g1_bytes.as_slice(), g2_bytes.as_slice()) -} - -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, settings.max_width as usize) - .iter() - .map(|r| ArkFr::from_blst_fr(*r)) - .collect::>() - }; - let mut expanded_roots_of_unity = roots_of_unity.clone(); - reverse_bit_order(&mut expanded_roots_of_unity)?; - expanded_roots_of_unity.push(ArkFr::one()); - let mut reverse_roots_of_unity = expanded_roots_of_unity.clone(); - reverse_roots_of_unity.reverse(); - - let mut first_root = expanded_roots_of_unity[1]; - let first_root_arr = [first_root; 1]; - first_root = first_root_arr[0]; - - Ok(FFTSettings { - max_width: settings.max_width as usize, - root_of_unity: first_root, - expanded_roots_of_unity, - reverse_roots_of_unity, - roots_of_unity, - }) -} - -fn kzg_settings_to_rust(c_settings: &CKZGSettings) -> Result { - let secret_g1 = unsafe { - core::slice::from_raw_parts(c_settings.g1_values, TRUSTED_SETUP_NUM_G1_POINTS) - .iter() - .map(|r| ArkG1::from_blst_p1(*r)) - .collect::>() - }; - let secret_g2 = unsafe { - core::slice::from_raw_parts(c_settings.g2_values, TRUSTED_SETUP_NUM_G2_POINTS) - .iter() - .map(|r| ArkG2::from_blst_p2(*r)) - .collect::>() - }; - Ok(KZGSettings { - fs: fft_settings_to_rust(c_settings)?, - secret_g1, - secret_g2, - // TODO: - precomputation: None, - }) -} - -fn kzg_settings_to_c(rust_settings: &KZGSettings) -> CKZGSettings { - let g1_val = rust_settings - .secret_g1 - .iter() - .map(|r| r.to_blst_p1()) - .collect::>(); - let g1_val = Box::new(g1_val); - let g2_val = rust_settings - .secret_g2 - .iter() - .map(|r| r.to_blst_p2()) - .collect::>(); - let x = g2_val.into_boxed_slice(); - let stat_ref = Box::leak(x); - let v = Box::into_raw(g1_val); - - let roots_of_unity = Box::new( - rust_settings - .fs - .roots_of_unity - .iter() - .map(|r| r.to_blst_fr()) - .collect::>(), - ); - - CKZGSettings { - max_width: rust_settings.fs.max_width as u64, - roots_of_unity: unsafe { (*Box::into_raw(roots_of_unity)).as_mut_ptr() }, - g1_values: unsafe { (*v).as_mut_ptr() }, - g2_values: stat_ref.as_mut_ptr(), - } -} - -unsafe fn deserialize_blob(blob: *const Blob) -> Result, C_KZG_RET> { - (*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) = ArkFr::from_bytes(&bytes) { - Ok(result) - } else { - Err(C_KZG_RET_BADARGS) - } - }) - .collect::, C_KZG_RET>>() -} - -macro_rules! handle_ckzg_badargs { - ($x: expr) => { - match $x { - Ok(value) => value, - Err(_) => return C_KZG_RET_BADARGS, - } - }; + let (g1_monomial_bytes, g1_lagrange_bytes, g2_monomial_bytes) = + load_trusted_setup_string(&contents)?; + load_trusted_setup_rust(&g1_monomial_bytes, &g1_lagrange_bytes, &g2_monomial_bytes) } /// # Safety @@ -173,22 +68,32 @@ pub unsafe extern "C" fn blob_to_kzg_commitment( #[no_mangle] pub unsafe extern "C" fn load_trusted_setup( out: *mut CKZGSettings, - g1_bytes: *const u8, - n1: usize, - g2_bytes: *const u8, - n2: usize, + g1_monomial_bytes: *const u8, + num_g1_monomial_bytes: u64, + g1_lagrange_bytes: *const u8, + num_g1_lagrange_bytes: u64, + g2_monomial_bytes: *const u8, + num_g2_monomial_bytes: u64, + _precompute: u64, ) -> C_KZG_RET { - let g1_bytes = core::slice::from_raw_parts(g1_bytes, n1 * BYTES_PER_G1); - let g2_bytes = core::slice::from_raw_parts(g2_bytes, n2 * BYTES_PER_G2); - TRUSTED_SETUP_NUM_G1_POINTS = g1_bytes.len() / BYTES_PER_G1; - let mut settings = handle_ckzg_badargs!(load_trusted_setup_rust(g1_bytes, g2_bytes)); + let g1_monomial_bytes = + core::slice::from_raw_parts(g1_monomial_bytes, num_g1_monomial_bytes as usize); + let g1_lagrange_bytes = + core::slice::from_raw_parts(g1_lagrange_bytes, num_g1_lagrange_bytes as usize); + let g2_monomial_bytes = + core::slice::from_raw_parts(g2_monomial_bytes, num_g2_monomial_bytes as usize); + TRUSTED_SETUP_NUM_G1_POINTS = num_g1_monomial_bytes as usize / BYTES_PER_G1; + let mut settings = handle_ckzg_badargs!(load_trusted_setup_rust( + g1_monomial_bytes, + g1_lagrange_bytes, + g2_monomial_bytes + )); let c_settings = kzg_settings_to_c(&settings); PRECOMPUTATION_TABLES.save_precomputation(settings.precomputation.take(), &c_settings); *out = c_settings; - C_KZG_RET_OK } @@ -202,8 +107,9 @@ pub unsafe extern "C" fn load_trusted_setup_file( let mut buf = vec![0u8; 1024 * 1024]; let len: usize = libc::fread(buf.as_mut_ptr() as *mut libc::c_void, 1, buf.len(), in_); let s = handle_ckzg_badargs!(String::from_utf8(buf[..len].to_vec())); - let (g1_bytes, g2_bytes) = handle_ckzg_badargs!(load_trusted_setup_string(&s)); - TRUSTED_SETUP_NUM_G1_POINTS = g1_bytes.len() / BYTES_PER_G1; + let (g1_monomial_bytes, g1_lagrange_bytes, g2_monomial_bytes) = + handle_ckzg_badargs!(load_trusted_setup_string(&s)); + TRUSTED_SETUP_NUM_G1_POINTS = g1_monomial_bytes.len() / BYTES_PER_G1; if TRUSTED_SETUP_NUM_G1_POINTS != FIELD_ELEMENTS_PER_BLOB { // Helps pass the Java test "shouldThrowExceptionOnIncorrectTrustedSetupFromFile", // as well as 5 others that pass only if this one passes (likely because Java doesn't @@ -211,8 +117,9 @@ pub unsafe extern "C" fn load_trusted_setup_file( return C_KZG_RET_BADARGS; } let mut settings = handle_ckzg_badargs!(load_trusted_setup_rust( - g1_bytes.as_slice(), - g2_bytes.as_slice() + &g1_monomial_bytes, + &g1_lagrange_bytes, + &g2_monomial_bytes )); let c_settings = kzg_settings_to_c(&settings); @@ -224,6 +131,31 @@ pub unsafe extern "C" fn load_trusted_setup_file( C_KZG_RET_OK } +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn compute_blob_kzg_proof( + out: *mut KZGProof, + blob: *const Blob, + commitment_bytes: *const Bytes48, + s: &CKZGSettings, +) -> C_KZG_RET { + let deserialized_blob = match deserialize_blob(blob) { + Ok(value) => value, + Err(err) => return err, + }; + + let commitment_g1 = handle_ckzg_badargs!(ArkG1::from_bytes(&(*commitment_bytes).bytes)); + let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); + let proof = handle_ckzg_badargs!(compute_blob_kzg_proof_rust( + &deserialized_blob, + &commitment_g1, + &settings + )); + + (*out).bytes = proof.to_bytes(); + C_KZG_RET_OK +} + /// # Safety #[no_mangle] pub unsafe extern "C" fn free_trusted_setup(s: *mut CKZGSettings) { @@ -231,30 +163,70 @@ pub unsafe extern "C" fn free_trusted_setup(s: *mut CKZGSettings) { return; } - PRECOMPUTATION_TABLES.remove_precomputation(&*s); + if !(*s).g1_values_monomial.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).g1_values_monomial, + FIELD_ELEMENTS_PER_BLOB, + )); + drop(v); + (*s).g1_values_monomial = ptr::null_mut(); + } - let max_width = (*s).max_width as usize; - let roots = Box::from_raw(core::slice::from_raw_parts_mut( - (*s).roots_of_unity, - max_width, - )); - drop(roots); - (*s).roots_of_unity = null_mut(); + if !(*s).g1_values_lagrange_brp.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).g1_values_lagrange_brp, + FIELD_ELEMENTS_PER_BLOB, + )); + drop(v); + (*s).g1_values_lagrange_brp = ptr::null_mut(); + } - let g1 = Box::from_raw(core::slice::from_raw_parts_mut( - (*s).g1_values, - TRUSTED_SETUP_NUM_G1_POINTS, - )); - drop(g1); - (*s).g1_values = null_mut(); + if !(*s).g2_values_monomial.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).g2_values_monomial, + TRUSTED_SETUP_NUM_G2_POINTS, + )); + drop(v); + (*s).g2_values_monomial = ptr::null_mut(); + } - let g2 = Box::from_raw(core::slice::from_raw_parts_mut( - (*s).g2_values, - TRUSTED_SETUP_NUM_G2_POINTS, - )); - drop(g2); - (*s).g2_values = null_mut(); - (*s).max_width = 0; + if !(*s).x_ext_fft_columns.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).x_ext_fft_columns, + 2 * ((FIELD_ELEMENTS_PER_EXT_BLOB / 2) / FIELD_ELEMENTS_PER_CELL), + )); + drop(v); + (*s).x_ext_fft_columns = ptr::null_mut(); + } + + if !(*s).roots_of_unity.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).roots_of_unity, + FIELD_ELEMENTS_PER_EXT_BLOB + 1, + )); + drop(v); + (*s).roots_of_unity = ptr::null_mut(); + } + + if !(*s).reverse_roots_of_unity.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).reverse_roots_of_unity, + FIELD_ELEMENTS_PER_EXT_BLOB + 1, + )); + drop(v); + (*s).reverse_roots_of_unity = ptr::null_mut(); + } + + if !(*s).brp_roots_of_unity.is_null() { + let v = Box::from_raw(core::slice::from_raw_parts_mut( + (*s).brp_roots_of_unity, + FIELD_ELEMENTS_PER_EXT_BLOB, + )); + drop(v); + (*s).brp_roots_of_unity = ptr::null_mut(); + } + + PRECOMPUTATION_TABLES.remove_precomputation(&*s); } /// # Safety @@ -364,31 +336,6 @@ pub unsafe extern "C" fn verify_blob_kzg_proof_batch( } } -/// # Safety -#[no_mangle] -pub unsafe extern "C" fn compute_blob_kzg_proof( - out: *mut KZGProof, - blob: *const Blob, - commitment_bytes: *const Bytes48, - s: &CKZGSettings, -) -> C_KZG_RET { - let deserialized_blob = match deserialize_blob(blob) { - Ok(value) => value, - Err(err) => return err, - }; - - let commitment_g1 = handle_ckzg_badargs!(ArkG1::from_bytes(&(*commitment_bytes).bytes)); - let settings = handle_ckzg_badargs!(kzg_settings_to_rust(s)); - let proof = handle_ckzg_badargs!(compute_blob_kzg_proof_rust( - &deserialized_blob, - &commitment_g1, - &settings - )); - - (*out).bytes = proof.to_bytes(); - C_KZG_RET_OK -} - /// # Safety #[no_mangle] pub unsafe extern "C" fn compute_kzg_proof( diff --git a/arkworks3/src/eip_7594.rs b/arkworks3/src/eip_7594.rs new file mode 100644 index 000000000..eaca25478 --- /dev/null +++ b/arkworks3/src/eip_7594.rs @@ -0,0 +1,277 @@ +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::{ + kzg_types::{ArkFr, ArkG1, LKZGSettings}, + utils::{deserialize_blob, kzg_settings_to_rust}, +}; + +pub fn compute_cells_and_kzg_proofs_rust( + cells: Option<&mut [[ArkFr; FIELD_ELEMENTS_PER_CELL]]>, + proofs: Option<&mut [ArkG1]>, + blob: &[ArkFr], + s: &LKZGSettings, +) -> 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 [[ArkFr; FIELD_ELEMENTS_PER_CELL]], + recovered_proofs: Option<&mut [ArkG1]>, + cell_indicies: &[usize], + cells: &[[ArkFr; FIELD_ELEMENTS_PER_CELL]], + s: &LKZGSettings, +) -> Result<(), String> { + kzg::eip_7594::recover_cells_and_kzg_proofs( + recovered_cells, + recovered_proofs, + cell_indicies, + cells, + s, + ) +} + +pub fn verify_cell_kzg_proof_batch_rust( + commitments: &[ArkG1], + cell_indices: &[usize], + cells: &[[ArkFr; FIELD_ELEMENTS_PER_CELL]], + proofs: &[ArkG1], + s: &LKZGSettings, +) -> 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![ + [ArkFr::default(); FIELD_ELEMENTS_PER_CELL]; + CELLS_PER_EXT_BLOB + ]) + }; + let mut proofs_rs = if proofs.is_null() { + None + } else { + Some(vec![ArkG1::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![[ArkFr::default(); FIELD_ELEMENTS_PER_CELL]; CELLS_PER_EXT_BLOB]; + + let mut recovered_proofs_rs = if recovered_proofs.is_null() { + None + } else { + Some(vec![ArkG1::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<[ArkFr; FIELD_ELEMENTS_PER_CELL], String> { + it.bytes + .chunks(BYTES_PER_FIELD_ELEMENT) + .map(ArkFr::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| ArkG1::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<[ArkFr; FIELD_ELEMENTS_PER_CELL], String> { + it.bytes + .chunks(BYTES_PER_FIELD_ELEMENT) + .map(ArkFr::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| ArkG1::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, + } +} diff --git a/arkworks3/src/fft.rs b/arkworks3/src/fft.rs index 0ce1a9a23..bd89ef5cc 100644 --- a/arkworks3/src/fft.rs +++ b/arkworks3/src/fft.rs @@ -1,34 +1,59 @@ -use crate::kzg_proofs::FFTSettings; +use crate::kzg_proofs::LFFTSettings; use crate::kzg_types::ArkFr as BlstFr; use kzg::{FFTFr, Fr as FFr}; -impl FFTFr for FFTSettings { - fn fft_fr(&self, data: &[BlstFr], inverse: bool) -> Result, String> { +impl LFFTSettings { + /// Fast Fourier Transform for finite field elements, `output` must be zeroes + pub(crate) fn fft_fr_output( + &self, + data: &[BlstFr], + inverse: bool, + output: &mut [BlstFr], + ) -> Result<(), String> { if data.len() > self.max_width { - return Err(String::from("data length is longer than allowed max width")); + return Err(String::from( + "Supplied list is longer than the available max width", + )); + } + if data.len() != output.len() { + return Err(format!( + "Output length {} doesn't match data length {}", + data.len(), + output.len() + )); } if !data.len().is_power_of_two() { - return Err(String::from("data length is not power of 2")); + return Err(String::from("A list with power-of-two length expected")); } + // In case more roots are provided with fft_settings, use a larger stride let stride = self.max_width / data.len(); - let mut ret = vec![BlstFr::default(); data.len()]; + // Inverse is same as regular, but all constants are reversed and results are divided by n + // This is a property of the DFT matrix let roots = if inverse { &self.reverse_roots_of_unity } else { - &self.expanded_roots_of_unity + &self.roots_of_unity }; - fft_fr_fast(&mut ret, data, 1, roots, stride); + fft_fr_fast(output, data, 1, roots, stride); if inverse { let inv_fr_len = BlstFr::from_u64(data.len() as u64).inverse(); - ret[..data.len()] - .iter_mut() - .for_each(|f| *f = BlstFr::mul(f, &inv_fr_len)); + output.iter_mut().for_each(|f| *f = f.mul(&inv_fr_len)); } + Ok(()) + } +} + +impl FFTFr for LFFTSettings { + fn fft_fr(&self, data: &[BlstFr], inverse: bool) -> Result, String> { + let mut ret = vec![BlstFr::default(); data.len()]; + + self.fft_fr_output(data, inverse, &mut ret)?; + Ok(ret) } } diff --git a/arkworks3/src/fft_g1.rs b/arkworks3/src/fft_g1.rs index 0292de7a6..23a1bb332 100644 --- a/arkworks3/src/fft_g1.rs +++ b/arkworks3/src/fft_g1.rs @@ -1,20 +1,16 @@ -use crate::kzg_proofs::FFTSettings; -use crate::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine}; +use crate::kzg_proofs::LFFTSettings; +use crate::kzg_types::ArkG1ProjAddAffine; +use crate::kzg_types::{ArkFp, ArkG1Affine}; +use crate::kzg_types::{ArkFr, ArkG1}; -use kzg::cfg_into_iter; -#[cfg(feature = "parallel")] -use rayon::prelude::*; +use kzg::msm::msm_impls::msm; -use ark_ec::ProjectiveCurve; -use ark_ff::PrimeField; use kzg::msm::precompute::PrecomputationTable; use kzg::{Fr as KzgFr, G1Mul}; use kzg::{FFTG1, G1}; -use std::ops::MulAssign; extern crate alloc; -#[allow(unused_variables)] pub fn g1_linear_combination( out: &mut ArkG1, points: &[ArkG1], @@ -24,13 +20,11 @@ pub fn g1_linear_combination( ) { #[cfg(feature = "sppark")] { - use ark_bls12_381::{Fr, G1Affine}; - use ark_ec::msm::VariableBaseMSM; - use ark_ff::BigInteger256; + use blst::{blst_fr, blst_scalar, blst_scalar_from_fr}; use kzg::{G1Mul, G1}; if len < 8 { - *out = ArkG1::default(); + *out = FsG1::default(); for i in 0..len { let tmp = points[i].mul(&scalars[i]); out.add_or_dbl_assign(&tmp); @@ -40,81 +34,29 @@ pub fn g1_linear_combination( } let scalars = - unsafe { alloc::slice::from_raw_parts(scalars.as_ptr() as *const BigInteger256, len) }; + unsafe { alloc::slice::from_raw_parts(scalars.as_ptr() as *const blst_fr, len) }; let point = if let Some(precomputation) = precomputation { - rust_kzg_arkworks3_sppark::multi_scalar_mult_prepared::( - precomputation.table, - scalars, - ) + rust_kzg_blst_sppark::multi_scalar_mult_prepared(precomputation.table, scalars) } else { - let affines = kzg::msm::msm_impls::batch_convert::(&points); - let affines = - unsafe { alloc::slice::from_raw_parts(affines.as_ptr() as *const G1Affine, len) }; - rust_kzg_arkworks3_sppark::multi_scalar_mult::(&affines[0..len], scalars) + let affines = kzg::msm::msm_impls::batch_convert::(&points); + let affines = unsafe { + alloc::slice::from_raw_parts(affines.as_ptr() as *const blst_p1_affine, len) + }; + rust_kzg_blst_sppark::multi_scalar_mult(&affines[0..len], &scalars) }; - *out = ArkG1(point); + *out = FsG1(point); } - #[cfg(feature = "sppark_wlc")] + #[cfg(not(feature = "sppark"))] { - use ark_bls12_381::{Fr, G1Affine}; - use ark_ff::BigInteger256; - use kzg::{G1Mul, G1}; - use rust_kzg_arkworks3_sppark_wlc::MultiScalarMultContext; - - if len < 8 { - *out = ArkG1::default(); - for i in 0..len { - let tmp = points[i].mul(&scalars[i]); - out.add_or_dbl_assign(&tmp); - } - - return; - } - - let ark_scalars = cfg_into_iter!(&scalars[0..len]) - .map(|scalar| scalar.fr.into_repr()) - .collect::>(); - - let mut context = if let Some(context) = precomputation { - let table = context.table; - MultiScalarMultContext { context: table } - } else { - let affines = kzg::msm::msm_impls::batch_convert::(&points); - let affines = - unsafe { alloc::slice::from_raw_parts(affines.as_ptr() as *const G1Affine, len) }; - - rust_kzg_arkworks3_sppark_wlc::multi_scalar_mult_init(affines) - }; - - let msm_results = rust_kzg_arkworks3_sppark_wlc::multi_scalar_mult::( - &mut context, + *out = msm::( + points, + scalars, len, - unsafe { std::mem::transmute::<&[_], &[BigInteger256]>(&ark_scalars) }, + precomputation, ); - - *out = ArkG1(msm_results[0]); - - if precomputation.is_none() { - rust_kzg_arkworks3_sppark_wlc::multi_scalar_mult_free(&mut context); - } - } - - #[cfg(not(any(feature = "sppark", feature = "sppark_wlc")))] - { - use ark_ec::msm::VariableBaseMSM; - let ark_points = cfg_into_iter!(&points[0..len]) - .map(|point| point.0.into_affine()) - .collect::>(); - let ark_scalars = cfg_into_iter!(&scalars[0..len]) - .map(|scalar| scalar.fr.into_repr()) - .collect::>(); - *out = ArkG1(VariableBaseMSM::multi_scalar_mul( - ark_points.as_slice(), - ark_scalars.as_slice(), - )); } } @@ -130,22 +72,23 @@ pub fn make_data(data: usize) -> Vec { vec } -impl FFTG1 for FFTSettings { +impl FFTG1 for LFFTSettings { fn fft_g1(&self, data: &[ArkG1], inverse: bool) -> Result, String> { if data.len() > self.max_width { - return Err(String::from("data length is longer than allowed max width")); - } - if !data.len().is_power_of_two() { - return Err(String::from("data length is not power of 2")); + return Err(String::from( + "Supplied list is longer than the available max width", + )); + } else if !data.len().is_power_of_two() { + return Err(String::from("A list with power-of-two length expected")); } - let stride: usize = self.max_width / data.len(); + let stride = self.max_width / data.len(); let mut ret = vec![ArkG1::default(); data.len()]; let roots = if inverse { &self.reverse_roots_of_unity } else { - &self.expanded_roots_of_unity + &self.roots_of_unity }; fft_g1_fast(&mut ret, data, 1, roots, stride, 1); @@ -154,8 +97,9 @@ impl FFTG1 for FFTSettings { let inv_fr_len = ArkFr::from_u64(data.len() as u64).inverse(); ret[..data.len()] .iter_mut() - .for_each(|f| f.0.mul_assign(inv_fr_len.fr)); + .for_each(|f| *f = f.mul(&inv_fr_len)); } + Ok(ret) } } @@ -169,11 +113,13 @@ pub fn fft_g1_slow( _width: usize, ) { for i in 0..data.len() { + // Evaluate first member at 1 ret[i] = data[0].mul(&roots[0]); + + // Evaluate the rest of members using a step of (i * J) % data.len() over the roots + // This distributes the roots over correct x^n members and saves on multiplication for j in 1..data.len() { - let jv = data[j * stride]; - let r = roots[((i * j) % data.len()) * roots_stride]; - let v = jv.mul(&r); + let v = data[j * stride].mul(&roots[((i * j) % data.len()) * roots_stride]); ret[i] = ret[i].add_or_dbl(&v); } } @@ -193,8 +139,8 @@ pub fn fft_g1_fast( { let (lo, hi) = ret.split_at_mut(half); rayon::join( - || fft_g1_fast(hi, &data[stride..], stride * 2, roots, roots_stride * 2, 1), || fft_g1_fast(lo, data, stride * 2, roots, roots_stride * 2, 1), + || fft_g1_fast(hi, &data[stride..], stride * 2, roots, roots_stride * 2, 1), ); } diff --git a/arkworks3/src/fk20_proofs.rs b/arkworks3/src/fk20_proofs.rs index 5ea09a8ef..9d7676054 100644 --- a/arkworks3/src/fk20_proofs.rs +++ b/arkworks3/src/fk20_proofs.rs @@ -1,4 +1,4 @@ -use crate::kzg_proofs::{FFTSettings, KZGSettings}; +use crate::kzg_proofs::{LFFTSettings, LKZGSettings}; use crate::kzg_types::{ArkFp, ArkFr as BlstFr, ArkG1, ArkG1Affine, ArkG2}; use crate::utils::PolyData; use kzg::common_utils::reverse_bit_order; @@ -10,7 +10,7 @@ use rayon::prelude::*; #[repr(C)] #[derive(Debug, Clone, Default)] pub struct KzgFK20SingleSettings { - pub ks: KZGSettings, + pub ks: LKZGSettings, pub x_ext_fft: Vec, pub x_ext_fft_len: usize, } @@ -18,17 +18,25 @@ pub struct KzgFK20SingleSettings { #[repr(C)] #[derive(Debug, Clone, Default)] pub struct KzgFK20MultiSettings { - pub ks: KZGSettings, + pub ks: LKZGSettings, pub chunk_len: usize, pub x_ext_fft_files: Vec>, pub length: usize, } impl - FK20SingleSettings - for KzgFK20SingleSettings + FK20SingleSettings< + BlstFr, + ArkG1, + ArkG2, + LFFTSettings, + PolyData, + LKZGSettings, + ArkFp, + ArkG1Affine, + > for KzgFK20SingleSettings { - fn new(ks: &KZGSettings, n2: usize) -> Result { + fn new(ks: &LKZGSettings, n2: usize) -> Result { let n = n2 / 2; if n2 > ks.fs.max_width { @@ -45,13 +53,13 @@ impl let mut x = Vec::new(); for i in 0..(n - 1) { - x.push(ks.secret_g1[n - 2 - i]) + x.push(ks.g1_values_monomial[n - 2 - i]) } x.push(ArkG1::identity()); - let new_ks = KZGSettings { + let new_ks = LKZGSettings { fs: ks.fs.clone(), - ..KZGSettings::default() + ..LKZGSettings::default() }; Ok(KzgFK20SingleSettings { @@ -84,10 +92,19 @@ impl } } -impl FK20MultiSettings - for KzgFK20MultiSettings +impl + FK20MultiSettings< + BlstFr, + ArkG1, + ArkG2, + LFFTSettings, + PolyData, + LKZGSettings, + ArkFp, + ArkG1Affine, + > for KzgFK20MultiSettings { - fn new(ks: &KZGSettings, n2: usize, chunk_len: usize) -> Result { + fn new(ks: &LKZGSettings, n2: usize, chunk_len: usize) -> Result { if n2 > ks.fs.max_width { return Err(String::from( "n2 must be equal or less than kzg settings max width", @@ -123,7 +140,7 @@ impl FK20MultiSettings= chunk_len { j -= chunk_len; } else { @@ -134,9 +151,9 @@ impl FK20MultiSettings Result, String> { +fn toeplitz_part_1(x: &[ArkG1], fs: &LFFTSettings) -> Result, String> { let n = x.len(); let n2 = n * 2; @@ -290,7 +307,7 @@ fn toeplitz_part_1(x: &[ArkG1], fs: &FFTSettings) -> Result, String> fn toeplitz_part_2( toeplitz_coeffs: &PolyData, x_ext_fft: &[ArkG1], - fs: &FFTSettings, + fs: &LFFTSettings, ) -> Result, String> { let toeplitz_coeffs_fft = fs.fft_fr(&toeplitz_coeffs.coeffs, false).unwrap(); @@ -313,7 +330,7 @@ fn toeplitz_part_2( } } -fn toeplitz_part_3(h_ext_fft: &[ArkG1], fs: &FFTSettings) -> Result, String> { +fn toeplitz_part_3(h_ext_fft: &[ArkG1], fs: &LFFTSettings) -> Result, String> { let n = h_ext_fft.len() / 2; let mut out = fs.fft_g1(h_ext_fft, true).unwrap(); diff --git a/arkworks3/src/kzg_proofs.rs b/arkworks3/src/kzg_proofs.rs index 5323fe202..3fb503529 100644 --- a/arkworks3/src/kzg_proofs.rs +++ b/arkworks3/src/kzg_proofs.rs @@ -1,25 +1,23 @@ #![allow(non_camel_case_types)] extern crate alloc; -use super::utils::{blst_poly_into_pc_poly, PolyData}; use crate::kzg_types::{ArkFp, ArkFr, ArkG1Affine}; use crate::kzg_types::{ArkFr as BlstFr, ArkG1, ArkG2}; use alloc::sync::Arc; use ark_bls12_381::Bls12_381; -use ark_ec::{PairingEngine, ProjectiveCurve}; -use ark_poly::Polynomial; -use ark_std::{vec, One}; +use ark_ec::PairingEngine; +use ark_ec::ProjectiveCurve; +use ark_std::One; use kzg::eip_4844::hash_to_bls_field; use kzg::msm::precompute::PrecomputationTable; -use kzg::{Fr as FrTrait, G1, G2}; -use kzg::{G1Mul, G2Mul}; +use kzg::{Fr, G1Mul, G2Mul, G1, G2}; use std::ops::Neg; #[derive(Debug, Clone)] -pub struct FFTSettings { +pub struct LFFTSettings { pub max_width: usize, pub root_of_unity: BlstFr, - pub expanded_roots_of_unity: Vec, + pub brp_roots_of_unity: Vec, pub reverse_roots_of_unity: Vec, pub roots_of_unity: Vec, } @@ -43,35 +41,35 @@ pub fn expand_root_of_unity(root: &BlstFr, width: usize) -> Result, } #[derive(Debug, Clone, Default)] -pub struct KZGSettings { - pub fs: FFTSettings, - pub secret_g1: Vec, - pub secret_g2: Vec, +pub struct LKZGSettings { + pub fs: LFFTSettings, + pub g1_values_monomial: Vec, + pub g1_values_lagrange_brp: Vec, + pub g2_values_monomial: Vec, pub precomputation: Option>>, + pub x_ext_fft_columns: Vec>, } -pub fn generate_trusted_setup(len: usize, secret: [u8; 32usize]) -> (Vec, Vec) { - let s = hash_to_bls_field::(&secret); - let mut s_pow = ArkFr::one(); +pub fn generate_trusted_setup( + n: usize, + secret: [u8; 32usize], +) -> (Vec, Vec, Vec) { + let s = hash_to_bls_field(&secret); + let mut s_pow = Fr::one(); - let mut s1 = Vec::with_capacity(len); - let mut s2 = Vec::with_capacity(len); + let mut s1 = Vec::with_capacity(n); + let mut s2 = Vec::with_capacity(n); + let mut s3 = Vec::with_capacity(n); - for _ in 0..len { + for _ in 0..n { s1.push(ArkG1::generator().mul(&s_pow)); - s2.push(ArkG2::generator().mul(&s_pow)); + s2.push(ArkG1::generator()); // TODO: this should be lagrange form + s3.push(ArkG2::generator().mul(&s_pow)); s_pow = s_pow.mul(&s); } - (s1, s2) -} - -pub fn eval_poly(p: &PolyData, x: &BlstFr) -> BlstFr { - let poly = blst_poly_into_pc_poly(&p.coeffs); - BlstFr { - fr: poly.evaluate(&x.fr), - } + (s1, s2, s3) } pub fn pairings_verify(a1: &ArkG1, a2: &ArkG2, b1: &ArkG1, b2: &ArkG2) -> bool { diff --git a/arkworks3/src/kzg_types.rs b/arkworks3/src/kzg_types.rs index 06737f23f..6e843d9ac 100644 --- a/arkworks3/src/kzg_types.rs +++ b/arkworks3/src/kzg_types.rs @@ -1,105 +1,92 @@ use crate::consts::SCALE2_ROOT_OF_UNITY; -use crate::fft_g1::g1_linear_combination; -use crate::kzg_proofs::{ - eval_poly, expand_root_of_unity, pairings_verify, FFTSettings as LFFTSettings, - KZGSettings as LKZGSettings, -}; -use crate::poly::{poly_fast_div, poly_inverse, poly_long_div, poly_mul_direct, poly_mul_fft}; +use crate::fft_g1::{fft_g1_fast, g1_linear_combination}; +pub use crate::kzg_proofs::{expand_root_of_unity, pairings_verify, LFFTSettings, LKZGSettings}; +// use crate::poly::{poly_fast_div, poly_inverse, poly_long_div, poly_mul_direct, poly_mul_fft}; use crate::recover::{scale_poly, unscale_poly}; use crate::utils::{ - blst_fp_into_pc_fq, blst_fr_into_pc_fr, blst_p1_into_pc_g1projective, - blst_p2_into_pc_g2projective, pc_fr_into_blst_fr, pc_g1projective_into_blst_p1, - pc_g2projective_into_blst_p2, PolyData, + blst_fp_into_pc_fq, blst_p1_into_pc_g1projective, blst_p2_into_pc_g2projective, + pc_g1projective_into_blst_p1, pc_g2projective_into_blst_p2, PolyData, }; use crate::P2; -use ark_bls12_381::{g1, g2, Fr, G1Affine}; -use ark_ec::ModelParameters; -use ark_ec::{models::short_weierstrass_jacobian::GroupProjective, AffineCurve, ProjectiveCurve}; -use ark_ff::PrimeField; -use ark_ff::{biginteger::BigInteger256, BigInteger, Field}; +use ark_bls12_381::{g1, g2, G1Affine}; +use ark_ec::{models::short_weierstrass_jacobian::GroupProjective, ProjectiveCurve}; +use ark_ec::{AffineCurve, ModelParameters}; +use ark_ff::{BigInteger, Field}; use ark_std::{One, Zero}; - -#[cfg(feature = "rand")] -use ark_std::UniformRand; - use blst::{ - blst_fp, blst_fp2, blst_fr, blst_p1, blst_p1_affine, blst_p1_compress, blst_p1_from_affine, + blst_bendian_from_scalar, blst_fp, blst_fp2, blst_fr, blst_fr_add, blst_fr_cneg, + blst_fr_eucl_inverse, blst_fr_from_scalar, blst_fr_from_uint64, blst_fr_inverse, blst_fr_mul, + blst_fr_sqr, blst_fr_sub, blst_p1, blst_p1_affine, blst_p1_compress, blst_p1_from_affine, blst_p1_in_g1, blst_p1_uncompress, blst_p2, blst_p2_affine, blst_p2_from_affine, - blst_p2_uncompress, BLST_ERROR, + blst_p2_uncompress, blst_scalar, blst_scalar_fr_check, blst_scalar_from_bendian, + blst_scalar_from_fr, blst_uint64_from_fr, BLST_ERROR, +}; +use kzg::common_utils::{log2_u64, reverse_bit_order}; +use kzg::eip_4844::{ + BYTES_PER_FIELD_ELEMENT, BYTES_PER_G1, BYTES_PER_G2, FIELD_ELEMENTS_PER_BLOB, + FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB, TRUSTED_SETUP_NUM_G2_POINTS, }; -use kzg::common_utils::reverse_bit_order; -use kzg::eip_4844::{BYTES_PER_FIELD_ELEMENT, BYTES_PER_G1, BYTES_PER_G2}; use kzg::msm::precompute::{precompute, PrecomputationTable}; use kzg::{ FFTFr, FFTSettings, FFTSettingsPoly, Fr as KzgFr, G1Affine as G1AffineTrait, G1Fp, G1GetFp, G1LinComb, G1Mul, G1ProjAddAffine, G2Mul, KZGSettings, PairingVerify, Poly, Scalar256, G1, G2, }; -use std::ops::{AddAssign, Neg, Sub}; +use std::ops::{AddAssign, Sub}; extern crate alloc; use alloc::sync::Arc; -fn bytes_be_to_uint64(inp: &[u8]) -> u64 { - u64::from_be_bytes(inp.try_into().expect("Input wasn't 8 elements...")) -} +// fn bytes_be_to_uint64(inp: &[u8]) -> u64 { +// u64::from_be_bytes(inp.try_into().expect("Input wasn't 8 elements...")) +// } -const BLS12_381_MOD_256: [u64; 4] = [ - 0xffffffff00000001, - 0x53bda402fffe5bfe, - 0x3339d80809a1d805, - 0x73eda753299d7d48, -]; +// const BLS12_381_MOD_256: [u64; 4] = [ +// 0xffffffff00000001, +// 0x53bda402fffe5bfe, +// 0x3339d80809a1d805, +// 0x73eda753299d7d48, +// ]; #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] -pub struct ArkFr { - pub fr: Fr, -} +pub struct ArkFr(pub blst_fr); -impl ArkFr { - pub fn from_blst_fr(fr: blst_fr) -> Self { - Self { - fr: blst_fr_into_pc_fr(fr), - } - } +// impl Fr for ArkFr { - pub fn to_blst_fr(&self) -> blst_fr { - pc_fr_into_blst_fr(self.fr) - } -} - -fn bigint_check_mod_256(a: &[u64; 4]) -> bool { - let (_, overflow) = a[0].overflowing_sub(BLS12_381_MOD_256[0]); - let (_, overflow) = a[1].overflowing_sub(BLS12_381_MOD_256[1] + overflow as u64); - let (_, overflow) = a[2].overflowing_sub(BLS12_381_MOD_256[2] + overflow as u64); - let (_, overflow) = a[3].overflowing_sub(BLS12_381_MOD_256[3] + overflow as u64); - overflow -} +// fn bigint_check_mod_256(a: &[u64; 4]) -> bool { +// let (_, overflow) = a[0].overflowing_sub(BLS12_381_MOD_256[0]); +// let (_, overflow) = a[1].overflowing_sub(BLS12_381_MOD_256[1] + overflow as u64); +// let (_, overflow) = a[2].overflowing_sub(BLS12_381_MOD_256[2] + overflow as u64); +// let (_, overflow) = a[3].overflowing_sub(BLS12_381_MOD_256[3] + overflow as u64); +// overflow +// } impl KzgFr for ArkFr { fn null() -> Self { - Self { - fr: Fr::new(BigInteger256::new([u64::MAX; 4])), - } + Self::from_u64_arr(&[u64::MAX, u64::MAX, u64::MAX, u64::MAX]) } fn zero() -> Self { - // Self::from_u64(0) - Self { fr: Fr::zero() } + Self::from_u64(0) } fn one() -> Self { - // let one = Fr::one(); - // // assert_eq!(one.0.0, [0, 1, 1, 1], "must be eq"); - // Self { fr: one } Self::from_u64(1) } #[cfg(feature = "rand")] fn rand() -> Self { - let mut rng = rand::thread_rng(); - Self { - fr: Fr::rand(&mut rng), + let val: [u64; 4] = [ + rand::random(), + rand::random(), + rand::random(), + rand::random(), + ]; + let mut ret = Self::default(); + unsafe { + blst_fr_from_uint64(&mut ret.0, val.as_ptr()); } + + ret } fn from_bytes(bytes: &[u8]) -> Result { @@ -113,19 +100,16 @@ impl KzgFr for ArkFr { ) }) .and_then(|bytes: &[u8; BYTES_PER_FIELD_ELEMENT]| { - let storage: [u64; 4] = [ - bytes_be_to_uint64(&bytes[24..32]), - bytes_be_to_uint64(&bytes[16..24]), - bytes_be_to_uint64(&bytes[8..16]), - bytes_be_to_uint64(&bytes[0..8]), - ]; - let big_int = BigInteger256::new(storage); - if !big_int.is_zero() && !bigint_check_mod_256(&big_int.0) { - return Err("Invalid scalar".to_string()); + let mut bls_scalar = blst_scalar::default(); + let mut fr = blst_fr::default(); + unsafe { + blst_scalar_from_bendian(&mut bls_scalar, bytes.as_ptr()); + if !blst_scalar_fr_check(&bls_scalar) { + return Err("Invalid scalar".to_string()); + } + blst_fr_from_scalar(&mut fr, &bls_scalar); } - Ok(Self { - fr: Fr::from(big_int), - }) + Ok(Self(fr)) }) } @@ -139,8 +123,14 @@ impl KzgFr for ArkFr { bytes.len() ) }) - .map(|bytes: &[u8; BYTES_PER_FIELD_ELEMENT]| Self { - fr: Fr::from_be_bytes_mod_order(bytes), + .map(|bytes: &[u8; BYTES_PER_FIELD_ELEMENT]| { + let mut bls_scalar = blst_scalar::default(); + let mut fr = blst_fr::default(); + unsafe { + blst_scalar_from_bendian(&mut bls_scalar, bytes.as_ptr()); + blst_fr_from_scalar(&mut fr, &bls_scalar); + } + Self(fr) }) } @@ -150,93 +140,169 @@ impl KzgFr for ArkFr { } fn from_u64_arr(u: &[u64; 4]) -> Self { - Self { - fr: Fr::from(BigInteger256::new(*u)), + let mut ret = Self::default(); + unsafe { + blst_fr_from_uint64(&mut ret.0, u.as_ptr()); } + + ret } fn from_u64(val: u64) -> Self { - Self { fr: Fr::from(val) } + Self::from_u64_arr(&[val, 0, 0, 0]) } fn to_bytes(&self) -> [u8; 32] { - let big_int_256: BigInteger256 = Fr::into(self.fr); - <[u8; 32]>::try_from(big_int_256.to_bytes_be()).unwrap() + let mut scalar = blst_scalar::default(); + let mut bytes = [0u8; 32]; + unsafe { + blst_scalar_from_fr(&mut scalar, &self.0); + blst_bendian_from_scalar(bytes.as_mut_ptr(), &scalar); + } + + bytes } fn to_u64_arr(&self) -> [u64; 4] { - let b: BigInteger256 = Fr::into(self.fr); - b.0 + let mut val: [u64; 4] = [0; 4]; + unsafe { + blst_uint64_from_fr(val.as_mut_ptr(), &self.0); + } + + val } fn is_one(&self) -> bool { - self.fr.is_one() + let mut val: [u64; 4] = [0; 4]; + unsafe { + blst_uint64_from_fr(val.as_mut_ptr(), &self.0); + } + + val[0] == 1 && val[1] == 0 && val[2] == 0 && val[3] == 0 } fn is_zero(&self) -> bool { - self.fr.is_zero() + let mut val: [u64; 4] = [0; 4]; + unsafe { + blst_uint64_from_fr(val.as_mut_ptr(), &self.0); + } + + val[0] == 0 && val[1] == 0 && val[2] == 0 && val[3] == 0 } fn is_null(&self) -> bool { - self.equals(&ArkFr::null()) + self.equals(&Self::null()) } fn sqr(&self) -> Self { - Self { - fr: self.fr.square(), + let mut ret = Self::default(); + unsafe { + blst_fr_sqr(&mut ret.0, &self.0); } + + ret } fn mul(&self, b: &Self) -> Self { - Self { fr: self.fr * b.fr } + let mut ret = Self::default(); + unsafe { + blst_fr_mul(&mut ret.0, &self.0, &b.0); + } + + ret } fn add(&self, b: &Self) -> Self { - Self { fr: self.fr + b.fr } + let mut ret = Self::default(); + unsafe { + blst_fr_add(&mut ret.0, &self.0, &b.0); + } + + ret } fn sub(&self, b: &Self) -> Self { - Self { fr: self.fr - b.fr } + let mut ret = Self::default(); + unsafe { + blst_fr_sub(&mut ret.0, &self.0, &b.0); + } + + ret } fn eucl_inverse(&self) -> Self { // Inverse and eucl inverse work the same way - Self { - fr: self.fr.inverse().unwrap(), + let mut ret = Self::default(); + unsafe { + blst_fr_eucl_inverse(&mut ret.0, &self.0); } + + ret } fn negate(&self) -> Self { - Self { fr: self.fr.neg() } + let mut ret = Self::default(); + unsafe { + blst_fr_cneg(&mut ret.0, &self.0, true); + } + + ret } fn inverse(&self) -> Self { - Self { - fr: self.fr.inverse().unwrap(), + let mut ret = Self::default(); + unsafe { + blst_fr_inverse(&mut ret.0, &self.0); } + + ret } fn pow(&self, n: usize) -> Self { - Self { - fr: self.fr.pow([n as u64]), + let mut out = Self::one(); + + let mut temp = *self; + let mut n = n; + loop { + if (n & 1) == 1 { + out = out.mul(&temp); + } + n >>= 1; + if n == 0 { + break; + } + + temp = temp.sqr(); } + + out } fn div(&self, b: &Self) -> Result { - let div = self.fr / b.fr; - if div.0 .0.is_empty() { - Ok(Self { fr: Fr::zero() }) - } else { - Ok(Self { fr: div }) - } + let tmp = b.eucl_inverse(); + let out = self.mul(&tmp); + + Ok(out) } fn equals(&self, b: &Self) -> bool { - self.fr == b.fr + let mut val_a: [u64; 4] = [0; 4]; + let mut val_b: [u64; 4] = [0; 4]; + + unsafe { + blst_uint64_from_fr(val_a.as_mut_ptr(), &self.0); + blst_uint64_from_fr(val_b.as_mut_ptr(), &b.0); + } + + val_a[0] == val_b[0] && val_a[1] == val_b[1] && val_a[2] == val_b[2] && val_a[3] == val_b[3] } fn to_scalar(&self) -> Scalar256 { - Scalar256::from_u64(self.fr.0 .0) + let mut blst_scalar = blst_scalar::default(); + unsafe { + blst_scalar_from_fr(&mut blst_scalar, &self.0); + } + Scalar256::from_u8(&blst_scalar.b) } } @@ -347,6 +413,8 @@ impl G1 for ArkG1 { #[cfg(feature = "rand")] fn rand() -> Self { + use ark_ff::UniformRand; + let mut rng = rand::thread_rng(); Self(GroupProjective::rand(&mut rng)) } @@ -666,17 +734,16 @@ impl G2 for ArkG2 { ) }) .and_then(|bytes: &[u8; BYTES_PER_G2]| { - let mut blst_affine = blst_p2_affine::default(); - let result = unsafe { blst_p2_uncompress(&mut blst_affine, bytes.as_ptr()) }; - - if result != BLST_ERROR::BLST_SUCCESS { - return Err("Failed to deserialize G1".to_owned()); + let mut tmp = blst_p2_affine::default(); + let mut g2 = blst_p2::default(); + unsafe { + // The uncompress routine also checks that the point is on the curve + if blst_p2_uncompress(&mut tmp, bytes.as_ptr()) != BLST_ERROR::BLST_SUCCESS { + return Err("Failed to uncompress".to_string()); + } + blst_p2_from_affine(&mut g2, &tmp); } - - let mut blst_point = blst_p2::default(); - unsafe { blst_p2_from_affine(&mut blst_point, &blst_affine) }; - - Ok(ArkG2::from_blst_p2(blst_point)) + Ok(ArkG2::from_blst_p2(g2)) }) } @@ -731,7 +798,22 @@ impl Poly for PolyData { } fn eval(&self, x: &ArkFr) -> ArkFr { - eval_poly(self, x) + if self.coeffs.is_empty() { + return ArkFr::zero(); + } else if x.is_zero() { + return self.coeffs[0]; + } + let mut ret = self.coeffs[self.coeffs.len() - 1]; + let mut i = self.coeffs.len() - 2; + loop { + let temp = ret.mul(x); + ret = temp.add(&self.coeffs[i]); + if i == 0 { + break; + } + i -= 1; + } + ret } fn scale(&mut self) { @@ -742,39 +824,207 @@ impl Poly for PolyData { unscale_poly(self); } - fn inverse(&mut self, new_len: usize) -> Result { - poly_inverse(self, new_len) + fn inverse(&mut self, output_len: usize) -> Result { + if output_len == 0 { + return Err(String::from("Can't produce a zero-length result")); + } else if self.coeffs.is_empty() { + return Err(String::from("Can't inverse a zero-length poly")); + } else if self.coeffs[0].is_zero() { + return Err(String::from( + "First coefficient of polynomial mustn't be zero", + )); + } + + let mut ret = PolyData { + coeffs: vec![ArkFr::zero(); output_len], + }; + // If the input polynomial is constant, the remainder of the series is zero + if self.coeffs.len() == 1 { + ret.coeffs[0] = self.coeffs[0].eucl_inverse(); + + return Ok(ret); + } + + let maxd = output_len - 1; + + // Max space for multiplications is (2 * length - 1) + // Don't need the following as its recalculated inside + // let scale: usize = log2_pow2(next_pow_of_2(2 * output_len - 1)); + // let fft_settings = FsFFTSettings::new(scale).unwrap(); + + // To store intermediate results + + // Base case for d == 0 + ret.coeffs[0] = self.coeffs[0].eucl_inverse(); + let mut d: usize = 0; + let mut mask: usize = 1 << log2_u64(maxd); + while mask != 0 { + d = 2 * d + usize::from((maxd & mask) != 0); + mask >>= 1; + + // b.c -> tmp0 (we're using out for c) + // tmp0.length = min_u64(d + 1, b->length + output->length - 1); + let len_temp = (d + 1).min(self.len() + output_len - 1); + let mut tmp0 = self.mul(&ret, len_temp).unwrap(); + + // 2 - b.c -> tmp0 + for i in 0..tmp0.len() { + tmp0.coeffs[i] = tmp0.coeffs[i].negate(); + } + let fr_two = kzg::Fr::from_u64(2); + tmp0.coeffs[0] = tmp0.coeffs[0].add(&fr_two); + + // c.(2 - b.c) -> tmp1; + let tmp1 = ret.mul(&tmp0, d + 1).unwrap(); + + for i in 0..tmp1.len() { + ret.coeffs[i] = tmp1.coeffs[i]; + } + } + + if d + 1 != output_len { + return Err(String::from("D + 1 must be equal to output_len")); + } + + Ok(ret) } fn div(&mut self, x: &Self) -> Result { if x.len() >= self.len() || x.len() < 128 { - poly_long_div(self, x) + self.long_div(x) } else { - poly_fast_div(self, x) + self.fast_div(x) } } - fn long_div(&mut self, x: &Self) -> Result { - poly_long_div(self, x) + fn long_div(&mut self, divisor: &Self) -> Result { + if divisor.coeffs.is_empty() { + return Err(String::from("Can't divide by zero")); + } else if divisor.coeffs[divisor.coeffs.len() - 1].is_zero() { + return Err(String::from("Highest coefficient must be non-zero")); + } + + let out_length = self.poly_quotient_length(divisor); + if out_length == 0 { + return Ok(PolyData { coeffs: vec![] }); + } + + // Special case for divisor.len() == 2 + if divisor.len() == 2 { + let divisor_0 = divisor.coeffs[0]; + let divisor_1 = divisor.coeffs[1]; + + let mut out_coeffs = Vec::from(&self.coeffs[1..]); + for i in (1..out_length).rev() { + out_coeffs[i] = out_coeffs[i].div(&divisor_1).unwrap(); + + let tmp = out_coeffs[i].mul(&divisor_0); + out_coeffs[i - 1] = out_coeffs[i - 1].sub(&tmp); + } + + out_coeffs[0] = out_coeffs[0].div(&divisor_1).unwrap(); + + Ok(PolyData { coeffs: out_coeffs }) + } else { + let mut out: PolyData = PolyData { + coeffs: vec![ArkFr::default(); out_length], + }; + + let mut a_pos = self.len() - 1; + let b_pos = divisor.len() - 1; + let mut diff = a_pos - b_pos; + + let mut a = self.coeffs.clone(); + + while diff > 0 { + out.coeffs[diff] = a[a_pos].div(&divisor.coeffs[b_pos]).unwrap(); + + for i in 0..(b_pos + 1) { + let tmp = out.coeffs[diff].mul(&divisor.coeffs[i]); + a[diff + i] = a[diff + i].sub(&tmp); + } + + diff -= 1; + a_pos -= 1; + } + + out.coeffs[0] = a[a_pos].div(&divisor.coeffs[b_pos]).unwrap(); + Ok(out) + } } - fn fast_div(&mut self, x: &Self) -> Result { - poly_fast_div(self, x) + fn fast_div(&mut self, divisor: &Self) -> Result { + if divisor.coeffs.is_empty() { + return Err(String::from("Cant divide by zero")); + } else if divisor.coeffs[divisor.coeffs.len() - 1].is_zero() { + return Err(String::from("Highest coefficient must be non-zero")); + } + + let m: usize = self.len() - 1; + let n: usize = divisor.len() - 1; + + // If the divisor is larger than the dividend, the result is zero-length + if n > m { + return Ok(PolyData { coeffs: Vec::new() }); + } + + // Special case for divisor.length == 1 (it's a constant) + if divisor.len() == 1 { + let mut out = PolyData { + coeffs: vec![ArkFr::zero(); self.len()], + }; + for i in 0..out.len() { + out.coeffs[i] = self.coeffs[i].div(&divisor.coeffs[0]).unwrap(); + } + return Ok(out); + } + + let mut a_flip = self.flip().unwrap(); + let mut b_flip = divisor.flip().unwrap(); + + let inv_b_flip = b_flip.inverse(m - n + 1).unwrap(); + let q_flip = a_flip.mul(&inv_b_flip, m - n + 1).unwrap(); + + let out = q_flip.flip().unwrap(); + Ok(out) } - fn mul_direct(&mut self, x: &Self, len: usize) -> Result { - poly_mul_direct(self, x, len) + fn mul_direct(&mut self, multiplier: &Self, output_len: usize) -> Result { + if self.len() == 0 || multiplier.len() == 0 { + return Ok(PolyData::new(0)); + } + + let a_degree = self.len() - 1; + let b_degree = multiplier.len() - 1; + + let mut ret = PolyData { + coeffs: vec![kzg::Fr::zero(); output_len], + }; + + // Truncate the output to the length of the output polynomial + for i in 0..(a_degree + 1) { + let mut j = 0; + while (j <= b_degree) && ((i + j) < output_len) { + let tmp = self.coeffs[i].mul(&multiplier.coeffs[j]); + let tmp = ret.coeffs[i + j].add(&tmp); + ret.coeffs[i + j] = tmp; + + j += 1; + } + } + + Ok(ret) } } impl FFTSettingsPoly for LFFTSettings { fn poly_mul_fft( a: &PolyData, - x: &PolyData, + b: &PolyData, len: usize, - fs: Option<&LFFTSettings>, + _fs: Option<&LFFTSettings>, ) -> Result { - poly_mul_fft(a, x, fs, len) + b.mul_fft(a, len) } } @@ -783,7 +1033,7 @@ impl Default for LFFTSettings { Self { max_width: 0, root_of_unity: ArkFr::zero(), - expanded_roots_of_unity: Vec::new(), + brp_roots_of_unity: Vec::new(), reverse_roots_of_unity: Vec::new(), roots_of_unity: Vec::new(), } @@ -801,18 +1051,26 @@ impl FFTSettings for LFFTSettings { let max_width: usize = 1 << scale; let root_of_unity = ArkFr::from_u64_arr(&SCALE2_ROOT_OF_UNITY[scale]); - let expanded_roots_of_unity = expand_root_of_unity(&root_of_unity, max_width)?; - let mut reverse_roots_of_unity = expanded_roots_of_unity.clone(); + let roots_of_unity = expand_root_of_unity(&root_of_unity, max_width)?; + + let mut brp_roots_of_unity = roots_of_unity.clone(); + brp_roots_of_unity.pop(); + reverse_bit_order(&mut brp_roots_of_unity)?; + let mut reverse_roots_of_unity = roots_of_unity.clone(); reverse_roots_of_unity.reverse(); - let mut roots_of_unity = expanded_roots_of_unity.clone(); - roots_of_unity.pop(); - reverse_bit_order(&mut roots_of_unity)?; + // let expanded_roots_of_unity = expand_root_of_unity(&root_of_unity, max_width)?; + // let mut reverse_roots_of_unity = expanded_roots_of_unity.clone(); + // reverse_roots_of_unity.reverse(); + + // let mut roots_of_unity = expanded_roots_of_unity.clone(); + // roots_of_unity.pop(); + // reverse_bit_order(&mut roots_of_unity)?; Ok(LFFTSettings { max_width, root_of_unity, - expanded_roots_of_unity, + brp_roots_of_unity, reverse_roots_of_unity, roots_of_unity, }) @@ -822,12 +1080,11 @@ impl FFTSettings for LFFTSettings { self.max_width } - fn get_expanded_roots_of_unity_at(&self, i: usize) -> ArkFr { - self.expanded_roots_of_unity[i] + fn get_brp_roots_of_unity(&self) -> &[ArkFr] { + &self.brp_roots_of_unity } - - fn get_expanded_roots_of_unity(&self) -> &[ArkFr] { - &self.expanded_roots_of_unity + fn get_brp_roots_of_unity_at(&self, i: usize) -> ArkFr { + self.brp_roots_of_unity[i] } fn get_reverse_roots_of_unity_at(&self, i: usize) -> ArkFr { @@ -847,17 +1104,72 @@ impl FFTSettings for LFFTSettings { } } +fn g1_fft(output: &mut [ArkG1], input: &[ArkG1], s: &LFFTSettings) -> Result<(), String> { + /* Ensure the length is valid */ + if input.len() > 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(); + fft_g1_fast(output, input, 1, &s.roots_of_unity, roots_stride, 1); + + Ok(()) +} + +fn toeplitz_part_1(output: &mut [ArkG1], x: &[ArkG1], s: &LFFTSettings) -> Result<(), String> { + let n = x.len(); + let n2 = n * 2; + let mut x_ext = vec![ArkG1::identity(); n2]; + + x_ext[..n].copy_from_slice(x); + + g1_fft(output, &x_ext, s)?; + + Ok(()) +} + impl KZGSettings for LKZGSettings { fn new( - secret_g1: &[ArkG1], - secret_g2: &[ArkG2], - _length: usize, + g1_monomial: &[ArkG1], + g1_lagrange_brp: &[ArkG1], + g2_monomial: &[ArkG2], fft_settings: &LFFTSettings, ) -> 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 + { + return Err("Length does not match FIELD_ELEMENTS_PER_BLOB".to_string()); + } + + let n = FIELD_ELEMENTS_PER_EXT_BLOB / 2; + let k = n / FIELD_ELEMENTS_PER_CELL; + let k2 = 2 * k; + + let mut points = vec![ArkG1::default(); k2]; + let mut x = vec![ArkG1::default(); k]; + let mut x_ext_fft_columns = vec![vec![ArkG1::default(); FIELD_ELEMENTS_PER_CELL]; k2]; + + for offset in 0..FIELD_ELEMENTS_PER_CELL { + let start = n - FIELD_ELEMENTS_PER_CELL - 1 - offset; + for (i, p) in x.iter_mut().enumerate().take(k - 1) { + let j = start - i * FIELD_ELEMENTS_PER_CELL; + *p = g1_monomial[j]; + } + x[k - 1] = ArkG1::identity(); + + toeplitz_part_1(&mut points, &x, fft_settings)?; + + for row in 0..k2 { + x_ext_fft_columns[row][offset] = points[row]; + } + } Ok(Self { - secret_g1: secret_g1.to_vec(), - secret_g2: secret_g2.to_vec(), + g1_values_monomial: g1_monomial.to_vec(), + g1_values_lagrange_brp: g1_lagrange_brp.to_vec(), + g2_values_monomial: g2_monomial.to_vec(), fs: fft_settings.clone(), + x_ext_fft_columns, precomputation: { #[cfg(feature = "sppark")] { @@ -892,21 +1204,21 @@ impl KZGSettings Result { - if p.coeffs.len() > self.secret_g1.len() { + if p.coeffs.len() > self.g1_values_lagrange_brp.len() { return Err(String::from("Polynomial is longer than secret g1")); } let mut out = ArkG1::default(); g1_linear_combination( &mut out, - &self.secret_g1, + &self.g1_values_lagrange_brp, &p.coeffs, p.coeffs.len(), self.get_precomputation(), @@ -945,7 +1257,7 @@ impl KZGSettings Result { let x_g2: ArkG2 = ArkG2::generator().mul(x); - let s_minus_x: ArkG2 = self.secret_g2[1].sub(&x_g2); + let s_minus_x: ArkG2 = self.g2_values_monomial[1].sub(&x_g2); let y_g1 = ArkG1::generator().mul(y); let commitment_minus_y: ArkG1 = com.sub(&y_g1); @@ -978,11 +1290,11 @@ impl KZGSettings ArkFr { - self.fs.get_expanded_roots_of_unity_at(i) - } - fn get_roots_of_unity_at(&self, i: usize) -> ArkFr { self.fs.get_roots_of_unity_at(i) } @@ -1053,12 +1361,18 @@ impl KZGSettings &[ArkG1] { - &self.secret_g1 + fn get_g1_lagrange_brp(&self) -> &[ArkG1] { + &self.g1_values_lagrange_brp } - fn get_g2_secret(&self) -> &[ArkG2] { - &self.secret_g2 + fn get_g1_monomial(&self) -> &[ArkG1] { + &self.g1_values_monomial + } + fn get_g2_monomial(&self) -> &[ArkG2] { + &self.g2_values_monomial + } + fn get_x_ext_fft_column(&self, index: usize) -> &[ArkG1] { + &self.x_ext_fft_columns[index] } fn get_precomputation(&self) -> Option<&PrecomputationTable> { @@ -1216,11 +1530,17 @@ impl G1AffineTrait for ArkG1Affine { } fn x(&self) -> &ArkFp { - unsafe { core::mem::transmute(&self.aff.x) } + unsafe { + // Transmute safe due to repr(C) on FsFp + core::mem::transmute(&self.aff.x) + } } fn y(&self) -> &ArkFp { - unsafe { core::mem::transmute(&self.aff.y) } + unsafe { + // Transmute safe due to repr(C) on FsFp + core::mem::transmute(&self.aff.y) + } } fn is_infinity(&self) -> bool { @@ -1238,11 +1558,17 @@ impl G1AffineTrait for ArkG1Affine { } fn x_mut(&mut self) -> &mut ArkFp { - unsafe { core::mem::transmute(&mut self.aff.x) } + unsafe { + // Transmute safe due to repr(C) on FsFp + core::mem::transmute(&mut self.aff.x) + } } fn y_mut(&mut self) -> &mut ArkFp { - unsafe { core::mem::transmute(&mut self.aff.y) } + unsafe { + // Transmute safe due to repr(C) on FsFp + core::mem::transmute(&mut self.aff.y) + } } } diff --git a/arkworks3/src/lib.rs b/arkworks3/src/lib.rs index 3681c1331..7d29aaa38 100644 --- a/arkworks3/src/lib.rs +++ b/arkworks3/src/lib.rs @@ -13,6 +13,7 @@ pub type Uniq = blst::blst_uniq; pub mod consts; pub mod das; pub mod eip_4844; +pub mod eip_7594; pub mod fft; pub mod fft_g1; pub mod fk20_proofs; diff --git a/arkworks3/src/poly.rs b/arkworks3/src/poly.rs index 01199a7d8..8e3176885 100644 --- a/arkworks3/src/poly.rs +++ b/arkworks3/src/poly.rs @@ -1,251 +1,130 @@ -use super::kzg_proofs::FFTSettings; -use super::utils::{blst_poly_into_pc_poly, PolyData}; +use super::kzg_proofs::LFFTSettings; +use super::utils::PolyData; use crate::kzg_types::ArkFr as BlstFr; -use crate::utils::pc_poly_into_blst_poly; -use crate::zero_poly::pad_poly; -use ark_bls12_381::Fr; -use ark_poly::univariate::DensePolynomial; -use ark_poly::UVPolynomial; -use ark_std::{log2, Zero}; use kzg::common_utils::{log2_pow2, next_pow_of_2}; use kzg::{FFTFr, FFTSettings as FFTSettingsT, Fr as FrTrait, Poly}; -use std::cmp::min; -pub fn poly_inverse(b: &PolyData, output_len: usize) -> Result { - if b.coeffs.is_empty() { - return Err(String::from("b.coeffs is empty")); +pub fn poly_flip(input: &PolyData) -> Result { + let mut output = PolyData::new(0); + for i in 0..input.len() { + output.coeffs.push(input.coeffs[input.coeffs.len() - i - 1]); } + Ok(output) +} - if BlstFr::is_zero(&b.coeffs[0]) { - return Err(String::from("b.coeffs[0] is zero")); - } +impl PolyData { + pub fn _poly_norm(&self) -> Self { + let mut ret = self.clone(); - let mut output = PolyData { - coeffs: vec![BlstFr::zero(); output_len], - }; - if b.coeffs.len() == 1 { - output.coeffs[0] = b.coeffs[0].inverse(); - for i in 1..output_len { - output.coeffs[i] = BlstFr::zero(); + let mut temp_len: usize = ret.coeffs.len(); + while temp_len > 0 && ret.coeffs[temp_len - 1].is_zero() { + temp_len -= 1; } - return Ok(output); - } - let maxd = output_len - 1; - let scale = next_pow_of_2(log2_pow2(2 * output_len - 1)); - let fs = FFTSettings::new(scale).unwrap(); - - let mut tmp0: PolyData; - let mut tmp1: PolyData; - - output.coeffs[0] = b.coeffs[0].inverse(); - let mut d: usize = 0; - let mut mask: usize = 1 << log2(maxd); - - while mask != 0 { - d = 2 * d + usize::from((maxd & mask) != 0); - mask >>= 1; - - let len_temp: usize = min(d + 1, b.coeffs.len() + output.coeffs.len() - 1); - - tmp0 = poly_mul(b, &output, Some(&fs), len_temp).unwrap(); - - for i in 0..len_temp { - tmp0.coeffs[i] = tmp0.coeffs[i].negate(); + if temp_len == 0 { + ret.coeffs = Vec::new(); + } else { + ret.coeffs = ret.coeffs[0..temp_len].to_vec(); } - let fr_two = BlstFr { fr: Fr::from(2) }; - tmp0.coeffs[0] = tmp0.coeffs[0].add(&fr_two); - let len_temp2: usize = d + 1; - - tmp1 = poly_mul(&output, &tmp0, Some(&fs), len_temp2).unwrap(); - - if tmp1.coeffs.len() > output_len { - tmp1.coeffs = tmp1.coeffs[..output_len].to_vec(); - } - for i in 0..tmp1.coeffs.len() { - output.coeffs[i] = tmp1.coeffs[i]; - } + ret } - if d + 1 != output_len { - return Err(String::from("d + 1 is not equals to output_len")); - } - Ok(output) -} -pub fn poly_mul_direct(p1: &PolyData, p2: &PolyData, len: usize) -> Result { - let p1 = blst_poly_into_pc_poly(&p1.coeffs); - let p2 = blst_poly_into_pc_poly(&p2.coeffs); - if p1.is_zero() || p2.is_zero() { - Ok(pc_poly_into_blst_poly(DensePolynomial::zero())) - } else { - let mut result = vec![Fr::zero(); len]; - for (i, self_coeff) in p1.coeffs.iter().enumerate() { - for (j, other_coeff) in p2.coeffs.iter().enumerate() { - if i + j >= len { - break; - } - result[i + j] += &(*self_coeff * other_coeff); - } + pub fn poly_quotient_length(&self, divisor: &Self) -> usize { + if self.len() >= divisor.len() { + self.len() - divisor.len() + 1 + } else { + 0 } - let p = pc_poly_into_blst_poly(DensePolynomial::from_coefficients_vec(result)); - Ok(PolyData { - coeffs: pad_poly(&p, len).unwrap(), - }) } -} -pub fn poly_long_div(p1: &PolyData, p2: &PolyData) -> Result { - Ok(pc_poly_into_blst_poly( - &blst_poly_into_pc_poly(&p1.coeffs) / &blst_poly_into_pc_poly(&p2.coeffs), - )) -} + pub fn pad(&self, out_length: usize) -> Self { + let mut ret = Self { + coeffs: vec![BlstFr::zero(); out_length], + }; -pub fn poly_mul( - a: &PolyData, - b: &PolyData, - fs: Option<&FFTSettings>, - len: usize, -) -> Result { - if a.coeffs.len() < 64 || b.coeffs.len() < 64 || len < 128 { - poly_mul_direct(a, b, len) - } else { - poly_mul_fft(a, b, fs, len) - } -} - -pub fn poly_mul_fft( - a: &PolyData, - b: &PolyData, - fs: Option<&FFTSettings>, - len: usize, -) -> Result { - // Truncate a and b so as not to do excess work for the number of coefficients required. - let a_len = min(a.len(), len); - let b_len = min(b.len(), len); - let length = next_pow_of_2(a_len + b_len - 1); - - // If the FFT settings are NULL then make a local set, otherwise use the ones passed in. - let fs_p = if let Some(x) = fs { - x.clone() - } else { - let scale = log2_pow2(length); - FFTSettings::new(scale).unwrap() - }; + for i in 0..self.len().min(out_length) { + ret.coeffs[i] = self.coeffs[i]; + } - if length > fs_p.max_width { - return Err(String::from( - "length should be equals or less than FFTSettings max width", - )); + ret } - let a = PolyData { - coeffs: a.coeffs[..a_len].to_vec(), - }; - let b = PolyData { - coeffs: b.coeffs[..b_len].to_vec(), - }; - let a_pad = PolyData { - coeffs: pad_poly(&a, length).unwrap(), - }; - let b_pad = PolyData { - coeffs: pad_poly(&b, length).unwrap(), - }; - - let a_fft; - let b_fft; - #[cfg(feature = "parallel")] - { - if length > 1024 { - let mut a_fft_temp = vec![]; - let mut b_fft_temp = vec![]; - - rayon::join( - || a_fft_temp = fs_p.fft_fr(&a_pad.coeffs, false).unwrap(), - || b_fft_temp = fs_p.fft_fr(&b_pad.coeffs, false).unwrap(), - ); - - a_fft = a_fft_temp; - b_fft = b_fft_temp; - } else { - a_fft = fs_p.fft_fr(&a_pad.coeffs, false).unwrap(); - b_fft = fs_p.fft_fr(&b_pad.coeffs, false).unwrap(); + pub fn flip(&self) -> Result { + let mut ret = PolyData { + coeffs: vec![BlstFr::default(); self.len()], + }; + for i in 0..self.len() { + ret.coeffs[i] = self.coeffs[self.coeffs.len() - i - 1] } - } - #[cfg(not(feature = "parallel"))] - { - a_fft = fs_p.fft_fr(&a_pad.coeffs, false).unwrap(); - b_fft = fs_p.fft_fr(&b_pad.coeffs, false).unwrap(); - } - let mut ab_fft = a_pad; - let mut ab = b_pad; - for i in 0..length { - ab_fft.coeffs[i] = a_fft[i].mul(&b_fft[i]); + Ok(ret) } - ab.coeffs = fs_p.fft_fr(&ab_fft.coeffs, true).unwrap(); + pub fn mul_fft(&self, multiplier: &Self, output_len: usize) -> Result { + let length = next_pow_of_2(self.len() + multiplier.len() - 1); - let data_len = min(len, length); - let mut out = PolyData::new(len); - - for i in 0..data_len { - out.coeffs[i] = ab.coeffs[i]; - } - for i in data_len..len { - out.coeffs[i] = BlstFr::zero(); - } + let scale = log2_pow2(length); + let fft_settings = LFFTSettings::new(scale).unwrap(); + + let a_pad = self.pad(length); + let b_pad = multiplier.pad(length); + + let a_fft: Vec; + let b_fft: Vec; + + #[cfg(feature = "parallel")] + { + if length > 1024 { + let mut a_fft_temp = vec![]; + let mut b_fft_temp = vec![]; + + rayon::join( + || a_fft_temp = fft_settings.fft_fr(&a_pad.coeffs, false).unwrap(), + || b_fft_temp = fft_settings.fft_fr(&b_pad.coeffs, false).unwrap(), + ); + + a_fft = a_fft_temp; + b_fft = b_fft_temp; + } else { + a_fft = fft_settings.fft_fr(&a_pad.coeffs, false).unwrap(); + b_fft = fft_settings.fft_fr(&b_pad.coeffs, false).unwrap(); + } + } - Ok(out) -} + #[cfg(not(feature = "parallel"))] + { + // Convert Poly to values + a_fft = fft_settings.fft_fr(&a_pad.coeffs, false).unwrap(); + b_fft = fft_settings.fft_fr(&b_pad.coeffs, false).unwrap(); + } -pub fn poly_fast_div(dividend: &PolyData, divisor: &PolyData) -> Result { - if divisor.coeffs.is_empty() { - return Err(String::from("divisor coeffs are empty")); - } + // Multiply two value ranges + let mut ab_fft = a_fft; + ab_fft.iter_mut().zip(b_fft).for_each(|(a, b)| { + *a = a.mul(&b); + }); - if divisor.coeffs[divisor.coeffs.len() - 1].is_zero() { - return Err(String::from("divisor coeffs last member is zero")); - } + // Convert value range multiplication to a resulting polynomial + let ab = fft_settings.fft_fr(&ab_fft, true).unwrap(); + drop(ab_fft); - let m = dividend.coeffs.len() - 1; - let n = divisor.coeffs.len() - 1; + let mut ret = PolyData { + coeffs: vec![BlstFr::zero(); output_len], + }; - if n > m { - return Ok(PolyData::new(0)); - } + let range = ..output_len.min(length); + ret.coeffs[range].clone_from_slice(&ab[range]); - if divisor.coeffs[divisor.coeffs.len() - 1].is_zero() { - return Err(String::from("divisor coeffs last member is zero")); + Ok(ret) } - let mut out = PolyData::new(0); - - if divisor.len() == 1 { - for i in 0..dividend.len() { - out.coeffs - .push(dividend.coeffs[i].div(&divisor.coeffs[0]).unwrap()); + pub fn mul(&mut self, multiplier: &Self, output_len: usize) -> Result { + if self.len() < 64 || multiplier.len() < 64 || output_len < 128 { + // Tunable parameter + self.mul_direct(multiplier, output_len) + } else { + self.mul_fft(multiplier, output_len) } - return Ok(out); - } - - let a_flip = poly_flip(dividend).unwrap(); - let b_flip = poly_flip(divisor).unwrap(); - - let inv_b_flip = poly_inverse(&b_flip, m - n + 1).unwrap(); - let q_flip = poly_mul(&a_flip, &inv_b_flip, None, m - n + 1).unwrap(); - - out = poly_flip(&q_flip).unwrap(); - - Ok(PolyData { - coeffs: out.coeffs[..m - n + 1].to_vec(), - }) -} - -pub fn poly_flip(input: &PolyData) -> Result { - let mut output = PolyData::new(0); - for i in 0..input.len() { - output.coeffs.push(input.coeffs[input.coeffs.len() - i - 1]); } - Ok(output) } diff --git a/arkworks3/src/recover.rs b/arkworks3/src/recover.rs index c57f0c24d..8ec10fd3d 100644 --- a/arkworks3/src/recover.rs +++ b/arkworks3/src/recover.rs @@ -1,5 +1,5 @@ use crate::consts::SCALE_FACTOR; -use crate::kzg_proofs::FFTSettings; +use crate::kzg_proofs::LFFTSettings; use crate::kzg_types::ArkFr as BlstFr; use crate::utils::PolyData; @@ -82,10 +82,10 @@ pub fn unscale_poly(p: &mut PolyData) { } } } -impl PolyRecover for PolyData { +impl PolyRecover for PolyData { fn recover_poly_coeffs_from_samples( samples: &[Option], - fs: &FFTSettings, + fs: &LFFTSettings, ) -> Result { if !samples.len().is_power_of_two() { return Err(String::from("samples lenght has to be power of 2")); @@ -212,7 +212,7 @@ impl PolyRecover for PolyData { fn recover_poly_from_samples( samples: &[Option], - fs: &FFTSettings, + fs: &LFFTSettings, ) -> Result { let reconstructed_poly = Self::recover_poly_coeffs_from_samples(samples, fs)?; diff --git a/arkworks3/src/utils.rs b/arkworks3/src/utils.rs index 2efa82740..36d38d87a 100644 --- a/arkworks3/src/utils.rs +++ b/arkworks3/src/utils.rs @@ -1,11 +1,24 @@ +extern crate alloc; + +use alloc::boxed::Box; + +use kzg::{ + eip_4844::{ + 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, + }, + Fr, +}; + +use crate::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2, LFFTSettings, LKZGSettings}; + use super::{Fp, P1}; -use crate::kzg_types::ArkFr; use crate::P2; -use ark_bls12_381::{g1, g2, Fq, Fq2, Fr}; +use ark_bls12_381::{g1, g2, Fq, Fq2, Fr as _Fr}; use ark_ec::models::short_weierstrass_jacobian::GroupProjective; use ark_ff::{BigInteger256, BigInteger384, Fp2}; use ark_poly::univariate::DensePolynomial as DensePoly; -use ark_poly::UVPolynomial; use blst::{blst_fp, blst_fp2, blst_fr, blst_p1, blst_p2}; #[derive(Debug, PartialEq, Eq)] @@ -17,33 +30,226 @@ pub struct PolyData { } // FIXME: Store just dense poly here -pub fn pc_poly_into_blst_poly(poly: DensePoly) -> PolyData { - let mut bls_pol: Vec = { Vec::new() }; - for x in poly.coeffs { - bls_pol.push(ArkFr { fr: x }); +pub(crate) unsafe fn deserialize_blob(blob: *const Blob) -> Result, C_KZG_RET> { + (*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) = ArkFr::from_bytes(&bytes) { + Ok(result) + } else { + Err(C_KZG_RET_BADARGS) + } + }) + .collect::, C_KZG_RET>>() +} + +macro_rules! handle_ckzg_badargs { + ($x: expr) => { + match $x { + Ok(value) => value, + Err(_) => return kzg::eip_4844::C_KZG_RET_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| ArkFr(*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| ArkFr(*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| ArkFr(*r)) + .collect::>() + }; + + Ok(LFFTSettings { + 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< + ArkFr, + ArkG1, + ArkFp, + ArkG1Affine, +> = PrecomputationTableManager::new(); + +pub(crate) fn kzg_settings_to_rust(c_settings: &CKZGSettings) -> Result { + Ok(LKZGSettings { + 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| ArkG1::from_blst_p1(*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| ArkG1::from_blst_p1(*r)) + .collect::>(), + g2_values_monomial: unsafe { + core::slice::from_raw_parts(c_settings.g2_values_monomial, TRUSTED_SETUP_NUM_G2_POINTS) + } + .iter() + .map(|r| ArkG2::from_blst_p2(*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| ArkG1::from_blst_p1(*it)) + .collect::>() + }) + .collect::>(), + precomputation: unsafe { PRECOMPUTATION_TABLES.get_precomputation(c_settings) }, + }) +} + +pub(crate) fn kzg_settings_to_c(rust_settings: &LKZGSettings) -> CKZGSettings { + CKZGSettings { + roots_of_unity: Box::leak( + rust_settings + .fs + .roots_of_unity + .iter() + .map(|r| r.0) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + brp_roots_of_unity: Box::leak( + rust_settings + .fs + .brp_roots_of_unity + .iter() + .map(|r| r.0) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + reverse_roots_of_unity: Box::leak( + rust_settings + .fs + .reverse_roots_of_unity + .iter() + .map(|r| r.0) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + g1_values_monomial: Box::leak( + rust_settings + .g1_values_monomial + .iter() + .map(|r| r.to_blst_p1()) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + g1_values_lagrange_brp: Box::leak( + rust_settings + .g1_values_lagrange_brp + .iter() + .map(|r| r.to_blst_p1()) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + g2_values_monomial: Box::leak( + rust_settings + .g2_values_monomial + .iter() + .map(|r| r.to_blst_p2()) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + x_ext_fft_columns: Box::leak( + rust_settings + .x_ext_fft_columns + .iter() + .map(|r| { + Box::leak( + r.iter() + .map(|it| it.to_blst_p1()) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr() + }) + .collect::>() + .into_boxed_slice(), + ) + .as_mut_ptr(), + tables: core::ptr::null_mut(), + wbits: 0, + scratch_size: 0, } - PolyData { coeffs: bls_pol } } -pub fn blst_poly_into_pc_poly(pd: &[ArkFr]) -> DensePoly { - let mut poly: Vec = vec![Fr::default(); pd.len()]; - for i in 0..pd.len() { - poly[i] = pd[i].fr; +pub fn pc_poly_into_blst_poly(poly: DensePoly<_Fr>) -> PolyData { + let mut bls_pol: Vec = { Vec::new() }; + for _x in poly.coeffs { + bls_pol.push(ArkFr::default()); } - DensePoly::from_coefficients_vec(poly) + PolyData { coeffs: bls_pol } } +// pub fn blst_poly_into_pc_poly(pd: &[ArkFr]) -> DensePoly<_Fr> { +// let mut poly: Vec<_Fr> = vec![_Fr::default(); pd.len()]; +// for i in 0..pd.len() { +// poly[i] = pd[i].0.; +// } +// DensePoly::from_coefficients_vec(poly) +// } + pub const fn pc_fq_into_blst_fp(fq: Fq) -> Fp { Fp { l: fq.0 .0 } } -pub const fn blst_fr_into_pc_fr(fr: blst_fr) -> Fr { +pub const fn blst_fr_into_pc_fr(fr: blst_fr) -> _Fr { let big_int = BigInteger256::new(fr.l); - Fr::new(big_int) + _Fr::new(big_int) } -pub const fn pc_fr_into_blst_fr(fr: Fr) -> blst_fr { +pub const fn pc_fr_into_blst_fr(fr: _Fr) -> blst_fr { blst::blst_fr { l: fr.0 .0 } } diff --git a/arkworks3/src/zero_poly.rs b/arkworks3/src/zero_poly.rs index 50619ef7e..57fca2869 100644 --- a/arkworks3/src/zero_poly.rs +++ b/arkworks3/src/zero_poly.rs @@ -1,196 +1,310 @@ -use super::kzg_proofs::FFTSettings; -use super::utils::{blst_poly_into_pc_poly, pc_poly_into_blst_poly, PolyData}; +use super::kzg_proofs::LFFTSettings; +use super::utils::PolyData; use crate::kzg_types::ArkFr as BlstFr; use kzg::common_utils::next_pow_of_2; use kzg::{FFTFr, Fr as FrTrait, ZeroPoly}; use std::cmp::{min, Ordering}; -use std::ops::Neg; -pub(crate) fn pad_poly(poly: &PolyData, new_length: usize) -> Result, String> { - if new_length <= poly.coeffs.len() { - return Ok(poly.coeffs.clone()); +#[cfg(feature = "parallel")] +use rayon::prelude::*; +use smallvec::{smallvec, SmallVec}; + +// Can be tuned & optimized (must be a power of 2) +const DEGREE_OF_PARTIAL: usize = 256; +// Can be tuned & optimized (but must be a power of 2) +const REDUCTION_FACTOR: usize = 4; + +pub fn pad_poly(mut poly: Vec, new_length: usize) -> Result, String> { + if new_length < poly.len() { + return Err(String::from( + "new_length must be longer or equal to poly length", + )); } - let mut out = poly.coeffs.to_vec(); + poly.resize(new_length, BlstFr::zero()); - for _i in poly.coeffs.len()..new_length { - out.push(BlstFr::zero()) + Ok(poly) +} + +pub fn pad_poly_coeffs( + mut coeffs: SmallVec<[T; N]>, + new_length: usize, +) -> Result, String> +where + T: Default + Clone, +{ + if new_length < coeffs.len() { + return Err(String::from( + "new_length must be longer or equal to coeffs length", + )); } - Ok(out) + coeffs.resize(new_length, T::default()); + + Ok(coeffs) } -impl ZeroPoly for FFTSettings { +impl LFFTSettings { fn do_zero_poly_mul_partial( &self, - indices: &[usize], + idxs: &[usize], stride: usize, - ) -> Result { - if indices.is_empty() { - return Err(String::from("idx array must be non-zero")); + ) -> Result, String> { + if idxs.is_empty() { + return Err(String::from("idx array must not be empty")); } - let blstpoly = PolyData { - coeffs: vec![BlstFr::one(); indices.len() + 1], - }; - let mut poly = blst_poly_into_pc_poly(&blstpoly.coeffs); - poly.coeffs[0] = (self.expanded_roots_of_unity[indices[0] * stride]).fr.neg(); - - for (i, indice) in indices.iter().enumerate().skip(1) { - let neg_di = (self.expanded_roots_of_unity[indice * stride]).fr.neg(); - - poly.coeffs[i] = neg_di + poly.coeffs[i - 1]; - - let mut j = i - 1; - while j > 0 { - let temp = poly.coeffs[j] * neg_di; - poly.coeffs[j] = temp + poly.coeffs[j - 1]; - j -= 1; + + // Makes use of long multiplication in terms of (x - w_0)(x - w_1).. + let mut coeffs = SmallVec::<[BlstFr; DEGREE_OF_PARTIAL]>::new(); + + // For the first member, store -w_0 as constant term + coeffs.push(self.roots_of_unity[idxs[0] * stride].negate()); + + for (i, idx) in idxs.iter().copied().enumerate().skip(1) { + // For member (x - w_i) take coefficient as -(w_i + w_{i-1} + ...) + let neg_di = self.roots_of_unity[idx * stride].negate(); + coeffs.push(neg_di.add(&coeffs[i - 1])); + + // Multiply all previous members by (x - w_i) + // It equals multiplying by - w_i and adding x^(i - 1) coefficient (implied multiplication by x) + for j in (1..i).rev() { + coeffs[j] = coeffs[j].mul(&neg_di).add(&coeffs[j - 1]); } - poly.coeffs[0] *= neg_di; + // Multiply x^0 member by - w_i + coeffs[0] = coeffs[0].mul(&neg_di); } - Ok(pc_poly_into_blst_poly(poly)) - } + coeffs.resize(idxs.len() + 1, BlstFr::one()); - fn reduce_partials(&self, len_out: usize, partials: &[PolyData]) -> Result { - let mut out_degree: usize = 0; - for partial in partials { - out_degree += partial.coeffs.len() - 1; - } + Ok(coeffs) + } - if out_degree + 1 > len_out { + fn reduce_partials( + &self, + domain_size: usize, + partial_coeffs: SmallVec<[SmallVec<[BlstFr; DEGREE_OF_PARTIAL]>; REDUCTION_FACTOR]>, + ) -> Result, String> { + if !domain_size.is_power_of_two() { return Err(String::from("Expected domain size to be a power of 2")); } - let mut p_partial = pad_poly(&partials[0], len_out).unwrap(); - let mut mul_eval_ps = self.fft_fr(&p_partial, false).unwrap(); + if partial_coeffs.is_empty() { + return Err(String::from("partials must not be empty")); + } - for partial in partials.iter().skip(1) { - p_partial = pad_poly(partial, len_out)?; + // Calculate the resulting polynomial degree + // E.g. (a * x^n + ...) (b * x^m + ...) has a degree of x^(n+m) + let out_degree = partial_coeffs + .iter() + .map(|partial| { + // TODO: Not guaranteed by function signature that this doesn't underflow + partial.len() - 1 + }) + .sum::(); + + if out_degree + 1 > domain_size { + return Err(String::from( + "Out degree is longer than possible polynomial size in domain", + )); + } - let p_eval = self.fft_fr(&p_partial, false).unwrap(); - for j in 0..len_out { - mul_eval_ps[j].fr *= p_eval[j].fr; - } + let mut partial_coeffs = partial_coeffs.into_iter(); + + // Pad all partial polynomials to same length, compute their FFT and multiply them together + let mut padded_partial = pad_poly_coeffs( + partial_coeffs + .next() + .expect("Not empty, checked above; qed"), + domain_size, + )?; + let mut eval_result: SmallVec<[BlstFr; DEGREE_OF_PARTIAL]> = + smallvec![BlstFr::zero(); domain_size]; + self.fft_fr_output(&padded_partial, false, &mut eval_result)?; + + for partial in partial_coeffs { + padded_partial = pad_poly_coeffs(partial, domain_size)?; + let mut evaluated_partial: SmallVec<[BlstFr; DEGREE_OF_PARTIAL]> = + smallvec![BlstFr::zero(); domain_size]; + self.fft_fr_output(&padded_partial, false, &mut evaluated_partial)?; + + eval_result + .iter_mut() + .zip(evaluated_partial.iter()) + .for_each(|(eval_result, evaluated_partial)| { + *eval_result = eval_result.mul(evaluated_partial); + }); } - let coeffs = self.fft_fr(&mul_eval_ps, true)?; + let mut coeffs = smallvec![BlstFr::zero(); domain_size]; + // Apply an inverse FFT to produce a new poly. Limit its size to out_degree + 1 + self.fft_fr_output(&eval_result, true, &mut coeffs)?; + coeffs.truncate(out_degree + 1); - let out = PolyData { - coeffs: coeffs[..(out_degree + 1)].to_vec(), - }; + Ok(coeffs) + } +} - Ok(out) +impl ZeroPoly for LFFTSettings { + fn do_zero_poly_mul_partial(&self, idxs: &[usize], stride: usize) -> Result { + self.do_zero_poly_mul_partial(idxs, stride) + .map(|coeffs| PolyData { + coeffs: coeffs.into_vec(), + }) + } + + fn reduce_partials( + &self, + domain_size: usize, + partials: &[PolyData], + ) -> Result { + self.reduce_partials( + domain_size, + partials + .iter() + .map(|partial| SmallVec::from_slice(&partial.coeffs)) + .collect(), + ) + .map(|coeffs| PolyData { + coeffs: coeffs.into_vec(), + }) } fn zero_poly_via_multiplication( &self, - length: usize, - missing_indices: &[usize], + domain_size: usize, + missing_idxs: &[usize], ) -> Result<(Vec, PolyData), String> { let zero_eval: Vec; let mut zero_poly: PolyData; - if missing_indices.is_empty() { + if missing_idxs.is_empty() { zero_eval = Vec::new(); zero_poly = PolyData { coeffs: Vec::new() }; return Ok((zero_eval, zero_poly)); } - if missing_indices.len() >= length { + if missing_idxs.len() >= domain_size { return Err(String::from("Missing idxs greater than domain size")); - } else if length > self.max_width { + } else if domain_size > self.max_width { return Err(String::from( "Domain size greater than fft_settings.max_width", )); - } else if !length.is_power_of_two() { + } else if !domain_size.is_power_of_two() { return Err(String::from("Domain size must be a power of 2")); } - let degree_of_partial = 256; - let missing_per_partial = degree_of_partial - 1; - let domain_stride = self.max_width / length; - let mut partial_count = - (missing_per_partial + missing_indices.len() - 1) / missing_per_partial; - let domain_ceiling = min(next_pow_of_2(partial_count * degree_of_partial), length); - - if missing_indices.len() <= missing_per_partial { - zero_poly = self.do_zero_poly_mul_partial(missing_indices, domain_stride)?; + let missing_per_partial = DEGREE_OF_PARTIAL - 1; // Number of missing idxs needed per partial + let domain_stride = self.max_width / domain_size; + + let mut partial_count = 1 + (missing_idxs.len() - 1) / missing_per_partial; // TODO: explain why -1 is used here + + let next_pow: usize = next_pow_of_2(partial_count * DEGREE_OF_PARTIAL); + let domain_ceiling = min(next_pow, domain_size); + // Calculate zero poly + if missing_idxs.len() <= missing_per_partial { + // When all idxs fit into a single multiplication + zero_poly = PolyData { + coeffs: self + .do_zero_poly_mul_partial(missing_idxs, domain_stride)? + .into_vec(), + }; } else { - let mut work = vec![BlstFr::zero(); next_pow_of_2(partial_count * degree_of_partial)]; - - let mut partial_lens = Vec::new(); - - let mut offset = 0; - let mut out_offset = 0; - let max = missing_indices.len(); - - for _i in 0..partial_count { - let end = min(offset + missing_per_partial, max); - - let mut partial = - self.do_zero_poly_mul_partial(&missing_indices[offset..end], domain_stride)?; - partial.coeffs = pad_poly(&partial, degree_of_partial)?; - work.splice( - out_offset..(out_offset + degree_of_partial), - partial.coeffs.to_vec(), + // Otherwise, construct a set of partial polynomials + // Save all constructed polynomials in a shared 'work' vector + let mut work = vec![BlstFr::zero(); next_pow]; + + let mut partial_lens = vec![DEGREE_OF_PARTIAL; partial_count]; + + #[cfg(not(feature = "parallel"))] + let iter = missing_idxs + .chunks(missing_per_partial) + .zip(work.chunks_exact_mut(DEGREE_OF_PARTIAL)); + #[cfg(feature = "parallel")] + let iter = missing_idxs + .par_chunks(missing_per_partial) + .zip(work.par_chunks_exact_mut(DEGREE_OF_PARTIAL)); + // Insert all generated partial polynomials at degree_of_partial intervals in work vector + iter.for_each(|(missing_idxs, work)| { + let partial_coeffs = self + .do_zero_poly_mul_partial(missing_idxs, domain_stride) + .expect("`missing_idxs` is guaranteed to not be empty; qed"); + + let partial_coeffs = pad_poly_coeffs(partial_coeffs, DEGREE_OF_PARTIAL).expect( + "`partial.coeffs.len()` (same as `missing_idxs.len() + 1`) is \ + guaranteed to be at most `degree_of_partial`; qed", ); - partial_lens.push(degree_of_partial); - - offset += missing_per_partial; - out_offset += degree_of_partial; - } + work[..DEGREE_OF_PARTIAL].copy_from_slice(&partial_coeffs); + }); + // Adjust last length to match its actual length partial_lens[partial_count - 1] = - 1 + missing_indices.len() - (partial_count - 1) * missing_per_partial; + 1 + missing_idxs.len() - (partial_count - 1) * missing_per_partial; - let reduction_factor = 4; + // Reduce all vectors into one by reducing them w/ varying size multiplications while partial_count > 1 { - let reduced_count = 1 + (partial_count - 1) / reduction_factor; + let reduced_count = 1 + (partial_count - 1) / REDUCTION_FACTOR; let partial_size = next_pow_of_2(partial_lens[0]); + // Step over polynomial space and produce larger multiplied polynomials for i in 0..reduced_count { - let start = i * reduction_factor; - let out_end = min((start + reduction_factor) * partial_size, domain_ceiling); - let reduced_len = min(out_end - start * partial_size, length); - let partials_num = min(reduction_factor, partial_count - start); - - let mut partial_vec = Vec::new(); - for j in 0..partials_num { - let k = (start + j) * partial_size; - partial_vec.push(PolyData { - coeffs: work[k..(k + partial_lens[i + j])].to_vec(), - }); + let start = i * REDUCTION_FACTOR; + let out_end = min((start + REDUCTION_FACTOR) * partial_size, domain_ceiling); + let reduced_len = min(out_end - start * partial_size, domain_size); + let partials_num = min(REDUCTION_FACTOR, partial_count - start); + + // Calculate partial views from lens and offsets + // Also update offsets to match current iteration + let partial_offset = start * partial_size; + let mut partial_coeffs = SmallVec::new(); + for (partial_offset, partial_len) in (partial_offset..) + .step_by(partial_size) + .zip(partial_lens.iter().skip(i).copied()) + .take(partials_num) + { + // We know the capacity required in `reduce_partials()` call below to avoid + // re-allocation + let mut coeffs = SmallVec::with_capacity(reduced_len); + coeffs.extend_from_slice(&work[partial_offset..][..partial_len]); + partial_coeffs.push(coeffs); } if partials_num > 1 { - let mut reduced_poly = self.reduce_partials(reduced_len, &partial_vec)?; - partial_lens[i] = reduced_poly.coeffs.len(); - reduced_poly.coeffs = pad_poly(&reduced_poly, partial_size * partials_num)?; - work.splice( - (start * partial_size) - ..(start * partial_size + reduced_poly.coeffs.len()), - reduced_poly.coeffs, - ); + let mut reduced_coeffs = + self.reduce_partials(reduced_len, partial_coeffs)?; + // Update partial length to match its length after reduction + partial_lens[i] = reduced_coeffs.len(); + reduced_coeffs = + pad_poly_coeffs(reduced_coeffs, partial_size * partials_num)?; + work[partial_offset..][..reduced_coeffs.len()] + .copy_from_slice(&reduced_coeffs); } else { + // Instead of keeping track of remaining polynomials, reuse i'th partial for start'th one partial_lens[i] = partial_lens[start]; } } + // Number of steps done equals the number of polynomials that we still need to reduce together partial_count = reduced_count; } zero_poly = PolyData { coeffs: work }; } - match zero_poly.coeffs.len().cmp(&length) { - Ordering::Less => zero_poly.coeffs = pad_poly(&zero_poly, length)?, - Ordering::Greater => zero_poly.coeffs = zero_poly.coeffs[..length].to_vec(), + // Pad resulting poly to expected + match zero_poly.coeffs.len().cmp(&domain_size) { + Ordering::Less => { + zero_poly.coeffs = pad_poly(zero_poly.coeffs, domain_size)?; + } Ordering::Equal => {} + Ordering::Greater => { + zero_poly.coeffs.truncate(domain_size); + } } + // Evaluate calculated poly zero_eval = self.fft_fr(&zero_poly.coeffs, false)?; + Ok((zero_eval, zero_poly)) } } diff --git a/arkworks3/tests/bls12_381.rs b/arkworks3/tests/bls12_381.rs index 4c5031cef..6f9babc22 100644 --- a/arkworks3/tests/bls12_381.rs +++ b/arkworks3/tests/bls12_381.rs @@ -47,7 +47,6 @@ mod tests { } #[test] - #[should_panic] pub fn fr_div_by_zero_() { fr_div_by_zero::(); } diff --git a/arkworks3/tests/consts.rs b/arkworks3/tests/consts.rs index 20d237682..880e69d71 100644 --- a/arkworks3/tests/consts.rs +++ b/arkworks3/tests/consts.rs @@ -6,12 +6,12 @@ mod tests { }; use rust_kzg_arkworks3::consts::SCALE2_ROOT_OF_UNITY; use rust_kzg_arkworks3::kzg_proofs::expand_root_of_unity; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; #[test] fn roots_of_unity_out_of_bounds_fails_() { - roots_of_unity_out_of_bounds_fails::(); + roots_of_unity_out_of_bounds_fails::(); } #[test] @@ -26,7 +26,7 @@ mod tests { #[test] fn new_fft_settings_is_plausible_() { - new_fft_settings_is_plausible::(); + new_fft_settings_is_plausible::(); } #[test] diff --git a/arkworks3/tests/das.rs b/arkworks3/tests/das.rs index f4576684c..714a383d1 100644 --- a/arkworks3/tests/das.rs +++ b/arkworks3/tests/das.rs @@ -1,16 +1,16 @@ #[cfg(test)] mod tests { use kzg_bench::tests::das::{das_extension_test_known, das_extension_test_random}; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; #[test] fn das_extension_test_known_() { - das_extension_test_known::(); + das_extension_test_known::(); } #[test] fn das_extension_test_random_() { - das_extension_test_random::(); + das_extension_test_random::(); } } diff --git a/arkworks3/tests/eip_4844.rs b/arkworks3/tests/eip_4844.rs index 7c6e27b9b..41d4dbbac 100644 --- a/arkworks3/tests/eip_4844.rs +++ b/arkworks3/tests/eip_4844.rs @@ -22,7 +22,9 @@ mod tests { }; use rust_kzg_arkworks3::consts::SCALE2_ROOT_OF_UNITY; use rust_kzg_arkworks3::eip_4844::load_trusted_setup_filename_rust; - use rust_kzg_arkworks3::kzg_proofs::{expand_root_of_unity, FFTSettings, KZGSettings}; + use rust_kzg_arkworks3::kzg_proofs::{ + expand_root_of_unity, LFFTSettings, LKZGSettings as KZGSettings, + }; use rust_kzg_arkworks3::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2}; use rust_kzg_arkworks3::utils::PolyData; @@ -43,7 +45,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -60,7 +62,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -79,7 +81,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -101,7 +103,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -123,7 +125,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -145,7 +147,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -165,7 +167,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -185,7 +187,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -205,7 +207,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -225,7 +227,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -243,7 +245,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -261,7 +263,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -279,7 +281,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -293,7 +295,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, @@ -311,7 +313,7 @@ mod tests { ArkG1, ArkG2, PolyData, - FFTSettings, + LFFTSettings, KZGSettings, ArkFp, ArkG1Affine, diff --git a/arkworks3/tests/eip_7594.rs b/arkworks3/tests/eip_7594.rs new file mode 100644 index 000000000..c9f7a8d4a --- /dev/null +++ b/arkworks3/tests/eip_7594.rs @@ -0,0 +1,40 @@ +#[cfg(test)] +mod tests { + use kzg::eip_4844::bytes_to_blob; + use kzg_bench::tests::eip_7594::{ + test_vectors_compute_cells_and_kzg_proofs, test_vectors_recover_cells_and_kzg_proofs, + test_vectors_verify_cell_kzg_proof_batch, + }; + use rust_kzg_arkworks3::{ + 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, + }, + }; + + #[test] + pub fn test_vectors_compute_cells_and_kzg_proofs_() { + 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] + 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, + ); + } +} diff --git a/arkworks3/tests/fft_fr.rs b/arkworks3/tests/fft_fr.rs index 0c778d71e..ea621901b 100644 --- a/arkworks3/tests/fft_fr.rs +++ b/arkworks3/tests/fft_fr.rs @@ -4,26 +4,26 @@ mod batch_adder; mod tests { use kzg_bench::tests::fft_fr::{compare_sft_fft, inverse_fft, roundtrip_fft, stride_fft}; use rust_kzg_arkworks3::fft::{fft_fr_fast, fft_fr_slow}; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; #[test] fn compare_sft_fft_() { - compare_sft_fft::(&fft_fr_slow, &fft_fr_fast); + compare_sft_fft::(&fft_fr_slow, &fft_fr_fast); } #[test] fn roundtrip_fft_() { - roundtrip_fft::(); + roundtrip_fft::(); } #[test] fn inverse_fft_() { - inverse_fft::(); + inverse_fft::(); } #[test] fn stride_fft_() { - stride_fft::(); + stride_fft::(); } } diff --git a/arkworks3/tests/fft_g1.rs b/arkworks3/tests/fft_g1.rs index 89e15c590..7ffb35b3a 100644 --- a/arkworks3/tests/fft_g1.rs +++ b/arkworks3/tests/fft_g1.rs @@ -2,7 +2,7 @@ mod tests { use kzg_bench::tests::fft_g1::{compare_sft_fft, roundtrip_fft, stride_fft}; use rust_kzg_arkworks3::fft_g1::{fft_g1_fast, fft_g1_slow, make_data}; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::{ArkFr, ArkG1}; #[test] diff --git a/arkworks3/tests/fk20_proofs.rs b/arkworks3/tests/fk20_proofs.rs index c9415ab3f..c4dbf3056 100644 --- a/arkworks3/tests/fk20_proofs.rs +++ b/arkworks3/tests/fk20_proofs.rs @@ -3,11 +3,14 @@ mod tests { use kzg_bench::tests::fk20_proofs::*; use rust_kzg_arkworks3::fk20_proofs::{KzgFK20MultiSettings, KzgFK20SingleSettings}; - use rust_kzg_arkworks3::kzg_proofs::{generate_trusted_setup, FFTSettings, KZGSettings}; + use rust_kzg_arkworks3::kzg_proofs::{ + generate_trusted_setup, LFFTSettings as FFTSettings, LKZGSettings as KZGSettings, + }; use rust_kzg_arkworks3::kzg_types::{ArkFp, ArkFr as BlstFr, ArkG1Affine}; use rust_kzg_arkworks3::kzg_types::{ArkG1, ArkG2}; use rust_kzg_arkworks3::utils::PolyData; + #[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::< @@ -23,6 +26,7 @@ mod tests { >(&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::< @@ -38,6 +42,7 @@ mod tests { >(&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::< @@ -53,6 +58,7 @@ mod tests { >(&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::< @@ -68,6 +74,7 @@ mod tests { >(&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::< @@ -83,6 +90,7 @@ mod tests { >(&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::< diff --git a/arkworks3/tests/kzg_proofs.rs b/arkworks3/tests/kzg_proofs.rs index a31e5ce95..af721f0bd 100644 --- a/arkworks3/tests/kzg_proofs.rs +++ b/arkworks3/tests/kzg_proofs.rs @@ -3,16 +3,21 @@ mod tests { use kzg_bench::tests::kzg_proofs::{ commit_to_nil_poly, commit_to_too_long_poly_returns_err, proof_multi, proof_single, }; - use rust_kzg_arkworks3::kzg_proofs::{generate_trusted_setup, FFTSettings, KZGSettings}; + use rust_kzg_arkworks3::kzg_proofs::{ + generate_trusted_setup, LFFTSettings as FFTSettings, LKZGSettings as KZGSettings, + }; use rust_kzg_arkworks3::kzg_types::{ArkFp, ArkFr, ArkG1, ArkG1Affine, ArkG2}; use rust_kzg_arkworks3::utils::PolyData; + #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn proof_single_() { proof_single::( &generate_trusted_setup, ); } + + #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn commit_to_nil_poly_() { commit_to_nil_poly::< @@ -26,6 +31,8 @@ mod tests { ArkG1Affine, >(&generate_trusted_setup); } + + #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn commit_to_too_long_poly_() { commit_to_too_long_poly_returns_err::< @@ -40,6 +47,7 @@ mod tests { >(&generate_trusted_setup); } + #[ignore = "KZG settings loading doesn't support trusted setup sizes other than FIELD_ELEMENTS_PER_BLOB (4096 points)"] #[test] fn proof_multi_() { proof_multi::( diff --git a/arkworks3/tests/poly.rs b/arkworks3/tests/poly.rs index 4460ffdf1..5c796f1a6 100644 --- a/arkworks3/tests/poly.rs +++ b/arkworks3/tests/poly.rs @@ -6,7 +6,7 @@ mod tests { poly_inverse_simple_0, poly_inverse_simple_1, poly_mul_direct_test, poly_mul_fft_test, poly_mul_random, poly_test_div, }; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; use rust_kzg_arkworks3::utils::PolyData; @@ -46,7 +46,6 @@ mod tests { } #[test] - #[should_panic] fn poly_div_by_zero_() { poly_div_by_zero::(); } diff --git a/arkworks3/tests/recover.rs b/arkworks3/tests/recover.rs index 9f53d88a3..1a552cf9e 100644 --- a/arkworks3/tests/recover.rs +++ b/arkworks3/tests/recover.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod recover_tests { use kzg_bench::tests::recover::*; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr as Fr; use rust_kzg_arkworks3::utils::PolyData; diff --git a/arkworks3/tests/zero_poly.rs b/arkworks3/tests/zero_poly.rs index a28463325..9cab5c154 100644 --- a/arkworks3/tests/zero_poly.rs +++ b/arkworks3/tests/zero_poly.rs @@ -4,7 +4,7 @@ mod tests { check_test_data, reduce_partials_random, test_reduce_partials, zero_poly_252, zero_poly_all_but_one, zero_poly_known, zero_poly_random, }; - use rust_kzg_arkworks3::kzg_proofs::FFTSettings; + use rust_kzg_arkworks3::kzg_proofs::LFFTSettings as FFTSettings; use rust_kzg_arkworks3::kzg_types::ArkFr; use rust_kzg_arkworks3::utils::PolyData;