use std::iter; use ff::Field; use group::Curve; use rand_core::RngCore; #[cfg(feature = "multicore")] use maybe_rayon::{current_num_threads, prelude::*}; #[cfg(feature = "multicore")] use rand_chacha::ChaCha20Rng; #[cfg(feature = "multicore")] use rand_core::SeedableRng; use super::Argument; use crate::{ arithmetic::{eval_polynomial, CurveAffine}, plonk::{ChallengeX, ChallengeY, Error}, poly::{ self, commitment::{Blind, Params}, multiopen::ProverQuery, Coeff, EvaluationDomain, ExtendedLagrangeCoeff, Polynomial, }, transcript::{EncodedChallenge, TranscriptWrite}, }; pub(in crate::plonk) struct Committed { random_poly: Polynomial, random_blind: Blind, } pub(in crate::plonk) struct Constructed { h_pieces: Vec>, h_blinds: Vec>, committed: Committed, } pub(in crate::plonk) struct Evaluated { h_poly: Polynomial, h_blind: Blind, committed: Committed, } impl Argument { pub(in crate::plonk) fn commit, R: RngCore, T: TranscriptWrite>( params: &Params, domain: &EvaluationDomain, mut rng: R, transcript: &mut T, ) -> Result, Error> { // Sample a random polynomial of degree n - 1 #[cfg(feature = "multicore")] let random_poly = { let n_threads = current_num_threads(); let n = 1usize << domain.k as usize; let n_chunks = n_threads + if n % n_threads != 0 { 1 } else { 0 }; let mut rand_vec = vec![C::Scalar::ZERO; n]; let mut thread_seeds: Vec = (0..n_chunks) .into_iter() .map(|_| { let mut seed = [0u8; 32]; rng.fill_bytes(&mut seed); ChaCha20Rng::from_seed(seed) }) .collect(); thread_seeds .par_iter_mut() .zip_eq(rand_vec.par_chunks_mut(n / n_threads)) .for_each(|(mut rng, chunk)| { chunk .iter_mut() .for_each(|v| *v = C::Scalar::random(&mut rng)) }); domain.coeff_from_vec(rand_vec) }; #[cfg(not(feature = "multicore"))] let random_poly = { let mut random_poly = domain.empty_coeff(); for coeff in random_poly.iter_mut() { *coeff = C::Scalar::random(&mut rng); } random_poly }; // Sample a random blinding factor let random_blind = Blind(C::Scalar::random(rng)); // Commit let c = params.commit(&random_poly, random_blind).to_affine(); transcript.write_point(c)?; Ok(Committed { random_poly, random_blind, }) } } impl Committed { #[allow(clippy::too_many_arguments)] pub(in crate::plonk) fn construct< E: EncodedChallenge, Ev: Copy + Send + Sync, R: RngCore, T: TranscriptWrite, >( self, params: &Params, domain: &EvaluationDomain, evaluator: poly::Evaluator, expressions: impl Iterator>, y: ChallengeY, mut rng: R, transcript: &mut T, ) -> Result, Error> { // Evaluate the h(X) polynomial's constraint system expressions for the constraints provided let h_poly = poly::Ast::distribute_powers(expressions, *y); // Fold the gates together with the y challenge let h_poly = evaluator.evaluate(&h_poly, domain); // Evaluate the h(X) polynomial // Divide by t(X) = X^{params.n} - 1. let h_poly = domain.divide_by_vanishing_poly(h_poly); // Obtain final h(X) polynomial let h_poly = domain.extended_to_coeff(h_poly); // Split h(X) up into pieces let h_pieces = h_poly .chunks_exact(params.n as usize) .map(|v| domain.coeff_from_vec(v.to_vec())) .collect::>(); drop(h_poly); let h_blinds: Vec<_> = h_pieces .iter() .map(|_| Blind(C::Scalar::random(&mut rng))) .collect(); // Compute commitments to each h(X) piece let h_commitments_projective: Vec<_> = h_pieces .iter() .zip(h_blinds.iter()) .map(|(h_piece, blind)| params.commit(h_piece, *blind)) .collect(); let mut h_commitments = vec![C::identity(); h_commitments_projective.len()]; C::Curve::batch_normalize(&h_commitments_projective, &mut h_commitments); let h_commitments = h_commitments; // Hash each h(X) piece for c in h_commitments.iter() { transcript.write_point(*c)?; } Ok(Constructed { h_pieces, h_blinds, committed: self, }) } } impl Constructed { pub(in crate::plonk) fn evaluate, T: TranscriptWrite>( self, x: ChallengeX, xn: C::Scalar, domain: &EvaluationDomain, transcript: &mut T, ) -> Result, Error> { let h_poly = self .h_pieces .iter() .rev() .fold(domain.empty_coeff(), |acc, eval| acc * xn + eval); let h_blind = self .h_blinds .iter() .rev() .fold(Blind(C::Scalar::ZERO), |acc, eval| acc * Blind(xn) + *eval); let random_eval = eval_polynomial(&self.committed.random_poly, *x); transcript.write_scalar(random_eval)?; Ok(Evaluated { h_poly, h_blind, committed: self.committed, }) } } impl Evaluated { pub(in crate::plonk) fn open( &self, x: ChallengeX, ) -> impl Iterator> + Clone { iter::empty() .chain(Some(ProverQuery { point: *x, poly: &self.h_poly, blind: self.h_blind, })) .chain(Some(ProverQuery { point: *x, poly: &self.committed.random_poly, blind: self.committed.random_blind, })) } }