diff --git a/pasta-tree/src/circuit_fat/prover.rs b/pasta-tree/src/circuit_fat/prover.rs index a5aa88a..3e5ad95 100644 --- a/pasta-tree/src/circuit_fat/prover.rs +++ b/pasta-tree/src/circuit_fat/prover.rs @@ -7,6 +7,7 @@ use ark_ec::CurveGroup; use ark_ff::One; use ark_ff::{FftField, PrimeField, Zero}; use ark_poly::Evaluations; +use ark_poly::Polynomial; use ark_poly::univariate::DensePolynomial; use ark_std::{vec, vec::Vec}; use w3f_pcs::pcs::commitment::WrappedAffine; @@ -166,6 +167,14 @@ impl> type Evaluations = ProofEvals; type Instance = G; + fn quotient(&self, alphas: &[C::ScalarField]) -> Option>> { + let chunks = + >>::_quotient_chunks(self, alphas); + debug_assert_eq!(chunks.as_ref().unwrap().len(), 4); + debug_assert_eq!(chunks.as_ref().unwrap()[3].degree(), 0); + chunks + } + fn committed_columns) -> WrappedAffine>( &self, commit: Fun, diff --git a/pasta-tree/src/circuit_tall/prover.rs b/pasta-tree/src/circuit_tall/prover.rs index 4ac3b60..c5faca8 100644 --- a/pasta-tree/src/circuit_tall/prover.rs +++ b/pasta-tree/src/circuit_tall/prover.rs @@ -107,6 +107,14 @@ impl> type Evaluations = ProofEvals; type Instance = G; + fn quotient(&self, alphas: &[C::ScalarField]) -> Option>> { + let chunks = + >>::_quotient_chunks(self, alphas); + debug_assert_eq!(chunks.as_ref().unwrap().len(), 4); + debug_assert_eq!(chunks.as_ref().unwrap()[3].degree(), 0); + chunks + } + fn committed_columns) -> WrappedAffine>( &self, commit: Fun, diff --git a/pasta-tree/src/lib.rs b/pasta-tree/src/lib.rs index b9353a4..1c11f44 100644 --- a/pasta-tree/src/lib.rs +++ b/pasta-tree/src/lib.rs @@ -243,41 +243,61 @@ mod tests { } } - // cargo test test_bench_curve_tree --release --features="print-trace" -- --show-output - // cargo test test_bench_curve_tree --release --features="print-trace parallel" -- --show-output #[test] - #[ignore] - fn test_bench_curve_tree() { - let (log_n, h) = (8, 2); - println!("n = {}, height = {h}, FAT", 1 << log_n); + fn test_circuit_tall() { _test_proof::< ark_pallas::Projective, ark_vesta::Projective, - CircuitParamsFat, - CircuitParamsFat, - >(log_n, h); - println!(); + CircuitParamsTall, + CircuitParamsTall, + >(9, 2); + } - let (log_n, h) = (9, 2); - println!("n = {}, height = {h}, TALL", 1 << log_n); + #[test] + fn test_circuit_fat() { _test_proof::< ark_pallas::Projective, ark_vesta::Projective, - CircuitParamsTall, - CircuitParamsTall, - >(log_n, h); - println!(); + CircuitParamsFat, + CircuitParamsFat, + >(8, 2); + } - let (log_n, h) = (10, 2); - println!("n = {}, height = {h}, TALL", 1 << log_n); + // cargo test test_bench_curve_tree --release --features="print-trace" -- --show-output --ignored + // cargo test test_bench_curve_tree --release --features="print-trace parallel" -- --show-output --ignored + #[test] + #[ignore] + fn test_bench_curve_tree() { + let (log_n, h) = (8, 2); + println!("n = {}, height = {h}, FAT", 1 << log_n); _test_proof::< ark_pallas::Projective, ark_vesta::Projective, - CircuitParamsTall, - CircuitParamsTall, + CircuitParamsFat, + CircuitParamsFat, >(log_n, h); println!(); + // let (log_n, h) = (9, 2); + // println!("n = {}, height = {h}, TALL", 1 << log_n); + // _test_proof::< + // ark_pallas::Projective, + // ark_vesta::Projective, + // CircuitParamsTall, + // CircuitParamsTall, + // >(log_n, h); + // println!(); + + // let (log_n, h) = (10, 2); + // println!("n = {}, height = {h}, TALL", 1 << log_n); + // _test_proof::< + // ark_pallas::Projective, + // ark_vesta::Projective, + // CircuitParamsTall, + // CircuitParamsTall, + // >(log_n, h); + // println!(); + let (log_n, h) = (8, 4); println!("n = {}, height = {h}, FAT", 1 << log_n); _test_proof::< @@ -288,7 +308,7 @@ mod tests { >(log_n, h); println!(); - let (log_n, h) = (9, 4); + let (log_n, h) = (10, 4); println!("n = {}, height = {h}, TALL", 1 << log_n); _test_proof::< ark_pallas::Projective, diff --git a/pasta-tree/src/prover.rs b/pasta-tree/src/prover.rs index 1095d77..69fdae9 100644 --- a/pasta-tree/src/prover.rs +++ b/pasta-tree/src/prover.rs @@ -56,15 +56,22 @@ impl, P: CircuitParams< witness: Vec>, rng: &mut R, ) -> CycleSideProof { - let curve_name = &std::any::type_name::()[70..]; + let curve_name = &std::any::type_name::()[53..]; // println!("\n\nprover {curve_name}\nchildren={blinded_path:?}\n"); + let n_levels = witness.len(); // number of tree levels on this side + debug_assert_eq!(blinded_path.len(), n_levels); + let mut piop_proofs = Vec::with_capacity(n_levels); - debug_assert_eq!(blinded_path.len(), witness.len()); - let n_polys = P::VerifierCircuit::N_COLUMNS + 2; // plus the quotient and the linearization polys - let mut piop_proofs = Vec::with_capacity(witness.len()); - let mut polys = Vec::with_capacity(witness.len() * n_polys); - let mut coords = Vec::with_capacity(witness.len() * n_polys); - let mut bfs = Vec::with_capacity(witness.len() * n_polys); + // per tree level + let n_columns = P::VerifierCircuit::N_COLUMNS; + let n_to_commit = n_columns + 4; // plus the quotient chunks + let n_to_open = n_columns + 2; // plus the (folded) quotient (chunks) and the linearization polynomial + + // per side + let n_openings = n_levels * n_to_open; + let mut polys_to_open = Vec::with_capacity(n_openings); + let mut at_coords = Vec::with_capacity(n_openings); + let mut with_bfs = Vec::with_capacity(n_openings); let plonk_prover = PlonkProver::, _>::init( self.pcs_params.ck(), @@ -73,11 +80,10 @@ impl, P: CircuitParams< ); let t_commit_side = start_timer!(|| format!( - "Committing to {} polynomials at {curve_name}", - witness.len() * (n_polys - 1) + "Committing {n_levels} x {n_to_commit} polynomials to {curve_name}" )); for (level, blinded_node) in witness.into_iter().zip(blinded_path.into_iter()) { - let t_commit_level = start_timer!(|| format!("Committing to {} polynomials", n_polys)); + // let t_commit_level = start_timer!(|| format!("Committing {n_to_commit} polynomials")); let piop: P::ProverCircuit =

>::prover_circuit(&self.piop_params, level.clone()); let result = @@ -98,31 +104,29 @@ impl, P: CircuitParams< // polys_at_zeta[polys_at_zeta.len() - 1].evaluate(&zeta) // ); - coords.extend(vec![BTreeSet::from([zeta]); polys_at_zeta.len()]); - polys.extend(polys_at_zeta); - coords.extend(vec![ + at_coords.extend(vec![BTreeSet::from([zeta]); polys_at_zeta.len()]); + polys_to_open.extend(polys_at_zeta); + at_coords.extend(vec![ BTreeSet::from([zeta_omega]); polys_at_zeta_omega.len() ]); - polys.extend(polys_at_zeta_omega); - bfs.push(level.parent_bf); - bfs.resize(polys.len(), C::ScalarField::zero()); - end_timer!(t_commit_level); + polys_to_open.extend(polys_at_zeta_omega); + with_bfs.push(level.parent_bf); + with_bfs.resize(polys_to_open.len(), C::ScalarField::zero()); + // end_timer!(t_commit_level); } end_timer!(t_commit_side); - let max_degree = polys.iter().map(|p| p.degree()).max().unwrap(); let t_open = start_timer!(|| format!( - "Opening {} polynomials with maximal degree-{}", - polys.len(), - max_degree + "Opening {n_openings} polynomials, max_degree = {}", + polys_to_open.iter().map(|p| p.degree()).max().unwrap() )); let todo = Coeffs(C::ScalarField::rand(rng), C::ScalarField::rand(rng)); let pcs_proof = Shplonk::>::open_many_hiding( &self.pcs_params, - &polys, - &bfs, - &coords, + &polys_to_open, + &with_bfs, + &at_coords, &mut todo.clone(), rng, ); diff --git a/pasta-tree/src/verifier.rs b/pasta-tree/src/verifier.rs index 33419bd..7072530 100644 --- a/pasta-tree/src/verifier.rs +++ b/pasta-tree/src/verifier.rs @@ -48,21 +48,26 @@ impl, P: CircuitParams< parents: Vec, side_proof: CycleSideProof, ) -> bool { - // let mut s = std::any::type_name::(); - // s = &s[65..s.len()]; - // println!("\n\nverifier {s}\nchildren={children:?}\nparents={parents:?}\n"); + // let curve_name = &std::any::type_name::()[53..]; + // println!("\n\nverifier {curve_name}\nchildren={children:?}\nparents={parents:?}\n"); + + // number of tree levels on this side + let n_levels = side_proof.piop_proofs.len(); + // per tree level + let n_to_open = P::VerifierCircuit::N_COLUMNS + 2; // plus the (folded) quotient (chunks) and the linearization polynomial + // per side + let n_openings = n_levels * n_to_open; + + let mut polys_to_open = Vec::with_capacity(n_openings); + let mut at_coords = Vec::with_capacity(n_openings); + let mut to_values = Vec::with_capacity(n_openings); let plonk_verifier: PlonkVerifier, _> = PlonkVerifier::init( self.pcs_params.vk(), - &(), + &(), // TODO ArkTranscript::new(b"pasta-tree-level-proof"), ); - let n_polys = P::VerifierCircuit::N_COLUMNS + 2; // plus the quotient and the linearization polys - let mut polys = Vec::with_capacity(side_proof.piop_proofs.len() * n_polys); - let mut coords = Vec::with_capacity(side_proof.piop_proofs.len() * n_polys); - let mut vals = Vec::with_capacity(side_proof.piop_proofs.len() * n_polys); - //TODO: precompute let fixed_cols = self.commit_fixed_columns(); @@ -99,21 +104,21 @@ impl, P: CircuitParams< // vals_at_zeta[vals_at_zeta.len() - 1] // ); - coords.extend(vec![vec![zeta]; open_at_zeta.len()]); - polys.extend(open_at_zeta); - coords.extend(vec![vec![zeta_omega]; open_at_zeta_omega.len()]); - polys.extend(open_at_zeta_omega); - vals.extend(vals_at_zeta.into_iter().map(|v| vec![v])); - vals.extend(vals_at_zeta_omega.into_iter().map(|v| vec![v])); + at_coords.extend(vec![vec![zeta]; open_at_zeta.len()]); + polys_to_open.extend(open_at_zeta); + at_coords.extend(vec![vec![zeta_omega]; open_at_zeta_omega.len()]); + polys_to_open.extend(open_at_zeta_omega); + to_values.extend(vals_at_zeta.into_iter().map(|v| vec![v])); + to_values.extend(vals_at_zeta_omega.into_iter().map(|v| vec![v])); } let mut todo = side_proof.todo; let valid = Shplonk::>::verify_many( &self.pcs_params.vk(), - &polys, + &polys_to_open, side_proof.pcs_proof, - &coords, - &vals, + &at_coords, + &to_values, &mut todo, ); valid diff --git a/w3f-plonk-common/src/domain.rs b/w3f-plonk-common/src/domain.rs index b7858f6..0114e3b 100644 --- a/w3f-plonk-common/src/domain.rs +++ b/w3f-plonk-common/src/domain.rs @@ -205,6 +205,7 @@ pub struct EvaluatedDomain { pub l_first: F, pub l_last: F, pub vanishing_polynomial_inv: F, + pub z_n: F, // z^N } impl EvaluatedDomain { @@ -243,6 +244,7 @@ impl EvaluatedDomain { l_first, l_last, vanishing_polynomial_inv, + z_n, } } diff --git a/w3f-plonk-common/src/kzg_acc.rs b/w3f-plonk-common/src/kzg_acc.rs index 0b7bbeb..306cb4c 100644 --- a/w3f-plonk-common/src/kzg_acc.rs +++ b/w3f-plonk-common/src/kzg_acc.rs @@ -1,6 +1,6 @@ use crate::piop::VerifierPiop; use crate::verifier::Challenges; -use crate::{ColumnsCommited, ColumnsEvaluated, Proof}; +use crate::{q_chunking, ColumnsCommited, ColumnsEvaluated, Proof}; use ark_ec::pairing::Pairing; use ark_ec::{CurveGroup, VariableBaseMSM}; use ark_ff::{PrimeField, Zero}; @@ -93,8 +93,8 @@ impl KzgAccumulator { let zeta_omega = zeta * piop.domain_evaluated().omega(); let lin_comm = piop.lin_poly_commitment(&challenges.alphas); - // Openning at `z` - // TODO: try to get rid of the commitment wrapper in flonk + // Openning at `z`. Columns and the quotient are aggregated using `nu`s. + let mut r_nus = challenges.nus.iter().map(|nu| r * nu); self.acc_points.extend( piop.precommitted_columns() .iter() @@ -109,9 +109,19 @@ impl KzgAccumulator { .map(|c| c.0) .collect::>(), ); - self.acc_points.push(proof.quotient_commitment.clone().0); self.acc_scalars - .extend(challenges.nus.iter().map(|nu| *nu * r).collect::>()); // numbers should match here + .extend(r_nus.by_ref().take(Piop::N_COLUMNS)); + + // quotient (chunks) at `z` + let r_nu_last = r_nus.next().unwrap(); + let z_n = piop.domain_evaluated().z_n; + self.acc_points + .extend(proof.quotient_chunks.iter().map(|c| c.0)); + self.acc_scalars.extend( + q_chunking::chunk_coeffs(z_n) + .map(|c| r_nu_last * c) + .take(proof.quotient_chunks.len()), + ); self.acc_points.push(proof.agg_at_zeta_proof); self.acc_scalars.push(zeta * r); diff --git a/w3f-plonk-common/src/lib.rs b/w3f-plonk-common/src/lib.rs index 7f0f89d..8e22961 100644 --- a/w3f-plonk-common/src/lib.rs +++ b/w3f-plonk-common/src/lib.rs @@ -14,6 +14,7 @@ pub mod gadgets; pub mod kzg_acc; pub mod piop; pub mod prover; +pub mod q_chunking; pub mod test_helpers; pub mod transcript; pub mod verifier; @@ -103,7 +104,7 @@ where { pub column_commitments: Commitments, pub columns_at_zeta: Evaluations, - pub quotient_commitment: CS::C, + pub quotient_chunks: Vec, pub lin_at_zeta_omega: F, pub agg_at_zeta_proof: CS::Proof, pub lin_at_zeta_omega_proof: CS::Proof, @@ -120,7 +121,7 @@ where { pub column_commitments: Commitments, pub columns_at_zeta: Evaluations, - pub quotient_commitment: C, + pub quotient_chunks: Vec, pub lin_at_zeta_omega: F, } @@ -135,7 +136,7 @@ where PiopProof { column_commitments: self.column_commitments.clone(), columns_at_zeta: self.columns_at_zeta.clone(), - quotient_commitment: self.quotient_commitment.clone(), + quotient_chunks: self.quotient_chunks.clone(), lin_at_zeta_omega: self.lin_at_zeta_omega, } } diff --git a/w3f-plonk-common/src/piop.rs b/w3f-plonk-common/src/piop.rs index fd9ec12..c3bc3e0 100644 --- a/w3f-plonk-common/src/piop.rs +++ b/w3f-plonk-common/src/piop.rs @@ -1,13 +1,14 @@ +use crate::domain::{Domain, EvaluatedDomain}; +use crate::{q_chunking, ColumnsCommited, ColumnsEvaluated}; use ark_ff::{FftField, PrimeField}; use ark_poly::univariate::DensePolynomial; use ark_poly::Evaluations; +use ark_poly::Polynomial; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::vec; use ark_std::vec::Vec; use w3f_pcs::pcs::Commitment; -use crate::domain::{Domain, EvaluatedDomain}; -use crate::{ColumnsCommited, ColumnsEvaluated}; - pub trait ProverPiop> { const N_COLUMNS: usize; const N_CONSTRAINTS: usize; @@ -32,7 +33,26 @@ pub trait ProverPiop> { // Constraint polynomials in evaluation form. fn constraints(&self) -> Vec>; - fn compute_quotient(&self, alphas: &[F]) -> Option> { + fn _quotient_chunks(&self, alphas: &[F]) -> Option>> { + let q = Self::_compute_quotient(self, alphas); + q.map(|q| { + let _q_deg = q.degree(); + let q_chunks = q_chunking::chunk_quotient(q, self.domain().domain_size()); + #[cfg(feature = "std")] + println!( + "Chunking deg {} polynomial into {} chunks", + _q_deg, + q_chunks.len() + ); + q_chunks + }) + } + + fn quotient(&self, alphas: &[F]) -> Option>> { + self._compute_quotient(alphas).map(|q| vec![q]) + } + + fn _compute_quotient(&self, alphas: &[F]) -> Option> { let constraints = self.constraints(); // Aggregate constraint polynomials in evaluation form... let agg_constraint = aggregate_evaluations(&constraints, &alphas); diff --git a/w3f-plonk-common/src/prover.rs b/w3f-plonk-common/src/prover.rs index 0779926..ba923e4 100644 --- a/w3f-plonk-common/src/prover.rs +++ b/w3f-plonk-common/src/prover.rs @@ -11,7 +11,7 @@ use w3f_pcs::pcs::PCS; use crate::piop::ProverPiop; use crate::transcript::PlonkTranscript; -use crate::{PiopProof, Proof}; +use crate::{q_chunking, PiopProof, Proof}; pub struct PlonkProver, T: PlonkTranscript> { // Polynomial commitment scheme committer's key. @@ -69,21 +69,31 @@ impl, T: PlonkTranscript> PlonkProver end_timer!(t_commit_cols); // ROUND 2 + // The prover commits to the quotient polynomial... let alphas = transcript.get_constraints_aggregation_coeffs(P::N_CONSTRAINTS); - let quotient_poly = piop.compute_quotient(&alphas).unwrap(); + let quotient_chunks = piop.quotient(&alphas).unwrap(); let t_commit_q = start_timer!(|| format!( - "Committing to the degree-{} quotient", - quotient_poly.degree() + "Committing to {} degree-{} quotient chunks", + quotient_chunks.len(), + quotient_chunks[0].degree() )); - // The prover commits to the quotient polynomial... - let quotient_commitment = CS::commit(&self.pcs_ck, "ient_poly).unwrap(); - transcript.add_quotient_commitment("ient_commitment); + let quotient_chunks_committed: Vec<_> = quotient_chunks + .iter() + .map(|qi| CS::commit(&self.pcs_ck, qi).unwrap()) + .collect(); + for qi_committed in quotient_chunks_committed.iter() { + transcript.add_quotient_commitment(&qi_committed); + } + // let quotient_commitment = CS::commit(&self.pcs_ck, "ient_poly).unwrap(); + // transcript.add_quotient_commitment("ient_commitment); end_timer!(t_commit_q); // and receives the evaluation point in response // ROUND 3 let zeta = transcript.get_evaluation_point(); + let z_n = zeta.pow([piop.domain().domain_size() as u64]); + let q_folded = q_chunking::fold_quotient_chunks("ient_chunks, z_n); let columns_to_open = piop.columns(); let columns_at_zeta = piop.columns_evaluated(&zeta); let constraint_polys_linearized = piop.constraints_lin(&zeta); @@ -94,11 +104,11 @@ impl, T: PlonkTranscript> PlonkProver transcript.add_evaluations(&columns_at_zeta, &lin_at_zeta_omega); let piop_proof = PiopProof { column_commitments, - quotient_commitment, + quotient_chunks: quotient_chunks_committed, columns_at_zeta, lin_at_zeta_omega, }; - let polys_at_zeta = [columns_to_open, vec![quotient_poly]].concat(); + let polys_at_zeta = [columns_to_open, vec![q_folded]].concat(); let pcs_openings = PcsOpeningAt2Points { polys_at_zeta, polys_at_zeta_omega: vec![lin], @@ -122,7 +132,7 @@ impl, T: PlonkTranscript> PlonkProver let lin = &polys_at_zeta_omega[0]; let PiopProof { column_commitments, - quotient_commitment, + quotient_chunks: quotient_commitment, columns_at_zeta, lin_at_zeta_omega, } = piop_proof; @@ -137,7 +147,7 @@ impl, T: PlonkTranscript> PlonkProver end_timer!(_t_open_zeta_omega); Proof { column_commitments, - quotient_commitment, + quotient_chunks: quotient_commitment, columns_at_zeta, lin_at_zeta_omega, agg_at_zeta_proof, diff --git a/w3f-plonk-common/src/q_chunking.rs b/w3f-plonk-common/src/q_chunking.rs new file mode 100644 index 0000000..31c9e3f --- /dev/null +++ b/w3f-plonk-common/src/q_chunking.rs @@ -0,0 +1,40 @@ +use ark_ff::{Field, PrimeField}; +use ark_poly::univariate::DensePolynomial; +use ark_poly::DenseUVPolynomial; +use ark_std::vec::Vec; +use w3f_pcs::pcs::Commitment; +use w3f_pcs::utils; + +pub fn chunk_quotient(q: DensePolynomial, n: usize) -> Vec> { + q.coeffs + .chunks(n) + .map(|coeffs| DensePolynomial::from_coefficients_slice(coeffs)) + .collect() +} + +pub fn chunk_coeffs(z_to_n: F) -> impl Iterator { + utils::powers(z_to_n) +} + +pub fn fold_quotient_chunks( + chunks: &[DensePolynomial], + z_to_n: F, +) -> DensePolynomial { + chunks + .iter() + .zip(chunk_coeffs(z_to_n)) + .map(|(chunk, coeff)| chunk * coeff) + .reduce(|acc, new| acc + new) + .unwrap() +} + +pub fn compose_quotient>(chunks: &[C], z_to_n: F) -> C { + chunks + .iter() + .zip(chunk_coeffs(z_to_n)) + .map(|(chunk, coeff)| chunk.mul(coeff)) + .sum() +} + +#[cfg(test)] +mod tests {} diff --git a/w3f-plonk-common/src/verifier.rs b/w3f-plonk-common/src/verifier.rs index 4561559..187f47e 100644 --- a/w3f-plonk-common/src/verifier.rs +++ b/w3f-plonk-common/src/verifier.rs @@ -6,7 +6,7 @@ use w3f_pcs::pcs::{Commitment, PcsParams, PCS}; use crate::piop::VerifierPiop; use crate::transcript::PlonkTranscript; -use crate::{ColumnsCommited, ColumnsEvaluated, PiopProof, Proof}; +use crate::{q_chunking, ColumnsCommited, ColumnsEvaluated, PiopProof, Proof}; pub struct PlonkVerifier, T: PlonkTranscript> { // Polynomial commitment scheme verifier's key. @@ -53,7 +53,12 @@ impl, T: PlonkTranscript> PlonkVerifier, T: PlonkTranscript> PlonkVerifier