Reuse evaluations when computing proofs (#647)

* Reuse precomputed evaluations instead of evaluation polynomial for each proof

* kzgrs benchmarks

* Clippy happy
This commit is contained in:
Daniel Sanchez 2024-05-08 10:06:53 +02:00 committed by Gusto
parent cbce61ed9d
commit a2e3015037
5 changed files with 144 additions and 23 deletions

View File

@ -94,13 +94,15 @@ impl DaEncoder {
fn compute_rows_proofs(
polynomials: &[Polynomial],
evals: &[Evaluations],
proof_count: usize,
) -> Result<Vec<Vec<Proof>>, 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<Vec<Proof>, 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();
}
}

View File

@ -179,7 +179,7 @@ mod test {
bytes_to_polynomial::<BYTES_PER_FIELD_ELEMENT>(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,

View File

@ -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"
rand = "0.8.5"
[dev-dependencies]
divan = "0.1"
[[bench]]
name = "kzg"
harness = false

View File

@ -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<UniversalParams<Bls12_381>> = Lazy::new(|| {
let mut rng = rand::thread_rng();
KZG10::<Bls12_381, DensePolynomial<Fr>>::setup(1024, true, &mut rng).unwrap()
});
static DOMAIN: Lazy<GeneralEvaluationDomain<Fr>> =
Lazy::new(|| GeneralEvaluationDomain::new(1024).unwrap());
fn rand_data_elements(elements_count: usize, chunk_size: usize) -> Vec<u8> {
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::<CHUNK_SIZE>(&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::<CHUNK_SIZE>(&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::<CHUNK_SIZE>(&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,
))
});
}

View File

@ -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<Fr>,
evaluations: &Evaluations,
global_parameters: &UniversalParams<Bls12_381>,
domain: GeneralEvaluationDomain<Fr>,
) -> Result<Proof<Bls12_381>, 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::<Fr>::from_coefficients_vec(vec![-v]);
let x_u = DensePolynomial::<Fr>::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