diff --git a/plonky2/src/batch_fri/oracle.rs b/plonky2/src/batch_fri/oracle.rs index 192e374451..58deeaa3c0 100644 --- a/plonky2/src/batch_fri/oracle.rs +++ b/plonky2/src/batch_fri/oracle.rs @@ -450,6 +450,8 @@ mod test { proof.pow_witness, k0, &fri_params.config, + None, + None, ); let degree_bits = [k0, k1, k2]; let merkle_cap = trace_oracle.batch_merkle_tree.cap; diff --git a/plonky2/src/batch_fri/prover.rs b/plonky2/src/batch_fri/prover.rs index 770c2c2285..e71fe25b44 100644 --- a/plonky2/src/batch_fri/prover.rs +++ b/plonky2/src/batch_fri/prover.rs @@ -318,6 +318,8 @@ mod tests { proof.pow_witness, k, &fri_params.config, + None, + None, ); let fri_opening_batch = FriOpeningBatch { @@ -440,6 +442,8 @@ mod tests { proof.pow_witness, k0, &fri_params.config, + None, + None, ); let fri_opening_batch_0 = FriOpenings { batches: vec![FriOpeningBatch { diff --git a/plonky2/src/fri/challenges.rs b/plonky2/src/fri/challenges.rs index be73a8c241..1252f54cad 100644 --- a/plonky2/src/fri/challenges.rs +++ b/plonky2/src/fri/challenges.rs @@ -1,10 +1,14 @@ +#[cfg(not(feature = "std"))] +use alloc::vec; + use crate::field::extension::Extendable; use crate::field::polynomial::PolynomialCoeffs; +use crate::field::types::Field; use crate::fri::proof::{FriChallenges, FriChallengesTarget}; use crate::fri::structure::{FriOpenings, FriOpeningsTarget}; use crate::fri::FriConfig; use crate::gadgets::polynomial::PolynomialCoeffsExtTarget; -use crate::hash::hash_types::{MerkleCapTarget, RichField}; +use crate::hash::hash_types::{MerkleCapTarget, RichField, NUM_HASH_OUT_ELTS}; use crate::hash::merkle_tree::MerkleCap; use crate::iop::challenger::{Challenger, RecursiveChallenger}; use crate::iop::target::Target; @@ -28,6 +32,8 @@ impl> Challenger { pow_witness: F, degree_bits: usize, config: &FriConfig, + final_poly_coeff_len: Option, + query_round_step_count: Option, ) -> FriChallenges where F: RichField + Extendable, @@ -46,7 +52,26 @@ impl> Challenger { }) .collect(); + // When this proof was generated in a circuit with a different number of query steps, + // the challenger needs to observe the additional hash caps. + if let Some(step_count) = query_round_step_count { + let cap_len = (1 << config.cap_height) * NUM_HASH_OUT_ELTS; + let zero_cap = vec![F::ZERO; cap_len]; + for _ in commit_phase_merkle_caps.len()..step_count { + self.observe_elements(&zero_cap); + self.get_extension_challenge::(); + } + } + self.observe_extension_elements(&final_poly.coeffs); + // When this proof was generated in a circuit with a different final polynomial length, + // the challenger needs to observe the full length of the final polynomial. + if let Some(len) = final_poly_coeff_len { + let current_len = final_poly.coeffs.len(); + for _ in current_len..len { + self.observe_extension_element(&F::Extension::ZERO); + } + } self.observe_element(pow_witness); let fri_pow_response = self.get_challenge(); diff --git a/plonky2/src/plonk/get_challenges.rs b/plonky2/src/plonk/get_challenges.rs index 45d79f99aa..5872cf6953 100644 --- a/plonky2/src/plonk/get_challenges.rs +++ b/plonky2/src/plonk/get_challenges.rs @@ -85,6 +85,8 @@ fn get_challenges, C: GenericConfig, cons pow_witness, common_data.degree_bits(), &config.fri_config, + None, + None, ), }) } diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 4a06dccebc..aa5823696a 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -186,7 +186,7 @@ mod tests { &mut TimingTree::default(), )?; - verify_stark_proof(stark, proof, &config) + verify_stark_proof(stark, proof, &config, None) } #[test] @@ -223,7 +223,7 @@ mod tests { None, &mut TimingTree::default(), )?; - verify_stark_proof(stark, proof.clone(), &config)?; + verify_stark_proof(stark, proof.clone(), &config, None)?; assert_eq!(degree_bits, proof.proof.degree_bits); recursive_proof::(stark, proof, &config, true) @@ -275,6 +275,10 @@ mod tests { stark_config.fri_config.num_query_rounds = 1; let min_degree_bits_to_support = 4; + // Currently, we only support verifier_degree_bits to be {30, 26, 22, 18, …}, as they + // generate the max final polynomial length when using the default configuration + // ConstantArityBits(4, 5). This ensures that for other degrees, the final proof polynomial + // will not be longer than the circuit’s final polynomial length. let verifier_degree_bits = 30; let degree_bits = 4..=15; let verifier_fri_params = stark_config.fri_params(verifier_degree_bits); @@ -304,8 +308,12 @@ mod tests { // Configure the circuit for recursive verification let num_rows = 1 << verifier_degree_bits; let stark = S::new(num_rows); - let circuit_config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(circuit_config); + for p in proofs.clone() { + verify_stark_proof(stark, p, &stark_config, Some(verifier_fri_params.clone()))?; + } + + let recursive_verification_circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(recursive_verification_circuit_config); let zero = builder.zero(); // Set up proof verification within the circuit diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index cae7544ef5..c14cb80904 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -1,6 +1,8 @@ use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialCoeffs; use plonky2::fri::proof::{FriProof, FriProofTarget}; +use plonky2::fri::prover::final_poly_coeff_len; +use plonky2::fri::FriParams; use plonky2::gadgets::polynomial::PolynomialCoeffsExtTarget; use plonky2::hash::hash_types::{MerkleCapTarget, RichField}; use plonky2::hash::merkle_tree::MerkleCap; @@ -35,6 +37,7 @@ fn get_challenges( pow_witness: F, config: &StarkConfig, degree_bits: usize, + verifier_circuit_fri_params: Option, ) -> StarkProofChallenges where F: RichField + Extendable, @@ -67,6 +70,19 @@ where challenger.observe_openings(&openings.to_fri_openings()); + let (final_poly_coeff_len, query_round_step_count) = + if let Some(verifier_circuit_fri_params) = verifier_circuit_fri_params { + ( + Some(final_poly_coeff_len( + verifier_circuit_fri_params.degree_bits, + &verifier_circuit_fri_params.reduction_arity_bits, + )), + Some(verifier_circuit_fri_params.reduction_arity_bits.len()), + ) + } else { + (None, None) + }; + StarkProofChallenges { lookup_challenge_set, stark_alphas, @@ -77,6 +93,8 @@ where pow_witness, degree_bits, &config.fri_config, + final_poly_coeff_len, + query_round_step_count, ), } } @@ -99,6 +117,7 @@ where challenges: Option<&GrandProductChallengeSet>, ignore_trace_cap: bool, config: &StarkConfig, + verifier_circuit_fri_params: Option, ) -> StarkProofChallenges { let StarkProof { trace_cap, @@ -133,6 +152,7 @@ where *pow_witness, config, *degree_bits, + verifier_circuit_fri_params, ) } } @@ -155,10 +175,16 @@ where challenges: Option<&GrandProductChallengeSet>, ignore_trace_cap: bool, config: &StarkConfig, + verifier_circuit_fri_params: Option, ) -> StarkProofChallenges { challenger.observe_elements(&self.public_inputs); - self.proof - .get_challenges(challenger, challenges, ignore_trace_cap, config) + self.proof.get_challenges( + challenger, + challenges, + ignore_trace_cap, + config, + verifier_circuit_fri_params, + ) } } diff --git a/starky/src/lib.rs b/starky/src/lib.rs index 27f37ade27..a0edcfe798 100644 --- a/starky/src/lib.rs +++ b/starky/src/lib.rs @@ -196,7 +196,7 @@ //! &mut TimingTree::default(), //! ).expect("We should have a valid proof!"); //! -//! verify_stark_proof(stark, proof, &CONFIG) +//! verify_stark_proof(stark, proof, &CONFIG, None) //! .expect("We should be able to verify this proof!") //! } //! ``` diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index 6a3d6bb2ad..31f8280c7b 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -145,7 +145,7 @@ mod tests { &mut TimingTree::default(), )?; - verify_stark_proof(stark, proof, &config) + verify_stark_proof(stark, proof, &config, None) } #[test] @@ -194,7 +194,7 @@ mod tests { None, &mut TimingTree::default(), )?; - verify_stark_proof(stark, proof.clone(), &config)?; + verify_stark_proof(stark, proof.clone(), &config, None)?; recursive_proof::(stark, proof, &config, true) } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index eae6e43b4b..a889719b20 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -14,6 +14,7 @@ use plonky2::field::types::Field; use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::prover::final_poly_coeff_len; +use plonky2::fri::reduction_strategies::FriReductionStrategy; use plonky2::fri::FriParams; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; @@ -58,6 +59,26 @@ where fri_params.total_arities() <= degree_bits + rate_bits - cap_height, "FRI total reduction arity is too large.", ); + let (final_poly_coeff_len, query_round_step_count) = + if let Some(verifier_circuit_fri_params) = verifier_circuit_fri_params { + assert_eq!(verifier_circuit_fri_params.config, fri_params.config); + match &config.fri_config.reduction_strategy { + FriReductionStrategy::ConstantArityBits(_, final_poly_bits) => { + let len = final_poly_coeff_len( + verifier_circuit_fri_params.degree_bits, + &verifier_circuit_fri_params.reduction_arity_bits, + ); + assert_eq!(len, 1 << (1 + *final_poly_bits)); + ( + Some(len), + Some(verifier_circuit_fri_params.reduction_arity_bits.len()), + ) + } + _ => panic!("Fri Reduction Strategy is not ConstantArityBits"), + } + } else { + (None, None) + }; let trace_commitment = timed!( timing, @@ -76,19 +97,6 @@ where let mut challenger = Challenger::new(); challenger.observe_elements(public_inputs); challenger.observe_cap(&trace_cap); - - let (final_poly_coeff_len, query_round_step_count) = - if let Some(verifier_circuit_fri_params) = verifier_circuit_fri_params { - ( - Some(final_poly_coeff_len( - verifier_circuit_fri_params.degree_bits, - &verifier_circuit_fri_params.reduction_arity_bits, - )), - Some(verifier_circuit_fri_params.reduction_arity_bits.len()), - ) - } else { - (None, None) - }; prove_with_commitment( &stark, config, diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index 3b8eaa70d1..6887b279e1 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -117,7 +117,7 @@ mod tests { let proof = prove::(stark, &config, trace, &[], None, &mut TimingTree::default())?; - verify_stark_proof(stark, proof, &config) + verify_stark_proof(stark, proof, &config, None) } #[test] @@ -159,7 +159,7 @@ mod tests { let trace = stark.generate_trace(); let proof = prove::(stark, &config, trace, &[], None, &mut TimingTree::default())?; - verify_stark_proof(stark, proof.clone(), &config)?; + verify_stark_proof(stark, proof.clone(), &config, None)?; recursive_proof::(stark, proof, &config, true) } diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 59e27db55f..c11a1f8ab3 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -10,6 +10,7 @@ use itertools::Itertools; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::types::Field; use plonky2::fri::verifier::verify_fri_proof; +use plonky2::fri::FriParams; use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::challenger::Challenger; @@ -35,11 +36,18 @@ pub fn verify_stark_proof< stark: S, proof_with_pis: StarkProofWithPublicInputs, config: &StarkConfig, + verifier_circuit_fri_params: Option, ) -> Result<()> { ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); let mut challenger = Challenger::::new(); - let challenges = proof_with_pis.get_challenges(&mut challenger, None, false, config); + let challenges = proof_with_pis.get_challenges( + &mut challenger, + None, + false, + config, + verifier_circuit_fri_params, + ); verify_stark_proof_with_challenges( &stark,