From a2e30150373360a862796885a9befaaaae7eebab Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Wed, 8 May 2024 10:06:53 +0200 Subject: [PATCH] Reuse evaluations when computing proofs (#647) * Reuse precomputed evaluations instead of evaluation polynomial for each proof * kzgrs benchmarks * Clippy happy --- nomos-da/kzgrs-backend/src/encoder.rs | 44 ++++++++----- nomos-da/kzgrs-backend/src/verifier.rs | 12 +++- nomos-da/kzgrs/Cargo.toml | 9 ++- nomos-da/kzgrs/benches/kzg.rs | 90 ++++++++++++++++++++++++++ nomos-da/kzgrs/src/kzg.rs | 12 ++-- 5 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 nomos-da/kzgrs/benches/kzg.rs diff --git a/nomos-da/kzgrs-backend/src/encoder.rs b/nomos-da/kzgrs-backend/src/encoder.rs index 37b17a57..a9be81ea 100644 --- a/nomos-da/kzgrs-backend/src/encoder.rs +++ b/nomos-da/kzgrs-backend/src/encoder.rs @@ -94,13 +94,15 @@ impl DaEncoder { fn compute_rows_proofs( polynomials: &[Polynomial], + evals: &[Evaluations], proof_count: usize, ) -> Result>, KzgRsError> { polynomials .iter() - .map(|poly| { + .zip(evals) + .map(|(poly, eval)| { (0..proof_count) - .map(|i| generate_element_proof(i, poly, &GLOBAL_PARAMETERS, *DOMAIN)) + .map(|i| generate_element_proof(i, poly, eval, &GLOBAL_PARAMETERS, *DOMAIN)) .collect() }) .collect() @@ -136,10 +138,11 @@ impl DaEncoder { fn compute_aggregated_column_proofs( polynomial: &Polynomial, + evals: &Evaluations, proof_count: usize, ) -> Result, KzgRsError> { (0..proof_count) - .map(|i| generate_element_proof(i, polynomial, &GLOBAL_PARAMETERS, *DOMAIN)) + .map(|i| generate_element_proof(i, polynomial, evals, &GLOBAL_PARAMETERS, *DOMAIN)) .collect() } @@ -164,18 +167,23 @@ impl DaEncoder { Self::compute_kzg_row_commitments(&chunked_data)? .into_iter() .unzip(); - let extended_data = - Self::evals_to_chunk_matrix(Self::rs_encode_rows(&row_polynomials).as_ref()); + let encoded_evaluations = Self::rs_encode_rows(&row_polynomials); + let extended_data = Self::evals_to_chunk_matrix(&encoded_evaluations); let row_polynomials: Vec<_> = row_polynomials.into_iter().map(|(_, p)| p).collect(); - let rows_proofs = Self::compute_rows_proofs(&row_polynomials, self.params.column_count)?; + let rows_proofs = Self::compute_rows_proofs( + &row_polynomials, + &encoded_evaluations, + self.params.column_count, + )?; let (_column_polynomials, column_commitments): (Vec<_>, Vec<_>) = Self::compute_kzg_column_commitments(&extended_data)? .into_iter() .unzip(); - let ((_aggregated_evals, aggregated_polynomial), aggregated_column_commitment) = + let ((aggregated_evals, aggregated_polynomial), aggregated_column_commitment) = Self::compute_aggregated_column_commitment(&extended_data, &column_commitments)?; let aggregated_column_proofs = Self::compute_aggregated_column_proofs( &aggregated_polynomial, + &aggregated_evals, column_commitments.len(), )?; Ok(EncodedData { @@ -291,13 +299,18 @@ pub mod test { .unwrap() .into_iter() .unzip(); - let extended_rows = DaEncoder::rs_encode_rows(&poly_data); - let (_evals, polynomials): (Vec<_>, Vec<_>) = poly_data.into_iter().unzip(); - let extended_matrix = DaEncoder::evals_to_chunk_matrix(&extended_rows); + let extended_evaluations = DaEncoder::rs_encode_rows(&poly_data); + let (evals, polynomials): (Vec<_>, Vec<_>) = poly_data.into_iter().unzip(); + let extended_matrix = DaEncoder::evals_to_chunk_matrix(&extended_evaluations); let original_proofs = - DaEncoder::compute_rows_proofs(&polynomials, PARAMS.column_count.div(2)).unwrap(); - let extended_proofs = - DaEncoder::compute_rows_proofs(&polynomials, PARAMS.column_count).unwrap(); + DaEncoder::compute_rows_proofs(&polynomials, &evals, PARAMS.column_count.div(2)) + .unwrap(); + let extended_proofs = DaEncoder::compute_rows_proofs( + &polynomials, + &extended_evaluations, + PARAMS.column_count, + ) + .unwrap(); let checks = izip!(matrix.iter(), &commitments, &original_proofs); for (row, commitment, proofs) in checks { @@ -359,8 +372,9 @@ pub mod test { .unwrap() .into_iter() .unzip(); - let ((_, polynomial), _aggregated_commitment) = + let ((evals, polynomial), _aggregated_commitment) = DaEncoder::compute_aggregated_column_commitment(&matrix, &commitments).unwrap(); - DaEncoder::compute_aggregated_column_proofs(&polynomial, commitments.len()).unwrap(); + DaEncoder::compute_aggregated_column_proofs(&polynomial, &evals, commitments.len()) + .unwrap(); } } diff --git a/nomos-da/kzgrs-backend/src/verifier.rs b/nomos-da/kzgrs-backend/src/verifier.rs index f3c5813a..abd264ac 100644 --- a/nomos-da/kzgrs-backend/src/verifier.rs +++ b/nomos-da/kzgrs-backend/src/verifier.rs @@ -179,7 +179,7 @@ mod test { bytes_to_polynomial::(column.as_bytes().as_slice(), *DOMAIN) .unwrap(); let column_commitment = commit_polynomial(&column_poly, &GLOBAL_PARAMETERS).unwrap(); - let (_, aggregated_poly) = bytes_to_polynomial::< + let (aggregated_evals, aggregated_poly) = bytes_to_polynomial::< { DaEncoderParams::MAX_BLS12_381_ENCODING_CHUNK_SIZE }, >( hash_column_and_commitment::<{ DaEncoderParams::MAX_BLS12_381_ENCODING_CHUNK_SIZE }>( @@ -192,8 +192,14 @@ mod test { .unwrap(); let aggregated_commitment = commit_polynomial(&aggregated_poly, &GLOBAL_PARAMETERS).unwrap(); - let column_proof = - generate_element_proof(0, &aggregated_poly, &GLOBAL_PARAMETERS, *DOMAIN).unwrap(); + let column_proof = generate_element_proof( + 0, + &aggregated_poly, + &aggregated_evals, + &GLOBAL_PARAMETERS, + *DOMAIN, + ) + .unwrap(); assert!(DaVerifier::verify_column( &column, &column_commitment, diff --git a/nomos-da/kzgrs/Cargo.toml b/nomos-da/kzgrs/Cargo.toml index cbdafe9c..e3c0dffb 100644 --- a/nomos-da/kzgrs/Cargo.toml +++ b/nomos-da/kzgrs/Cargo.toml @@ -17,4 +17,11 @@ ark-serialize = { version = "0.4" } num-bigint = "0.4.4" thiserror = "1.0.58" num-traits = "0.2.18" -rand = "0.8.5" \ No newline at end of file +rand = "0.8.5" + +[dev-dependencies] +divan = "0.1" + +[[bench]] +name = "kzg" +harness = false \ No newline at end of file diff --git a/nomos-da/kzgrs/benches/kzg.rs b/nomos-da/kzgrs/benches/kzg.rs new file mode 100644 index 00000000..e0fc9046 --- /dev/null +++ b/nomos-da/kzgrs/benches/kzg.rs @@ -0,0 +1,90 @@ +use ark_bls12_381::{Bls12_381, Fr}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::{EvaluationDomain, GeneralEvaluationDomain}; +use ark_poly_commit::kzg10::{UniversalParams, KZG10}; +use divan::counter::ItemsCount; +use divan::{black_box, counter::BytesCount, AllocProfiler, Bencher}; +use once_cell::sync::Lazy; +use rand::RngCore; + +use kzgrs::{common::bytes_to_polynomial_unchecked, kzg::*}; + +fn main() { + divan::main() +} + +#[global_allocator] +static ALLOC: AllocProfiler = AllocProfiler::system(); + +static GLOBAL_PARAMETERS: Lazy> = Lazy::new(|| { + let mut rng = rand::thread_rng(); + KZG10::>::setup(1024, true, &mut rng).unwrap() +}); + +static DOMAIN: Lazy> = + Lazy::new(|| GeneralEvaluationDomain::new(1024).unwrap()); + +fn rand_data_elements(elements_count: usize, chunk_size: usize) -> Vec { + let mut buff = vec![0u8; elements_count * chunk_size]; + rand::thread_rng().fill_bytes(&mut buff); + buff +} + +const CHUNK_SIZE: usize = 31; + +#[allow(non_snake_case)] +#[divan::bench(args = [10, 100, 1000])] +fn commit_polynomial_with_element_count(bencher: Bencher, element_count: usize) { + bencher + .with_inputs(|| { + let data = rand_data_elements(element_count, CHUNK_SIZE); + bytes_to_polynomial_unchecked::(&data, *DOMAIN) + }) + .input_counter(move |(_evals, _poly)| BytesCount::new(element_count * CHUNK_SIZE)) + .bench_refs(|(_evals, poly)| black_box(commit_polynomial(poly, &GLOBAL_PARAMETERS))); +} + +#[allow(non_snake_case)] +#[divan::bench] +fn compute_single_proof(bencher: Bencher) { + bencher + .with_inputs(|| { + let data = rand_data_elements(10, CHUNK_SIZE); + bytes_to_polynomial_unchecked::(&data, *DOMAIN) + }) + .input_counter(|_| ItemsCount::new(1usize)) + .bench_refs(|(evals, poly)| { + black_box(generate_element_proof( + 7, + poly, + evals, + &GLOBAL_PARAMETERS, + *DOMAIN, + )) + }); +} + +#[allow(non_snake_case)] +#[divan::bench] +fn verify_single_proof(bencher: Bencher) { + bencher + .with_inputs(|| { + let data = rand_data_elements(10, CHUNK_SIZE); + let (eval, poly) = bytes_to_polynomial_unchecked::(&data, *DOMAIN); + let commitment = commit_polynomial(&poly, &GLOBAL_PARAMETERS).unwrap(); + let proof = + generate_element_proof(0, &poly, &eval, &GLOBAL_PARAMETERS, *DOMAIN).unwrap(); + (0usize, eval.evals[0], commitment, proof) + }) + .input_counter(|_| ItemsCount::new(1usize)) + .bench_refs(|(index, elemnent, commitment, proof)| { + black_box(verify_element_proof( + index.clone(), + elemnent, + commitment, + proof, + *DOMAIN, + &GLOBAL_PARAMETERS, + )) + }); +} diff --git a/nomos-da/kzgrs/src/kzg.rs b/nomos-da/kzgrs/src/kzg.rs index 2fefc0d8..8ff0230e 100644 --- a/nomos-da/kzgrs/src/kzg.rs +++ b/nomos-da/kzgrs/src/kzg.rs @@ -1,8 +1,9 @@ use crate::common::KzgRsError; +use crate::Evaluations; use ark_bls12_381::{Bls12_381, Fr}; use ark_ec::pairing::Pairing; use ark_poly::univariate::DensePolynomial; -use ark_poly::{DenseUVPolynomial, EvaluationDomain, GeneralEvaluationDomain, Polynomial}; +use ark_poly::{DenseUVPolynomial, EvaluationDomain, GeneralEvaluationDomain}; use ark_poly_commit::kzg10::{Commitment, Powers, Proof, UniversalParams, KZG10}; use num_traits::One; use std::borrow::Cow; @@ -27,11 +28,14 @@ pub fn commit_polynomial( pub fn generate_element_proof( element_index: usize, polynomial: &DensePolynomial, + evaluations: &Evaluations, global_parameters: &UniversalParams, domain: GeneralEvaluationDomain, ) -> Result, KzgRsError> { let u = domain.element(element_index); - let v = polynomial.evaluate(&u); + // Instead of evaluating over the polynomial, we can reuse the evaluation points from the rs encoding + // let v = polynomial.evaluate(&u); + let v = evaluations.evals[element_index]; let f_x_v = polynomial + &DensePolynomial::::from_coefficients_vec(vec![-v]); let x_u = DensePolynomial::::from_coefficients_vec(vec![-u, Fr::one()]); let witness_polynomial: DensePolynomial<_> = &f_x_v / &x_u; @@ -100,10 +104,10 @@ mod test { let mut rng = thread_rng(); bytes.try_fill(&mut rng).unwrap(); let evaluations = bytes_to_evaluations::<31>(&bytes, *DOMAIN).evals; - let (_, poly) = bytes_to_polynomial::<31>(&bytes, *DOMAIN).unwrap(); + let (eval, poly) = bytes_to_polynomial::<31>(&bytes, *DOMAIN).unwrap(); let commitment = commit_polynomial(&poly, &GLOBAL_PARAMETERS).unwrap(); let proofs: Vec<_> = (0..10) - .map(|i| generate_element_proof(i, &poly, &GLOBAL_PARAMETERS, *DOMAIN).unwrap()) + .map(|i| generate_element_proof(i, &poly, &eval, &GLOBAL_PARAMETERS, *DOMAIN).unwrap()) .collect(); for (i, (element, proof)) in evaluations.iter().zip(proofs.iter()).enumerate() { // verifying works