diff --git a/src/fri/commitment.rs b/src/fri/commitment.rs index f9c96a55..c704c33c 100644 --- a/src/fri/commitment.rs +++ b/src/fri/commitment.rs @@ -250,129 +250,3 @@ impl PolynomialBatchCommitment { quotient.padded(quotient.degree_plus_one().next_power_of_two()) } } - -#[cfg(test)] -mod tests { - use anyhow::Result; - - use super::*; - use crate::fri::verifier::verify_fri_proof; - use crate::fri::FriConfig; - use crate::hash::hash_types::HashOut; - use crate::plonk::circuit_data::CircuitConfig; - - fn gen_random_test_case, const D: usize>( - k: usize, - degree_log: usize, - ) -> Vec> { - let degree = 1 << degree_log; - - (0..k) - .map(|_| PolynomialValues::new(F::rand_vec(degree))) - .collect() - } - - fn gen_random_point, const D: usize>( - degree_log: usize, - ) -> F::Extension { - let degree = 1 << degree_log; - - let mut point = F::Extension::rand(); - while point.exp_u64(degree as u64).is_one() { - point = F::Extension::rand(); - } - - point - } - - fn check_batch_polynomial_commitment, const D: usize>( - ) -> Result<()> { - let ks = [10, 2, 10, 8]; - let degree_bits = 11; - let fri_config = FriConfig { - proof_of_work_bits: 2, - reduction_arity_bits: vec![2, 3, 1, 2], - num_query_rounds: 3, - }; - - // We only care about `fri_config, num_constants`, and `num_routed_wires` here. - let common_data = CommonCircuitData { - config: CircuitConfig { - fri_config, - num_routed_wires: 6, - ..CircuitConfig::large_config() - }, - degree_bits, - gates: vec![], - quotient_degree_factor: 0, - num_gate_constraints: 0, - num_constants: 4, - k_is: vec![F::ONE; 6], - num_partial_products: (0, 0), - circuit_digest: HashOut::from_partial(vec![]), - }; - - let commitments = (0..4) - .map(|i| { - PolynomialBatchCommitment::::from_values( - gen_random_test_case(ks[i], degree_bits), - common_data.config.rate_bits, - common_data.config.zero_knowledge && PlonkPolynomials::polynomials(i).blinding, - common_data.config.cap_height, - &mut TimingTree::default(), - None, - ) - }) - .collect::>(); - - let zeta = gen_random_point::(degree_bits); - let (proof, os) = PolynomialBatchCommitment::open_plonk::( - &[ - &commitments[0], - &commitments[1], - &commitments[2], - &commitments[3], - ], - zeta, - &mut Challenger::new(), - &common_data, - &mut TimingTree::default(), - ); - - let merkle_caps = &[ - commitments[0].merkle_tree.cap.clone(), - commitments[1].merkle_tree.cap.clone(), - commitments[2].merkle_tree.cap.clone(), - commitments[3].merkle_tree.cap.clone(), - ]; - - verify_fri_proof( - &os, - zeta, - merkle_caps, - &proof, - &mut Challenger::new(), - &common_data, - ) - } - - mod quadratic { - use super::*; - use crate::field::crandall_field::CrandallField; - - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - check_batch_polynomial_commitment::() - } - } - - mod quartic { - use super::*; - use crate::field::crandall_field::CrandallField; - - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - check_batch_polynomial_commitment::() - } - } -} diff --git a/src/fri/proof.rs b/src/fri/proof.rs index eef39ad6..5fc6a9a8 100644 --- a/src/fri/proof.rs +++ b/src/fri/proof.rs @@ -67,7 +67,6 @@ impl FriInitialTreeProofTarget { #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] pub struct FriQueryRound, const D: usize> { - pub index: usize, pub initial_trees_proof: FriInitialTreeProof, pub steps: Vec>, } @@ -102,7 +101,7 @@ pub struct FriProofTarget { impl, const D: usize> FriProof { /// Compress all the Merkle paths in the FRI proof. - pub fn compress(self, common_data: &CommonCircuitData) -> Self { + pub fn compress(self, indices: &[usize], common_data: &CommonCircuitData) -> Self { if self.is_compressed { panic!("Proof is already compressed."); } @@ -126,9 +125,8 @@ impl, const D: usize> FriProof { let mut steps_evals = vec![vec![]; num_reductions]; let mut steps_proofs = vec![vec![]; num_reductions]; - for qrp in &query_round_proofs { + for (mut index, qrp) in indices.iter().cloned().zip(&query_round_proofs) { let FriQueryRound { - mut index, initial_trees_proof, steps, } = qrp.clone(); @@ -189,7 +187,7 @@ impl, const D: usize> FriProof { } /// Decompress all the Merkle paths in the FRI proof. - pub fn decompress(self, common_data: &CommonCircuitData) -> Self { + pub fn decompress(self, indices: &[usize], common_data: &CommonCircuitData) -> Self { if !self.is_compressed { panic!("Proof is not compressed."); } @@ -221,9 +219,8 @@ impl, const D: usize> FriProof { }) .collect::>(); - for qrp in &query_round_proofs { + for (mut index, qrp) in indices.iter().cloned().zip(&query_round_proofs) { let FriQueryRound { - mut index, initial_trees_proof, steps, } = qrp.clone(); diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 6bc0562a..a2534e3f 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -153,8 +153,7 @@ fn fri_prover_query_round, const D: usize>( ) -> FriQueryRound { let mut query_steps = Vec::new(); let x = challenger.get_challenge(); - let initial_index = x.to_canonical_u64() as usize % n; - let mut x_index = initial_index; + let mut x_index = x.to_canonical_u64() as usize % n; let initial_proof = initial_merkle_trees .iter() .map(|t| (t.get(x_index).to_vec(), t.prove(x_index))) @@ -172,7 +171,6 @@ fn fri_prover_query_round, const D: usize>( x_index >>= arity_bits; } FriQueryRound { - index: initial_index, initial_trees_proof: FriInitialTreeProof { evals_proofs: initial_proof, }, diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 20579da7..6662830a 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -5,13 +5,11 @@ use crate::field::field_types::{Field, RichField}; use crate::field::interpolation::{barycentric_weights, interpolate, interpolate2}; use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound}; use crate::fri::FriConfig; -use crate::hash::hashing::hash_n_to_1; use crate::hash::merkle_proofs::verify_merkle_proof; use crate::hash::merkle_tree::MerkleCap; -use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::plonk_common::PlonkPolynomials; -use crate::plonk::proof::OpeningSet; +use crate::plonk::proof::{OpeningSet, ProofChallenges}; use crate::util::reducing::ReducingFactor; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -44,23 +42,12 @@ fn compute_evaluation, const D: usize>( interpolate(&points, beta, &barycentric_weights) } -fn fri_verify_proof_of_work, const D: usize>( - proof: &FriProof, - challenger: &mut Challenger, +pub(crate) fn fri_verify_proof_of_work, const D: usize>( + fri_pow_response: F, config: &FriConfig, ) -> Result<()> { - let hash = hash_n_to_1( - challenger - .get_hash() - .elements - .iter() - .copied() - .chain(Some(proof.pow_witness)) - .collect(), - false, - ); ensure!( - hash.to_canonical_u64().leading_zeros() + fri_pow_response.to_canonical_u64().leading_zeros() >= config.proof_of_work_bits + (64 - F::order().bits()) as u32, "Invalid proof of work witness." ); @@ -68,14 +55,12 @@ fn fri_verify_proof_of_work, const D: usize>( Ok(()) } -pub fn verify_fri_proof, const D: usize>( +pub(crate) fn verify_fri_proof, const D: usize>( // Openings of the PLONK polynomials. os: &OpeningSet, - // Point at which the PLONK polynomials are opened. - zeta: F::Extension, + challenges: &ProofChallenges, initial_merkle_caps: &[MerkleCap], proof: &FriProof, - challenger: &mut Challenger, common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config; @@ -85,27 +70,11 @@ pub fn verify_fri_proof, const D: usize>( "Final polynomial has wrong degree." ); - challenger.observe_opening_set(os); - - // Scaling factor to combine polynomials. - let alpha = challenger.get_extension_challenge(); - // Size of the LDE domain. let n = proof.final_poly.len() << (total_arities + config.rate_bits); - // Recover the random betas used in the FRI reductions. - let betas = proof - .commit_phase_merkle_caps - .iter() - .map(|cap| { - challenger.observe_cap(cap); - challenger.get_extension_challenge() - }) - .collect::>(); - challenger.observe_extension_elements(&proof.final_poly.coeffs); - // Check PoW. - fri_verify_proof_of_work(proof, challenger, &config.fri_config)?; + fri_verify_proof_of_work(challenges.fri_pow_response, &config.fri_config)?; // Check that parameters are coherent. ensure!( @@ -117,17 +86,20 @@ pub fn verify_fri_proof, const D: usize>( "Number of reductions should be non-zero." ); - let precomputed_reduced_evals = PrecomputedReducedEvals::from_os_and_alpha(os, alpha); - for round_proof in &proof.query_round_proofs { + let precomputed_reduced_evals = + PrecomputedReducedEvals::from_os_and_alpha(os, challenges.fri_alpha); + for (&x_index, round_proof) in challenges + .fri_query_indices + .iter() + .zip(&proof.query_round_proofs) + { fri_verifier_query_round( - zeta, - alpha, + challenges, precomputed_reduced_evals, initial_merkle_caps, &proof, - challenger, + x_index, n, - &betas, round_proof, common_data, )?; @@ -245,21 +217,16 @@ fn fri_combine_initial, const D: usize>( } fn fri_verifier_query_round, const D: usize>( - zeta: F::Extension, - alpha: F::Extension, + challenges: &ProofChallenges, precomputed_reduced_evals: PrecomputedReducedEvals, initial_merkle_caps: &[MerkleCap], proof: &FriProof, - challenger: &mut Challenger, + mut x_index: usize, n: usize, - betas: &[F::Extension], round_proof: &FriQueryRound, common_data: &CommonCircuitData, ) -> Result<()> { let config = &common_data.config.fri_config; - let x = challenger.get_challenge(); - let mut x_index = x.to_canonical_u64() as usize % n; - ensure!(x_index == round_proof.index, "Wrong index."); fri_verify_initial_proof( x_index, &round_proof.initial_trees_proof, @@ -274,8 +241,8 @@ fn fri_verifier_query_round, const D: usize>( // committed "parent" value in the next iteration. let mut old_eval = fri_combine_initial( &round_proof.initial_trees_proof, - alpha, - zeta, + challenges.fri_alpha, + challenges.plonk_zeta, subgroup_x, precomputed_reduced_evals, common_data, @@ -298,7 +265,7 @@ fn fri_verifier_query_round, const D: usize>( x_index_within_coset, arity_bits, evals, - betas[i], + challenges.fri_betas[i], ); verify_merkle_proof( diff --git a/src/hash/hashing.rs b/src/hash/hashing.rs index d5474cc4..ae14e058 100644 --- a/src/hash/hashing.rs +++ b/src/hash/hashing.rs @@ -2,7 +2,6 @@ use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; -use crate::gates::poseidon::PoseidonGate; use crate::hash::hash_types::{HashOut, HashOutTarget}; use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; diff --git a/src/plonk/get_challenges.rs b/src/plonk/get_challenges.rs new file mode 100644 index 00000000..d63c2550 --- /dev/null +++ b/src/plonk/get_challenges.rs @@ -0,0 +1,86 @@ +use crate::field::extension_field::Extendable; +use crate::field::field_types::RichField; +use crate::hash::hashing::hash_n_to_1; +use crate::iop::challenger::Challenger; +use crate::plonk::circuit_data::CommonCircuitData; +use crate::plonk::proof::{ProofChallenges, ProofWithPublicInputs}; + +impl, const D: usize> ProofWithPublicInputs { + pub(crate) fn fri_query_indices( + &self, + common_data: &CommonCircuitData, + ) -> anyhow::Result> { + Ok(self.get_challenges(common_data)?.fri_query_indices) + } + + pub(crate) fn get_challenges( + &self, + common_data: &CommonCircuitData, + ) -> anyhow::Result> { + let config = &common_data.config; + let num_challenges = config.num_challenges; + let num_fri_queries = config.fri_config.num_query_rounds; + let lde_size = common_data.lde_size(); + + let mut challenger = Challenger::new(); + + // Observe the instance. + challenger.observe_hash(&common_data.circuit_digest); + challenger.observe_hash(&self.get_public_inputs_hash()); + + challenger.observe_cap(&self.proof.wires_cap); + let plonk_betas = challenger.get_n_challenges(num_challenges); + let plonk_gammas = challenger.get_n_challenges(num_challenges); + + challenger.observe_cap(&self.proof.plonk_zs_partial_products_cap); + let plonk_alphas = challenger.get_n_challenges(num_challenges); + + challenger.observe_cap(&self.proof.quotient_polys_cap); + let plonk_zeta = challenger.get_extension_challenge(); + + challenger.observe_opening_set(&self.proof.openings); + + // Scaling factor to combine polynomials. + let fri_alpha = challenger.get_extension_challenge(); + + // Recover the random betas used in the FRI reductions. + let fri_betas = self + .proof + .opening_proof + .commit_phase_merkle_caps + .iter() + .map(|cap| { + challenger.observe_cap(cap); + challenger.get_extension_challenge() + }) + .collect(); + + challenger.observe_extension_elements(&self.proof.opening_proof.final_poly.coeffs); + + let fri_pow_response = hash_n_to_1( + challenger + .get_hash() + .elements + .iter() + .copied() + .chain(Some(self.proof.opening_proof.pow_witness)) + .collect(), + false, + ); + + let fri_query_indices = (0..num_fri_queries) + .map(|_| challenger.get_challenge().to_canonical_u64() as usize % lde_size) + .collect(); + + Ok(ProofChallenges { + plonk_betas, + plonk_gammas, + plonk_alphas, + plonk_zeta, + fri_alpha, + fri_betas, + fri_pow_response, + fri_query_indices, + }) + } +} diff --git a/src/plonk/mod.rs b/src/plonk/mod.rs index 3d569443..3b8fdd6b 100644 --- a/src/plonk/mod.rs +++ b/src/plonk/mod.rs @@ -1,6 +1,7 @@ pub mod circuit_builder; pub mod circuit_data; pub(crate) mod copy_constraint; +mod get_challenges; pub(crate) mod permutation_argument; pub(crate) mod plonk_common; pub mod proof; diff --git a/src/plonk/proof.rs b/src/plonk/proof.rs index a4fc665e..cf8bfc74 100644 --- a/src/plonk/proof.rs +++ b/src/plonk/proof.rs @@ -6,7 +6,8 @@ use crate::field::extension_field::Extendable; use crate::field::field_types::RichField; use crate::fri::commitment::PolynomialBatchCommitment; use crate::fri::proof::{FriProof, FriProofTarget}; -use crate::hash::hash_types::MerkleCapTarget; +use crate::hash::hash_types::{HashOut, MerkleCapTarget}; +use crate::hash::hashing::hash_n_to_hash; use crate::hash::merkle_tree::MerkleCap; use crate::iop::target::Target; use crate::plonk::circuit_data::CommonCircuitData; @@ -41,30 +42,25 @@ impl, const D: usize> Proof { } /// Compress the opening proof. - pub fn compress(mut self, common_data: &CommonCircuitData) -> Self { - self.opening_proof = self.opening_proof.compress(common_data); + pub fn compress(mut self, indices: &[usize], common_data: &CommonCircuitData) -> Self { + self.opening_proof = self.opening_proof.compress(&indices, common_data); self } /// Decompress the opening proof. - pub fn decompress(mut self, common_data: &CommonCircuitData) -> Self { - self.opening_proof = self.opening_proof.decompress(common_data); + pub fn decompress(mut self, indices: &[usize], common_data: &CommonCircuitData) -> Self { + self.opening_proof = self.opening_proof.decompress(&indices, common_data); self } } #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] #[serde(bound = "")] -pub struct ProofWithPublicInputs, const D: usize> { +pub struct ProofWithPublicInputs, const D: usize> { pub proof: Proof, pub public_inputs: Vec, } -pub struct ProofWithPublicInputsTarget { - pub proof: ProofTarget, - pub public_inputs: Vec, -} - impl, const D: usize> ProofWithPublicInputs { /// Returns `true` iff the opening proof is compressed. pub fn is_compressed(&self) -> bool { @@ -72,16 +68,51 @@ impl, const D: usize> ProofWithPublicInputs { } /// Compress the opening proof. - pub fn compress(mut self, common_data: &CommonCircuitData) -> Self { - self.proof = self.proof.compress(common_data); - self + pub fn compress(mut self, common_data: &CommonCircuitData) -> anyhow::Result { + let indices = self.fri_query_indices(common_data)?; + self.proof = self.proof.compress(&indices, common_data); + Ok(self) } /// Decompress the opening proof. - pub fn decompress(mut self, common_data: &CommonCircuitData) -> Self { - self.proof = self.proof.decompress(common_data); - self + pub fn decompress(mut self, common_data: &CommonCircuitData) -> anyhow::Result { + let indices = self.fri_query_indices(common_data)?; + self.proof = self.proof.decompress(&indices, common_data); + Ok(self) } + + pub(crate) fn get_public_inputs_hash(&self) -> HashOut { + hash_n_to_hash(self.public_inputs.clone(), true) + } +} + +pub(crate) struct ProofChallenges, const D: usize> { + // Random values used in Plonk's permutation argument. + pub plonk_betas: Vec, + + // Random values used in Plonk's permutation argument. + pub plonk_gammas: Vec, + + // Random values used to combine PLONK constraints. + pub plonk_alphas: Vec, + + // Point at which the PLONK polynomials are opened. + pub plonk_zeta: F::Extension, + + // Scaling factor to combine polynomials. + pub fri_alpha: F::Extension, + + // Betas used in the FRI commit phase reductions. + pub fri_betas: Vec, + + pub fri_pow_response: F, + + pub fri_query_indices: Vec, +} + +pub struct ProofWithPublicInputsTarget { + pub proof: ProofTarget, + pub public_inputs: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] @@ -175,8 +206,8 @@ mod tests { let proof = data.prove(pw)?; // Verify that `decompress ∘ compress = identity`. - let compressed_proof = proof.clone().compress(&data.common); - let decompressed_compressed_proof = compressed_proof.clone().decompress(&data.common); + let compressed_proof = proof.clone().compress(&data.common)?; + let decompressed_compressed_proof = compressed_proof.clone().decompress(&data.common)?; assert_eq!(proof, decompressed_compressed_proof); verify(proof, &data.verifier_only, &data.common)?; diff --git a/src/plonk/recursive_verifier.rs b/src/plonk/recursive_verifier.rs index 98d5d7cc..45dabc15 100644 --- a/src/plonk/recursive_verifier.rs +++ b/src/plonk/recursive_verifier.rs @@ -234,7 +234,7 @@ mod tests { } // Set the targets in a `ProofTarget` to their corresponding values in a `Proof`. - fn set_proof_target, const D: usize>( + fn set_proof_target, const D: usize>( proof: &ProofWithPublicInputs, pt: &ProofWithPublicInputsTarget, pw: &mut PartialWitness, @@ -480,7 +480,7 @@ mod tests { let data = builder.build(); let recursive_proof = data.prove(pw)?; let now = std::time::Instant::now(); - let compressed_recursive_proof = recursive_proof.clone().compress(&data.common); + let compressed_recursive_proof = recursive_proof.clone().compress(&data.common)?; info!("{:.4} to compress proof", now.elapsed().as_secs_f64()); let proof_bytes = serde_cbor::to_vec(&recursive_proof).unwrap(); info!("Proof length: {} bytes", proof_bytes.len()); diff --git a/src/plonk/verifier.rs b/src/plonk/verifier.rs index 217e5cb4..df7ca1df 100644 --- a/src/plonk/verifier.rs +++ b/src/plonk/verifier.rs @@ -3,8 +3,6 @@ use anyhow::{ensure, Result}; use crate::field::extension_field::Extendable; use crate::field::field_types::{Field, RichField}; use crate::fri::verifier::verify_fri_proof; -use crate::hash::hashing::hash_n_to_hash; -use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use crate::plonk::plonk_common::reduce_with_powers; use crate::plonk::proof::ProofWithPublicInputs; @@ -18,32 +16,19 @@ pub(crate) fn verify, const D: usize>( ) -> Result<()> { // Decompress the proof if needed. if proof_with_pis.is_compressed() { - proof_with_pis = proof_with_pis.decompress(common_data); + proof_with_pis = proof_with_pis.decompress(common_data)?; } + + let public_inputs_hash = &proof_with_pis.get_public_inputs_hash(); + + let challenges = proof_with_pis.get_challenges(common_data)?; + let ProofWithPublicInputs { proof, public_inputs, } = proof_with_pis; + let config = &common_data.config; - let num_challenges = config.num_challenges; - - let public_inputs_hash = &hash_n_to_hash(public_inputs, true); - - let mut challenger = Challenger::new(); - - // Observe the instance. - challenger.observe_hash(&common_data.circuit_digest); - challenger.observe_hash(&public_inputs_hash); - - challenger.observe_cap(&proof.wires_cap); - let betas = challenger.get_n_challenges(num_challenges); - let gammas = challenger.get_n_challenges(num_challenges); - - challenger.observe_cap(&proof.plonk_zs_partial_products_cap); - let alphas = challenger.get_n_challenges(num_challenges); - - challenger.observe_cap(&proof.quotient_polys_cap); - let zeta = challenger.get_extension_challenge(); let local_constants = &proof.openings.constants; let local_wires = &proof.openings.wires; @@ -60,20 +45,22 @@ pub(crate) fn verify, const D: usize>( // Evaluate the vanishing polynomial at our challenge point, zeta. let vanishing_polys_zeta = eval_vanishing_poly( common_data, - zeta, + challenges.plonk_zeta, vars, local_zs, next_zs, partial_products, s_sigmas, - &betas, - &gammas, - &alphas, + &challenges.plonk_betas, + &challenges.plonk_gammas, + &challenges.plonk_alphas, ); // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; - let zeta_pow_deg = zeta.exp_power_of_2(common_data.degree_bits); + let zeta_pow_deg = challenges + .plonk_zeta + .exp_power_of_2(common_data.degree_bits); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` @@ -96,10 +83,9 @@ pub(crate) fn verify, const D: usize>( verify_fri_proof( &proof.openings, - zeta, + &challenges, merkle_caps, &proof.opening_proof, - &mut challenger, common_data, )?;