From 56bc63228ea77c8a8785776b674258ed1519547d Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Sat, 26 Oct 2024 16:10:03 -0700 Subject: [PATCH 01/42] add test --- starky/src/fibonacci_stark.rs | 70 ++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 69dfc46d37..123df715b8 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -156,17 +156,17 @@ mod tests { use crate::stark_testing::{test_stark_circuit_constraints, test_stark_low_degree}; use crate::verifier::verify_stark_proof; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + fn fibonacci(n: usize, x0: F, x1: F) -> F { (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 } #[test] fn test_fibonacci_stark() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; @@ -186,11 +186,6 @@ mod tests { #[test] fn test_fibonacci_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - let num_rows = 1 << 5; let stark = S::new(num_rows); test_stark_low_degree(stark) @@ -198,11 +193,6 @@ mod tests { #[test] fn test_fibonacci_stark_circuit() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - let num_rows = 1 << 5; let stark = S::new(num_rows); test_stark_circuit_constraints::(stark) @@ -211,13 +201,10 @@ mod tests { #[test] fn test_recursive_stark_verifier() -> Result<()> { init_logger(); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; let config = StarkConfig::standard_fast_config(); let num_rows = 1 << 5; + let degree_bits = 5; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; // Test first STARK @@ -231,8 +218,9 @@ mod tests { &mut TimingTree::default(), )?; verify_stark_proof(stark, proof.clone(), &config)?; + assert_eq!(degree_bits, proof.proof.recover_degree_bits(&config)); - recursive_proof::(stark, proof, &config, true) + recursive_proof::(stark, proof, &config, 5, true) } fn recursive_proof< @@ -245,6 +233,7 @@ mod tests { stark: S, inner_proof: StarkProofWithPublicInputs, inner_config: &StarkConfig, + degree_bits: usize, print_gate_counts: bool, ) -> Result<()> where @@ -253,7 +242,6 @@ mod tests { let circuit_config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(circuit_config); let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; @@ -272,4 +260,44 @@ mod tests { fn init_logger() { let _ = env_logger::builder().format_timestamp(None).try_init(); } + + #[test] + fn test_recursive_stark_verifier_in_different_degree() -> Result<()> { + init_logger(); + + let config = StarkConfig::standard_fast_config(); + + // Test first STARK + let degree_bits0 = 5; + let num_rows = 1 << degree_bits0; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark0 = S::new(num_rows); + let trace = stark0.generate_trace(public_inputs[0], public_inputs[1]); + let proof0 = prove::( + stark0, + &config, + trace, + &public_inputs, + &mut TimingTree::default(), + )?; + verify_stark_proof(stark0, proof0.clone(), &config)?; + + // Test second STARK + let degree_bits1 = 6; + let num_rows = 1 << degree_bits1; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark1 = S::new(num_rows); + let trace = stark1.generate_trace(public_inputs[0], public_inputs[1]); + let proof1 = prove::( + stark1, + &config, + trace, + &public_inputs, + &mut TimingTree::default(), + )?; + verify_stark_proof(stark1, proof1.clone(), &config)?; + + // Verify proof0 with the recursion circuit at different degree. + recursive_proof::(stark1, proof0, &config, degree_bits1, true) + } } From 3d7c4433597a2f36ebf4154c5b25b5dc3a5e44d2 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Wed, 30 Oct 2024 14:20:26 -0700 Subject: [PATCH 02/42] wip --- starky/src/fibonacci_stark.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 123df715b8..568e22f0f8 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -265,10 +265,11 @@ mod tests { fn test_recursive_stark_verifier_in_different_degree() -> Result<()> { init_logger(); - let config = StarkConfig::standard_fast_config(); + let mut config = StarkConfig::standard_fast_config(); + config.fri_config.num_query_rounds = 1; // Test first STARK - let degree_bits0 = 5; + let degree_bits0 = 7; let num_rows = 1 << degree_bits0; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark0 = S::new(num_rows); @@ -283,7 +284,7 @@ mod tests { verify_stark_proof(stark0, proof0.clone(), &config)?; // Test second STARK - let degree_bits1 = 6; + let degree_bits1 = 8; let num_rows = 1 << degree_bits1; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark1 = S::new(num_rows); @@ -298,6 +299,6 @@ mod tests { verify_stark_proof(stark1, proof1.clone(), &config)?; // Verify proof0 with the recursion circuit at different degree. - recursive_proof::(stark1, proof0, &config, degree_bits1, true) + recursive_proof::(stark0, proof0, &config, degree_bits1, true) } } From 78d3851992a52e54c92cbb0a800cf15407a580dd Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Wed, 30 Oct 2024 15:30:30 -0700 Subject: [PATCH 03/42] update witness util --- plonky2/src/fri/witness_util.rs | 72 ++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/plonky2/src/fri/witness_util.rs b/plonky2/src/fri/witness_util.rs index 041d43c134..cba1785672 100644 --- a/plonky2/src/fri/witness_util.rs +++ b/plonky2/src/fri/witness_util.rs @@ -1,9 +1,10 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use itertools::Itertools; +use plonky2_field::types::Field; use crate::field::extension::Extendable; use crate::fri::proof::{FriProof, FriProofTarget}; -use crate::hash::hash_types::RichField; +use crate::hash::hash_types::{HashOut, RichField}; use crate::iop::witness::WitnessWrite; use crate::plonk::config::AlgebraicHasher; @@ -20,13 +21,26 @@ where { witness.set_target(fri_proof_target.pow_witness, fri_proof.pow_witness)?; - for (&t, &x) in fri_proof_target - .final_poly - .0 - .iter() - .zip_eq(&fri_proof.final_poly.coeffs) - { - witness.set_extension_target(t, x)?; + let target_len = fri_proof_target.final_poly.0.len(); + let coeffs_len = fri_proof.final_poly.coeffs.len(); + + if target_len < coeffs_len { + return Err(anyhow!( + "fri_proof->final_poly's target length is less than the proof length" + )); + } + + // Set overlapping elements + for i in 0..coeffs_len { + witness.set_extension_target( + fri_proof_target.final_poly.0[i], + fri_proof.final_poly.coeffs[i], + )?; + } + + // Set remaining elements in target to ZERO if target is longer + for i in coeffs_len..target_len { + witness.set_extension_target(fri_proof_target.final_poly.0[i], F::Extension::ZERO)?; } for (t, x) in fri_proof_target @@ -51,8 +65,21 @@ where for (&t, &x) in at.0.iter().zip_eq(&a.0) { witness.set_target(t, x)?; } - for (&t, &x) in at.1.siblings.iter().zip_eq(&a.1.siblings) { - witness.set_hash_target(t, x)?; + let target_len = at.1.siblings.len(); + let siblings_len = a.1.siblings.len(); + + if target_len < siblings_len { + return Err(anyhow!("fri_proof->query_round_proofs->initial_trees_proof->evals_proofs->siblings' target length is less than the proof length")); + } + + // Set overlapping elements + for i in 0..siblings_len { + witness.set_hash_target(at.1.siblings[i], a.1.siblings[i])?; + } + + // Set remaining elements in target to ZERO if target is longer + for i in siblings_len..target_len { + witness.set_hash_target(at.1.siblings[i], HashOut::ZERO)?; } } @@ -60,13 +87,22 @@ where for (&t, &x) in st.evals.iter().zip_eq(&s.evals) { witness.set_extension_target(t, x)?; } - for (&t, &x) in st - .merkle_proof - .siblings - .iter() - .zip_eq(&s.merkle_proof.siblings) - { - witness.set_hash_target(t, x)?; + + let target_len = st.merkle_proof.siblings.len(); + let siblings_len = s.merkle_proof.siblings.len(); + + if target_len < siblings_len { + return Err(anyhow!("fri_proof->query_round_proofs->steps->merkle_proof->siblings' target length is less than the proof length")); + } + + // Set overlapping elements + for i in 0..siblings_len { + witness.set_hash_target(st.merkle_proof.siblings[i], s.merkle_proof.siblings[i])?; + } + + // Set remaining elements in target to ZERO if target is longer + for i in siblings_len..target_len { + witness.set_hash_target(st.merkle_proof.siblings[i], HashOut::ZERO)?; } } } From b4213bb905947903f9b214d33ff143074bb2fba3 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 31 Oct 2024 15:29:47 -0700 Subject: [PATCH 04/42] degree_bits: usize->target --- plonky2/src/gadgets/arithmetic_extension.rs | 17 +++++- starky/src/get_challenges.rs | 1 + starky/src/proof.rs | 5 ++ starky/src/recursive_verifier.rs | 61 +++++++++++++++++---- starky/src/stark.rs | 6 +- 5 files changed, 77 insertions(+), 13 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 6026a8cb16..1b31b10d20 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -15,7 +15,7 @@ use crate::gates::multiplication_extension::MulExtensionGate; use crate::hash::hash_types::RichField; use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget}; use crate::iop::generator::{GeneratedValues, SimpleGenerator}; -use crate::iop::target::Target; +use crate::iop::target::{BoolTarget, Target}; use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite}; use crate::plonk::circuit_builder::CircuitBuilder; use crate::plonk::circuit_data::CommonCircuitData; @@ -421,6 +421,21 @@ impl, const D: usize> CircuitBuilder { self.scalar_mul_add_ext_algebra(a, b, zero) } + /// Exponentiates `base` to the power of exponent expressed as `exponent_bits`. + pub fn exp_extension_from_bits( + &mut self, + mut base: ExtensionTarget, + exponent_bits: &Vec, + ) -> ExtensionTarget { + let mut res = self.one_extension(); + for i in 0..exponent_bits.len() { + let new_res = self.mul_extension(res, base); + res = self.select_ext(exponent_bits[i], new_res, res); + base = self.mul_extension(base, base); + } + res + } + /// Exponentiate `base` to the power of `2^power_log`. // TODO: Test pub fn exp_power_of_2_extension( diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 6e8f4bc70e..1d9b55b437 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -258,6 +258,7 @@ impl StarkProofTarget { pow_witness, .. }, + .. } = self; let trace_cap = if ignore_trace_cap { diff --git a/starky/src/proof.rs b/starky/src/proof.rs index cad1f1e787..2d71d8708c 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -66,11 +66,14 @@ pub struct StarkProofTarget { pub openings: StarkOpeningSetTarget, /// `Target`s for the batch FRI argument for all openings. pub opening_proof: FriProofTarget, + /// `Target`s for the proof's degree bits. + pub degree_bits: Target, } impl StarkProofTarget { /// Serializes a STARK proof. pub fn to_buffer(&self, buffer: &mut Vec) -> IoResult<()> { + buffer.write_target(self.degree_bits)?; buffer.write_target_merkle_cap(&self.trace_cap)?; buffer.write_bool(self.auxiliary_polys_cap.is_some())?; if let Some(poly) = &self.auxiliary_polys_cap { @@ -87,6 +90,7 @@ impl StarkProofTarget { /// Deserializes a STARK proof. pub fn from_buffer(buffer: &mut Buffer) -> IoResult { + let degree_bits = buffer.read_target()?; let trace_cap = buffer.read_target_merkle_cap()?; let auxiliary_polys_cap = if buffer.read_bool()? { Some(buffer.read_target_merkle_cap()?) @@ -107,6 +111,7 @@ impl StarkProofTarget { quotient_polys_cap, openings, opening_proof, + degree_bits, }) } diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 2c485d0d0d..f65ac855b2 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -19,6 +19,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::util::reducing::ReducingFactorTarget; use plonky2::with_context; +use plonky2_util::log2_ceil; use crate::config::StarkConfig; use crate::constraint_consumer::RecursiveConstraintConsumer; @@ -111,13 +112,37 @@ pub fn verify_stark_proof_with_challenges_circuit< .collect::>(), ); - let degree_bits = proof.recover_degree_bits(inner_config); - let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits); + let max_degree_bits = log2_ceil(F::TWO_ADICITY); + let degree_bits_bits = builder.split_le(proof.degree_bits, max_degree_bits); + + // degree_bits should be nonzero. + let mut or_all_bits = builder._false(); + for i in 0..max_degree_bits { + or_all_bits = builder.or(or_all_bits, degree_bits_bits[i]); + } + builder.assert_one(or_all_bits.target); + + let zeta_pow_deg = builder.exp_extension_from_bits(challenges.stark_zeta, °ree_bits_bits); let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); + let two = builder.two(); + let degree = builder.exp(two, proof.degree_bits, max_degree_bits); + let degree_ext = builder.convert_to_ext(degree); + + // Calculate primitive_root_of_unity(degree_bits) + let two_adicity = builder.constant(F::from_canonical_usize(F::Extension::TWO_ADICITY)); + let two_adicity_sub_degree_bits = builder.sub(two_adicity, proof.degree_bits); + let two_exp_two_adicity_sub_degree_bits = + builder.exp(two, two_adicity_sub_degree_bits, F::Extension::TWO_ADICITY); + let exponent_bits = builder.split_le( + two_exp_two_adicity_sub_degree_bits, + F::Extension::TWO_ADICITY, + ); + let base = builder.constant_extension(F::Extension::POWER_OF_TWO_GENERATOR); + let g = builder.exp_extension_from_bits(base, &exponent_bits); + let (l_0, l_last) = - eval_l_0_and_l_last_circuit(builder, degree_bits, challenges.stark_zeta, z_h_zeta); - let last = - builder.constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse()); + eval_l_0_and_l_last_circuit(builder, degree_ext, g, challenges.stark_zeta, z_h_zeta); + let last = builder.inverse_extension(g); let z_last = builder.sub_extension(challenges.stark_zeta, last); let mut consumer = RecursiveConstraintConsumer::::new( @@ -178,14 +203,24 @@ pub fn verify_stark_proof_with_challenges_circuit< .chain(proof.quotient_polys_cap.clone()) .collect_vec(); + // Calculate primitive_root_of_unity(degree_bits) + let two_adicity = builder.constant(F::from_canonical_usize(F::TWO_ADICITY)); + let two_adicity_sub_degree_bits = builder.sub(two_adicity, proof.degree_bits); + let two_exp_two_adicity_sub_degree_bits = + builder.exp(two, two_adicity_sub_degree_bits, F::TWO_ADICITY); + let base = builder.constant(F::POWER_OF_TWO_GENERATOR); + let g = builder.exp(base, two_exp_two_adicity_sub_degree_bits, F::TWO_ADICITY); + let fri_instance = stark.fri_instance_target( builder, challenges.stark_zeta, - F::primitive_root_of_unity(degree_bits), + g, num_ctl_polys, ctl_zs_first.as_ref().map_or(0, |c| c.len()), inner_config, ); + //TODO + let degree_bits = proof.recover_degree_bits(inner_config); builder.verify_fri_proof::( &fri_instance, &proof.openings.to_fri_openings(zero), @@ -198,12 +233,11 @@ pub fn verify_stark_proof_with_challenges_circuit< fn eval_l_0_and_l_last_circuit, const D: usize>( builder: &mut CircuitBuilder, - log_n: usize, + n: ExtensionTarget, + g: ExtensionTarget, x: ExtensionTarget, z_x: ExtensionTarget, ) -> (ExtensionTarget, ExtensionTarget) { - let n = builder.constant_extension(F::Extension::from_canonical_usize(1 << log_n)); - let g = builder.constant_extension(F::Extension::primitive_root_of_unity(log_n)); let one = builder.one_extension(); let l_0_deno = builder.mul_sub_extension(n, x, n); let l_last_deno = builder.mul_sub_extension(g, x, one); @@ -284,6 +318,7 @@ pub fn add_virtual_stark_proof, S: Stark, con config, ), opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params), + degree_bits: builder.add_virtual_target(), } } @@ -324,6 +359,7 @@ pub fn set_stark_proof_with_pis_target, W, const D witness: &mut W, stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget, stark_proof_with_pis: &StarkProofWithPublicInputs, + degree_bits: usize, zero: Target, ) -> Result<()> where @@ -345,7 +381,7 @@ where witness.set_target(pi_t, pi)?; } - set_stark_proof_target(witness, pt, proof, zero) + set_stark_proof_target(witness, pt, proof, degree_bits, zero) } /// Set the targets in a [`StarkProofTarget`] to their corresponding values in a @@ -354,6 +390,7 @@ pub fn set_stark_proof_target, W, const D: usize>( witness: &mut W, proof_target: &StarkProofTarget, proof: &StarkProof, + degree_bits: usize, zero: Target, ) -> Result<()> where @@ -361,6 +398,10 @@ where C::Hasher: AlgebraicHasher, W: WitnessWrite, { + witness.set_target( + proof_target.degree_bits, + F::from_canonical_usize(degree_bits), + )?; witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap)?; if let (Some(quotient_polys_cap_target), Some(quotient_polys_cap)) = (&proof_target.quotient_polys_cap, &proof.quotient_polys_cap) diff --git a/starky/src/stark.rs b/starky/src/stark.rs index c47f969245..8d429ba011 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -13,6 +13,7 @@ use plonky2::fri::structure::{ }; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::config::StarkConfig; @@ -175,7 +176,7 @@ pub trait Stark, const D: usize>: Sync { &self, builder: &mut CircuitBuilder, zeta: ExtensionTarget, - g: F, + g: Target, num_ctl_helper_polys: usize, num_ctl_zs: usize, config: &StarkConfig, @@ -222,7 +223,8 @@ pub trait Stark, const D: usize>: Sync { ] .concat(), }; - let zeta_next = builder.mul_const_extension(g, zeta); + let g_ext = builder.convert_to_ext(g); + let zeta_next = builder.mul_extension(g_ext, zeta); let zeta_next_batch = FriBatchInfoTarget { point: zeta_next, polynomials: [trace_info, auxiliary_polys_info].concat(), From fc46e074af9abb00cc6e6266915bc13d26c89b50 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 10:26:52 -0700 Subject: [PATCH 05/42] wip --- starky/src/fibonacci_stark.rs | 4 ++-- starky/src/permutation_stark.rs | 2 +- starky/src/unconstrained_stark.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 568e22f0f8..94065c0bfa 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -220,7 +220,7 @@ mod tests { verify_stark_proof(stark, proof.clone(), &config)?; assert_eq!(degree_bits, proof.proof.recover_degree_bits(&config)); - recursive_proof::(stark, proof, &config, 5, true) + recursive_proof::(stark, proof, &config, degree_bits, true) } fn recursive_proof< @@ -244,7 +244,7 @@ mod tests { let mut pw = PartialWitness::new(); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index ec7f2e41ef..1eb48e6dcc 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -218,7 +218,7 @@ mod tests { let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index 495123acf8..58f95abca8 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -183,7 +183,7 @@ mod tests { let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); From 99c3ccbd001f5722cdcefa57978b22aa1a21de47 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 11:45:20 -0700 Subject: [PATCH 06/42] fix --- starky/src/fibonacci_stark.rs | 2 +- starky/src/recursive_verifier.rs | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 94065c0bfa..dec3840ecf 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -203,8 +203,8 @@ mod tests { init_logger(); let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 5; let degree_bits = 5; + let num_rows = 1 << degree_bits; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; // Test first STARK diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index f65ac855b2..8042e70fb9 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -112,20 +112,24 @@ pub fn verify_stark_proof_with_challenges_circuit< .collect::>(), ); - let max_degree_bits = log2_ceil(F::TWO_ADICITY); - let degree_bits_bits = builder.split_le(proof.degree_bits, max_degree_bits); - - // degree_bits should be nonzero. - let mut or_all_bits = builder._false(); - for i in 0..max_degree_bits { - or_all_bits = builder.or(or_all_bits, degree_bits_bits[i]); + let max_num_degree_bits = F::TWO_ADICITY; + { + // degree_bits should be nonzero. + let max_num_degree_bits_bits = log2_ceil(F::TWO_ADICITY); + let degree_bits_bits = builder.split_le(proof.degree_bits, max_num_degree_bits_bits); + let mut or_all_bits = builder._false(); + for i in 0..max_num_degree_bits_bits { + or_all_bits = builder.or(or_all_bits, degree_bits_bits[i]); + } + builder.assert_one(or_all_bits.target); } - builder.assert_one(or_all_bits.target); - let zeta_pow_deg = builder.exp_extension_from_bits(challenges.stark_zeta, °ree_bits_bits); - let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); let two = builder.two(); - let degree = builder.exp(two, proof.degree_bits, max_degree_bits); + let degree = builder.exp(two, proof.degree_bits, max_num_degree_bits); + let degree_bits_vec = builder.split_le(degree, max_num_degree_bits); + + let zeta_pow_deg = builder.exp_extension_from_bits(challenges.stark_zeta, °ree_bits_vec); + let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); let degree_ext = builder.convert_to_ext(degree); // Calculate primitive_root_of_unity(degree_bits) From 8561399e9ff3daa3c4965f999afd0909907d7c54 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 11:52:16 -0700 Subject: [PATCH 07/42] opt --- starky/src/recursive_verifier.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 8042e70fb9..4c2feff12a 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -135,8 +135,11 @@ pub fn verify_stark_proof_with_challenges_circuit< // Calculate primitive_root_of_unity(degree_bits) let two_adicity = builder.constant(F::from_canonical_usize(F::Extension::TWO_ADICITY)); let two_adicity_sub_degree_bits = builder.sub(two_adicity, proof.degree_bits); - let two_exp_two_adicity_sub_degree_bits = - builder.exp(two, two_adicity_sub_degree_bits, F::Extension::TWO_ADICITY); + let two_exp_two_adicity_sub_degree_bits = builder.exp( + two, + two_adicity_sub_degree_bits, + log2_ceil(F::Extension::TWO_ADICITY), + ); let exponent_bits = builder.split_le( two_exp_two_adicity_sub_degree_bits, F::Extension::TWO_ADICITY, From 8ea195218022f3f218359abb8ea344e6a6527ab8 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 15:05:08 -0700 Subject: [PATCH 08/42] passed 3 tests --- starky/src/fibonacci_stark.rs | 8 +++++++- starky/src/permutation_stark.rs | 8 +++++++- starky/src/recursive_verifier.rs | 5 +++-- starky/src/unconstrained_stark.rs | 8 +++++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index dec3840ecf..0ddb7177a9 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -246,7 +246,13 @@ mod tests { add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; - verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); + verify_stark_proof_circuit::( + &mut builder, + stark, + pt, + inner_config, + degree_bits, + ); if print_gate_counts { builder.print_gate_counts(0); diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index 1eb48e6dcc..d45e2e9389 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -220,7 +220,13 @@ mod tests { add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; - verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); + verify_stark_proof_circuit::( + &mut builder, + stark, + pt, + inner_config, + degree_bits, + ); if print_gate_counts { builder.print_gate_counts(0); diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 4c2feff12a..a9f14c1fd7 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -45,6 +45,7 @@ pub fn verify_stark_proof_circuit< stark: S, proof_with_pis: StarkProofWithPublicInputsTarget, inner_config: &StarkConfig, + degree_bits: usize, ) where C::Hasher: AlgebraicHasher, { @@ -65,6 +66,7 @@ pub fn verify_stark_proof_circuit< challenges, None, inner_config, + degree_bits, ); } @@ -82,6 +84,7 @@ pub fn verify_stark_proof_with_challenges_circuit< challenges: StarkProofChallengesTarget, ctl_vars: Option<&[CtlCheckVarsTarget]>, inner_config: &StarkConfig, + degree_bits: usize, ) where C::Hasher: AlgebraicHasher, { @@ -226,8 +229,6 @@ pub fn verify_stark_proof_with_challenges_circuit< ctl_zs_first.as_ref().map_or(0, |c| c.len()), inner_config, ); - //TODO - let degree_bits = proof.recover_degree_bits(inner_config); builder.verify_fri_proof::( &fri_instance, &proof.openings.to_fri_openings(zero), diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index 58f95abca8..ac9fabe29e 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -185,7 +185,13 @@ mod tests { add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; - verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); + verify_stark_proof_circuit::( + &mut builder, + stark, + pt, + inner_config, + degree_bits, + ); if print_gate_counts { builder.print_gate_counts(0); From c231038ddb97c2836e5948cf6fd35510ce73e111 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 15:19:22 -0700 Subject: [PATCH 09/42] fix --- starky/src/fibonacci_stark.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 0ddb7177a9..ff5bdc8275 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -244,7 +244,15 @@ mod tests { let mut pw = PartialWitness::new(); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; + let proof_degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + assert!(proof_degree_bits != degree_bits); + set_stark_proof_with_pis_target( + &mut pw, + &pt, + &inner_proof, + proof_degree_bits, + builder.zero(), + )?; verify_stark_proof_circuit::( &mut builder, From cff4da4401fa59b25610718ea4ff97218fc4080d Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 15:25:47 -0700 Subject: [PATCH 10/42] convert g to g_ext --- starky/src/fibonacci_stark.rs | 1 - starky/src/recursive_verifier.rs | 30 ++++++++---------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index ff5bdc8275..702522ea18 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -245,7 +245,6 @@ mod tests { let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); let proof_degree_bits = inner_proof.proof.recover_degree_bits(inner_config); - assert!(proof_degree_bits != degree_bits); set_stark_proof_with_pis_target( &mut pw, &pt, diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index a9f14c1fd7..4cf2975cd6 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -136,23 +136,17 @@ pub fn verify_stark_proof_with_challenges_circuit< let degree_ext = builder.convert_to_ext(degree); // Calculate primitive_root_of_unity(degree_bits) - let two_adicity = builder.constant(F::from_canonical_usize(F::Extension::TWO_ADICITY)); + let two_adicity = builder.constant(F::from_canonical_usize(F::TWO_ADICITY)); let two_adicity_sub_degree_bits = builder.sub(two_adicity, proof.degree_bits); - let two_exp_two_adicity_sub_degree_bits = builder.exp( - two, - two_adicity_sub_degree_bits, - log2_ceil(F::Extension::TWO_ADICITY), - ); - let exponent_bits = builder.split_le( - two_exp_two_adicity_sub_degree_bits, - F::Extension::TWO_ADICITY, - ); - let base = builder.constant_extension(F::Extension::POWER_OF_TWO_GENERATOR); - let g = builder.exp_extension_from_bits(base, &exponent_bits); + let two_exp_two_adicity_sub_degree_bits = + builder.exp(two, two_adicity_sub_degree_bits, F::TWO_ADICITY); + let base = builder.constant(F::POWER_OF_TWO_GENERATOR); + let g = builder.exp(base, two_exp_two_adicity_sub_degree_bits, F::TWO_ADICITY); + let g_ext = builder.convert_to_ext(g); let (l_0, l_last) = - eval_l_0_and_l_last_circuit(builder, degree_ext, g, challenges.stark_zeta, z_h_zeta); - let last = builder.inverse_extension(g); + eval_l_0_and_l_last_circuit(builder, degree_ext, g_ext, challenges.stark_zeta, z_h_zeta); + let last = builder.inverse_extension(g_ext); let z_last = builder.sub_extension(challenges.stark_zeta, last); let mut consumer = RecursiveConstraintConsumer::::new( @@ -213,14 +207,6 @@ pub fn verify_stark_proof_with_challenges_circuit< .chain(proof.quotient_polys_cap.clone()) .collect_vec(); - // Calculate primitive_root_of_unity(degree_bits) - let two_adicity = builder.constant(F::from_canonical_usize(F::TWO_ADICITY)); - let two_adicity_sub_degree_bits = builder.sub(two_adicity, proof.degree_bits); - let two_exp_two_adicity_sub_degree_bits = - builder.exp(two, two_adicity_sub_degree_bits, F::TWO_ADICITY); - let base = builder.constant(F::POWER_OF_TWO_GENERATOR); - let g = builder.exp(base, two_exp_two_adicity_sub_degree_bits, F::TWO_ADICITY); - let fri_instance = stark.fri_instance_target( builder, challenges.stark_zeta, From 13801e42a7b45260c9ad36842ca094ef14edb796 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 1 Nov 2024 17:09:36 -0700 Subject: [PATCH 11/42] hack observe final poly coeffs --- plonky2/src/batch_fri/oracle.rs | 1 + plonky2/src/batch_fri/prover.rs | 2 + plonky2/src/fri/mod.rs | 13 +++- plonky2/src/fri/prover.rs | 11 ++++ plonky2/src/fri/recursive_verifier.rs | 88 +++++++++++++------------- plonky2/src/plonk/circuit_builder.rs | 14 ++-- plonky2/src/recursion/dummy_circuit.rs | 1 + plonky2/src/util/serialization/mod.rs | 14 ++++ starky/src/config.rs | 5 +- starky/src/fibonacci_stark.rs | 2 +- starky/src/prover.rs | 5 +- starky/src/recursive_verifier.rs | 4 +- starky/src/verifier.rs | 4 +- 13 files changed, 104 insertions(+), 60 deletions(-) diff --git a/plonky2/src/batch_fri/oracle.rs b/plonky2/src/batch_fri/oracle.rs index 192e374451..2ab8138f1d 100644 --- a/plonky2/src/batch_fri/oracle.rs +++ b/plonky2/src/batch_fri/oracle.rs @@ -298,6 +298,7 @@ mod test { hiding: false, degree_bits: k0, reduction_arity_bits, + final_poly_coeff_len: None, }; let n0 = 1 << k0; diff --git a/plonky2/src/batch_fri/prover.rs b/plonky2/src/batch_fri/prover.rs index 770c2c2285..bc74959aaa 100644 --- a/plonky2/src/batch_fri/prover.rs +++ b/plonky2/src/batch_fri/prover.rs @@ -260,6 +260,7 @@ mod tests { hiding: false, degree_bits: k, reduction_arity_bits, + final_poly_coeff_len: None, }; let n = 1 << k; @@ -355,6 +356,7 @@ mod tests { hiding: false, degree_bits: k0, reduction_arity_bits, + final_poly_coeff_len: None, }; let n0 = 1 << k0; diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 5f18600c3c..3fb8db44fe 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -45,7 +45,12 @@ impl FriConfig { 1.0 / ((1 << self.rate_bits) as f64) } - pub fn fri_params(&self, degree_bits: usize, hiding: bool) -> FriParams { + pub fn fri_params( + &self, + degree_bits: usize, + final_poly_coeff_len: Option, + hiding: bool, + ) -> FriParams { let reduction_arity_bits = self.reduction_strategy.reduction_arity_bits( degree_bits, self.rate_bits, @@ -57,6 +62,7 @@ impl FriConfig { hiding, degree_bits, reduction_arity_bits, + final_poly_coeff_len, } } @@ -83,6 +89,11 @@ pub struct FriParams { /// a 4-to-1 reduction, then a 2-to-1 reduction. After these reductions, the reduced polynomial /// is sent directly. pub reduction_arity_bits: Vec, + + /// The length of the final polynomial coefficients. + /// This is only used when the proof will be verified in a circuit that is generated based on + /// larger degree bits. + pub final_poly_coeff_len: Option, } impl FriParams { diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index b385fb5369..4a3e3e98bc 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -1,6 +1,7 @@ #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use plonky2_field::types::Field; use plonky2_maybe_rayon::*; use crate::field::extension::{flatten, unflatten, Extendable}; @@ -109,6 +110,16 @@ fn fri_committed_trees, C: GenericConfig, .truncate(coeffs.len() >> fri_params.config.rate_bits); challenger.observe_extension_elements(&coeffs.coeffs); + if let Some(len) = fri_params.final_poly_coeff_len { + // Calculate the number of zeros to append + let current_len = coeffs.coeffs.len(); + dbg!(len); + dbg!(current_len); + for _ in current_len..len { + challenger.observe_extension_element(&F::Extension::ZERO); + } + } + (trees, coeffs) } diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 16e02f6a81..139c89bb5d 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -133,50 +133,50 @@ impl, const D: usize> CircuitBuilder { ); // Check that parameters are coherent. - debug_assert_eq!( - params.config.num_query_rounds, - proof.query_round_proofs.len(), - "Number of query rounds does not match config." - ); - - let precomputed_reduced_evals = with_context!( - self, - "precompute reduced evaluations", - PrecomputedReducedOpeningsTarget::from_os_and_alpha( - openings, - challenges.fri_alpha, - self - ) - ); - - for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { - // To minimize noise in our logs, we will only record a context for a single FRI query. - // The very first query will have some extra gates due to constants being registered, so - // the second query is a better representative. - let level = if i == 1 { - log::Level::Debug - } else { - log::Level::Trace - }; - - let num_queries = proof.query_round_proofs.len(); - with_context!( - self, - level, - &format!("verify one (of {num_queries}) query rounds"), - self.fri_verifier_query_round::( - instance, - challenges, - &precomputed_reduced_evals, - initial_merkle_caps, - proof, - challenges.fri_query_indices[i], - n, - round_proof, - params, - ) - ); - } + // debug_assert_eq!( + // params.config.num_query_rounds, + // proof.query_round_proofs.len(), + // "Number of query rounds does not match config." + // ); + // + // let precomputed_reduced_evals = with_context!( + // self, + // "precompute reduced evaluations", + // PrecomputedReducedOpeningsTarget::from_os_and_alpha( + // openings, + // challenges.fri_alpha, + // self + // ) + // ); + + // for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { + // // To minimize noise in our logs, we will only record a context for a single FRI query. + // // The very first query will have some extra gates due to constants being registered, so + // // the second query is a better representative. + // let level = if i == 1 { + // log::Level::Debug + // } else { + // log::Level::Trace + // }; + // + // let num_queries = proof.query_round_proofs.len(); + // with_context!( + // self, + // level, + // &format!("verify one (of {num_queries}) query rounds"), + // self.fri_verifier_query_round::( + // instance, + // challenges, + // &precomputed_reduced_evals, + // initial_merkle_caps, + // proof, + // challenges.fri_query_indices[i], + // n, + // round_proof, + // params, + // ) + // ); + // } } fn fri_verify_initial_proof>( diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index d0d96f39ba..6a216f826b 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -827,10 +827,12 @@ impl, const D: usize> CircuitBuilder { (gate_idx, slot_idx) } - fn fri_params(&self, degree_bits: usize) -> FriParams { - self.config - .fri_config - .fri_params(degree_bits, self.config.zero_knowledge) + fn fri_params(&self, degree_bits: usize, final_poly_coeff_len: Option) -> FriParams { + self.config.fri_config.fri_params( + degree_bits, + final_poly_coeff_len, + self.config.zero_knowledge, + ) } /// The number of (base field) `arithmetic` operations that can be performed in a single gate. @@ -855,7 +857,7 @@ impl, const D: usize> CircuitBuilder { let degree_bits_estimate = log2_strict(degree_estimate); let fri_queries = self.config.fri_config.num_query_rounds; let arities: Vec = self - .fri_params(degree_bits_estimate) + .fri_params(degree_bits_estimate, None) .reduction_arity_bits .iter() .map(|x| 1 << x) @@ -1125,7 +1127,7 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); debug!("Degree after blinding & padding: {}", degree); let degree_bits = log2_strict(degree); - let fri_params = self.fri_params(degree_bits); + let fri_params = self.fri_params(degree_bits, None); assert!( fri_params.total_arities() <= degree_bits + rate_bits - cap_height, "FRI total reduction arity is too large.", diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index cd85593822..d0f23a5873 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -227,6 +227,7 @@ where hiding: false, degree_bits: 0, reduction_arity_bits: vec![], + final_poly_coeff_len: None, }, gates: vec![], selectors_info: SelectorsInfo { diff --git a/plonky2/src/util/serialization/mod.rs b/plonky2/src/util/serialization/mod.rs index 90e150ea85..2d7d63c6a3 100644 --- a/plonky2/src/util/serialization/mod.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -681,12 +681,19 @@ pub trait Read { let reduction_arity_bits = self.read_usize_vec()?; let degree_bits = self.read_usize()?; let hiding = self.read_bool()?; + let has_final_poly_coeff_len = self.read_bool()?; + let final_poly_coeff_len = if has_final_poly_coeff_len { + Some(self.read_usize()?) + } else { + None + }; Ok(FriParams { config, reduction_arity_bits, degree_bits, hiding, + final_poly_coeff_len, }) } @@ -1676,12 +1683,19 @@ pub trait Write { reduction_arity_bits, degree_bits, hiding, + final_poly_coeff_len, } = fri_params; self.write_fri_config(config)?; self.write_usize_vec(reduction_arity_bits.as_slice())?; self.write_usize(*degree_bits)?; self.write_bool(*hiding)?; + if let Some(len) = final_poly_coeff_len { + self.write_bool(true)?; + self.write_usize(*len)?; + } else { + self.write_bool(false)?; + } Ok(()) } diff --git a/starky/src/config.rs b/starky/src/config.rs index 8f95c0ea1c..9ca9e6b288 100644 --- a/starky/src/config.rs +++ b/starky/src/config.rs @@ -61,8 +61,9 @@ impl StarkConfig { } /// Outputs the [`FriParams`] used during the FRI sub-protocol by this [`StarkConfig`]. - pub fn fri_params(&self, degree_bits: usize) -> FriParams { - self.fri_config.fri_params(degree_bits, false) + pub fn fri_params(&self, degree_bits: usize, final_poly_coeff_len: Option) -> FriParams { + self.fri_config + .fri_params(degree_bits, final_poly_coeff_len, false) } /// Checks that this STARK configuration is consistent, i.e. that the different diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 702522ea18..2b493c6611 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -294,7 +294,7 @@ mod tests { &public_inputs, &mut TimingTree::default(), )?; - verify_stark_proof(stark0, proof0.clone(), &config)?; + // verify_stark_proof(stark0, proof0.clone(), &config)?; // Test second STARK let degree_bits1 = 8; diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 36d544b624..1285399afb 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -48,7 +48,7 @@ where { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); - let fri_params = config.fri_params(degree_bits); + let fri_params = config.fri_params(degree_bits, None); let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; assert!( @@ -112,7 +112,8 @@ where { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); - let fri_params = config.fri_params(degree_bits); + // TODO + let fri_params = config.fri_params(degree_bits, Some(16)); let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; assert!( diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 4cf2975cd6..299df24be4 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -221,7 +221,7 @@ pub fn verify_stark_proof_with_challenges_circuit< &challenges.fri_challenges, &merkle_caps, &proof.opening_proof, - &inner_config.fri_params(degree_bits), + &inner_config.fri_params(degree_bits, None), ); } @@ -280,7 +280,7 @@ pub fn add_virtual_stark_proof, S: Stark, con num_ctl_helper_zs: usize, num_ctl_zs: usize, ) -> StarkProofTarget { - let fri_params = config.fri_params(degree_bits); + let fri_params = config.fri_params(degree_bits, None); let cap_height = fri_params.config.cap_height; let num_leaves_per_oracle = once(S::COLUMNS) diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index d56072ad3a..0ba160cd45 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -201,7 +201,7 @@ where &challenges.fri_challenges, &merkle_caps, &proof.opening_proof, - &config.fri_params(degree_bits), + &config.fri_params(degree_bits, None), )?; Ok(()) @@ -243,7 +243,7 @@ where ensure!(public_inputs.len() == S::PUBLIC_INPUTS); - let fri_params = config.fri_params(degree_bits); + let fri_params = config.fri_params(degree_bits, None); let cap_height = fri_params.config.cap_height; ensure!(trace_cap.height() == cap_height); From de75e83b6d1933bcc319991e3ad78406907bd4f2 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Tue, 5 Nov 2024 16:29:32 -0800 Subject: [PATCH 12/42] wip --- plonky2/src/fri/recursive_verifier.rs | 139 ++++++++++++-------- plonky2/src/hash/merkle_proofs.rs | 55 ++++++++ plonky2/src/recursion/recursive_verifier.rs | 1 + starky/src/fibonacci_stark.rs | 7 +- starky/src/recursive_verifier.rs | 1 + 5 files changed, 148 insertions(+), 55 deletions(-) diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 139c89bb5d..f37116b3c7 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -110,6 +110,7 @@ impl, const D: usize> CircuitBuilder { initial_merkle_caps: &[MerkleCapTarget], proof: &FriProofTarget, params: &FriParams, + degree_bits: Option, ) where C::Hasher: AlgebraicHasher, { @@ -124,7 +125,14 @@ impl, const D: usize> CircuitBuilder { ); // Size of the LDE domain. - let n = params.lde_size(); + let log_n = params.config.rate_bits + params.degree_bits; + let mut log_n_target = self.constant(F::from_canonical_usize(params.config.rate_bits)); + if let Some(degree_bits) = degree_bits { + log_n_target = self.add(log_n_target, degree_bits); + } else { + let degree_bits_target = self.constant(F::from_canonical_usize(params.degree_bits)); + log_n_target = self.add(log_n_target, degree_bits_target); + } with_context!( self, @@ -133,55 +141,60 @@ impl, const D: usize> CircuitBuilder { ); // Check that parameters are coherent. - // debug_assert_eq!( - // params.config.num_query_rounds, - // proof.query_round_proofs.len(), - // "Number of query rounds does not match config." - // ); - // - // let precomputed_reduced_evals = with_context!( - // self, - // "precompute reduced evaluations", - // PrecomputedReducedOpeningsTarget::from_os_and_alpha( - // openings, - // challenges.fri_alpha, - // self - // ) - // ); - - // for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { - // // To minimize noise in our logs, we will only record a context for a single FRI query. - // // The very first query will have some extra gates due to constants being registered, so - // // the second query is a better representative. - // let level = if i == 1 { - // log::Level::Debug - // } else { - // log::Level::Trace - // }; - // - // let num_queries = proof.query_round_proofs.len(); - // with_context!( - // self, - // level, - // &format!("verify one (of {num_queries}) query rounds"), - // self.fri_verifier_query_round::( - // instance, - // challenges, - // &precomputed_reduced_evals, - // initial_merkle_caps, - // proof, - // challenges.fri_query_indices[i], - // n, - // round_proof, - // params, - // ) - // ); - // } + debug_assert_eq!( + params.config.num_query_rounds, + proof.query_round_proofs.len(), + "Number of query rounds does not match config." + ); + + let precomputed_reduced_evals = with_context!( + self, + "precompute reduced evaluations", + PrecomputedReducedOpeningsTarget::from_os_and_alpha( + openings, + challenges.fri_alpha, + self + ) + ); + + for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { + // To minimize noise in our logs, we will only record a context for a single FRI query. + // The very first query will have some extra gates due to constants being registered, so + // the second query is a better representative. + let level = if i == 1 { + log::Level::Debug + } else { + log::Level::Trace + }; + + let num_queries = proof.query_round_proofs.len(); + with_context!( + self, + level, + &format!("verify one (of {num_queries}) query rounds"), + self.fri_verifier_query_round::( + instance, + challenges, + &precomputed_reduced_evals, + initial_merkle_caps, + proof, + challenges.fri_query_indices[i], + log_n, + // TODO + log_n - 3, + log_n_target, + round_proof, + params, + ) + ); + } } fn fri_verify_initial_proof>( &mut self, x_index_bits: &[BoolTarget], + circuit_min_log_n: usize, + n_index: Target, proof: &FriInitialTreeProofTarget, initial_merkle_caps: &[MerkleCapTarget], cap_index: Target, @@ -195,9 +208,11 @@ impl, const D: usize> CircuitBuilder { with_context!( self, &format!("verify {i}'th initial Merkle proof"), - self.verify_merkle_proof_to_cap_with_cap_index::( + self.verify_merkle_proof_to_cap_with_cap_indices::( evals.clone(), x_index_bits, + circuit_min_log_n, + n_index, cap_index, cap, merkle_proof @@ -258,36 +273,54 @@ impl, const D: usize> CircuitBuilder { initial_merkle_caps: &[MerkleCapTarget], proof: &FriProofTarget, x_index: Target, - n: usize, + circuit_log_n: usize, + circuit_min_log_n: usize, + log_n: Target, round_proof: &FriQueryRoundTarget, params: &FriParams, ) where C::Hasher: AlgebraicHasher, { - let n_log = log2_strict(n); + assert!(circuit_min_log_n > params.config.cap_height); + let n_index = { + let min_log_n = self.constant(F::from_canonical_usize(circuit_min_log_n)); + self.sub(log_n, min_log_n) + }; // Note that this `low_bits` decomposition permits non-canonical binary encodings. Here we // verify that this has a negligible impact on soundness error. Self::assert_noncanonical_indices_ok(¶ms.config); - let mut x_index_bits = self.low_bits(x_index, n_log, F::BITS); + let mut x_index_bits = self.low_bits(x_index, circuit_log_n, F::BITS); - let cap_index = - self.le_sum(x_index_bits[x_index_bits.len() - params.config.cap_height..].iter()); + dbg!(circuit_log_n); + dbg!(circuit_min_log_n); + + let cap_indices: Vec<_> = (circuit_min_log_n..=circuit_log_n) + .map(|n| { + let slice_start = n - params.config.cap_height; + self.le_sum(x_index_bits[slice_start..n].iter()) + }) + .collect(); + let cap_index = self.random_access(n_index, cap_indices); with_context!( self, "check FRI initial proof", self.fri_verify_initial_proof::( &x_index_bits, + circuit_min_log_n, + n_index, &round_proof.initial_trees_proof, initial_merkle_caps, - cap_index + cap_index, ) ); + return; + // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. let mut subgroup_x = with_context!(self, "compute x from its index", { let g = self.constant(F::coset_shift()); - let phi = F::primitive_root_of_unity(n_log); + let phi = F::primitive_root_of_unity(circuit_log_n); let phi = self.exp_from_bits_const_base(phi, x_index_bits.iter().rev()); // subgroup_x = g * phi self.mul(g, phi) diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 021240a98e..0e09d3ca96 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -180,6 +180,61 @@ impl, const D: usize> CircuitBuilder { } } + /// Same as `verify_merkle_proof_to_cap`, except with the final "cap index" as separate parameter, + /// rather than being contained in `leaf_index_bits`. + pub(crate) fn verify_merkle_proof_to_cap_with_cap_indices>( + &mut self, + leaf_data: Vec, + leaf_index_bits: &[BoolTarget], + circuit_min_log_n: usize, + n_index: Target, + cap_index: Target, + merkle_cap: &MerkleCapTarget, + proof: &MerkleProofTarget, + ) { + debug_assert!(H::AlgebraicPermutation::RATE >= NUM_HASH_OUT_ELTS); + + let zero = self.zero(); + let mut state: HashOutTarget = self.hash_or_noop::(leaf_data); + debug_assert_eq!(state.elements.len(), NUM_HASH_OUT_ELTS); + + let mut final_states = Vec::new(); + let start_index = proof.siblings.len() - (leaf_index_bits.len() - circuit_min_log_n + 1); + + for (i, (&bit, &sibling)) in leaf_index_bits.iter().zip(&proof.siblings).enumerate() { + debug_assert_eq!(sibling.elements.len(), NUM_HASH_OUT_ELTS); + + let mut perm_inputs = H::AlgebraicPermutation::default(); + perm_inputs.set_from_slice(&state.elements, 0); + perm_inputs.set_from_slice(&sibling.elements, NUM_HASH_OUT_ELTS); + // Ensure the rest of the state, if any, is zero: + perm_inputs.set_from_iter(core::iter::repeat(zero), 2 * NUM_HASH_OUT_ELTS); + let perm_outs = self.permute_swapped::(perm_inputs, bit); + let hash_outs = perm_outs.squeeze()[0..NUM_HASH_OUT_ELTS] + .try_into() + .unwrap(); + state = HashOutTarget { + elements: hash_outs, + }; + // Store state at specific indices + if i >= start_index { + final_states.push(state.clone()); + } + } + + for i in 0..NUM_HASH_OUT_ELTS { + let result = self.random_access( + cap_index, + merkle_cap.0.iter().map(|h| h.elements[i]).collect(), + ); + let state = self.random_access( + n_index, + final_states.iter().map(|s| s.elements[i]).collect(), + ); + self.connect(result, state); + } + } + /// Same as `verify_batch_merkle_proof_to_cap`, except with the final "cap index" as separate parameter, /// rather than being contained in `leaf_index_bits`. pub(crate) fn verify_batch_merkle_proof_to_cap_with_cap_index>( diff --git a/plonky2/src/recursion/recursive_verifier.rs b/plonky2/src/recursion/recursive_verifier.rs index 16a8ba85b2..c4a0d4c806 100644 --- a/plonky2/src/recursion/recursive_verifier.rs +++ b/plonky2/src/recursion/recursive_verifier.rs @@ -130,6 +130,7 @@ impl, const D: usize> CircuitBuilder { merkle_caps, &proof.opening_proof, &inner_common_data.fri_params, + None, ) ); } diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 2b493c6611..5563b6f84b 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -279,7 +279,7 @@ mod tests { init_logger(); let mut config = StarkConfig::standard_fast_config(); - config.fri_config.num_query_rounds = 1; + config.fri_config.num_query_rounds = 8; // Test first STARK let degree_bits0 = 7; @@ -295,6 +295,7 @@ mod tests { &mut TimingTree::default(), )?; // verify_stark_proof(stark0, proof0.clone(), &config)?; + // recursive_proof::(stark0, proof0.clone(), &config, degree_bits0, true)?; // Test second STARK let degree_bits1 = 8; @@ -312,6 +313,8 @@ mod tests { verify_stark_proof(stark1, proof1.clone(), &config)?; // Verify proof0 with the recursion circuit at different degree. - recursive_proof::(stark0, proof0, &config, degree_bits1, true) + // recursive_proof::(stark1, proof1, &config, degree_bits1, true)?; + recursive_proof::(stark1, proof0, &config, degree_bits1, true)?; + Ok(()) } } diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 299df24be4..82016614e7 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -222,6 +222,7 @@ pub fn verify_stark_proof_with_challenges_circuit< &merkle_caps, &proof.opening_proof, &inner_config.fri_params(degree_bits, None), + Some(proof.degree_bits), ); } From 33836d5456efb164e84131f8ef60bdd83780a6e7 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Tue, 5 Nov 2024 17:04:09 -0800 Subject: [PATCH 13/42] poc works --- plonky2/src/fri/recursive_verifier.rs | 30 ++++++++++++++++++--------- plonky2/src/hash/merkle_proofs.rs | 12 ++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index f37116b3c7..4e5581cf80 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -193,6 +193,7 @@ impl, const D: usize> CircuitBuilder { fn fri_verify_initial_proof>( &mut self, x_index_bits: &[BoolTarget], + circuit_log_n: usize, circuit_min_log_n: usize, n_index: Target, proof: &FriInitialTreeProofTarget, @@ -211,6 +212,7 @@ impl, const D: usize> CircuitBuilder { self.verify_merkle_proof_to_cap_with_cap_indices::( evals.clone(), x_index_bits, + circuit_log_n, circuit_min_log_n, n_index, cap_index, @@ -307,6 +309,7 @@ impl, const D: usize> CircuitBuilder { "check FRI initial proof", self.fri_verify_initial_proof::( &x_index_bits, + circuit_log_n, circuit_min_log_n, n_index, &round_proof.initial_trees_proof, @@ -315,16 +318,20 @@ impl, const D: usize> CircuitBuilder { ) ); - return; - + let g = self.constant(F::coset_shift()); // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let mut subgroup_x = with_context!(self, "compute x from its index", { - let g = self.constant(F::coset_shift()); - let phi = F::primitive_root_of_unity(circuit_log_n); - let phi = self.exp_from_bits_const_base(phi, x_index_bits.iter().rev()); - // subgroup_x = g * phi - self.mul(g, phi) - }); + let subgroup_x_vec: Vec<_> = (circuit_min_log_n..=circuit_log_n) + .map(|n| { + with_context!(self, "compute x from its index", { + let phi = F::primitive_root_of_unity(n); + let phi = self.exp_from_bits_const_base(phi, x_index_bits[..n].iter().rev()); + // subgroup_x = g * phi + self.mul(g, phi) + }) + }) + .collect(); + + let mut subgroup_x = self.random_access(n_index, subgroup_x_vec); // old_eval is the last derived evaluation; it will be checked for consistency with its // committed "parent" value in the next iteration. @@ -369,9 +376,12 @@ impl, const D: usize> CircuitBuilder { with_context!( self, "verify FRI round Merkle proof.", - self.verify_merkle_proof_to_cap_with_cap_index::( + self.verify_merkle_proof_to_cap_with_cap_indices::( flatten_target(evals), &coset_index_bits, + circuit_log_n, + circuit_min_log_n, + n_index, cap_index, &proof.commit_phase_merkle_caps[i], &round_proof.steps[i].merkle_proof, diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 0e09d3ca96..708b014f7f 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -186,6 +186,7 @@ impl, const D: usize> CircuitBuilder { &mut self, leaf_data: Vec, leaf_index_bits: &[BoolTarget], + circuit_log_n: usize, circuit_min_log_n: usize, n_index: Target, cap_index: Target, @@ -198,10 +199,10 @@ impl, const D: usize> CircuitBuilder { let mut state: HashOutTarget = self.hash_or_noop::(leaf_data); debug_assert_eq!(state.elements.len(), NUM_HASH_OUT_ELTS); - let mut final_states = Vec::new(); - let start_index = proof.siblings.len() - (leaf_index_bits.len() - circuit_min_log_n + 1); + let num_log_n = circuit_log_n + 1 - circuit_min_log_n; + let mut final_states = vec![state.clone(); num_log_n]; - for (i, (&bit, &sibling)) in leaf_index_bits.iter().zip(&proof.siblings).enumerate() { + for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) { debug_assert_eq!(sibling.elements.len(), NUM_HASH_OUT_ELTS); let mut perm_inputs = H::AlgebraicPermutation::default(); @@ -217,9 +218,10 @@ impl, const D: usize> CircuitBuilder { elements: hash_outs, }; // Store state at specific indices - if i >= start_index { - final_states.push(state.clone()); + for n in 0..num_log_n - 1 { + final_states[n] = final_states[n + 1].clone(); } + final_states[num_log_n - 1] = state.clone(); } for i in 0..NUM_HASH_OUT_ELTS { From e685b9a225aa4b9f6564354a41c01de395232b16 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 09:27:41 -0800 Subject: [PATCH 14/42] wip --- plonky2/src/batch_fri/oracle.rs | 1 + plonky2/src/batch_fri/prover.rs | 2 + plonky2/src/fri/mod.rs | 12 +- plonky2/src/fri/prover.rs | 15 +- plonky2/src/fri/recursive_verifier.rs | 271 +++++++++++++++++--- plonky2/src/hash/merkle_proofs.rs | 6 +- plonky2/src/plonk/circuit_builder.rs | 12 +- plonky2/src/recursion/dummy_circuit.rs | 1 + plonky2/src/recursion/recursive_verifier.rs | 1 - plonky2/src/util/serialization/mod.rs | 14 + starky/src/config.rs | 15 +- starky/src/prover.rs | 12 +- 12 files changed, 311 insertions(+), 51 deletions(-) diff --git a/plonky2/src/batch_fri/oracle.rs b/plonky2/src/batch_fri/oracle.rs index 2ab8138f1d..117ca83710 100644 --- a/plonky2/src/batch_fri/oracle.rs +++ b/plonky2/src/batch_fri/oracle.rs @@ -299,6 +299,7 @@ mod test { degree_bits: k0, reduction_arity_bits, final_poly_coeff_len: None, + min_degree_bits_to_support: None, }; let n0 = 1 << k0; diff --git a/plonky2/src/batch_fri/prover.rs b/plonky2/src/batch_fri/prover.rs index bc74959aaa..043dda40b1 100644 --- a/plonky2/src/batch_fri/prover.rs +++ b/plonky2/src/batch_fri/prover.rs @@ -261,6 +261,7 @@ mod tests { degree_bits: k, reduction_arity_bits, final_poly_coeff_len: None, + min_degree_bits_to_support: None, }; let n = 1 << k; @@ -357,6 +358,7 @@ mod tests { degree_bits: k0, reduction_arity_bits, final_poly_coeff_len: None, + min_degree_bits_to_support: None, }; let n0 = 1 << k0; diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 3fb8db44fe..76478c0868 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -49,6 +49,7 @@ impl FriConfig { &self, degree_bits: usize, final_poly_coeff_len: Option, + min_degree_bits_to_support: Option, hiding: bool, ) -> FriParams { let reduction_arity_bits = self.reduction_strategy.reduction_arity_bits( @@ -63,6 +64,7 @@ impl FriConfig { degree_bits, reduction_arity_bits, final_poly_coeff_len, + min_degree_bits_to_support, } } @@ -90,10 +92,14 @@ pub struct FriParams { /// is sent directly. pub reduction_arity_bits: Vec, - /// The length of the final polynomial coefficients. - /// This is only used when the proof will be verified in a circuit that is generated based on - /// larger degree bits. + /// The length of the final polynomial's coefficients. + /// This is used only when the proof will be verified in a circuit generated with a + /// larger degree bit size. pub final_poly_coeff_len: Option, + + /// Specifies the minimum degree bit size to support verification of proofs with + /// varying degree bits. + pub min_degree_bits_to_support: Option, } impl FriParams { diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 4a3e3e98bc..1997ffe532 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -68,6 +68,16 @@ pub(crate) type FriCommitedTrees = ( PolynomialCoeffs<>::Extension>, ); +fn final_poly_coeff_len( + mut degree_bits: usize, + reduction_arity_bits: &Vec, +) -> usize { + for arity_bits in reduction_arity_bits { + degree_bits -= *arity_bits; + } + 1 << degree_bits +} + fn fri_committed_trees, C: GenericConfig, const D: usize>( mut coeffs: PolynomialCoeffs, mut values: PolynomialValues, @@ -110,11 +120,10 @@ fn fri_committed_trees, C: GenericConfig, .truncate(coeffs.len() >> fri_params.config.rate_bits); challenger.observe_extension_elements(&coeffs.coeffs); + // When verifying this proof 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) = fri_params.final_poly_coeff_len { - // Calculate the number of zeros to append let current_len = coeffs.coeffs.len(); - dbg!(len); - dbg!(current_len); for _ in current_len..len { challenger.observe_extension_element(&F::Extension::ZERO); } diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 4e5581cf80..38a60b063b 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -1,5 +1,6 @@ #[cfg(not(feature = "std"))] use alloc::{format, vec::Vec}; +use std::ops::RangeInclusive; use itertools::Itertools; @@ -110,7 +111,6 @@ impl, const D: usize> CircuitBuilder { initial_merkle_caps: &[MerkleCapTarget], proof: &FriProofTarget, params: &FriParams, - degree_bits: Option, ) where C::Hasher: AlgebraicHasher, { @@ -125,14 +125,7 @@ impl, const D: usize> CircuitBuilder { ); // Size of the LDE domain. - let log_n = params.config.rate_bits + params.degree_bits; - let mut log_n_target = self.constant(F::from_canonical_usize(params.config.rate_bits)); - if let Some(degree_bits) = degree_bits { - log_n_target = self.add(log_n_target, degree_bits); - } else { - let degree_bits_target = self.constant(F::from_canonical_usize(params.degree_bits)); - log_n_target = self.add(log_n_target, degree_bits_target); - } + let n = params.lde_size(); with_context!( self, @@ -179,10 +172,90 @@ impl, const D: usize> CircuitBuilder { initial_merkle_caps, proof, challenges.fri_query_indices[i], - log_n, - // TODO - log_n - 3, - log_n_target, + n, + round_proof, + params, + ) + ); + } + } + + pub fn verify_fri_proof_with_multiple_degree_bits>( + &mut self, + instance: &FriInstanceInfoTarget, + openings: &FriOpeningsTarget, + challenges: &FriChallengesTarget, + initial_merkle_caps: &[MerkleCapTarget], + proof: &FriProofTarget, + params: &FriParams, + current_degree_bits: Target, + ) where + C::Hasher: AlgebraicHasher, + { + if let Some(max_arity_bits) = params.max_arity_bits() { + self.check_recursion_config(max_arity_bits); + } + + debug_assert_eq!( + params.final_poly_len(), + proof.final_poly.len(), + "Final polynomial has wrong degree." + ); + + // Size of the LDE domain. + let log_n = params.config.rate_bits + params.degree_bits; + let mut current_log_n = self.constant(F::from_canonical_usize(params.config.rate_bits)); + current_log_n = self.add(current_log_n, current_degree_bits); + let min_log_n_to_support = + log_n - (params.degree_bits - params.min_degree_bits_to_support.unwrap()); + + with_context!( + self, + "check PoW", + self.fri_verify_proof_of_work(challenges.fri_pow_response, ¶ms.config) + ); + + // Check that parameters are coherent. + debug_assert_eq!( + params.config.num_query_rounds, + proof.query_round_proofs.len(), + "Number of query rounds does not match config." + ); + + let precomputed_reduced_evals = with_context!( + self, + "precompute reduced evaluations", + PrecomputedReducedOpeningsTarget::from_os_and_alpha( + openings, + challenges.fri_alpha, + self + ) + ); + + for (i, round_proof) in proof.query_round_proofs.iter().enumerate() { + // To minimize noise in our logs, we will only record a context for a single FRI query. + // The very first query will have some extra gates due to constants being registered, so + // the second query is a better representative. + let level = if i == 1 { + log::Level::Debug + } else { + log::Level::Trace + }; + + let num_queries = proof.query_round_proofs.len(); + with_context!( + self, + level, + &format!("verify one (of {num_queries}) query rounds"), + self.fri_verifier_query_round_with_multiple_degree_bits::( + instance, + challenges, + &precomputed_reduced_evals, + initial_merkle_caps, + proof, + challenges.fri_query_indices[i], + min_log_n_to_support..=log_n, + current_log_n, round_proof, params, ) @@ -193,8 +266,34 @@ impl, const D: usize> CircuitBuilder { fn fri_verify_initial_proof>( &mut self, x_index_bits: &[BoolTarget], - circuit_log_n: usize, - circuit_min_log_n: usize, + proof: &FriInitialTreeProofTarget, + initial_merkle_caps: &[MerkleCapTarget], + cap_index: Target, + ) { + for (i, ((evals, merkle_proof), cap)) in proof + .evals_proofs + .iter() + .zip(initial_merkle_caps) + .enumerate() + { + with_context!( + self, + &format!("verify {i}'th initial Merkle proof"), + self.verify_merkle_proof_to_cap_with_cap_index::( + evals.clone(), + x_index_bits, + cap_index, + cap, + merkle_proof + ) + ); + } + } + + fn fri_verify_initial_proof_with_multiple_degree_bits>( + &mut self, + x_index_bits: &[BoolTarget], + log_n_range: RangeInclusive, n_index: Target, proof: &FriInitialTreeProofTarget, initial_merkle_caps: &[MerkleCapTarget], @@ -212,8 +311,7 @@ impl, const D: usize> CircuitBuilder { self.verify_merkle_proof_to_cap_with_cap_indices::( evals.clone(), x_index_bits, - circuit_log_n, - circuit_min_log_n, + log_n_range.clone(), n_index, cap_index, cap, @@ -275,29 +373,140 @@ impl, const D: usize> CircuitBuilder { initial_merkle_caps: &[MerkleCapTarget], proof: &FriProofTarget, x_index: Target, - circuit_log_n: usize, - circuit_min_log_n: usize, + n: usize, + round_proof: &FriQueryRoundTarget, + params: &FriParams, + ) where + C::Hasher: AlgebraicHasher, + { + let n_log = log2_strict(n); + + // Note that this `low_bits` decomposition permits non-canonical binary encodings. Here we + // verify that this has a negligible impact on soundness error. + Self::assert_noncanonical_indices_ok(¶ms.config); + let mut x_index_bits = self.low_bits(x_index, n_log, F::BITS); + + let cap_index = + self.le_sum(x_index_bits[x_index_bits.len() - params.config.cap_height..].iter()); + with_context!( + self, + "check FRI initial proof", + self.fri_verify_initial_proof::( + &x_index_bits, + &round_proof.initial_trees_proof, + initial_merkle_caps, + cap_index + ) + ); + + // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. + let mut subgroup_x = with_context!(self, "compute x from its index", { + let g = self.constant(F::coset_shift()); + let phi = F::primitive_root_of_unity(n_log); + let phi = self.exp_from_bits_const_base(phi, x_index_bits.iter().rev()); + // subgroup_x = g * phi + self.mul(g, phi) + }); + + // old_eval is the last derived evaluation; it will be checked for consistency with its + // committed "parent" value in the next iteration. + let mut old_eval = with_context!( + self, + "combine initial oracles", + self.fri_combine_initial( + instance, + &round_proof.initial_trees_proof, + challenges.fri_alpha, + subgroup_x, + precomputed_reduced_evals, + params, + ) + ); + + for (i, &arity_bits) in params.reduction_arity_bits.iter().enumerate() { + let evals = &round_proof.steps[i].evals; + + // Split x_index into the index of the coset x is in, and the index of x within that coset. + let coset_index_bits = x_index_bits[arity_bits..].to_vec(); + let x_index_within_coset_bits = &x_index_bits[..arity_bits]; + let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); + + // Check consistency with our old evaluation from the previous round. + let new_eval = self.random_access_extension(x_index_within_coset, evals.clone()); + self.connect_extension(new_eval, old_eval); + + // Infer P(y) from {P(x)}_{x^arity=y}. + old_eval = with_context!( + self, + "infer evaluation using interpolation", + self.compute_evaluation( + subgroup_x, + x_index_within_coset_bits, + arity_bits, + evals, + challenges.fri_betas[i], + ) + ); + + with_context!( + self, + "verify FRI round Merkle proof.", + self.verify_merkle_proof_to_cap_with_cap_index::( + flatten_target(evals), + &coset_index_bits, + cap_index, + &proof.commit_phase_merkle_caps[i], + &round_proof.steps[i].merkle_proof, + ) + ); + + // Update the point x to x^arity. + subgroup_x = self.exp_power_of_2(subgroup_x, arity_bits); + + x_index_bits = coset_index_bits; + } + + // Final check of FRI. After all the reductions, we check that the final polynomial is equal + // to the one sent by the prover. + let eval = with_context!( + self, + &format!( + "evaluate final polynomial of length {}", + proof.final_poly.len() + ), + proof.final_poly.eval_scalar(self, subgroup_x) + ); + self.connect_extension(eval, old_eval); + } + + fn fri_verifier_query_round_with_multiple_degree_bits>( + &mut self, + instance: &FriInstanceInfoTarget, + challenges: &FriChallengesTarget, + precomputed_reduced_evals: &PrecomputedReducedOpeningsTarget, + initial_merkle_caps: &[MerkleCapTarget], + proof: &FriProofTarget, + x_index: Target, + log_n_range: RangeInclusive, log_n: Target, round_proof: &FriQueryRoundTarget, params: &FriParams, ) where C::Hasher: AlgebraicHasher, { - assert!(circuit_min_log_n > params.config.cap_height); + assert!(*log_n_range.start() > params.config.cap_height); let n_index = { - let min_log_n = self.constant(F::from_canonical_usize(circuit_min_log_n)); + let min_log_n = self.constant(F::from_canonical_usize(*log_n_range.start())); self.sub(log_n, min_log_n) }; // Note that this `low_bits` decomposition permits non-canonical binary encodings. Here we // verify that this has a negligible impact on soundness error. Self::assert_noncanonical_indices_ok(¶ms.config); - let mut x_index_bits = self.low_bits(x_index, circuit_log_n, F::BITS); - - dbg!(circuit_log_n); - dbg!(circuit_min_log_n); + let mut x_index_bits = self.low_bits(x_index, *log_n_range.end(), F::BITS); - let cap_indices: Vec<_> = (circuit_min_log_n..=circuit_log_n) + let cap_indices: Vec<_> = log_n_range + .clone() .map(|n| { let slice_start = n - params.config.cap_height; self.le_sum(x_index_bits[slice_start..n].iter()) @@ -307,10 +516,9 @@ impl, const D: usize> CircuitBuilder { with_context!( self, "check FRI initial proof", - self.fri_verify_initial_proof::( + self.fri_verify_initial_proof_with_multiple_degree_bits::( &x_index_bits, - circuit_log_n, - circuit_min_log_n, + log_n_range.clone(), n_index, &round_proof.initial_trees_proof, initial_merkle_caps, @@ -320,7 +528,7 @@ impl, const D: usize> CircuitBuilder { let g = self.constant(F::coset_shift()); // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let subgroup_x_vec: Vec<_> = (circuit_min_log_n..=circuit_log_n) + let subgroup_x_vec: Vec<_> = log_n_range.clone() .map(|n| { with_context!(self, "compute x from its index", { let phi = F::primitive_root_of_unity(n); @@ -379,8 +587,7 @@ impl, const D: usize> CircuitBuilder { self.verify_merkle_proof_to_cap_with_cap_indices::( flatten_target(evals), &coset_index_bits, - circuit_log_n, - circuit_min_log_n, + log_n_range.clone(), n_index, cap_index, &proof.commit_phase_merkle_caps[i], diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 708b014f7f..735a764c03 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -1,5 +1,6 @@ #[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; +use std::ops::RangeInclusive; use anyhow::{ensure, Result}; use itertools::Itertools; @@ -186,8 +187,7 @@ impl, const D: usize> CircuitBuilder { &mut self, leaf_data: Vec, leaf_index_bits: &[BoolTarget], - circuit_log_n: usize, - circuit_min_log_n: usize, + log_n_range: RangeInclusive, n_index: Target, cap_index: Target, merkle_cap: &MerkleCapTarget, @@ -199,7 +199,7 @@ impl, const D: usize> CircuitBuilder { let mut state: HashOutTarget = self.hash_or_noop::(leaf_data); debug_assert_eq!(state.elements.len(), NUM_HASH_OUT_ELTS); - let num_log_n = circuit_log_n + 1 - circuit_min_log_n; + let num_log_n = log_n_range.clone().count(); let mut final_states = vec![state.clone(); num_log_n]; for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) { diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 6a216f826b..4a1dffec03 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -827,10 +827,16 @@ impl, const D: usize> CircuitBuilder { (gate_idx, slot_idx) } - fn fri_params(&self, degree_bits: usize, final_poly_coeff_len: Option) -> FriParams { + fn fri_params( + &self, + degree_bits: usize, + final_poly_coeff_len: Option, + min_degree_bits_to_support: Option, + ) -> FriParams { self.config.fri_config.fri_params( degree_bits, final_poly_coeff_len, + min_degree_bits_to_support, self.config.zero_knowledge, ) } @@ -857,7 +863,7 @@ impl, const D: usize> CircuitBuilder { let degree_bits_estimate = log2_strict(degree_estimate); let fri_queries = self.config.fri_config.num_query_rounds; let arities: Vec = self - .fri_params(degree_bits_estimate, None) + .fri_params(degree_bits_estimate, None, None) .reduction_arity_bits .iter() .map(|x| 1 << x) @@ -1127,7 +1133,7 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); debug!("Degree after blinding & padding: {}", degree); let degree_bits = log2_strict(degree); - let fri_params = self.fri_params(degree_bits, None); + let fri_params = self.fri_params(degree_bits, None, None); assert!( fri_params.total_arities() <= degree_bits + rate_bits - cap_height, "FRI total reduction arity is too large.", diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index d0f23a5873..1828a34de3 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -228,6 +228,7 @@ where degree_bits: 0, reduction_arity_bits: vec![], final_poly_coeff_len: None, + min_degree_bits_to_support: None, }, gates: vec![], selectors_info: SelectorsInfo { diff --git a/plonky2/src/recursion/recursive_verifier.rs b/plonky2/src/recursion/recursive_verifier.rs index c4a0d4c806..16a8ba85b2 100644 --- a/plonky2/src/recursion/recursive_verifier.rs +++ b/plonky2/src/recursion/recursive_verifier.rs @@ -130,7 +130,6 @@ impl, const D: usize> CircuitBuilder { merkle_caps, &proof.opening_proof, &inner_common_data.fri_params, - None, ) ); } diff --git a/plonky2/src/util/serialization/mod.rs b/plonky2/src/util/serialization/mod.rs index 2d7d63c6a3..64caf3ddfd 100644 --- a/plonky2/src/util/serialization/mod.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -687,6 +687,12 @@ pub trait Read { } else { None }; + let has_min_degree_bits_to_support = self.read_bool()?; + let min_degree_bits_to_support = if has_min_degree_bits_to_support { + Some(self.read_usize()?) + } else { + None + }; Ok(FriParams { config, @@ -694,6 +700,7 @@ pub trait Read { degree_bits, hiding, final_poly_coeff_len, + min_degree_bits_to_support, }) } @@ -1684,6 +1691,7 @@ pub trait Write { degree_bits, hiding, final_poly_coeff_len, + min_degree_bits_to_support, } = fri_params; self.write_fri_config(config)?; @@ -1696,6 +1704,12 @@ pub trait Write { } else { self.write_bool(false)?; } + if let Some(db) = min_degree_bits_to_support { + self.write_bool(true)?; + self.write_usize(*db)?; + } else { + self.write_bool(false)?; + } Ok(()) } diff --git a/starky/src/config.rs b/starky/src/config.rs index 9ca9e6b288..57fe611d73 100644 --- a/starky/src/config.rs +++ b/starky/src/config.rs @@ -61,9 +61,18 @@ impl StarkConfig { } /// Outputs the [`FriParams`] used during the FRI sub-protocol by this [`StarkConfig`]. - pub fn fri_params(&self, degree_bits: usize, final_poly_coeff_len: Option) -> FriParams { - self.fri_config - .fri_params(degree_bits, final_poly_coeff_len, false) + pub fn fri_params( + &self, + degree_bits: usize, + final_poly_coeff_len: Option, + min_degree_bits_to_support: Option, + ) -> FriParams { + self.fri_config.fri_params( + degree_bits, + final_poly_coeff_len, + min_degree_bits_to_support, + false, + ) } /// Checks that this STARK configuration is consistent, i.e. that the different diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 1285399afb..ba3018c806 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -15,6 +15,7 @@ use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; use plonky2::fri::oracle::PolynomialBatch; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; +use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierCircuitData}; use plonky2::plonk::config::GenericConfig; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -48,7 +49,7 @@ where { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); - let fri_params = config.fri_params(degree_bits, None); + let fri_params = config.fri_params(degree_bits, None, None); let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; assert!( @@ -103,6 +104,7 @@ pub fn prove_with_commitment( ctl_challenges: Option<&GrandProductChallengeSet>, challenger: &mut Challenger, public_inputs: &[F], + common_circuit_data: Option>, timing: &mut TimingTree, ) -> Result> where @@ -112,8 +114,12 @@ where { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); - // TODO - let fri_params = config.fri_params(degree_bits, Some(16)); + let fri_params = if let Some(cd) = common_circuit_data { + + config.fri_params(degree_bits, Some(cd.degree_bits()), Some(cd.fri_params.)) + } else { + config.fri_params(degree_bits, None, None) + }; let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; assert!( From 02548a054472271c5214a1c41cc81c07d2a51f2f Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 11:25:58 -0800 Subject: [PATCH 15/42] pass tests --- plonky2/src/batch_fri/oracle.rs | 2 -- plonky2/src/batch_fri/prover.rs | 4 --- plonky2/src/fri/mod.rs | 19 +------------- plonky2/src/fri/oracle.rs | 2 ++ plonky2/src/fri/prover.rs | 10 +++---- plonky2/src/fri/recursive_verifier.rs | 7 ++--- plonky2/src/plonk/circuit_builder.rs | 20 +++++--------- plonky2/src/plonk/prover.rs | 1 + plonky2/src/recursion/dummy_circuit.rs | 2 -- plonky2/src/util/serialization/mod.rs | 29 --------------------- starky/src/config.rs | 14 ++-------- starky/src/fibonacci_stark.rs | 25 +++++++++++++----- starky/src/lib.rs | 1 + starky/src/permutation_stark.rs | 3 +++ starky/src/prover.rs | 25 +++++++++++------- starky/src/recursive_verifier.rs | 36 ++++++++++++++++++-------- starky/src/unconstrained_stark.rs | 7 +++-- starky/src/verifier.rs | 4 +-- 18 files changed, 91 insertions(+), 120 deletions(-) diff --git a/plonky2/src/batch_fri/oracle.rs b/plonky2/src/batch_fri/oracle.rs index 117ca83710..192e374451 100644 --- a/plonky2/src/batch_fri/oracle.rs +++ b/plonky2/src/batch_fri/oracle.rs @@ -298,8 +298,6 @@ mod test { hiding: false, degree_bits: k0, reduction_arity_bits, - final_poly_coeff_len: None, - min_degree_bits_to_support: None, }; let n0 = 1 << k0; diff --git a/plonky2/src/batch_fri/prover.rs b/plonky2/src/batch_fri/prover.rs index 043dda40b1..770c2c2285 100644 --- a/plonky2/src/batch_fri/prover.rs +++ b/plonky2/src/batch_fri/prover.rs @@ -260,8 +260,6 @@ mod tests { hiding: false, degree_bits: k, reduction_arity_bits, - final_poly_coeff_len: None, - min_degree_bits_to_support: None, }; let n = 1 << k; @@ -357,8 +355,6 @@ mod tests { hiding: false, degree_bits: k0, reduction_arity_bits, - final_poly_coeff_len: None, - min_degree_bits_to_support: None, }; let n0 = 1 << k0; diff --git a/plonky2/src/fri/mod.rs b/plonky2/src/fri/mod.rs index 76478c0868..5f18600c3c 100644 --- a/plonky2/src/fri/mod.rs +++ b/plonky2/src/fri/mod.rs @@ -45,13 +45,7 @@ impl FriConfig { 1.0 / ((1 << self.rate_bits) as f64) } - pub fn fri_params( - &self, - degree_bits: usize, - final_poly_coeff_len: Option, - min_degree_bits_to_support: Option, - hiding: bool, - ) -> FriParams { + pub fn fri_params(&self, degree_bits: usize, hiding: bool) -> FriParams { let reduction_arity_bits = self.reduction_strategy.reduction_arity_bits( degree_bits, self.rate_bits, @@ -63,8 +57,6 @@ impl FriConfig { hiding, degree_bits, reduction_arity_bits, - final_poly_coeff_len, - min_degree_bits_to_support, } } @@ -91,15 +83,6 @@ pub struct FriParams { /// a 4-to-1 reduction, then a 2-to-1 reduction. After these reductions, the reduced polynomial /// is sent directly. pub reduction_arity_bits: Vec, - - /// The length of the final polynomial's coefficients. - /// This is used only when the proof will be verified in a circuit generated with a - /// larger degree bit size. - pub final_poly_coeff_len: Option, - - /// Specifies the minimum degree bit size to support verification of proofs with - /// varying degree bits. - pub min_degree_bits_to_support: Option, } impl FriParams { diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index 3e1ac781b1..eb8cc020e1 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -178,6 +178,7 @@ impl, C: GenericConfig, const D: usize> oracles: &[&Self], challenger: &mut Challenger, fri_params: &FriParams, + final_poly_coeff_len: Option, timing: &mut TimingTree, ) -> FriProof { assert!(D > 1, "Not implemented for D=1."); @@ -226,6 +227,7 @@ impl, C: GenericConfig, const D: usize> lde_final_values, challenger, fri_params, + final_poly_coeff_len, timing, ); diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 1997ffe532..0f08da71d3 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -27,6 +27,7 @@ pub fn fri_proof, C: GenericConfig, const lde_polynomial_values: PolynomialValues, challenger: &mut Challenger, fri_params: &FriParams, + final_poly_coeff_len: Option, timing: &mut TimingTree, ) -> FriProof { let n = lde_polynomial_values.len(); @@ -41,6 +42,7 @@ pub fn fri_proof, C: GenericConfig, const lde_polynomial_values, challenger, fri_params, + final_poly_coeff_len, ) ); @@ -68,10 +70,7 @@ pub(crate) type FriCommitedTrees = ( PolynomialCoeffs<>::Extension>, ); -fn final_poly_coeff_len( - mut degree_bits: usize, - reduction_arity_bits: &Vec, -) -> usize { +pub fn final_poly_coeff_len(mut degree_bits: usize, reduction_arity_bits: &Vec) -> usize { for arity_bits in reduction_arity_bits { degree_bits -= *arity_bits; } @@ -83,6 +82,7 @@ fn fri_committed_trees, C: GenericConfig, mut values: PolynomialValues, challenger: &mut Challenger, fri_params: &FriParams, + final_poly_coeff_len: Option, ) -> FriCommitedTrees { let mut trees = Vec::with_capacity(fri_params.reduction_arity_bits.len()); @@ -122,7 +122,7 @@ fn fri_committed_trees, C: GenericConfig, challenger.observe_extension_elements(&coeffs.coeffs); // When verifying this proof 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) = fri_params.final_poly_coeff_len { + if let Some(len) = final_poly_coeff_len { let current_len = coeffs.coeffs.len(); for _ in current_len..len { challenger.observe_extension_element(&F::Extension::ZERO); diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 38a60b063b..c7e4f87790 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -189,6 +189,7 @@ impl, const D: usize> CircuitBuilder { proof: &FriProofTarget, params: &FriParams, current_degree_bits: Target, + min_degree_bits_to_support: usize, ) where C::Hasher: AlgebraicHasher, { @@ -206,8 +207,7 @@ impl, const D: usize> CircuitBuilder { let log_n = params.config.rate_bits + params.degree_bits; let mut current_log_n = self.constant(F::from_canonical_usize(params.config.rate_bits)); current_log_n = self.add(current_log_n, current_degree_bits); - let min_log_n_to_support = - log_n - (params.degree_bits - params.min_degree_bits_to_support.unwrap()); + let min_log_n_to_support = log_n - (params.degree_bits - min_degree_bits_to_support); with_context!( self, @@ -528,7 +528,8 @@ impl, const D: usize> CircuitBuilder { let g = self.constant(F::coset_shift()); // `subgroup_x` is `subgroup[x_index]`, i.e., the actual field element in the domain. - let subgroup_x_vec: Vec<_> = log_n_range.clone() + let subgroup_x_vec: Vec<_> = log_n_range + .clone() .map(|n| { with_context!(self, "compute x from its index", { let phi = F::primitive_root_of_unity(n); diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index 4a1dffec03..d0d96f39ba 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -827,18 +827,10 @@ impl, const D: usize> CircuitBuilder { (gate_idx, slot_idx) } - fn fri_params( - &self, - degree_bits: usize, - final_poly_coeff_len: Option, - min_degree_bits_to_support: Option, - ) -> FriParams { - self.config.fri_config.fri_params( - degree_bits, - final_poly_coeff_len, - min_degree_bits_to_support, - self.config.zero_knowledge, - ) + fn fri_params(&self, degree_bits: usize) -> FriParams { + self.config + .fri_config + .fri_params(degree_bits, self.config.zero_knowledge) } /// The number of (base field) `arithmetic` operations that can be performed in a single gate. @@ -863,7 +855,7 @@ impl, const D: usize> CircuitBuilder { let degree_bits_estimate = log2_strict(degree_estimate); let fri_queries = self.config.fri_config.num_query_rounds; let arities: Vec = self - .fri_params(degree_bits_estimate, None, None) + .fri_params(degree_bits_estimate) .reduction_arity_bits .iter() .map(|x| 1 << x) @@ -1133,7 +1125,7 @@ impl, const D: usize> CircuitBuilder { let degree = self.gate_instances.len(); debug!("Degree after blinding & padding: {}", degree); let degree_bits = log2_strict(degree); - let fri_params = self.fri_params(degree_bits, None, None); + let fri_params = self.fri_params(degree_bits); assert!( fri_params.total_arities() <= degree_bits + rate_bits - cap_height, "FRI total reduction arity is too large.", diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index fcd784f326..280337ca55 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -340,6 +340,7 @@ where ], &mut challenger, &common_data.fri_params, + None, timing, ) ); diff --git a/plonky2/src/recursion/dummy_circuit.rs b/plonky2/src/recursion/dummy_circuit.rs index 1828a34de3..cd85593822 100644 --- a/plonky2/src/recursion/dummy_circuit.rs +++ b/plonky2/src/recursion/dummy_circuit.rs @@ -227,8 +227,6 @@ where hiding: false, degree_bits: 0, reduction_arity_bits: vec![], - final_poly_coeff_len: None, - min_degree_bits_to_support: None, }, gates: vec![], selectors_info: SelectorsInfo { diff --git a/plonky2/src/util/serialization/mod.rs b/plonky2/src/util/serialization/mod.rs index 64caf3ddfd..f4b7b920ac 100644 --- a/plonky2/src/util/serialization/mod.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -681,26 +681,11 @@ pub trait Read { let reduction_arity_bits = self.read_usize_vec()?; let degree_bits = self.read_usize()?; let hiding = self.read_bool()?; - let has_final_poly_coeff_len = self.read_bool()?; - let final_poly_coeff_len = if has_final_poly_coeff_len { - Some(self.read_usize()?) - } else { - None - }; - let has_min_degree_bits_to_support = self.read_bool()?; - let min_degree_bits_to_support = if has_min_degree_bits_to_support { - Some(self.read_usize()?) - } else { - None - }; - Ok(FriParams { config, reduction_arity_bits, degree_bits, hiding, - final_poly_coeff_len, - min_degree_bits_to_support, }) } @@ -1690,26 +1675,12 @@ pub trait Write { reduction_arity_bits, degree_bits, hiding, - final_poly_coeff_len, - min_degree_bits_to_support, } = fri_params; self.write_fri_config(config)?; self.write_usize_vec(reduction_arity_bits.as_slice())?; self.write_usize(*degree_bits)?; self.write_bool(*hiding)?; - if let Some(len) = final_poly_coeff_len { - self.write_bool(true)?; - self.write_usize(*len)?; - } else { - self.write_bool(false)?; - } - if let Some(db) = min_degree_bits_to_support { - self.write_bool(true)?; - self.write_usize(*db)?; - } else { - self.write_bool(false)?; - } Ok(()) } diff --git a/starky/src/config.rs b/starky/src/config.rs index 57fe611d73..8f95c0ea1c 100644 --- a/starky/src/config.rs +++ b/starky/src/config.rs @@ -61,18 +61,8 @@ impl StarkConfig { } /// Outputs the [`FriParams`] used during the FRI sub-protocol by this [`StarkConfig`]. - pub fn fri_params( - &self, - degree_bits: usize, - final_poly_coeff_len: Option, - min_degree_bits_to_support: Option, - ) -> FriParams { - self.fri_config.fri_params( - degree_bits, - final_poly_coeff_len, - min_degree_bits_to_support, - false, - ) + pub fn fri_params(&self, degree_bits: usize) -> FriParams { + self.fri_config.fri_params(degree_bits, false) } /// Checks that this STARK configuration is consistent, i.e. that the different diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 5563b6f84b..1268310ff3 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -178,6 +178,7 @@ mod tests { &config, trace, &public_inputs, + None, &mut TimingTree::default(), )?; @@ -215,12 +216,13 @@ mod tests { &config, trace, &public_inputs, + None, &mut TimingTree::default(), )?; verify_stark_proof(stark, proof.clone(), &config)?; assert_eq!(degree_bits, proof.proof.recover_degree_bits(&config)); - recursive_proof::(stark, proof, &config, degree_bits, true) + recursive_proof::(stark, proof, &config, degree_bits, None, true) } fn recursive_proof< @@ -234,6 +236,7 @@ mod tests { inner_proof: StarkProofWithPublicInputs, inner_config: &StarkConfig, degree_bits: usize, + min_degree_bits_to_support: Option, print_gate_counts: bool, ) -> Result<()> where @@ -259,6 +262,7 @@ mod tests { pt, inner_config, degree_bits, + min_degree_bits_to_support, ); if print_gate_counts { @@ -281,8 +285,10 @@ mod tests { let mut config = StarkConfig::standard_fast_config(); config.fri_config.num_query_rounds = 8; - // Test first STARK let degree_bits0 = 7; + let degree_bits1 = 8; + + // Test first STARK let num_rows = 1 << degree_bits0; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark0 = S::new(num_rows); @@ -292,13 +298,11 @@ mod tests { &config, trace, &public_inputs, + Some(degree_bits1), &mut TimingTree::default(), )?; - // verify_stark_proof(stark0, proof0.clone(), &config)?; - // recursive_proof::(stark0, proof0.clone(), &config, degree_bits0, true)?; // Test second STARK - let degree_bits1 = 8; let num_rows = 1 << degree_bits1; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark1 = S::new(num_rows); @@ -308,13 +312,20 @@ mod tests { &config, trace, &public_inputs, + None, &mut TimingTree::default(), )?; verify_stark_proof(stark1, proof1.clone(), &config)?; // Verify proof0 with the recursion circuit at different degree. - // recursive_proof::(stark1, proof1, &config, degree_bits1, true)?; - recursive_proof::(stark1, proof0, &config, degree_bits1, true)?; + recursive_proof::( + stark1, + proof0, + &config, + degree_bits1, + Some(degree_bits0), + true, + )?; Ok(()) } } diff --git a/starky/src/lib.rs b/starky/src/lib.rs index 607a9879a4..27f37ade27 100644 --- a/starky/src/lib.rs +++ b/starky/src/lib.rs @@ -192,6 +192,7 @@ //! &CONFIG, //! trace, //! &public_inputs, +//! None, //! &mut TimingTree::default(), //! ).expect("We should have a valid proof!"); //! diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index d45e2e9389..936346daac 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -141,6 +141,7 @@ mod tests { &config, trace, &[public_input], + None, &mut TimingTree::default(), )?; @@ -190,6 +191,7 @@ mod tests { &config, trace, &[public_input], + None, &mut TimingTree::default(), )?; verify_stark_proof(stark, proof.clone(), &config)?; @@ -226,6 +228,7 @@ mod tests { pt, inner_config, degree_bits, + None, ); if print_gate_counts { diff --git a/starky/src/prover.rs b/starky/src/prover.rs index ba3018c806..a934d3af1a 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -13,9 +13,9 @@ use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; 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::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; -use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierCircuitData}; use plonky2::plonk::config::GenericConfig; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -40,6 +40,7 @@ pub fn prove( config: &StarkConfig, trace_poly_values: Vec>, public_inputs: &[F], + verifier_circuit_degree_bits: Option, timing: &mut TimingTree, ) -> Result> where @@ -49,7 +50,7 @@ where { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); - let fri_params = config.fri_params(degree_bits, None, None); + let fri_params = config.fri_params(degree_bits); let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; assert!( @@ -75,6 +76,15 @@ where challenger.observe_elements(public_inputs); challenger.observe_cap(&trace_cap); + let final_poly_coeff_len = + if let Some(verifier_circuit_degree_bits) = verifier_circuit_degree_bits { + Some(final_poly_coeff_len( + verifier_circuit_degree_bits, + &fri_params.reduction_arity_bits, + )) + } else { + None + }; prove_with_commitment( &stark, config, @@ -84,6 +94,7 @@ where None, &mut challenger, public_inputs, + final_poly_coeff_len, timing, ) } @@ -104,7 +115,7 @@ pub fn prove_with_commitment( ctl_challenges: Option<&GrandProductChallengeSet>, challenger: &mut Challenger, public_inputs: &[F], - common_circuit_data: Option>, + final_poly_coeff_len: Option, timing: &mut TimingTree, ) -> Result> where @@ -114,12 +125,7 @@ where { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); - let fri_params = if let Some(cd) = common_circuit_data { - - config.fri_params(degree_bits, Some(cd.degree_bits()), Some(cd.fri_params.)) - } else { - config.fri_params(degree_bits, None, None) - }; + let fri_params = config.fri_params(degree_bits); let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; assert!( @@ -326,6 +332,7 @@ where &initial_merkle_trees, challenger, &fri_params, + final_poly_coeff_len, timing, ) ); diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 82016614e7..aae2ef7a0c 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -8,7 +8,6 @@ use core::iter::once; use anyhow::{ensure, Result}; use itertools::Itertools; use plonky2::field::extension::Extendable; -use plonky2::field::types::Field; use plonky2::fri::witness_util::set_fri_proof_target; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::RecursiveChallenger; @@ -46,6 +45,7 @@ pub fn verify_stark_proof_circuit< proof_with_pis: StarkProofWithPublicInputsTarget, inner_config: &StarkConfig, degree_bits: usize, + min_degree_bits_to_support: Option, ) where C::Hasher: AlgebraicHasher, { @@ -67,6 +67,7 @@ pub fn verify_stark_proof_circuit< None, inner_config, degree_bits, + min_degree_bits_to_support, ); } @@ -85,6 +86,7 @@ pub fn verify_stark_proof_with_challenges_circuit< ctl_vars: Option<&[CtlCheckVarsTarget]>, inner_config: &StarkConfig, degree_bits: usize, + min_degree_bits_to_support: Option, ) where C::Hasher: AlgebraicHasher, { @@ -215,15 +217,27 @@ pub fn verify_stark_proof_with_challenges_circuit< ctl_zs_first.as_ref().map_or(0, |c| c.len()), inner_config, ); - builder.verify_fri_proof::( - &fri_instance, - &proof.openings.to_fri_openings(zero), - &challenges.fri_challenges, - &merkle_caps, - &proof.opening_proof, - &inner_config.fri_params(degree_bits, None), - Some(proof.degree_bits), - ); + if let Some(min_degree_bits_to_support) = min_degree_bits_to_support { + builder.verify_fri_proof_with_multiple_degree_bits::( + &fri_instance, + &proof.openings.to_fri_openings(zero), + &challenges.fri_challenges, + &merkle_caps, + &proof.opening_proof, + &inner_config.fri_params(degree_bits), + proof.degree_bits, + min_degree_bits_to_support, + ); + } else { + builder.verify_fri_proof::( + &fri_instance, + &proof.openings.to_fri_openings(zero), + &challenges.fri_challenges, + &merkle_caps, + &proof.opening_proof, + &inner_config.fri_params(degree_bits), + ); + } } fn eval_l_0_and_l_last_circuit, const D: usize>( @@ -281,7 +295,7 @@ pub fn add_virtual_stark_proof, S: Stark, con num_ctl_helper_zs: usize, num_ctl_zs: usize, ) -> StarkProofTarget { - let fri_params = config.fri_params(degree_bits, None); + let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; let num_leaves_per_oracle = once(S::COLUMNS) diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index ac9fabe29e..d287a2eec2 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -114,7 +114,8 @@ mod tests { let stark = S::new(num_rows); let trace = stark.generate_trace(); - let proof = prove::(stark, &config, trace, &[], &mut TimingTree::default())?; + let proof = + prove::(stark, &config, trace, &[], None, &mut TimingTree::default())?; verify_stark_proof(stark, proof, &config) } @@ -156,7 +157,8 @@ mod tests { let stark = S::new(num_rows); let trace = stark.generate_trace(); - let proof = prove::(stark, &config, trace, &[], &mut TimingTree::default())?; + let proof = + prove::(stark, &config, trace, &[], None, &mut TimingTree::default())?; verify_stark_proof(stark, proof.clone(), &config)?; recursive_proof::(stark, proof, &config, true) @@ -191,6 +193,7 @@ mod tests { pt, inner_config, degree_bits, + None, ); if print_gate_counts { diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 0ba160cd45..d56072ad3a 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -201,7 +201,7 @@ where &challenges.fri_challenges, &merkle_caps, &proof.opening_proof, - &config.fri_params(degree_bits, None), + &config.fri_params(degree_bits), )?; Ok(()) @@ -243,7 +243,7 @@ where ensure!(public_inputs.len() == S::PUBLIC_INPUTS); - let fri_params = config.fri_params(degree_bits, None); + let fri_params = config.fri_params(degree_bits); let cap_height = fri_params.config.cap_height; ensure!(trace_cap.height() == cap_height); From c9ea54cb443ef099f5c61d13c186dd1d7b8a2be0 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 15:48:45 -0800 Subject: [PATCH 16/42] more in test --- starky/src/fibonacci_stark.rs | 99 ++++++++++++++--------------------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 1268310ff3..09431d7bab 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -135,6 +135,7 @@ impl, const D: usize> Stark for FibonacciStar #[cfg(test)] mod tests { use anyhow::Result; + use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; @@ -235,8 +236,6 @@ mod tests { stark: S, inner_proof: StarkProofWithPublicInputs, inner_config: &StarkConfig, - degree_bits: usize, - min_degree_bits_to_support: Option, print_gate_counts: bool, ) -> Result<()> where @@ -245,25 +244,12 @@ mod tests { let circuit_config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(circuit_config); let mut pw = PartialWitness::new(); + let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - let proof_degree_bits = inner_proof.proof.recover_degree_bits(inner_config); - set_stark_proof_with_pis_target( - &mut pw, - &pt, - &inner_proof, - proof_degree_bits, - builder.zero(), - )?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; - verify_stark_proof_circuit::( - &mut builder, - stark, - pt, - inner_config, - degree_bits, - min_degree_bits_to_support, - ); + verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); if print_gate_counts { builder.print_gate_counts(0); @@ -283,49 +269,44 @@ mod tests { init_logger(); let mut config = StarkConfig::standard_fast_config(); - config.fri_config.num_query_rounds = 8; - - let degree_bits0 = 7; - let degree_bits1 = 8; - - // Test first STARK - let num_rows = 1 << degree_bits0; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark0 = S::new(num_rows); - let trace = stark0.generate_trace(public_inputs[0], public_inputs[1]); - let proof0 = prove::( - stark0, - &config, - trace, - &public_inputs, - Some(degree_bits1), - &mut TimingTree::default(), - )?; + config.fri_config.num_query_rounds = 1; + + let degree_bits = [7, 8, 9, 10]; + let verifier_degree_bits = 10; + + let proofs = degree_bits.iter().map(|degree_bits|{ + let num_rows = 1 << degree_bits; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + prove::( + stark, + &config, + trace, + &public_inputs, + Some(verifier_degree_bits), + &mut TimingTree::default(), + ).unwrap() + }).collect(); + + // Generate the second STARK proof + let num_rows = 1 << verifier_degree_bits; + let stark = S::new(num_rows); - // Test second STARK - let num_rows = 1 << degree_bits1; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark1 = S::new(num_rows); - let trace = stark1.generate_trace(public_inputs[0], public_inputs[1]); - let proof1 = prove::( - stark1, - &config, - trace, - &public_inputs, - None, - &mut TimingTree::default(), - )?; - verify_stark_proof(stark1, proof1.clone(), &config)?; + let circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(circuit_config); + let pt = + add_virtual_stark_proof_with_pis(&mut builder, &stark, &config, verifier_degree_bits, 0, 0); + verify_stark_proof_circuit::(&mut builder, stark, pt, &config, verifier_degree_bits, Some(degree_bits[0])); + builder.print_gate_counts(0); - // Verify proof0 with the recursion circuit at different degree. - recursive_proof::( - stark1, - proof0, - &config, - degree_bits1, - Some(degree_bits0), - true, - )?; + let data = builder.build::(); + degree_bits.iter().zip_eq(proofs).map(|(degree_bits, proof)|{ + let mut pw = PartialWitness::new(); + set_stark_proof_with_pis_target(&mut pw, &pt, &proof, degree_bits, builder.zero())?; + let proof = data.prove(pw)?; + data.verify(proof)?; + }); Ok(()) } } From 999532c8cc0101e4610be06007720d9cf7e34663 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 16:22:35 -0800 Subject: [PATCH 17/42] better test --- starky/src/fibonacci_stark.rs | 96 ++++++++++++++++++++----------- starky/src/permutation_stark.rs | 9 +-- starky/src/recursive_verifier.rs | 4 +- starky/src/unconstrained_stark.rs | 9 +-- 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 09431d7bab..3621f90462 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -205,7 +205,7 @@ mod tests { init_logger(); let config = StarkConfig::standard_fast_config(); - let degree_bits = 5; + let degree_bits = 10; let num_rows = 1 << degree_bits; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; @@ -223,7 +223,7 @@ mod tests { verify_stark_proof(stark, proof.clone(), &config)?; assert_eq!(degree_bits, proof.proof.recover_degree_bits(&config)); - recursive_proof::(stark, proof, &config, degree_bits, None, true) + recursive_proof::(stark, proof, &config, true) } fn recursive_proof< @@ -247,9 +247,9 @@ mod tests { let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; - verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config); + verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config, None); if print_gate_counts { builder.print_gate_counts(0); @@ -265,48 +265,76 @@ mod tests { } #[test] - fn test_recursive_stark_verifier_in_different_degree() -> Result<()> { + fn test_recursive_verifier_with_multiple_degree_bits() -> Result<()> { init_logger(); - let mut config = StarkConfig::standard_fast_config(); - config.fri_config.num_query_rounds = 1; + let stark_config = StarkConfig::standard_fast_config(); - let degree_bits = [7, 8, 9, 10]; + let min_degree_bits_to_support = 7; let verifier_degree_bits = 10; + let degree_bits = min_degree_bits_to_support..=verifier_degree_bits; + + // Generate STARK proofs for each degree in `degree_bits` + let proofs: Vec<_> = degree_bits + .clone() + .map(|degree_bits| { + let num_rows = 1 << degree_bits; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + + // Generate proof with the specified verifier degree + let proof = prove::( + stark, + &stark_config, + trace, + &public_inputs, + Some(verifier_degree_bits), + &mut TimingTree::default(), + ) + .unwrap(); + proof + }) + .collect(); - let proofs = degree_bits.iter().map(|degree_bits|{ - let num_rows = 1 << degree_bits; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - prove::( - stark, - &config, - trace, - &public_inputs, - Some(verifier_degree_bits), - &mut TimingTree::default(), - ).unwrap() - }).collect(); - - // Generate the second STARK proof + // 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); - let pt = - add_virtual_stark_proof_with_pis(&mut builder, &stark, &config, verifier_degree_bits, 0, 0); - verify_stark_proof_circuit::(&mut builder, stark, pt, &config, verifier_degree_bits, Some(degree_bits[0])); + let zero = builder.zero(); + + // Set up proof verification within the circuit + let pt = add_virtual_stark_proof_with_pis( + &mut builder, + &stark, + &stark_config, + verifier_degree_bits, + 0, + 0, + ); + verify_stark_proof_circuit::( + &mut builder, + stark, + pt.clone(), + &stark_config, + Some(min_degree_bits_to_support), + ); builder.print_gate_counts(0); + // Build the recursive circuit let data = builder.build::(); - degree_bits.iter().zip_eq(proofs).map(|(degree_bits, proof)|{ - let mut pw = PartialWitness::new(); - set_stark_proof_with_pis_target(&mut pw, &pt, &proof, degree_bits, builder.zero())?; - let proof = data.prove(pw)?; - data.verify(proof)?; - }); + + // Verify each proof using partial witnesses + degree_bits + .zip_eq(proofs) + .try_for_each(|(degree_bits, proof)| { + let mut pw = PartialWitness::new(); + set_stark_proof_with_pis_target(&mut pw, &pt, &proof, degree_bits, zero)?; + let proof = data.prove(pw)?; + data.verify(proof) + })?; + Ok(()) } } diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index 936346daac..81a0897f5f 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -222,14 +222,7 @@ mod tests { add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; - verify_stark_proof_circuit::( - &mut builder, - stark, - pt, - inner_config, - degree_bits, - None, - ); + verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config, None); if print_gate_counts { builder.print_gate_counts(0); diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index aae2ef7a0c..c9b3dd61f4 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -44,12 +44,12 @@ pub fn verify_stark_proof_circuit< stark: S, proof_with_pis: StarkProofWithPublicInputsTarget, inner_config: &StarkConfig, - degree_bits: usize, min_degree_bits_to_support: Option, ) where C::Hasher: AlgebraicHasher, { assert_eq!(proof_with_pis.public_inputs.len(), S::PUBLIC_INPUTS); + let max_degree_bits_to_support = proof_with_pis.proof.recover_degree_bits(inner_config); let mut challenger = RecursiveChallenger::::new(builder); let challenges = with_context!( @@ -66,7 +66,7 @@ pub fn verify_stark_proof_circuit< challenges, None, inner_config, - degree_bits, + max_degree_bits_to_support, min_degree_bits_to_support, ); } diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index d287a2eec2..08f52d692a 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -187,14 +187,7 @@ mod tests { add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; - verify_stark_proof_circuit::( - &mut builder, - stark, - pt, - inner_config, - degree_bits, - None, - ); + verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config, None); if print_gate_counts { builder.print_gate_counts(0); From 9667294afe54feea275173424a978ab5a2b15d89 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 16:27:46 -0800 Subject: [PATCH 18/42] fix ci --- plonky2/src/fri/recursive_verifier.rs | 2 +- plonky2/src/hash/merkle_proofs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index c7e4f87790..4e4bd02418 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "std"))] use alloc::{format, vec::Vec}; -use std::ops::RangeInclusive; +use core::ops::RangeInclusive; use itertools::Itertools; diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 735a764c03..8247ad37b7 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -1,6 +1,6 @@ #[cfg(not(feature = "std"))] use alloc::{vec, vec::Vec}; -use std::ops::RangeInclusive; +use core::ops::RangeInclusive; use anyhow::{ensure, Result}; use itertools::Itertools; From 773b695fb9f40d8832f3214bbf932e4843d567ee Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 16:30:32 -0800 Subject: [PATCH 19/42] clippy --- plonky2/src/gadgets/arithmetic_extension.rs | 2 +- plonky2/src/hash/merkle_proofs.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plonky2/src/gadgets/arithmetic_extension.rs b/plonky2/src/gadgets/arithmetic_extension.rs index 1b31b10d20..355d5e253a 100644 --- a/plonky2/src/gadgets/arithmetic_extension.rs +++ b/plonky2/src/gadgets/arithmetic_extension.rs @@ -425,7 +425,7 @@ impl, const D: usize> CircuitBuilder { pub fn exp_extension_from_bits( &mut self, mut base: ExtensionTarget, - exponent_bits: &Vec, + exponent_bits: &[BoolTarget], ) -> ExtensionTarget { let mut res = self.one_extension(); for i in 0..exponent_bits.len() { diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 8247ad37b7..9386d9133a 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -200,7 +200,7 @@ impl, const D: usize> CircuitBuilder { debug_assert_eq!(state.elements.len(), NUM_HASH_OUT_ELTS); let num_log_n = log_n_range.clone().count(); - let mut final_states = vec![state.clone(); num_log_n]; + let mut final_states = vec![state; num_log_n]; for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) { debug_assert_eq!(sibling.elements.len(), NUM_HASH_OUT_ELTS); @@ -219,9 +219,9 @@ impl, const D: usize> CircuitBuilder { }; // Store state at specific indices for n in 0..num_log_n - 1 { - final_states[n] = final_states[n + 1].clone(); + final_states[n] = final_states[n + 1]; } - final_states[num_log_n - 1] = state.clone(); + final_states[num_log_n - 1] = state; } for i in 0..NUM_HASH_OUT_ELTS { From 58196b8df619d5db1e42d9d908fde4c45642c4e3 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 19:11:21 -0800 Subject: [PATCH 20/42] fix --- starky/src/fibonacci_stark.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 3621f90462..485cea3b15 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -134,6 +134,9 @@ impl, const D: usize> Stark for FibonacciStar #[cfg(test)] mod tests { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + use anyhow::Result; use itertools::Itertools; use plonky2::field::extension::Extendable; From dcd973df8eecfd14f6ccef818b0f4cf161e421d2 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 7 Nov 2024 19:44:00 -0800 Subject: [PATCH 21/42] fix --- starky/src/fibonacci_stark.rs | 5 ++--- starky/src/recursive_verifier.rs | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 485cea3b15..e46b97b5d6 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -287,7 +287,7 @@ mod tests { let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); // Generate proof with the specified verifier degree - let proof = prove::( + prove::( stark, &stark_config, trace, @@ -295,8 +295,7 @@ mod tests { Some(verifier_degree_bits), &mut TimingTree::default(), ) - .unwrap(); - proof + .unwrap() }) .collect(); diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index c9b3dd61f4..dfb0735822 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -368,7 +368,7 @@ pub fn set_stark_proof_with_pis_target, W, const D witness: &mut W, stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget, stark_proof_with_pis: &StarkProofWithPublicInputs, - degree_bits: usize, + pis_degree_bits: usize, zero: Target, ) -> Result<()> where @@ -390,7 +390,7 @@ where witness.set_target(pi_t, pi)?; } - set_stark_proof_target(witness, pt, proof, degree_bits, zero) + set_stark_proof_target(witness, pt, proof, pis_degree_bits, zero) } /// Set the targets in a [`StarkProofTarget`] to their corresponding values in a @@ -399,7 +399,7 @@ pub fn set_stark_proof_target, W, const D: usize>( witness: &mut W, proof_target: &StarkProofTarget, proof: &StarkProof, - degree_bits: usize, + pis_degree_bits: usize, zero: Target, ) -> Result<()> where @@ -409,7 +409,7 @@ where { witness.set_target( proof_target.degree_bits, - F::from_canonical_usize(degree_bits), + F::from_canonical_usize(pis_degree_bits), )?; witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap)?; if let (Some(quotient_polys_cap_target), Some(quotient_polys_cap)) = From b9c33655e7e0dd0c78e3d14a553c55fbe86853bc Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 8 Nov 2024 19:50:13 -0800 Subject: [PATCH 22/42] start on multi steps --- plonky2/src/fri/oracle.rs | 2 ++ plonky2/src/fri/prover.rs | 14 +++++++++++++- plonky2/src/plonk/prover.rs | 1 + starky/src/fibonacci_stark.rs | 11 ++++++++--- starky/src/prover.rs | 23 +++++++++++++++-------- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index eb8cc020e1..aca68d672d 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -179,6 +179,7 @@ impl, C: GenericConfig, const D: usize> challenger: &mut Challenger, fri_params: &FriParams, final_poly_coeff_len: Option, + query_round_step_count: Option, timing: &mut TimingTree, ) -> FriProof { assert!(D > 1, "Not implemented for D=1."); @@ -228,6 +229,7 @@ impl, C: GenericConfig, const D: usize> challenger, fri_params, final_poly_coeff_len, + query_round_step_count, timing, ); diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 0f08da71d3..7fe93b7d80 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -8,7 +8,7 @@ use crate::field::extension::{flatten, unflatten, Extendable}; use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound, FriQueryStep}; use crate::fri::{FriConfig, FriParams}; -use crate::hash::hash_types::RichField; +use crate::hash::hash_types::{RichField, NUM_HASH_OUT_ELTS}; use crate::hash::hashing::PlonkyPermutation; use crate::hash::merkle_tree::MerkleTree; use crate::iop::challenger::Challenger; @@ -28,6 +28,7 @@ pub fn fri_proof, C: GenericConfig, const challenger: &mut Challenger, fri_params: &FriParams, final_poly_coeff_len: Option, + query_round_step_count: Option, timing: &mut TimingTree, ) -> FriProof { let n = lde_polynomial_values.len(); @@ -43,6 +44,7 @@ pub fn fri_proof, C: GenericConfig, const challenger, fri_params, final_poly_coeff_len, + query_round_step_count, ) ); @@ -83,6 +85,7 @@ fn fri_committed_trees, C: GenericConfig, challenger: &mut Challenger, fri_params: &FriParams, final_poly_coeff_len: Option, + query_round_step_count: Option, ) -> FriCommitedTrees { let mut trees = Vec::with_capacity(fri_params.reduction_arity_bits.len()); @@ -114,6 +117,15 @@ fn fri_committed_trees, C: GenericConfig, values = coeffs.coset_fft(shift.into()) } + if let Some(step_count) = query_round_step_count { + let cap_len = 1 << fri_params.config.cap_height * NUM_HASH_OUT_ELTS; + let zero_cap = vec![F::ZERO; cap_len]; + for _ in step_count..fri_params.reduction_arity_bits.len() { + challenger.observe_elements(&zero_cap); + challenger.get_extension_challenge::(); + } + } + // The coefficients being removed here should always be zero. coeffs .coeffs diff --git a/plonky2/src/plonk/prover.rs b/plonky2/src/plonk/prover.rs index 280337ca55..2b450b01e0 100644 --- a/plonky2/src/plonk/prover.rs +++ b/plonky2/src/plonk/prover.rs @@ -341,6 +341,7 @@ where &mut challenger, &common_data.fri_params, None, + None, timing, ) ); diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index e46b97b5d6..8fece2db20 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -271,11 +271,13 @@ mod tests { fn test_recursive_verifier_with_multiple_degree_bits() -> Result<()> { init_logger(); - let stark_config = StarkConfig::standard_fast_config(); + let mut stark_config = StarkConfig::standard_fast_config(); + stark_config.fri_config.num_query_rounds = 1; let min_degree_bits_to_support = 7; - let verifier_degree_bits = 10; + let verifier_degree_bits = 10; // 14; let degree_bits = min_degree_bits_to_support..=verifier_degree_bits; + let fri_params = stark_config.fri_params(verifier_degree_bits); // Generate STARK proofs for each degree in `degree_bits` let proofs: Vec<_> = degree_bits @@ -292,13 +294,16 @@ mod tests { &stark_config, trace, &public_inputs, - Some(verifier_degree_bits), + Some(fri_params.clone()), &mut TimingTree::default(), ) .unwrap() }) .collect(); + // dbg!(proofs[0].clone()); + // dbg!(proofs[7].clone()); + // Configure the circuit for recursive verification let num_rows = 1 << verifier_degree_bits; let stark = S::new(num_rows); diff --git a/starky/src/prover.rs b/starky/src/prover.rs index a934d3af1a..86b4911e5e 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::FriParams; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::GenericConfig; @@ -40,7 +41,7 @@ pub fn prove( config: &StarkConfig, trace_poly_values: Vec>, public_inputs: &[F], - verifier_circuit_degree_bits: Option, + verifier_circuit_fri_params: Option, timing: &mut TimingTree, ) -> Result> where @@ -76,14 +77,17 @@ where challenger.observe_elements(public_inputs); challenger.observe_cap(&trace_cap); - let final_poly_coeff_len = - if let Some(verifier_circuit_degree_bits) = verifier_circuit_degree_bits { - Some(final_poly_coeff_len( - verifier_circuit_degree_bits, - &fri_params.reduction_arity_bits, - )) + 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, + &fri_params.reduction_arity_bits, + )), + Some(fri_params.reduction_arity_bits.len()), + ) } else { - None + (None, None) }; prove_with_commitment( &stark, @@ -95,6 +99,7 @@ where &mut challenger, public_inputs, final_poly_coeff_len, + query_round_step_count, timing, ) } @@ -116,6 +121,7 @@ pub fn prove_with_commitment( challenger: &mut Challenger, public_inputs: &[F], final_poly_coeff_len: Option, + query_round_step_count: Option, timing: &mut TimingTree, ) -> Result> where @@ -333,6 +339,7 @@ where challenger, &fri_params, final_poly_coeff_len, + query_round_step_count, timing, ) ); From 1eb70530a1be0b22ccdb23773deeaa434e3215c8 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Sat, 9 Nov 2024 15:21:18 -0800 Subject: [PATCH 23/42] wip --- plonky2/src/fri/prover.rs | 2 +- plonky2/src/fri/witness_util.rs | 27 +++++++++++++++++++++------ starky/src/fibonacci_stark.rs | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 7fe93b7d80..66238c5b13 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -118,7 +118,7 @@ fn fri_committed_trees, C: GenericConfig, } if let Some(step_count) = query_round_step_count { - let cap_len = 1 << fri_params.config.cap_height * NUM_HASH_OUT_ELTS; + let cap_len = (1 << fri_params.config.cap_height) * NUM_HASH_OUT_ELTS; let zero_cap = vec![F::ZERO; cap_len]; for _ in step_count..fri_params.reduction_arity_bits.len() { challenger.observe_elements(&zero_cap); diff --git a/plonky2/src/fri/witness_util.rs b/plonky2/src/fri/witness_util.rs index cba1785672..a3a1310a97 100644 --- a/plonky2/src/fri/witness_util.rs +++ b/plonky2/src/fri/witness_util.rs @@ -43,12 +43,27 @@ where witness.set_extension_target(fri_proof_target.final_poly.0[i], F::Extension::ZERO)?; } - for (t, x) in fri_proof_target - .commit_phase_merkle_caps - .iter() - .zip_eq(&fri_proof.commit_phase_merkle_caps) - { - witness.set_cap_target(t, x)?; + let target_caps = &fri_proof_target.commit_phase_merkle_caps; + let proof_caps = &fri_proof.commit_phase_merkle_caps; + + if target_caps.len() < proof_caps.len() { + return Err(anyhow!( + "fri_proof->commit_phase_merkle_caps's target length is less than the proof length" + )); + } + + // Set matching elements in both proof and target caps + for (target_cap, proof_cap) in target_caps.iter().zip(proof_caps) { + witness.set_cap_target(target_cap, proof_cap)?; + } + + // Set remaining elements in target caps to ZERO if target is longer + for target_cap in target_caps.iter().skip(proof_caps.len()) { + for cap_element in target_cap.0.iter() { + for element in cap_element.elements.iter() { + witness.set_target(*element, F::ZERO)?; + } + } } for (qt, q) in fri_proof_target diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 8fece2db20..b89fbcc75f 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -275,7 +275,7 @@ mod tests { stark_config.fri_config.num_query_rounds = 1; let min_degree_bits_to_support = 7; - let verifier_degree_bits = 10; // 14; + let verifier_degree_bits = 14; let degree_bits = min_degree_bits_to_support..=verifier_degree_bits; let fri_params = stark_config.fri_params(verifier_degree_bits); From d94800f27d5a05ac6ae05b7ba108ffb112ace143 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Sat, 9 Nov 2024 15:37:00 -0800 Subject: [PATCH 24/42] set all zeros --- plonky2/src/fri/witness_util.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plonky2/src/fri/witness_util.rs b/plonky2/src/fri/witness_util.rs index a3a1310a97..7f8cee147a 100644 --- a/plonky2/src/fri/witness_util.rs +++ b/plonky2/src/fri/witness_util.rs @@ -59,10 +59,8 @@ where // Set remaining elements in target caps to ZERO if target is longer for target_cap in target_caps.iter().skip(proof_caps.len()) { - for cap_element in target_cap.0.iter() { - for element in cap_element.elements.iter() { - witness.set_target(*element, F::ZERO)?; - } + for hash in target_cap.0.iter() { + witness.set_hash_target(*hash, HashOut::ZERO)?; } } @@ -98,7 +96,7 @@ where } } - for (st, s) in qt.steps.iter().zip_eq(&q.steps) { + for (st, s) in qt.steps.iter().zip(&q.steps) { for (&t, &x) in st.evals.iter().zip_eq(&s.evals) { witness.set_extension_target(t, x)?; } @@ -120,6 +118,17 @@ where witness.set_hash_target(st.merkle_proof.siblings[i], HashOut::ZERO)?; } } + + // Set remaining steps in qt to ZERO if qt.steps is longer + for st in qt.steps.iter().skip(q.steps.len()) { + for &eval in &st.evals { + witness.set_extension_target(eval, F::Extension::ZERO)?; + } + + for &sibling in &st.merkle_proof.siblings { + witness.set_hash_target(sibling, HashOut::ZERO)?; + } + } } Ok(()) From 104ed5760a2c0a05eff8bdaee85e73c83bde3338 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Sat, 9 Nov 2024 15:53:11 -0800 Subject: [PATCH 25/42] wip --- plonky2/src/fri/recursive_verifier.rs | 2 ++ starky/src/fibonacci_stark.rs | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 4e4bd02418..7a0eed541b 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -215,6 +215,8 @@ impl, const D: usize> CircuitBuilder { self.fri_verify_proof_of_work(challenges.fri_pow_response, ¶ms.config) ); + return; + // Check that parameters are coherent. debug_assert_eq!( params.config.num_query_rounds, diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index b89fbcc75f..2e1b5d5378 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -277,7 +277,7 @@ mod tests { let min_degree_bits_to_support = 7; let verifier_degree_bits = 14; let degree_bits = min_degree_bits_to_support..=verifier_degree_bits; - let fri_params = stark_config.fri_params(verifier_degree_bits); + let verifier_fri_params = stark_config.fri_params(verifier_degree_bits); // Generate STARK proofs for each degree in `degree_bits` let proofs: Vec<_> = degree_bits @@ -294,16 +294,13 @@ mod tests { &stark_config, trace, &public_inputs, - Some(fri_params.clone()), + Some(verifier_fri_params.clone()), &mut TimingTree::default(), ) .unwrap() }) .collect(); - // dbg!(proofs[0].clone()); - // dbg!(proofs[7].clone()); - // Configure the circuit for recursive verification let num_rows = 1 << verifier_degree_bits; let stark = S::new(num_rows); From a6fbf7c0850512e426326b6d77b4b021bd5948e4 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Sat, 9 Nov 2024 16:08:46 -0800 Subject: [PATCH 26/42] challenge passes --- plonky2/src/fri/prover.rs | 2 +- starky/src/prover.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 66238c5b13..39f61a6c18 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -120,7 +120,7 @@ fn fri_committed_trees, C: GenericConfig, if let Some(step_count) = query_round_step_count { let cap_len = (1 << fri_params.config.cap_height) * NUM_HASH_OUT_ELTS; let zero_cap = vec![F::ZERO; cap_len]; - for _ in step_count..fri_params.reduction_arity_bits.len() { + for _ in fri_params.reduction_arity_bits.len()..step_count { challenger.observe_elements(&zero_cap); challenger.get_extension_challenge::(); } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 86b4911e5e..f8a98077c9 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -82,9 +82,9 @@ where ( Some(final_poly_coeff_len( verifier_circuit_fri_params.degree_bits, - &fri_params.reduction_arity_bits, + &verifier_circuit_fri_params.reduction_arity_bits, )), - Some(fri_params.reduction_arity_bits.len()), + Some(verifier_circuit_fri_params.reduction_arity_bits.len()), ) } else { (None, None) From 63687266dbb0a75352b0d95f77d9a71ac8bfcf85 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Tue, 12 Nov 2024 11:54:02 -0800 Subject: [PATCH 27/42] work --- plonky2/src/fri/recursive_verifier.rs | 25 ++++++++++++++++++++----- plonky2/src/hash/merkle_proofs.rs | 3 ++- plonky2/src/plonk/circuit_builder.rs | 12 ++++++++++++ starky/src/recursive_verifier.rs | 12 +++++++++--- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 7a0eed541b..65f48ea3e0 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -189,6 +189,7 @@ impl, const D: usize> CircuitBuilder { proof: &FriProofTarget, params: &FriParams, current_degree_bits: Target, + degree_sub_one_bits_vec: &[BoolTarget], min_degree_bits_to_support: usize, ) where C::Hasher: AlgebraicHasher, @@ -215,8 +216,6 @@ impl, const D: usize> CircuitBuilder { self.fri_verify_proof_of_work(challenges.fri_pow_response, ¶ms.config) ); - return; - // Check that parameters are coherent. debug_assert_eq!( params.config.num_query_rounds, @@ -258,6 +257,7 @@ impl, const D: usize> CircuitBuilder { challenges.fri_query_indices[i], min_log_n_to_support..=log_n, current_log_n, + degree_sub_one_bits_vec, round_proof, params, ) @@ -301,6 +301,7 @@ impl, const D: usize> CircuitBuilder { initial_merkle_caps: &[MerkleCapTarget], cap_index: Target, ) { + let one = self.one(); for (i, ((evals, merkle_proof), cap)) in proof .evals_proofs .iter() @@ -311,6 +312,7 @@ impl, const D: usize> CircuitBuilder { self, &format!("verify {i}'th initial Merkle proof"), self.verify_merkle_proof_to_cap_with_cap_indices::( + one, evals.clone(), x_index_bits, log_n_range.clone(), @@ -491,6 +493,7 @@ impl, const D: usize> CircuitBuilder { x_index: Target, log_n_range: RangeInclusive, log_n: Target, + degree_sub_one_bits_vec: &[BoolTarget], round_proof: &FriQueryRoundTarget, params: &FriParams, ) where @@ -559,6 +562,13 @@ impl, const D: usize> CircuitBuilder { ) ); + let mut index_in_degree_sub_one_bits_vec = { + let mut degree_bits_len = degree_sub_one_bits_vec.len(); + for artity_bits in ¶ms.reduction_arity_bits { + degree_bits_len -= artity_bits; + } + degree_bits_len + }; for (i, &arity_bits) in params.reduction_arity_bits.iter().enumerate() { let evals = &round_proof.steps[i].evals; @@ -569,10 +579,11 @@ impl, const D: usize> CircuitBuilder { // Check consistency with our old evaluation from the previous round. let new_eval = self.random_access_extension(x_index_within_coset, evals.clone()); - self.connect_extension(new_eval, old_eval); + let step_active = degree_sub_one_bits_vec[index_in_degree_sub_one_bits_vec]; + self.conditional_assert_eq_ext(step_active.target, new_eval, old_eval); // Infer P(y) from {P(x)}_{x^arity=y}. - old_eval = with_context!( + let eval = with_context!( self, "infer evaluation using interpolation", self.compute_evaluation( @@ -583,11 +594,13 @@ impl, const D: usize> CircuitBuilder { challenges.fri_betas[i], ) ); + old_eval = self.select_ext(step_active, eval, old_eval); with_context!( self, "verify FRI round Merkle proof.", self.verify_merkle_proof_to_cap_with_cap_indices::( + step_active.target, flatten_target(evals), &coset_index_bits, log_n_range.clone(), @@ -599,9 +612,11 @@ impl, const D: usize> CircuitBuilder { ); // Update the point x to x^arity. - subgroup_x = self.exp_power_of_2(subgroup_x, arity_bits); + let subgroup_x_cur = self.exp_power_of_2(subgroup_x, arity_bits); + subgroup_x = self.select(step_active, subgroup_x_cur, subgroup_x); x_index_bits = coset_index_bits; + index_in_degree_sub_one_bits_vec += arity_bits; } // Final check of FRI. After all the reductions, we check that the final polynomial is equal diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 9386d9133a..424e03ae64 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -185,6 +185,7 @@ impl, const D: usize> CircuitBuilder { /// rather than being contained in `leaf_index_bits`. pub(crate) fn verify_merkle_proof_to_cap_with_cap_indices>( &mut self, + condition: Target, leaf_data: Vec, leaf_index_bits: &[BoolTarget], log_n_range: RangeInclusive, @@ -233,7 +234,7 @@ impl, const D: usize> CircuitBuilder { n_index, final_states.iter().map(|s| s.elements[i]).collect(), ); - self.connect(result, state); + self.conditional_assert_eq(condition, result, state); } } diff --git a/plonky2/src/plonk/circuit_builder.rs b/plonky2/src/plonk/circuit_builder.rs index d0d96f39ba..16f2a61c32 100644 --- a/plonky2/src/plonk/circuit_builder.rs +++ b/plonky2/src/plonk/circuit_builder.rs @@ -548,6 +548,18 @@ impl, const D: usize> CircuitBuilder { self.connect(constr, zero); } + /// If `condition`, enforces that two `ExtensionTarget` values are equal. + pub fn conditional_assert_eq_ext( + &mut self, + condition: Target, + x: ExtensionTarget, + y: ExtensionTarget, + ) { + for i in 0..D { + self.conditional_assert_eq(condition, x.0[i], y.0[i]); + } + } + /// Enforces that a routable `Target` value is 0, using Plonk's permutation argument. pub fn assert_zero(&mut self, x: Target) { let zero = self.zero(); diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index dfb0735822..5c716d5abc 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -117,7 +117,7 @@ pub fn verify_stark_proof_with_challenges_circuit< .collect::>(), ); - let max_num_degree_bits = F::TWO_ADICITY; + let max_num_of_bits_in_degree = degree_bits + 1; { // degree_bits should be nonzero. let max_num_degree_bits_bits = log2_ceil(F::TWO_ADICITY); @@ -130,8 +130,8 @@ pub fn verify_stark_proof_with_challenges_circuit< } let two = builder.two(); - let degree = builder.exp(two, proof.degree_bits, max_num_degree_bits); - let degree_bits_vec = builder.split_le(degree, max_num_degree_bits); + let degree = builder.exp(two, proof.degree_bits, max_num_of_bits_in_degree); + let degree_bits_vec = builder.split_le(degree, max_num_of_bits_in_degree); let zeta_pow_deg = builder.exp_extension_from_bits(challenges.stark_zeta, °ree_bits_vec); let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); @@ -217,6 +217,11 @@ pub fn verify_stark_proof_with_challenges_circuit< ctl_zs_first.as_ref().map_or(0, |c| c.len()), inner_config, ); + + let one = builder.one(); + let degree_sub_one = builder.sub(degree, one); + let degree_sub_one_bits_vec = builder.split_le(degree_sub_one, degree_bits); + if let Some(min_degree_bits_to_support) = min_degree_bits_to_support { builder.verify_fri_proof_with_multiple_degree_bits::( &fri_instance, @@ -226,6 +231,7 @@ pub fn verify_stark_proof_with_challenges_circuit< &proof.opening_proof, &inner_config.fri_params(degree_bits), proof.degree_bits, + °ree_sub_one_bits_vec, min_degree_bits_to_support, ); } else { From 309ecaa8587d02bb5c137548bb6aee0988844f54 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Tue, 12 Nov 2024 13:27:08 -0800 Subject: [PATCH 28/42] poc done --- plonky2/src/fri/recursive_verifier.rs | 7 +++--- plonky2/src/gadgets/random_access.rs | 34 +++++++++++++++++++++++++++ plonky2/src/hash/merkle_proofs.rs | 4 ++-- starky/src/fibonacci_stark.rs | 6 ++--- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index 65f48ea3e0..dd5d147088 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -517,7 +517,7 @@ impl, const D: usize> CircuitBuilder { self.le_sum(x_index_bits[slice_start..n].iter()) }) .collect(); - let cap_index = self.random_access(n_index, cap_indices); + let cap_index = self.random_access_with_padding(n_index, cap_indices); with_context!( self, "check FRI initial proof", @@ -545,7 +545,7 @@ impl, const D: usize> CircuitBuilder { }) .collect(); - let mut subgroup_x = self.random_access(n_index, subgroup_x_vec); + let mut subgroup_x = self.random_access_with_padding(n_index, subgroup_x_vec); // old_eval is the last derived evaluation; it will be checked for consistency with its // committed "parent" value in the next iteration. @@ -578,7 +578,8 @@ impl, const D: usize> CircuitBuilder { let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); // Check consistency with our old evaluation from the previous round. - let new_eval = self.random_access_extension(x_index_within_coset, evals.clone()); + let new_eval = + self.random_access_extension_with_padding(x_index_within_coset, evals.clone()); let step_active = degree_sub_one_bits_vec[index_in_degree_sub_one_bits_vec]; self.conditional_assert_eq_ext(step_active.target, new_eval, old_eval); diff --git a/plonky2/src/gadgets/random_access.rs b/plonky2/src/gadgets/random_access.rs index 0d99a3e918..f740b6a6bb 100644 --- a/plonky2/src/gadgets/random_access.rs +++ b/plonky2/src/gadgets/random_access.rs @@ -1,6 +1,8 @@ #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use itertools::repeat_n; + use crate::field::extension::Extendable; use crate::gates::random_access::RandomAccessGate; use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField}; @@ -39,6 +41,20 @@ impl, const D: usize> CircuitBuilder { claimed_element } + /// Like `random_access`, but padding `v` with the last element to a power of two. + pub fn random_access_with_padding(&mut self, access_index: Target, v: Vec) -> Target { + let mut v = v; + let current_len = v.len(); + let next_power_of_two = current_len.next_power_of_two(); + if current_len < next_power_of_two { + // Get the last element (if there is one) and extend with it + if let Some(&last) = v.last() { + v.extend(repeat_n(last, next_power_of_two - current_len)); + } + } + self.random_access(access_index, v) + } + /// Like `random_access`, but with `ExtensionTarget`s rather than simple `Target`s. pub fn random_access_extension( &mut self, @@ -52,6 +68,24 @@ impl, const D: usize> CircuitBuilder { ExtensionTarget(selected.try_into().unwrap()) } + /// Like `random_access_extension`, but padding `v` with the last element to a power of two. + pub fn random_access_extension_with_padding( + &mut self, + access_index: Target, + v: Vec>, + ) -> ExtensionTarget { + let mut v = v; + let current_len = v.len(); + let next_power_of_two = current_len.next_power_of_two(); + if current_len < next_power_of_two { + // Get the last element (if there is one) and extend with it + if let Some(&last) = v.last() { + v.extend(repeat_n(last, next_power_of_two - current_len)); + } + } + self.random_access_extension(access_index, v) + } + /// Like `random_access`, but with `HashOutTarget`s rather than simple `Target`s. pub fn random_access_hash( &mut self, diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 424e03ae64..21a822fa19 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -226,11 +226,11 @@ impl, const D: usize> CircuitBuilder { } for i in 0..NUM_HASH_OUT_ELTS { - let result = self.random_access( + let result = self.random_access_with_padding( cap_index, merkle_cap.0.iter().map(|h| h.elements[i]).collect(), ); - let state = self.random_access( + let state = self.random_access_with_padding( n_index, final_states.iter().map(|s| s.elements[i]).collect(), ); diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 2e1b5d5378..35fb13b5ce 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -274,9 +274,9 @@ mod tests { let mut stark_config = StarkConfig::standard_fast_config(); stark_config.fri_config.num_query_rounds = 1; - let min_degree_bits_to_support = 7; - let verifier_degree_bits = 14; - let degree_bits = min_degree_bits_to_support..=verifier_degree_bits; + let min_degree_bits_to_support = 4; + let verifier_degree_bits = 30; + let degree_bits = 6..=15; let verifier_fri_params = stark_config.fri_params(verifier_degree_bits); // Generate STARK proofs for each degree in `degree_bits` From de1c230a1c0c27f3eb685bb45aeb331dd3de1380 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Tue, 12 Nov 2024 13:39:12 -0800 Subject: [PATCH 29/42] fix non std build --- plonky2/src/fri/prover.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 39f61a6c18..a3a566cc1a 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -1,4 +1,6 @@ #[cfg(not(feature = "std"))] +use alloc::vec; +#[cfg(not(feature = "std"))] use alloc::vec::Vec; use plonky2_field::types::Field; From 95089274ecf22779a918102c4fe2b0ace0f4282d Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Tue, 12 Nov 2024 14:29:06 -0800 Subject: [PATCH 30/42] add comments --- plonky2/src/fri/prover.rs | 2 ++ plonky2/src/util/serialization/mod.rs | 1 + starky/src/fibonacci_stark.rs | 4 ++-- starky/src/recursive_verifier.rs | 18 +++++------------- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index a3a566cc1a..44f67a136a 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -119,6 +119,8 @@ fn fri_committed_trees, C: GenericConfig, values = coeffs.coset_fft(shift.into()) } + // When verifying this proof 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 << fri_params.config.cap_height) * NUM_HASH_OUT_ELTS; let zero_cap = vec![F::ZERO; cap_len]; diff --git a/plonky2/src/util/serialization/mod.rs b/plonky2/src/util/serialization/mod.rs index f4b7b920ac..90e150ea85 100644 --- a/plonky2/src/util/serialization/mod.rs +++ b/plonky2/src/util/serialization/mod.rs @@ -681,6 +681,7 @@ pub trait Read { let reduction_arity_bits = self.read_usize_vec()?; let degree_bits = self.read_usize()?; let hiding = self.read_bool()?; + Ok(FriParams { config, reduction_arity_bits, diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 35fb13b5ce..56f60af430 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -208,7 +208,7 @@ mod tests { init_logger(); let config = StarkConfig::standard_fast_config(); - let degree_bits = 10; + let degree_bits = 5; let num_rows = 1 << degree_bits; let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; @@ -276,7 +276,7 @@ mod tests { let min_degree_bits_to_support = 4; let verifier_degree_bits = 30; - let degree_bits = 6..=15; + let degree_bits = 4..=15; let verifier_fri_params = stark_config.fri_params(verifier_degree_bits); // Generate STARK proofs for each degree in `degree_bits` diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 5c716d5abc..92895eb935 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -18,7 +18,6 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::util::reducing::ReducingFactorTarget; use plonky2::with_context; -use plonky2_util::log2_ceil; use crate::config::StarkConfig; use crate::constraint_consumer::RecursiveConstraintConsumer; @@ -94,6 +93,7 @@ pub fn verify_stark_proof_with_challenges_circuit< let zero = builder.zero(); let one = builder.one_extension(); + let two = builder.two(); let num_ctl_polys = ctl_vars .map(|v| v.iter().map(|ctl| ctl.helper_columns.len()).sum::()) @@ -117,19 +117,10 @@ pub fn verify_stark_proof_with_challenges_circuit< .collect::>(), ); - let max_num_of_bits_in_degree = degree_bits + 1; - { - // degree_bits should be nonzero. - let max_num_degree_bits_bits = log2_ceil(F::TWO_ADICITY); - let degree_bits_bits = builder.split_le(proof.degree_bits, max_num_degree_bits_bits); - let mut or_all_bits = builder._false(); - for i in 0..max_num_degree_bits_bits { - or_all_bits = builder.or(or_all_bits, degree_bits_bits[i]); - } - builder.assert_one(or_all_bits.target); - } + // degree_bits should be nonzero. + let _ = builder.inverse(proof.degree_bits); - let two = builder.two(); + let max_num_of_bits_in_degree = degree_bits + 1; let degree = builder.exp(two, proof.degree_bits, max_num_of_bits_in_degree); let degree_bits_vec = builder.split_le(degree, max_num_of_bits_in_degree); @@ -220,6 +211,7 @@ pub fn verify_stark_proof_with_challenges_circuit< let one = builder.one(); let degree_sub_one = builder.sub(degree, one); + // Used to check if we want to skip a Fri query step. let degree_sub_one_bits_vec = builder.split_le(degree_sub_one, degree_bits); if let Some(min_degree_bits_to_support) = min_degree_bits_to_support { From 1ca436fd2c2e3a9906eb92f6a2111cefe3c0740b Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Thu, 14 Nov 2024 16:10:02 -0800 Subject: [PATCH 31/42] add degree bits --- starky/src/fibonacci_stark.rs | 4 ++-- starky/src/get_challenges.rs | 5 ++--- starky/src/permutation_stark.rs | 2 +- starky/src/proof.rs | 14 ++------------ starky/src/prover.rs | 1 + starky/src/unconstrained_stark.rs | 2 +- starky/src/verifier.rs | 7 +++---- 7 files changed, 12 insertions(+), 23 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 56f60af430..4a06dccebc 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -224,7 +224,7 @@ mod tests { &mut TimingTree::default(), )?; verify_stark_proof(stark, proof.clone(), &config)?; - assert_eq!(degree_bits, proof.proof.recover_degree_bits(&config)); + assert_eq!(degree_bits, proof.proof.degree_bits); recursive_proof::(stark, proof, &config, true) } @@ -247,7 +247,7 @@ mod tests { let circuit_config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(circuit_config); let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let degree_bits = inner_proof.proof.degree_bits; let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 1d9b55b437..cae7544ef5 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -100,8 +100,6 @@ where ignore_trace_cap: bool, config: &StarkConfig, ) -> StarkProofChallenges { - let degree_bits = self.recover_degree_bits(config); - let StarkProof { trace_cap, auxiliary_polys_cap, @@ -114,6 +112,7 @@ where pow_witness, .. }, + degree_bits, } = &self; let trace_cap = if ignore_trace_cap { @@ -133,7 +132,7 @@ where final_poly, *pow_witness, config, - degree_bits, + *degree_bits, ) } } diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index 81a0897f5f..6a3d6bb2ad 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -217,7 +217,7 @@ mod tests { let circuit_config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(circuit_config); let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let degree_bits = inner_proof.proof.degree_bits; let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 2d71d8708c..2ec2736241 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -38,18 +38,8 @@ pub struct StarkProof, C: GenericConfig, pub openings: StarkOpeningSet, /// A batch FRI argument for all openings. pub opening_proof: FriProof, -} - -impl, C: GenericConfig, const D: usize> StarkProof { - /// Recover the length of the trace from a STARK proof and a STARK config. - pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { - let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] - .initial_trees_proof - .evals_proofs[0] - .1; - let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len(); - lde_bits - config.fri_config.rate_bits - } + /// Log2 of the trace table's degree + pub degree_bits: usize, } /// Circuit version of [`StarkProof`]. diff --git a/starky/src/prover.rs b/starky/src/prover.rs index f8a98077c9..eae6e43b4b 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -350,6 +350,7 @@ where quotient_polys_cap, openings, opening_proof, + degree_bits, }; Ok(StarkProofWithPublicInputs { diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index 08f52d692a..3b8eaa70d1 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -182,7 +182,7 @@ mod tests { let circuit_config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(circuit_config); let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let degree_bits = inner_proof.proof.degree_bits; let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index d56072ad3a..59e27db55f 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -107,7 +107,7 @@ where .collect::>(), ); - let degree_bits = proof.recover_degree_bits(config); + let degree_bits = proof.degree_bits; let (l_0, l_last) = eval_l_0_and_l_last(degree_bits, challenges.stark_zeta); let last = F::primitive_root_of_unity(degree_bits).inverse(); let z_last = challenges.stark_zeta - last.into(); @@ -220,8 +220,6 @@ where C: GenericConfig, S: Stark, { - let degree_bits = proof.recover_degree_bits(config); - let StarkProof { trace_cap, auxiliary_polys_cap, @@ -230,6 +228,7 @@ where // The shape of the opening proof will be checked in the FRI verifier (see // validate_fri_proof_shape), so we ignore it here. opening_proof: _, + degree_bits, } = proof; let StarkOpeningSet { @@ -243,7 +242,7 @@ where ensure!(public_inputs.len() == S::PUBLIC_INPUTS); - let fri_params = config.fri_params(degree_bits); + let fri_params = config.fri_params(*degree_bits); let cap_height = fri_params.config.cap_height; ensure!(trace_cap.height() == cap_height); From bafcf740b18118bace2eae2236fbc6a8fe31f295 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 09:56:07 -0800 Subject: [PATCH 32/42] update stark verifier --- plonky2/src/fri/challenges.rs | 25 +++++++++++++++++++++++- plonky2/src/plonk/get_challenges.rs | 2 ++ starky/src/fibonacci_stark.rs | 12 ++++++++---- starky/src/get_challenges.rs | 30 +++++++++++++++++++++++++++-- starky/src/permutation_stark.rs | 4 ++-- starky/src/unconstrained_stark.rs | 4 ++-- starky/src/verifier.rs | 10 +++++++++- 7 files changed, 75 insertions(+), 12 deletions(-) diff --git a/plonky2/src/fri/challenges.rs b/plonky2/src/fri/challenges.rs index be73a8c241..6af3183f00 100644 --- a/plonky2/src/fri/challenges.rs +++ b/plonky2/src/fri/challenges.rs @@ -1,10 +1,12 @@ +use plonky2_field::types::Field; + use crate::field::extension::Extendable; use crate::field::polynomial::PolynomialCoeffs; 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 +30,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 +50,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 56f60af430..201ae62f32 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.recover_degree_bits(&config)); recursive_proof::(stark, proof, &config, true) @@ -304,8 +304,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 1d9b55b437..fd50d5068e 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 degree_bits = self.recover_degree_bits(config); @@ -134,6 +153,7 @@ where *pow_witness, config, degree_bits, + verifier_circuit_fri_params, ) } } @@ -156,10 +176,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/permutation_stark.rs b/starky/src/permutation_stark.rs index 81a0897f5f..e656554104 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/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index 08f52d692a..ec6045c4bc 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 d56072ad3a..7bf43a4e0e 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, From 1e3c8b9ebf5587a7184abe00a6b3b088a7879bfb Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 09:58:42 -0800 Subject: [PATCH 33/42] fix clippy --- plonky2/src/fri/challenges.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plonky2/src/fri/challenges.rs b/plonky2/src/fri/challenges.rs index 6af3183f00..1252f54cad 100644 --- a/plonky2/src/fri/challenges.rs +++ b/plonky2/src/fri/challenges.rs @@ -1,7 +1,9 @@ -use plonky2_field::types::Field; +#[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; From f31babe9039b80424e0d8e831a3407b42799e904 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 10:37:32 -0800 Subject: [PATCH 34/42] fix test build --- plonky2/src/batch_fri/oracle.rs | 2 ++ plonky2/src/batch_fri/prover.rs | 4 ++++ 2 files changed, 6 insertions(+) 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 { From 8572c8151a0bd6705da596fe4e1e28c5c6843c60 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 10:45:35 -0800 Subject: [PATCH 35/42] fix tests --- starky/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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!") //! } //! ``` From 93ba39cce86502a57b36ebc71e0c9407ad7d1868 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 10:56:57 -0800 Subject: [PATCH 36/42] add comments --- starky/src/fibonacci_stark.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 201ae62f32..4dc1752f33 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -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); From 853acb4530cad0343f861b1b5c871ed55cba2b06 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 11:11:20 -0800 Subject: [PATCH 37/42] add checks --- starky/src/prover.rs | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/starky/src/prover.rs b/starky/src/prover.rs index f8a98077c9..a1647a6913 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; @@ -59,6 +60,31 @@ where "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 { + let len = final_poly_coeff_len( + verifier_circuit_fri_params.degree_bits, + &verifier_circuit_fri_params.reduction_arity_bits, + ); + let strategy = &config.fri_config.reduction_strategy; + // Assert that the strategy is `ConstantArityBits` + assert!( + matches!(strategy, FriReductionStrategy::ConstantArityBits(_, _)), + "Fri Reduction Strategy is not ConstantArityBits" + ); + if let FriReductionStrategy::ConstantArityBits(_, final_poly_bits) = strategy { + assert_eq!(len, 1 << (1 + *final_poly_bits)); + } else { + panic!("Fri Reduction Strategy is not ConstantArityBits"); + } + ( + Some(len), + Some(verifier_circuit_fri_params.reduction_arity_bits.len()), + ) + } else { + (None, None) + }; + let trace_commitment = timed!( timing, "compute trace commitment", @@ -76,19 +102,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, From 281d738774a34ac7f97f50a9032e3813fd2eca3c Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 11:19:13 -0800 Subject: [PATCH 38/42] polish the checks --- starky/src/prover.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/starky/src/prover.rs b/starky/src/prover.rs index a1647a6913..55a734f6a6 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -59,28 +59,23 @@ 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 { let len = final_poly_coeff_len( verifier_circuit_fri_params.degree_bits, &verifier_circuit_fri_params.reduction_arity_bits, ); - let strategy = &config.fri_config.reduction_strategy; - // Assert that the strategy is `ConstantArityBits` - assert!( - matches!(strategy, FriReductionStrategy::ConstantArityBits(_, _)), - "Fri Reduction Strategy is not ConstantArityBits" - ); - if let FriReductionStrategy::ConstantArityBits(_, final_poly_bits) = strategy { - assert_eq!(len, 1 << (1 + *final_poly_bits)); - } else { - panic!("Fri Reduction Strategy is not ConstantArityBits"); + + match &config.fri_config.reduction_strategy { + FriReductionStrategy::ConstantArityBits(_, final_poly_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"), } - ( - Some(len), - Some(verifier_circuit_fri_params.reduction_arity_bits.len()), - ) } else { (None, None) }; From 1e32adf5d75b1a087a9b1802757efcd114a4d97b Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 15 Nov 2024 11:31:59 -0800 Subject: [PATCH 39/42] more checks --- starky/src/prover.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 55a734f6a6..65989e04c1 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -61,13 +61,13 @@ where ); let (final_poly_coeff_len, query_round_step_count) = if let Some(verifier_circuit_fri_params) = verifier_circuit_fri_params { - let len = final_poly_coeff_len( - verifier_circuit_fri_params.degree_bits, - &verifier_circuit_fri_params.reduction_arity_bits, - ); - + 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), From af50158b7fe37b7db85fc8f19023cb97374725c1 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Mon, 18 Nov 2024 11:53:59 -0800 Subject: [PATCH 40/42] more --- starky/src/fibonacci_stark.rs | 17 +++++++---------- starky/src/permutation_stark.rs | 2 +- starky/src/recursive_verifier.rs | 9 +++++++-- starky/src/unconstrained_stark.rs | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index aa5823696a..2a6698c7f5 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -138,7 +138,6 @@ mod tests { use alloc::vec::Vec; use anyhow::Result; - use itertools::Itertools; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; @@ -250,7 +249,7 @@ mod tests { let degree_bits = inner_proof.proof.degree_bits; let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config, None); @@ -338,14 +337,12 @@ mod tests { let data = builder.build::(); // Verify each proof using partial witnesses - degree_bits - .zip_eq(proofs) - .try_for_each(|(degree_bits, proof)| { - let mut pw = PartialWitness::new(); - set_stark_proof_with_pis_target(&mut pw, &pt, &proof, degree_bits, zero)?; - let proof = data.prove(pw)?; - data.verify(proof) - })?; + proofs.iter().try_for_each(|proof| { + let mut pw = PartialWitness::new(); + set_stark_proof_with_pis_target(&mut pw, &pt, &proof, zero)?; + let proof = data.prove(pw)?; + data.verify(proof) + })?; Ok(()) } diff --git a/starky/src/permutation_stark.rs b/starky/src/permutation_stark.rs index 31f8280c7b..2dc0d8f1f1 100644 --- a/starky/src/permutation_stark.rs +++ b/starky/src/permutation_stark.rs @@ -220,7 +220,7 @@ mod tests { let degree_bits = inner_proof.proof.degree_bits; let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config, None); diff --git a/starky/src/recursive_verifier.rs b/starky/src/recursive_verifier.rs index 92895eb935..0badbed53f 100644 --- a/starky/src/recursive_verifier.rs +++ b/starky/src/recursive_verifier.rs @@ -366,7 +366,6 @@ pub fn set_stark_proof_with_pis_target, W, const D witness: &mut W, stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget, stark_proof_with_pis: &StarkProofWithPublicInputs, - pis_degree_bits: usize, zero: Target, ) -> Result<()> where @@ -388,7 +387,13 @@ where witness.set_target(pi_t, pi)?; } - set_stark_proof_target(witness, pt, proof, pis_degree_bits, zero) + set_stark_proof_target( + witness, + pt, + proof, + stark_proof_with_pis.proof.degree_bits, + zero, + ) } /// Set the targets in a [`StarkProofTarget`] to their corresponding values in a diff --git a/starky/src/unconstrained_stark.rs b/starky/src/unconstrained_stark.rs index 6887b279e1..08fc3252e2 100644 --- a/starky/src/unconstrained_stark.rs +++ b/starky/src/unconstrained_stark.rs @@ -185,7 +185,7 @@ mod tests { let degree_bits = inner_proof.proof.degree_bits; let pt = add_virtual_stark_proof_with_pis(&mut builder, &stark, inner_config, degree_bits, 0, 0); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, degree_bits, builder.zero())?; + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof, builder.zero())?; verify_stark_proof_circuit::(&mut builder, stark, pt, inner_config, None); From 7e69abc5858faffb0a9adcb25faf14303e360aed Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Mon, 18 Nov 2024 12:19:55 -0800 Subject: [PATCH 41/42] clippy --- starky/src/fibonacci_stark.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 2a6698c7f5..64bd75063b 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -339,7 +339,7 @@ mod tests { // Verify each proof using partial witnesses proofs.iter().try_for_each(|proof| { let mut pw = PartialWitness::new(); - set_stark_proof_with_pis_target(&mut pw, &pt, &proof, zero)?; + set_stark_proof_with_pis_target(&mut pw, &pt, proof, zero)?; let proof = data.prove(pw)?; data.verify(proof) })?; From bcb72ff82c20076d9fb0761b9622d40295192942 Mon Sep 17 00:00:00 2001 From: Sai Deng Date: Fri, 22 Nov 2024 10:35:16 -0800 Subject: [PATCH 42/42] comments --- plonky2/src/fri/challenges.rs | 4 +-- plonky2/src/fri/oracle.rs | 4 +-- plonky2/src/fri/prover.rs | 11 +++---- plonky2/src/fri/recursive_verifier.rs | 19 +++++++----- plonky2/src/gadgets/random_access.rs | 42 +++++++++------------------ plonky2/src/hash/merkle_proofs.rs | 4 +-- starky/src/get_challenges.rs | 4 +-- starky/src/prover.rs | 8 ++--- 8 files changed, 44 insertions(+), 52 deletions(-) diff --git a/plonky2/src/fri/challenges.rs b/plonky2/src/fri/challenges.rs index 1252f54cad..b261a55e31 100644 --- a/plonky2/src/fri/challenges.rs +++ b/plonky2/src/fri/challenges.rs @@ -33,7 +33,7 @@ impl> Challenger { degree_bits: usize, config: &FriConfig, final_poly_coeff_len: Option, - query_round_step_count: Option, + max_num_query_steps: Option, ) -> FriChallenges where F: RichField + Extendable, @@ -54,7 +54,7 @@ impl> Challenger { // 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 { + if let Some(step_count) = max_num_query_steps { 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 { diff --git a/plonky2/src/fri/oracle.rs b/plonky2/src/fri/oracle.rs index aca68d672d..e413071a45 100644 --- a/plonky2/src/fri/oracle.rs +++ b/plonky2/src/fri/oracle.rs @@ -179,7 +179,7 @@ impl, C: GenericConfig, const D: usize> challenger: &mut Challenger, fri_params: &FriParams, final_poly_coeff_len: Option, - query_round_step_count: Option, + max_num_query_steps: Option, timing: &mut TimingTree, ) -> FriProof { assert!(D > 1, "Not implemented for D=1."); @@ -229,7 +229,7 @@ impl, C: GenericConfig, const D: usize> challenger, fri_params, final_poly_coeff_len, - query_round_step_count, + max_num_query_steps, timing, ); diff --git a/plonky2/src/fri/prover.rs b/plonky2/src/fri/prover.rs index 44f67a136a..24c88ced70 100644 --- a/plonky2/src/fri/prover.rs +++ b/plonky2/src/fri/prover.rs @@ -30,7 +30,7 @@ pub fn fri_proof, C: GenericConfig, const challenger: &mut Challenger, fri_params: &FriParams, final_poly_coeff_len: Option, - query_round_step_count: Option, + max_num_query_steps: Option, timing: &mut TimingTree, ) -> FriProof { let n = lde_polynomial_values.len(); @@ -46,7 +46,7 @@ pub fn fri_proof, C: GenericConfig, const challenger, fri_params, final_poly_coeff_len, - query_round_step_count, + max_num_query_steps, ) ); @@ -87,7 +87,7 @@ fn fri_committed_trees, C: GenericConfig, challenger: &mut Challenger, fri_params: &FriParams, final_poly_coeff_len: Option, - query_round_step_count: Option, + max_num_query_steps: Option, ) -> FriCommitedTrees { let mut trees = Vec::with_capacity(fri_params.reduction_arity_bits.len()); @@ -120,8 +120,9 @@ fn fri_committed_trees, C: GenericConfig, } // When verifying this proof 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 { + // we need the challenger to stay in sync with the verifier. Therefore, the challenger + // must observe the additional hash caps and generate dummy challenges. + if let Some(step_count) = max_num_query_steps { let cap_len = (1 << fri_params.config.cap_height) * NUM_HASH_OUT_ELTS; let zero_cap = vec![F::ZERO; cap_len]; for _ in fri_params.reduction_arity_bits.len()..step_count { diff --git a/plonky2/src/fri/recursive_verifier.rs b/plonky2/src/fri/recursive_verifier.rs index dd5d147088..f2880f678f 100644 --- a/plonky2/src/fri/recursive_verifier.rs +++ b/plonky2/src/fri/recursive_verifier.rs @@ -180,6 +180,12 @@ impl, const D: usize> CircuitBuilder { } } + /// Verifies the current FRI proof with `current_degree_bits`, which may differ from the + /// circuit's `degree_bits` in `params`. + /// The circuit uses random access gates to select and connect the current hash/evaluation + /// values with those in the proof. It is designed with the maximum number of query/folding + /// steps and final polynomial length at `degree_bits`, "skipping" steps when the actual proof + /// has fewer. pub fn verify_fri_proof_with_multiple_degree_bits>( &mut self, instance: &FriInstanceInfoTarget, @@ -208,7 +214,7 @@ impl, const D: usize> CircuitBuilder { let log_n = params.config.rate_bits + params.degree_bits; let mut current_log_n = self.constant(F::from_canonical_usize(params.config.rate_bits)); current_log_n = self.add(current_log_n, current_degree_bits); - let min_log_n_to_support = log_n - (params.degree_bits - min_degree_bits_to_support); + let min_log_n_to_support = params.config.rate_bits + min_degree_bits_to_support; with_context!( self, @@ -517,7 +523,7 @@ impl, const D: usize> CircuitBuilder { self.le_sum(x_index_bits[slice_start..n].iter()) }) .collect(); - let cap_index = self.random_access_with_padding(n_index, cap_indices); + let cap_index = self.random_access(n_index, cap_indices); with_context!( self, "check FRI initial proof", @@ -545,7 +551,7 @@ impl, const D: usize> CircuitBuilder { }) .collect(); - let mut subgroup_x = self.random_access_with_padding(n_index, subgroup_x_vec); + let mut subgroup_x = self.random_access(n_index, subgroup_x_vec); // old_eval is the last derived evaluation; it will be checked for consistency with its // committed "parent" value in the next iteration. @@ -564,8 +570,8 @@ impl, const D: usize> CircuitBuilder { let mut index_in_degree_sub_one_bits_vec = { let mut degree_bits_len = degree_sub_one_bits_vec.len(); - for artity_bits in ¶ms.reduction_arity_bits { - degree_bits_len -= artity_bits; + for arity_bits in ¶ms.reduction_arity_bits { + degree_bits_len -= arity_bits; } degree_bits_len }; @@ -578,8 +584,7 @@ impl, const D: usize> CircuitBuilder { let x_index_within_coset = self.le_sum(x_index_within_coset_bits.iter()); // Check consistency with our old evaluation from the previous round. - let new_eval = - self.random_access_extension_with_padding(x_index_within_coset, evals.clone()); + let new_eval = self.random_access_extension(x_index_within_coset, evals.clone()); let step_active = degree_sub_one_bits_vec[index_in_degree_sub_one_bits_vec]; self.conditional_assert_eq_ext(step_active.target, new_eval, old_eval); diff --git a/plonky2/src/gadgets/random_access.rs b/plonky2/src/gadgets/random_access.rs index f740b6a6bb..543248bf4f 100644 --- a/plonky2/src/gadgets/random_access.rs +++ b/plonky2/src/gadgets/random_access.rs @@ -15,6 +15,15 @@ use crate::util::log2_strict; impl, const D: usize> CircuitBuilder { /// Checks that a `Target` matches a vector at a particular index. pub fn random_access(&mut self, access_index: Target, v: Vec) -> Target { + let mut v = v; + let current_len = v.len(); + let next_power_of_two = current_len.next_power_of_two(); + if current_len < next_power_of_two { + // Get the last element (if there is one) and extend with it + if let Some(&last) = v.last() { + v.extend(repeat_n(last, next_power_of_two - current_len)); + } + } let vec_size = v.len(); let bits = log2_strict(vec_size); debug_assert!(vec_size > 0); @@ -41,38 +50,11 @@ impl, const D: usize> CircuitBuilder { claimed_element } - /// Like `random_access`, but padding `v` with the last element to a power of two. - pub fn random_access_with_padding(&mut self, access_index: Target, v: Vec) -> Target { - let mut v = v; - let current_len = v.len(); - let next_power_of_two = current_len.next_power_of_two(); - if current_len < next_power_of_two { - // Get the last element (if there is one) and extend with it - if let Some(&last) = v.last() { - v.extend(repeat_n(last, next_power_of_two - current_len)); - } - } - self.random_access(access_index, v) - } - /// Like `random_access`, but with `ExtensionTarget`s rather than simple `Target`s. pub fn random_access_extension( &mut self, access_index: Target, v: Vec>, - ) -> ExtensionTarget { - let selected: Vec<_> = (0..D) - .map(|i| self.random_access(access_index, v.iter().map(|et| et.0[i]).collect())) - .collect(); - - ExtensionTarget(selected.try_into().unwrap()) - } - - /// Like `random_access_extension`, but padding `v` with the last element to a power of two. - pub fn random_access_extension_with_padding( - &mut self, - access_index: Target, - v: Vec>, ) -> ExtensionTarget { let mut v = v; let current_len = v.len(); @@ -83,7 +65,11 @@ impl, const D: usize> CircuitBuilder { v.extend(repeat_n(last, next_power_of_two - current_len)); } } - self.random_access_extension(access_index, v) + let selected: Vec<_> = (0..D) + .map(|i| self.random_access(access_index, v.iter().map(|et| et.0[i]).collect())) + .collect(); + + ExtensionTarget(selected.try_into().unwrap()) } /// Like `random_access`, but with `HashOutTarget`s rather than simple `Target`s. diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index 21a822fa19..424e03ae64 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -226,11 +226,11 @@ impl, const D: usize> CircuitBuilder { } for i in 0..NUM_HASH_OUT_ELTS { - let result = self.random_access_with_padding( + let result = self.random_access( cap_index, merkle_cap.0.iter().map(|h| h.elements[i]).collect(), ); - let state = self.random_access_with_padding( + let state = self.random_access( n_index, final_states.iter().map(|s| s.elements[i]).collect(), ); diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index c14cb80904..7766e4403f 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -70,7 +70,7 @@ where challenger.observe_openings(&openings.to_fri_openings()); - let (final_poly_coeff_len, query_round_step_count) = + let (final_poly_coeff_len, max_num_query_steps) = if let Some(verifier_circuit_fri_params) = verifier_circuit_fri_params { ( Some(final_poly_coeff_len( @@ -94,7 +94,7 @@ where degree_bits, &config.fri_config, final_poly_coeff_len, - query_round_step_count, + max_num_query_steps, ), } } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index a889719b20..b3e0f90fef 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -59,7 +59,7 @@ 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) = + let (final_poly_coeff_len, max_num_query_steps) = 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 { @@ -107,7 +107,7 @@ where &mut challenger, public_inputs, final_poly_coeff_len, - query_round_step_count, + max_num_query_steps, timing, ) } @@ -129,7 +129,7 @@ pub fn prove_with_commitment( challenger: &mut Challenger, public_inputs: &[F], final_poly_coeff_len: Option, - query_round_step_count: Option, + max_num_query_steps: Option, timing: &mut TimingTree, ) -> Result> where @@ -347,7 +347,7 @@ where challenger, &fri_params, final_poly_coeff_len, - query_round_step_count, + max_num_query_steps, timing, ) );