From 8993270f80b8bad808c109b15807849ef42e5bf5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Sat, 29 Jan 2022 12:49:00 +0100 Subject: [PATCH 01/14] Progress --- starky/src/get_challenges.rs | 219 +++++++++++++++++++++++++++++++++++ starky/src/lib.rs | 2 + starky/src/proof.rs | 57 ++++++++- starky/src/prover.rs | 17 ++- starky/src/verifier.rs | 105 +++++++++++++++++ 5 files changed, 394 insertions(+), 6 deletions(-) create mode 100644 starky/src/get_challenges.rs create mode 100644 starky/src/verifier.rs diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs new file mode 100644 index 00000000..f3c78701 --- /dev/null +++ b/starky/src/get_challenges.rs @@ -0,0 +1,219 @@ +use anyhow::Result; +use plonky2::field::extension_field::Extendable; +use plonky2::field::polynomial::PolynomialCoeffs; +use plonky2::fri::proof::FriProof; +use plonky2::hash::hash_types::RichField; +use plonky2::hash::merkle_tree::MerkleCap; +use plonky2::iop::challenger::Challenger; +use plonky2::plonk::config::{GenericConfig, Hasher}; + +use crate::config::StarkConfig; +use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; + +fn get_challenges, C: GenericConfig, const D: usize>( + trace_cap: &MerkleCap, + quotient_polys_cap: &MerkleCap, + openings: &StarkOpeningSet, + commit_phase_merkle_caps: &[MerkleCap], + final_poly: &PolynomialCoeffs, + pow_witness: F, + config: &StarkConfig, + degree_bits: usize, +) -> Result> { + let num_challenges = config.num_challenges; + let num_fri_queries = config.fri_config.num_query_rounds; + let lde_size = 1 << (degree_bits + config.fri_config.rate_bits); + + let mut challenger = Challenger::::new(); + + challenger.observe_cap(trace_cap); + let stark_alphas = challenger.get_n_challenges(num_challenges); + + challenger.observe_cap(quotient_polys_cap); + let stark_zeta = challenger.get_extension_challenge::(); + + openings.observe(&mut challenger); + + // Scaling factor to combine polynomials. + let fri_alpha = challenger.get_extension_challenge::(); + + // Recover the random betas used in the FRI reductions. + let fri_betas = commit_phase_merkle_caps + .iter() + .map(|cap| { + challenger.observe_cap(cap); + challenger.get_extension_challenge::() + }) + .collect(); + + challenger.observe_extension_elements(&final_poly.coeffs); + + let fri_pow_response = C::InnerHasher::hash( + &challenger + .get_hash() + .elements + .iter() + .copied() + .chain(Some(pow_witness)) + .collect::>(), + false, + ) + .elements[0]; + + let fri_query_indices = (0..num_fri_queries) + .map(|_| challenger.get_challenge().to_canonical_u64() as usize % lde_size) + .collect(); + + Ok(StarkProofChallenges { + stark_alphas, + stark_zeta, + fri_alpha, + fri_betas, + fri_pow_response, + fri_query_indices, + }) +} + +impl, C: GenericConfig, const D: usize> + StarkProofWithPublicInputs +{ + pub(crate) fn fri_query_indices(&self, config: &StarkConfig) -> anyhow::Result> { + Ok(self.get_challenges(config)?.fri_query_indices) + } + + /// Computes all Fiat-Shamir challenges used in the Plonk proof. + pub(crate) fn get_challenges( + &self, + config: &StarkConfig, + ) -> Result> { + let StarkProof { + trace_cap, + quotient_polys_cap, + openings, + opening_proof: + FriProof { + commit_phase_merkle_caps, + final_poly, + pow_witness, + .. + }, + } = &self.proof; + + get_challenges( + trace_cap, + quotient_polys_cap, + openings, + commit_phase_merkle_caps, + final_poly, + *pow_witness, + config, + ) + } +} + +// impl, C: GenericConfig, const D: usize> +// CompressedProofWithPublicInputs +// { +// /// Computes all Fiat-Shamir challenges used in the Plonk proof. +// pub(crate) fn get_challenges( +// &self, +// common_data: &CommonCircuitData, +// ) -> anyhow::Result> { +// let CompressedProof { +// wires_cap, +// plonk_zs_partial_products_cap, +// quotient_polys_cap, +// openings, +// opening_proof: +// CompressedFriProof { +// commit_phase_merkle_caps, +// final_poly, +// pow_witness, +// .. +// }, +// } = &self.proof; +// +// get_challenges( +// self.get_public_inputs_hash(), +// wires_cap, +// plonk_zs_partial_products_cap, +// quotient_polys_cap, +// openings, +// commit_phase_merkle_caps, +// final_poly, +// *pow_witness, +// common_data, +// ) +// } +// +// /// Computes all coset elements that can be inferred in the FRI reduction steps. +// pub(crate) fn get_inferred_elements( +// &self, +// challenges: &ProofChallenges, +// common_data: &CommonCircuitData, +// ) -> FriInferredElements { +// let ProofChallenges { +// plonk_zeta, +// fri_alpha, +// fri_betas, +// fri_query_indices, +// .. +// } = challenges; +// let mut fri_inferred_elements = Vec::new(); +// // Holds the indices that have already been seen at each reduction depth. +// let mut seen_indices_by_depth = +// vec![HashSet::new(); common_data.fri_params.reduction_arity_bits.len()]; +// let precomputed_reduced_evals = PrecomputedReducedOpenings::from_os_and_alpha( +// &self.proof.openings.to_fri_openings(), +// *fri_alpha, +// ); +// let log_n = common_data.degree_bits + common_data.config.fri_config.rate_bits; +// // Simulate the proof verification and collect the inferred elements. +// // The content of the loop is basically the same as the `fri_verifier_query_round` function. +// for &(mut x_index) in fri_query_indices { +// let mut subgroup_x = F::MULTIPLICATIVE_GROUP_GENERATOR +// * F::primitive_root_of_unity(log_n).exp_u64(reverse_bits(x_index, log_n) as u64); +// let mut old_eval = fri_combine_initial::( +// &common_data.get_fri_instance(*plonk_zeta), +// &self +// .proof +// .opening_proof +// .query_round_proofs +// .initial_trees_proofs[&x_index], +// *fri_alpha, +// subgroup_x, +// &precomputed_reduced_evals, +// &common_data.fri_params, +// ); +// for (i, &arity_bits) in common_data +// .fri_params +// .reduction_arity_bits +// .iter() +// .enumerate() +// { +// let coset_index = x_index >> arity_bits; +// if !seen_indices_by_depth[i].insert(coset_index) { +// // If this index has already been seen, we can skip the rest of the reductions. +// break; +// } +// fri_inferred_elements.push(old_eval); +// let arity = 1 << arity_bits; +// let mut evals = self.proof.opening_proof.query_round_proofs.steps[i][&coset_index] +// .evals +// .clone(); +// let x_index_within_coset = x_index & (arity - 1); +// evals.insert(x_index_within_coset, old_eval); +// old_eval = compute_evaluation( +// subgroup_x, +// x_index_within_coset, +// arity_bits, +// &evals, +// fri_betas[i], +// ); +// subgroup_x = subgroup_x.exp_power_of_2(arity_bits); +// x_index = coset_index; +// } +// } +// FriInferredElements(fri_inferred_elements) +// } +// } diff --git a/starky/src/lib.rs b/starky/src/lib.rs index 541950ab..e56c0ef6 100644 --- a/starky/src/lib.rs +++ b/starky/src/lib.rs @@ -8,10 +8,12 @@ pub mod config; pub mod constraint_consumer; +mod get_challenges; pub mod proof; pub mod prover; pub mod stark; pub mod vars; +pub mod verifier; #[cfg(test)] pub mod fibonacci_stark; diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 4218e71f..4d81793e 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -1,20 +1,34 @@ use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::Field; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{CompressedFriProof, FriProof}; use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; -use plonky2::plonk::config::GenericConfig; +use plonky2::iop::challenger::Challenger; +use plonky2::plonk::config::{GenericConfig, Hasher}; use rayon::prelude::*; pub struct StarkProof, C: GenericConfig, const D: usize> { /// Merkle cap of LDEs of trace values. pub trace_cap: MerkleCap, + /// Merkle cap of LDEs of trace values. + pub quotient_polys_cap: MerkleCap, /// Purported values of each polynomial at the challenge point. pub openings: StarkOpeningSet, /// A batch FRI argument for all openings. pub opening_proof: FriProof, } +pub struct StarkProofWithPublicInputs< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +> { + pub proof: StarkProof, + // TODO: Maybe make it generic over a `S: Start` and replace with `[F; S::PUBLIC_INPUTS]`. + pub public_inputs: Vec, +} + pub struct CompressedStarkProof< F: RichField + Extendable, C: GenericConfig, @@ -28,6 +42,34 @@ pub struct CompressedStarkProof< pub opening_proof: CompressedFriProof, } +pub struct CompressedStarkProofWithPublicInputs< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +> { + pub proof: CompressedStarkProof, + pub public_inputs: Vec, +} + +pub(crate) struct StarkProofChallenges, const D: usize> { + // Random values used to combine PLONK constraints. + pub stark_alphas: Vec, + + // Point at which the PLONK polynomials are opened. + pub stark_zeta: F::Extension, + + // Scaling factor to combine polynomials. + pub fri_alpha: F::Extension, + + // Betas used in the FRI commit phase reductions. + pub fri_betas: Vec, + + pub fri_pow_response: F, + + // Indices at which the oracle is queried in FRI. + pub fri_query_indices: Vec, +} + /// Purported values of each polynomial at the challenge point. pub struct StarkOpeningSet, const D: usize> { pub local_values: Vec, @@ -56,4 +98,17 @@ impl, const D: usize> StarkOpeningSet { quotient_polys: eval_commitment(zeta, quotient_commitment), } } + + // Note: Can't implement this directly on `Challenger` as it's in a different crate. + pub fn observe>(&self, challenger: &mut Challenger) { + let StarkOpeningSet { + local_values, + next_values, + permutation_zs, + quotient_polys, + } = self; + for v in &[local_values, next_values, permutation_zs, quotient_polys] { + self.observe_extension_elements(v); + } + } } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index e0652b24..4ba09e22 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -16,7 +16,7 @@ use rayon::prelude::*; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::proof::{StarkOpeningSet, StarkProof}; +use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vars::StarkEvaluationVars; @@ -27,7 +27,7 @@ pub fn prove( trace: Vec<[F; S::COLUMNS]>, public_inputs: [F; S::PUBLIC_INPUTS], timing: &mut TimingTree, -) -> Result> +) -> Result> where F: RichField + Extendable, C: GenericConfig, @@ -101,7 +101,8 @@ where None, ) ); - challenger.observe_cap("ient_commitment.merkle_tree.cap); + let quotient_polys_cap = quotient_commitment.merkle_tree.cap; + challenger.observe_cap(quotient_polys_cap); let zeta = challenger.get_extension_challenge::(); // To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and @@ -113,6 +114,7 @@ where "Opening point is in the subgroup." ); let openings = StarkOpeningSet::new(zeta, g, &trace_commitment, "ient_commitment); + openings.observe(&mut challenger); // TODO: Add permuation checks let initial_merkle_trees = &[&trace_commitment, "ient_commitment]; @@ -129,11 +131,16 @@ where timing, ) ); - - Ok(StarkProof { + let proof = StarkProof { trace_cap, + quotient_polys_cap, openings, opening_proof, + }; + + Ok(StarkProofWithPublicInputs { + proof, + public_inputs: public_inputs.to_vec(), }) } diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs new file mode 100644 index 00000000..d9c8c309 --- /dev/null +++ b/starky/src/verifier.rs @@ -0,0 +1,105 @@ +use anyhow::{ensure, Result}; +use plonky2::field::extension_field::Extendable; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_data::CommonCircuitData; +use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::proof::ProofWithPublicInputs; + +use crate::config::StarkConfig; +use crate::proof::{StarkProof, StarkProofWithPublicInputs}; +use crate::stark::Stark; + +pub(crate) fn verify< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + proof_with_pis: StarkProofWithPublicInputs, + config: &StarkConfig, +) -> Result<()> { + let challenges = proof_with_pis.get_challenges(config)?; + verify_with_challenges(proof_with_pis, challenges, verifier_data, common_data) +} + +pub(crate) fn verify_with_challenges< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + proof_with_pis: StarkProofWithPublicInputs, + challenges: ProofChallenges, + verifier_data: &VerifierOnlyCircuitData, + common_data: &CommonCircuitData, +) -> Result<()> { + assert_eq!( + proof_with_pis.public_inputs.len(), + common_data.num_public_inputs + ); + let public_inputs_hash = &proof_with_pis.get_public_inputs_hash(); + + let ProofWithPublicInputs { proof, .. } = proof_with_pis; + + let local_constants = &proof.openings.constants; + let local_wires = &proof.openings.wires; + let vars = EvaluationVars { + local_constants, + local_wires, + public_inputs_hash, + }; + let local_zs = &proof.openings.plonk_zs; + let next_zs = &proof.openings.plonk_zs_right; + let s_sigmas = &proof.openings.plonk_sigmas; + let partial_products = &proof.openings.partial_products; + + // Evaluate the vanishing polynomial at our challenge point, zeta. + let vanishing_polys_zeta = eval_vanishing_poly( + common_data, + challenges.plonk_zeta, + vars, + local_zs, + next_zs, + partial_products, + s_sigmas, + &challenges.plonk_betas, + &challenges.plonk_gammas, + &challenges.plonk_alphas, + ); + + // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. + let quotient_polys_zeta = &proof.openings.quotient_polys; + let zeta_pow_deg = challenges + .plonk_zeta + .exp_power_of_2(common_data.degree_bits); + let z_h_zeta = zeta_pow_deg - F::Extension::ONE; + // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. + // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` + // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. + // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each + // `quotient_degree_factor`-sized chunk of the original evaluations. + for (i, chunk) in quotient_polys_zeta + .chunks(common_data.quotient_degree_factor) + .enumerate() + { + ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); + } + + let merkle_caps = &[ + verifier_data.constants_sigmas_cap.clone(), + proof.wires_cap, + proof.plonk_zs_partial_products_cap, + proof.quotient_polys_cap, + ]; + + verify_fri_proof::( + &common_data.get_fri_instance(challenges.plonk_zeta), + &proof.openings, + &challenges, + merkle_caps, + &proof.opening_proof, + &common_data.fri_params, + )?; + + Ok(()) +} From 851455a26aa88a0ff600e3416e884bbe2153f8e2 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 31 Jan 2022 10:07:01 +0100 Subject: [PATCH 02/14] Eval Lagrange --- plonky2/src/plonk/plonk_common.rs | 1 + starky/src/verifier.rs | 78 ++++++++++++++++++------------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/plonky2/src/plonk/plonk_common.rs b/plonky2/src/plonk/plonk_common.rs index 74495198..937f3ccd 100644 --- a/plonky2/src/plonk/plonk_common.rs +++ b/plonky2/src/plonk/plonk_common.rs @@ -1,6 +1,7 @@ use plonky2_field::extension_field::Extendable; use plonky2_field::field_types::Field; use plonky2_field::packed_field::PackedField; +use plonky2_util::log2_strict; use crate::fri::oracle::SALT_SIZE; use crate::fri::structure::FriOracleInfo; diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index d9c8c309..cbde85c0 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -1,13 +1,17 @@ use anyhow::{ensure, Result}; use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::Field; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_data::CommonCircuitData; use plonky2::plonk::config::GenericConfig; use plonky2::plonk::proof::ProofWithPublicInputs; +use plonky2_util::log2_strict; use crate::config::StarkConfig; -use crate::proof::{StarkProof, StarkProofWithPublicInputs}; +use crate::constraint_consumer::ConstraintConsumer; +use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; use crate::stark::Stark; +use crate::vars::StarkEvaluationVars; pub(crate) fn verify< F: RichField + Extendable, @@ -28,44 +32,40 @@ pub(crate) fn verify_with_challenges< S: Stark, const D: usize, >( + stark: S, proof_with_pis: StarkProofWithPublicInputs, - challenges: ProofChallenges, - verifier_data: &VerifierOnlyCircuitData, - common_data: &CommonCircuitData, + challenges: StarkProofChallenges, + config: &StarkConfig, ) -> Result<()> { - assert_eq!( - proof_with_pis.public_inputs.len(), - common_data.num_public_inputs - ); - let public_inputs_hash = &proof_with_pis.get_public_inputs_hash(); - - let ProofWithPublicInputs { proof, .. } = proof_with_pis; + let StarkProofWithPublicInputs { + proof, + public_inputs, + } = proof_with_pis; + let degree = recover_degree(&proof, config); + let degree_log = log2_strict(degree); let local_constants = &proof.openings.constants; - let local_wires = &proof.openings.wires; - let vars = EvaluationVars { - local_constants, - local_wires, - public_inputs_hash, + let local_values = &proof.openings.local_values; + let next_values = &proof.openings.local_values; + let StarkOpeningSet { + local_values, + next_values, + permutation_zs, + quotient_polys, + } = &proof.openings; + let vars = StarkEvaluationVars { + local_values, + next_values, + public_inputs: &public_inputs, }; - let local_zs = &proof.openings.plonk_zs; - let next_zs = &proof.openings.plonk_zs_right; - let s_sigmas = &proof.openings.plonk_sigmas; - let partial_products = &proof.openings.partial_products; - // Evaluate the vanishing polynomial at our challenge point, zeta. - let vanishing_polys_zeta = eval_vanishing_poly( - common_data, - challenges.plonk_zeta, - vars, - local_zs, - next_zs, - partial_products, - s_sigmas, - &challenges.plonk_betas, - &challenges.plonk_gammas, - &challenges.plonk_alphas, + let mut consumer = ConstraintConsumer::::new( + challenges.stark_alphas, + lagrange_first.values[i], + lagrange_last.values[i], ); + let (l_1, l_n) = eval_l_1_and_l_last(degree_log, challenges.stark_zeta); + stark.eval_ext() // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; @@ -103,3 +103,17 @@ pub(crate) fn verify_with_challenges< Ok(()) } + +/// Evaluate the Lagrange basis `L_1` and `L_n` at a point `x`. +fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F,F) { + let n = 1 << log_n; + let g = F::primitive_root_of_unity(log_n); + let z_x = x.exp_power_of_2(log_n); + let invs = F::batch_multiplicative_inverse(&[F::from_canonical_usize(n) * (x - F::ONE), F::from_canonical_usize(n) * (g*x - F::ONE)]); + + (z_x * invs[0], z_x * invs[1]) +} + +fn recover_degree, C: GenericConfig, const D: usize>(proof: &StarkProof, config: &StarkConfig) -> usize { + 1<<(proof.opening_proof.query_round_proofs[0].initial_trees_proof.evals_proofs[0].1.siblings.len() + config.fri_config.cap_height) +} \ No newline at end of file From d24d26e5c05a3e0cbe6d0b2d27ba527448b17530 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 31 Jan 2022 16:19:30 +0100 Subject: [PATCH 03/14] Add FRI challenges --- plonky2/src/fri/proof.rs | 6 +- plonky2/src/fri/verifier.rs | 8 +-- plonky2/src/plonk/get_challenges.rs | 29 ++++++--- plonky2/src/plonk/mod.rs | 2 +- plonky2/src/plonk/plonk_common.rs | 2 +- plonky2/src/plonk/proof.rs | 4 ++ plonky2/src/plonk/verifier.rs | 2 +- starky/src/get_challenges.rs | 12 +++- starky/src/proof.rs | 2 +- starky/src/prover.rs | 2 +- starky/src/verifier.rs | 96 +++++++++++++++++++---------- 11 files changed, 107 insertions(+), 58 deletions(-) diff --git a/plonky2/src/fri/proof.rs b/plonky2/src/fri/proof.rs index f96db781..bca7b8db 100644 --- a/plonky2/src/fri/proof.rs +++ b/plonky2/src/fri/proof.rs @@ -16,7 +16,7 @@ use crate::iop::ext_target::ExtensionTarget; use crate::iop::target::Target; use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::plonk_common::salt_size; -use crate::plonk::proof::{FriInferredElements, ProofChallenges}; +use crate::plonk::proof::{FriChallenges, FriInferredElements, ProofChallenges}; /// Evaluations and Merkle proof produced by the prover in a FRI query step. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] @@ -253,10 +253,10 @@ impl, H: Hasher, const D: usize> CompressedFriPr pow_witness, .. } = self; - let ProofChallenges { + let FriChallenges { fri_query_indices: indices, .. - } = challenges; + } = &challenges.fri_challenges; let mut fri_inferred_elements = fri_inferred_elements.0.into_iter(); let cap_height = params.config.cap_height; let reduction_arity_bits = ¶ms.reduction_arity_bits; diff --git a/plonky2/src/fri/verifier.rs b/plonky2/src/fri/verifier.rs index 40d1ab25..f41ae969 100644 --- a/plonky2/src/fri/verifier.rs +++ b/plonky2/src/fri/verifier.rs @@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField; use crate::hash::merkle_proofs::verify_merkle_proof; use crate::hash::merkle_tree::MerkleCap; use crate::plonk::config::{GenericConfig, Hasher}; -use crate::plonk::proof::{OpeningSet, ProofChallenges}; +use crate::plonk::proof::{FriChallenges, OpeningSet, ProofChallenges}; use crate::util::reducing::ReducingFactor; use crate::util::reverse_bits; @@ -57,7 +57,7 @@ pub(crate) fn fri_verify_proof_of_work, const D: us Ok(()) } -pub(crate) fn verify_fri_proof< +pub fn verify_fri_proof< F: RichField + Extendable, C: GenericConfig, const D: usize, @@ -65,7 +65,7 @@ pub(crate) fn verify_fri_proof< instance: &FriInstanceInfo, // Openings of the PLONK polynomials. os: &OpeningSet, - challenges: &ProofChallenges, + challenges: &FriChallenges, initial_merkle_caps: &[MerkleCap], proof: &FriProof, params: &FriParams, @@ -171,7 +171,7 @@ fn fri_verifier_query_round< const D: usize, >( instance: &FriInstanceInfo, - challenges: &ProofChallenges, + challenges: &FriChallenges, precomputed_reduced_evals: &PrecomputedReducedOpenings, initial_merkle_caps: &[MerkleCap], proof: &FriProof, diff --git a/plonky2/src/plonk/get_challenges.rs b/plonky2/src/plonk/get_challenges.rs index d28f29da..440705ce 100644 --- a/plonky2/src/plonk/get_challenges.rs +++ b/plonky2/src/plonk/get_challenges.rs @@ -12,8 +12,8 @@ use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::proof::{ - CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet, Proof, - ProofChallenges, ProofWithPublicInputs, + CompressedProof, CompressedProofWithPublicInputs, FriChallenges, FriInferredElements, + OpeningSet, Proof, ProofChallenges, ProofWithPublicInputs, }; use crate::util::reverse_bits; @@ -86,10 +86,12 @@ fn get_challenges, C: GenericConfig, cons plonk_gammas, plonk_alphas, plonk_zeta, - fri_alpha, - fri_betas, - fri_pow_response, - fri_query_indices, + fri_challenges: FriChallenges { + fri_alpha, + fri_betas, + fri_pow_response, + fri_query_indices, + }, }) } @@ -100,7 +102,10 @@ impl, C: GenericConfig, const D: usize> &self, common_data: &CommonCircuitData, ) -> anyhow::Result> { - Ok(self.get_challenges(common_data)?.fri_query_indices) + Ok(self + .get_challenges(common_data)? + .fri_challenges + .fri_query_indices) } /// Computes all Fiat-Shamir challenges used in the Plonk proof. @@ -179,9 +184,13 @@ impl, C: GenericConfig, const D: usize> ) -> FriInferredElements { let ProofChallenges { plonk_zeta, - fri_alpha, - fri_betas, - fri_query_indices, + fri_challenges: + FriChallenges { + fri_alpha, + fri_betas, + fri_query_indices, + .. + }, .. } = challenges; let mut fri_inferred_elements = Vec::new(); diff --git a/plonky2/src/plonk/mod.rs b/plonky2/src/plonk/mod.rs index b2d1ed03..4f2fa4e1 100644 --- a/plonky2/src/plonk/mod.rs +++ b/plonky2/src/plonk/mod.rs @@ -4,7 +4,7 @@ pub mod config; pub(crate) mod copy_constraint; mod get_challenges; pub(crate) mod permutation_argument; -pub(crate) mod plonk_common; +pub mod plonk_common; pub mod proof; pub mod prover; pub mod recursive_verifier; diff --git a/plonky2/src/plonk/plonk_common.rs b/plonky2/src/plonk/plonk_common.rs index 937f3ccd..94279d12 100644 --- a/plonky2/src/plonk/plonk_common.rs +++ b/plonky2/src/plonk/plonk_common.rs @@ -125,7 +125,7 @@ pub(crate) fn reduce_with_powers_multi< cumul } -pub(crate) fn reduce_with_powers<'a, P: PackedField, T: IntoIterator>( +pub fn reduce_with_powers<'a, P: PackedField, T: IntoIterator>( terms: T, alpha: P::Scalar, ) -> P diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index 7fbdc671..401b9f52 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -239,6 +239,10 @@ pub(crate) struct ProofChallenges, const D: usize> // Point at which the PLONK polynomials are opened. pub plonk_zeta: F::Extension, + pub fri_challenges: FriChallenges, +} + +pub struct FriChallenges, const D: usize> { // Scaling factor to combine polynomials. pub fri_alpha: F::Extension, diff --git a/plonky2/src/plonk/verifier.rs b/plonky2/src/plonk/verifier.rs index cbaec6d9..f0c976fa 100644 --- a/plonky2/src/plonk/verifier.rs +++ b/plonky2/src/plonk/verifier.rs @@ -92,7 +92,7 @@ pub(crate) fn verify_with_challenges< verify_fri_proof::( &common_data.get_fri_instance(challenges.plonk_zeta), &proof.openings, - &challenges, + &challenges.fri_challenges, merkle_caps, &proof.opening_proof, &common_data.fri_params, diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index f3c78701..927eef6c 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -77,14 +77,19 @@ fn get_challenges, C: GenericConfig, cons impl, C: GenericConfig, const D: usize> StarkProofWithPublicInputs { - pub(crate) fn fri_query_indices(&self, config: &StarkConfig) -> anyhow::Result> { - Ok(self.get_challenges(config)?.fri_query_indices) + pub(crate) fn fri_query_indices( + &self, + config: &StarkConfig, + degree_bits: usize, + ) -> anyhow::Result> { + Ok(self.get_challenges(config, degree_bits)?.fri_query_indices) } /// Computes all Fiat-Shamir challenges used in the Plonk proof. pub(crate) fn get_challenges( &self, config: &StarkConfig, + degree_bits: usize, ) -> Result> { let StarkProof { trace_cap, @@ -99,7 +104,7 @@ impl, C: GenericConfig, const D: usize> }, } = &self.proof; - get_challenges( + get_challenges::( trace_cap, quotient_polys_cap, openings, @@ -107,6 +112,7 @@ impl, C: GenericConfig, const D: usize> final_poly, *pow_witness, config, + degree_bits, ) } } diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 4d81793e..fe4ac43d 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -108,7 +108,7 @@ impl, const D: usize> StarkOpeningSet { quotient_polys, } = self; for v in &[local_values, next_values, permutation_zs, quotient_polys] { - self.observe_extension_elements(v); + challenger.observe_extension_elements(v); } } } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 4ba09e22..a3f2cb2d 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -102,7 +102,7 @@ where ) ); let quotient_polys_cap = quotient_commitment.merkle_tree.cap; - challenger.observe_cap(quotient_polys_cap); + challenger.observe_cap("ient_polys_cap); let zeta = challenger.get_extension_challenge::(); // To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index cbde85c0..46dc948f 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -1,9 +1,11 @@ use anyhow::{ensure, Result}; -use plonky2::field::extension_field::Extendable; +use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; +use plonky2::fri::verifier::verify_fri_proof; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_data::CommonCircuitData; use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::plonk_common::reduce_with_powers; use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2_util::log2_strict; @@ -19,11 +21,17 @@ pub(crate) fn verify< S: Stark, const D: usize, >( + stark: S, proof_with_pis: StarkProofWithPublicInputs, config: &StarkConfig, -) -> Result<()> { - let challenges = proof_with_pis.get_challenges(config)?; - verify_with_challenges(proof_with_pis, challenges, verifier_data, common_data) + degree_bits: usize, +) -> Result<()> +where + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + let challenges = proof_with_pis.get_challenges(config, degree_bits)?; + verify_with_challenges(stark, proof_with_pis, challenges, config) } pub(crate) fn verify_with_challenges< @@ -36,15 +44,18 @@ pub(crate) fn verify_with_challenges< proof_with_pis: StarkProofWithPublicInputs, challenges: StarkProofChallenges, config: &StarkConfig, -) -> Result<()> { +) -> Result<()> +where + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ let StarkProofWithPublicInputs { proof, public_inputs, } = proof_with_pis; let degree = recover_degree(&proof, config); - let degree_log = log2_strict(degree); + let degree_bits = log2_strict(degree); - let local_constants = &proof.openings.constants; let local_values = &proof.openings.local_values; let next_values = &proof.openings.local_values; let StarkOpeningSet { @@ -54,24 +65,32 @@ pub(crate) fn verify_with_challenges< quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { - local_values, - next_values, - public_inputs: &public_inputs, + local_values: &local_values.to_vec().try_into().unwrap(), + next_values: &next_values.to_vec().try_into().unwrap(), + public_inputs: &public_inputs + .into_iter() + .map(F::Extension::from_basefield) + .collect::>() + .try_into() + .unwrap(), }; + let (l_1, l_last) = eval_l_1_and_l_last(degree_bits, challenges.stark_zeta); let mut consumer = ConstraintConsumer::::new( - challenges.stark_alphas, - lagrange_first.values[i], - lagrange_last.values[i], + challenges + .stark_alphas + .iter() + .map(|&alpha| F::Extension::from_basefield(alpha)) + .collect::>(), + l_1.into(), + l_last.into(), ); - let (l_1, l_n) = eval_l_1_and_l_last(degree_log, challenges.stark_zeta); - stark.eval_ext() + stark.eval_ext(vars, &mut consumer); + let acc = consumer.accumulators(); // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; - let zeta_pow_deg = challenges - .plonk_zeta - .exp_power_of_2(common_data.degree_bits); + let zeta_pow_deg = challenges.stark_zeta.exp_power_of_2(degree_bits); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` @@ -79,41 +98,52 @@ pub(crate) fn verify_with_challenges< // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each // `quotient_degree_factor`-sized chunk of the original evaluations. for (i, chunk) in quotient_polys_zeta - .chunks(common_data.quotient_degree_factor) + .chunks(config.fri_config.rate_bits) .enumerate() { - ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); + ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); } - let merkle_caps = &[ - verifier_data.constants_sigmas_cap.clone(), - proof.wires_cap, - proof.plonk_zs_partial_products_cap, - proof.quotient_polys_cap, - ]; + let merkle_caps = &[proof.trace_cap, proof.quotient_polys_cap]; verify_fri_proof::( - &common_data.get_fri_instance(challenges.plonk_zeta), + &S::fri_instance( + challenges.stark_zeta, + F::primitive_root_of_unity(degree_bits).into(), + config.fri_config.rate_bits, + ), &proof.openings, &challenges, merkle_caps, &proof.opening_proof, - &common_data.fri_params, + &config.fri_params(degree_bits), )?; Ok(()) } /// Evaluate the Lagrange basis `L_1` and `L_n` at a point `x`. -fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F,F) { +fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { let n = 1 << log_n; let g = F::primitive_root_of_unity(log_n); let z_x = x.exp_power_of_2(log_n); - let invs = F::batch_multiplicative_inverse(&[F::from_canonical_usize(n) * (x - F::ONE), F::from_canonical_usize(n) * (g*x - F::ONE)]); + let invs = F::batch_multiplicative_inverse(&[ + F::from_canonical_usize(n) * (x - F::ONE), + F::from_canonical_usize(n) * (g * x - F::ONE), + ]); (z_x * invs[0], z_x * invs[1]) } -fn recover_degree, C: GenericConfig, const D: usize>(proof: &StarkProof, config: &StarkConfig) -> usize { - 1<<(proof.opening_proof.query_round_proofs[0].initial_trees_proof.evals_proofs[0].1.siblings.len() + config.fri_config.cap_height) -} \ No newline at end of file +fn recover_degree, C: GenericConfig, const D: usize>( + proof: &StarkProof, + config: &StarkConfig, +) -> usize { + 1 << (proof.opening_proof.query_round_proofs[0] + .initial_trees_proof + .evals_proofs[0] + .1 + .siblings + .len() + + config.fri_config.cap_height) +} From 92ea4b65d1829f90f07e7c705821c0397daee06f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 31 Jan 2022 18:00:07 +0100 Subject: [PATCH 04/14] Constraint check working --- field/src/field_types.rs | 1 + plonky2/src/fri/verifier.rs | 5 ++--- plonky2/src/hash/merkle_proofs.rs | 1 + plonky2/src/plonk/verifier.rs | 2 +- starky/src/fibonacci_stark.rs | 16 +++++++++------- starky/src/get_challenges.rs | 16 +++++++++++----- starky/src/proof.rs | 30 ++++++++++++++++++++---------- starky/src/prover.rs | 23 ++++++++++++++++------- starky/src/verifier.rs | 14 +++++++++----- system_zero/src/system_zero.rs | 2 +- 10 files changed, 71 insertions(+), 39 deletions(-) diff --git a/field/src/field_types.rs b/field/src/field_types.rs index 0d7b314f..845d8e83 100644 --- a/field/src/field_types.rs +++ b/field/src/field_types.rs @@ -389,6 +389,7 @@ pub trait Field: /// Representative `g` of the coset used in FRI, so that LDEs in FRI are done over `gH`. fn coset_shift() -> Self { Self::MULTIPLICATIVE_GROUP_GENERATOR + // Self::ONE } /// Equivalent to *self + x * y, but may be cheaper. diff --git a/plonky2/src/fri/verifier.rs b/plonky2/src/fri/verifier.rs index f41ae969..3e70c025 100644 --- a/plonky2/src/fri/verifier.rs +++ b/plonky2/src/fri/verifier.rs @@ -63,8 +63,7 @@ pub fn verify_fri_proof< const D: usize, >( instance: &FriInstanceInfo, - // Openings of the PLONK polynomials. - os: &OpeningSet, + openings: &FriOpenings, challenges: &FriChallenges, initial_merkle_caps: &[MerkleCap], proof: &FriProof, @@ -88,7 +87,7 @@ pub fn verify_fri_proof< ); let precomputed_reduced_evals = - PrecomputedReducedOpenings::from_os_and_alpha(&os.to_fri_openings(), challenges.fri_alpha); + PrecomputedReducedOpenings::from_os_and_alpha(&openings, challenges.fri_alpha); for (&x_index, round_proof) in challenges .fri_query_indices .iter() diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index c2f3655d..feb39791 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -31,6 +31,7 @@ pub(crate) fn verify_merkle_proof>( merkle_cap: &MerkleCap, proof: &MerkleProof, ) -> Result<()> { + dbg!(leaf_index); let mut index = leaf_index; let mut current_digest = H::hash(&leaf_data, false); for &sibling_digest in proof.siblings.iter() { diff --git a/plonky2/src/plonk/verifier.rs b/plonky2/src/plonk/verifier.rs index f0c976fa..46d41bfe 100644 --- a/plonky2/src/plonk/verifier.rs +++ b/plonky2/src/plonk/verifier.rs @@ -91,7 +91,7 @@ pub(crate) fn verify_with_challenges< verify_fri_proof::( &common_data.get_fri_instance(challenges.plonk_zeta), - &proof.openings, + &proof.openings.to_fri_openings(), &challenges.fri_challenges, merkle_caps, &proof.opening_proof, diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index ea834e99..ffaa14a7 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -12,6 +12,7 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; /// Toy STARK system used for testing. /// Computes a Fibonacci sequence with state `[x0, x1]` using the state transition /// `x0 <- x1, x1 <- x0 + x1`. +#[derive(Copy, Clone)] struct FibonacciStark, const D: usize> { num_rows: usize, _phantom: PhantomData, @@ -58,10 +59,10 @@ impl, const D: usize> Stark for FibonacciStar FE: FieldExtension, P: PackedField, { - // Check public inputs. - yield_constr.one_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); - yield_constr.one_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); - yield_constr.one_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); + // // Check public inputs. + // yield_constr.one_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); + // yield_constr.one_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); + // yield_constr.one_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); // x0 <- x1 yield_constr.one(vars.next_values[0] - vars.local_values[1]); @@ -89,6 +90,7 @@ mod tests { use crate::config::StarkConfig; use crate::fibonacci_stark::FibonacciStark; use crate::prover::prove; + use crate::verifier::verify; fn fibonacci(n: usize, x0: usize, x1: usize) -> usize { (0..n).fold((0, 1), |x, _| (x.1, x.0 + x.1)).1 @@ -110,14 +112,14 @@ mod tests { ]; let stark = S::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - prove::( + let proof = prove::( stark, - config, + &config, trace, public_inputs, &mut TimingTree::default(), )?; - Ok(()) + verify(stark, proof, &config, num_rows) } } diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 927eef6c..d6a9b562 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -6,6 +6,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::plonk::proof::FriChallenges; use crate::config::StarkConfig; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; @@ -67,10 +68,12 @@ fn get_challenges, C: GenericConfig, cons Ok(StarkProofChallenges { stark_alphas, stark_zeta, - fri_alpha, - fri_betas, - fri_pow_response, - fri_query_indices, + fri_challenges: FriChallenges { + fri_alpha, + fri_betas, + fri_pow_response, + fri_query_indices, + }, }) } @@ -82,7 +85,10 @@ impl, C: GenericConfig, const D: usize> config: &StarkConfig, degree_bits: usize, ) -> anyhow::Result> { - Ok(self.get_challenges(config, degree_bits)?.fri_query_indices) + Ok(self + .get_challenges(config, degree_bits)? + .fri_challenges + .fri_query_indices) } /// Computes all Fiat-Shamir challenges used in the Plonk proof. diff --git a/starky/src/proof.rs b/starky/src/proof.rs index fe4ac43d..2d9597d0 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -2,10 +2,12 @@ use plonky2::field::extension_field::Extendable; use plonky2::field::field_types::Field; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{CompressedFriProof, FriProof}; +use plonky2::fri::structure::{FriOpeningBatch, FriOpenings}; use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::plonk::proof::FriChallenges; use rayon::prelude::*; pub struct StarkProof, C: GenericConfig, const D: usize> { @@ -58,16 +60,7 @@ pub(crate) struct StarkProofChallenges, const D: us // Point at which the PLONK polynomials are opened. pub stark_zeta: F::Extension, - // Scaling factor to combine polynomials. - pub fri_alpha: F::Extension, - - // Betas used in the FRI commit phase reductions. - pub fri_betas: Vec, - - pub fri_pow_response: F, - - // Indices at which the oracle is queried in FRI. - pub fri_query_indices: Vec, + pub fri_challenges: FriChallenges, } /// Purported values of each polynomial at the challenge point. @@ -111,4 +104,21 @@ impl, const D: usize> StarkOpeningSet { challenger.observe_extension_elements(v); } } + + pub(crate) fn to_fri_openings(&self) -> FriOpenings { + let zeta_batch = FriOpeningBatch { + values: [ + self.local_values.as_slice(), + self.quotient_polys.as_slice(), + self.permutation_zs.as_slice(), + ] + .concat(), + }; + let zeta_right_batch = FriOpeningBatch { + values: self.next_values.to_vec(), + }; + FriOpenings { + batches: vec![zeta_batch, zeta_right_batch], + } + } } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index a3f2cb2d..6a22e671 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -23,7 +23,7 @@ use crate::vars::StarkEvaluationVars; // TODO: Deal with public inputs. pub fn prove( stark: S, - config: StarkConfig, + config: &StarkConfig, trace: Vec<[F; S::COLUMNS]>, public_inputs: [F; S::PUBLIC_INPUTS], timing: &mut TimingTree, @@ -101,7 +101,7 @@ where None, ) ); - let quotient_polys_cap = quotient_commitment.merkle_tree.cap; + let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); challenger.observe_cap("ient_polys_cap); let zeta = challenger.get_extension_challenge::(); @@ -164,7 +164,6 @@ where [(); S::PUBLIC_INPUTS]:, { let degree = 1 << degree_bits; - let points = F::two_adic_subgroup(degree_bits + rate_bits); // Evaluation of the first Lagrange polynomial on the LDE domain. let lagrange_first = { @@ -179,12 +178,18 @@ where evals.lde(rate_bits) }; - let z_h_on_coset = ZeroPolyOnCoset::new(degree_bits, rate_bits); + let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, rate_bits); // Retrieve the LDE values at index `i`. let get_at_index = |comm: &PolynomialBatch, i: usize| -> [F; S::COLUMNS] { comm.get_lde_values(i).try_into().unwrap() }; + let last = F::primitive_root_of_unity(degree_bits).inverse(); + let coset = F::cyclic_subgroup_coset_known_order( + F::primitive_root_of_unity(degree_bits + rate_bits), + F::coset_shift(), + degree << rate_bits, + ); let quotient_values = (0..degree << rate_bits) .into_par_iter() @@ -197,15 +202,19 @@ where ); let vars = StarkEvaluationVars:: { local_values: &get_at_index(trace_commitment, i), - next_values: &get_at_index(trace_commitment, (i + 1) % (degree << rate_bits)), + next_values: &get_at_index( + trace_commitment, + (i + (1 << rate_bits)) % (degree << rate_bits), + ), public_inputs: &public_inputs, }; stark.eval_packed_base(vars, &mut consumer); - // TODO: Fix this once we a genuine `PackedField`. + // TODO: Fix this once we use a genuine `PackedField`. let mut constraints_evals = consumer.accumulators(); let denominator_inv = z_h_on_coset.eval_inverse(i); + let z_last = coset[i] - last; for eval in &mut constraints_evals { - *eval *= denominator_inv; + *eval *= denominator_inv * z_last; } constraints_evals }) diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 46dc948f..63e063af 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -92,16 +92,19 @@ where let quotient_polys_zeta = &proof.openings.quotient_polys; let zeta_pow_deg = challenges.stark_zeta.exp_power_of_2(degree_bits); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; + let g = F::primitive_root_of_unity(degree_bits + config.fri_config.rate_bits); + let last = F::primitive_root_of_unity(degree_bits).inverse(); + let z_last = challenges.stark_zeta - last.into(); // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. // So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each // `quotient_degree_factor`-sized chunk of the original evaluations. for (i, chunk) in quotient_polys_zeta - .chunks(config.fri_config.rate_bits) + .chunks(1 << config.fri_config.rate_bits) .enumerate() { - ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); + ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg) / z_last); } let merkle_caps = &[proof.trace_cap, proof.quotient_polys_cap]; @@ -112,8 +115,8 @@ where F::primitive_root_of_unity(degree_bits).into(), config.fri_config.rate_bits, ), - &proof.openings, - &challenges, + &proof.openings.to_fri_openings(), + &challenges.fri_challenges, merkle_caps, &proof.opening_proof, &config.fri_params(degree_bits), @@ -145,5 +148,6 @@ fn recover_degree, C: GenericConfig, cons .1 .siblings .len() - + config.fri_config.cap_height) + + config.fri_config.cap_height + - config.fri_config.rate_bits) } diff --git a/system_zero/src/system_zero.rs b/system_zero/src/system_zero.rs index 49e25e6c..38326b68 100644 --- a/system_zero/src/system_zero.rs +++ b/system_zero/src/system_zero.rs @@ -108,7 +108,7 @@ mod tests { let config = StarkConfig::standard_fast_config(); let mut timing = TimingTree::new("prove", Level::Debug); let trace = system.generate_trace(); - prove::(system, config, trace, public_inputs, &mut timing)?; + prove::(system, &config, trace, public_inputs, &mut timing)?; Ok(()) } From b0738c2094bed3b345b7f9cc109056746f526e83 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 31 Jan 2022 21:05:23 +0100 Subject: [PATCH 05/14] Fix degree issue --- starky/src/fibonacci_stark.rs | 2 +- starky/src/get_challenges.rs | 1 + starky/src/verifier.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index ffaa14a7..06e366dc 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -120,6 +120,6 @@ mod tests { &mut TimingTree::default(), )?; - verify(stark, proof, &config, num_rows) + verify(stark, proof, &config, 5) } } diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index d6a9b562..1498f764 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -24,6 +24,7 @@ fn get_challenges, C: GenericConfig, cons let num_challenges = config.num_challenges; let num_fri_queries = config.fri_config.num_query_rounds; let lde_size = 1 << (degree_bits + config.fri_config.rate_bits); + dbg!(lde_size); let mut challenger = Challenger::::new(); diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 63e063af..ea9945ff 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -31,6 +31,7 @@ where [(); S::PUBLIC_INPUTS]:, { let challenges = proof_with_pis.get_challenges(config, degree_bits)?; + dbg!(&challenges.fri_challenges.fri_query_indices); verify_with_challenges(stark, proof_with_pis, challenges, config) } From f2369f4fae0dbc4b334067780d014778a3820ad1 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 1 Feb 2022 10:48:53 +0100 Subject: [PATCH 06/14] Test pass --- plonky2/src/hash/merkle_proofs.rs | 1 - starky/src/fibonacci_stark.rs | 2 +- starky/src/get_challenges.rs | 1 - starky/src/prover.rs | 2 +- starky/src/stark.rs | 3 ++- starky/src/verifier.rs | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/plonky2/src/hash/merkle_proofs.rs b/plonky2/src/hash/merkle_proofs.rs index feb39791..c2f3655d 100644 --- a/plonky2/src/hash/merkle_proofs.rs +++ b/plonky2/src/hash/merkle_proofs.rs @@ -31,7 +31,6 @@ pub(crate) fn verify_merkle_proof>( merkle_cap: &MerkleCap, proof: &MerkleProof, ) -> Result<()> { - dbg!(leaf_index); let mut index = leaf_index; let mut current_digest = H::hash(&leaf_data, false); for &sibling_digest in proof.siblings.iter() { diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 06e366dc..488f1d46 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -59,7 +59,7 @@ impl, const D: usize> Stark for FibonacciStar FE: FieldExtension, P: PackedField, { - // // Check public inputs. + // Check public inputs. // yield_constr.one_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); // yield_constr.one_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); // yield_constr.one_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 1498f764..d6a9b562 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -24,7 +24,6 @@ fn get_challenges, C: GenericConfig, cons let num_challenges = config.num_challenges; let num_fri_queries = config.fri_config.num_query_rounds; let lde_size = 1 << (degree_bits + config.fri_config.rate_bits); - dbg!(lde_size); let mut challenger = Challenger::::new(); diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 6a22e671..e9c31d98 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -124,7 +124,7 @@ where timing, "compute openings proof", PolynomialBatch::prove_openings( - &S::fri_instance(zeta, g, rate_bits), + &S::fri_instance(zeta, g, rate_bits, config.num_challenges), initial_merkle_trees, &mut challenger, &fri_params, diff --git a/starky/src/stark.rs b/starky/src/stark.rs index f91d4fdd..3b8c795a 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -67,10 +67,11 @@ pub trait Stark, const D: usize>: Sync { zeta: F::Extension, g: F::Extension, rate_bits: usize, + num_challenges: usize, ) -> FriInstanceInfo { let no_blinding_oracle = FriOracleInfo { blinding: false }; let trace_info = FriPolynomialInfo::from_range(0, 0..Self::COLUMNS); - let quotient_info = FriPolynomialInfo::from_range(1, 0..1 << rate_bits); + let quotient_info = FriPolynomialInfo::from_range(1, 0..(1 << rate_bits) * num_challenges); let zeta_batch = FriBatchInfo { point: zeta, polynomials: [trace_info.clone(), quotient_info].concat(), diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index ea9945ff..f01c72c5 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -31,7 +31,6 @@ where [(); S::PUBLIC_INPUTS]:, { let challenges = proof_with_pis.get_challenges(config, degree_bits)?; - dbg!(&challenges.fri_challenges.fri_query_indices); verify_with_challenges(stark, proof_with_pis, challenges, config) } @@ -115,6 +114,7 @@ where challenges.stark_zeta, F::primitive_root_of_unity(degree_bits).into(), config.fri_config.rate_bits, + config.num_challenges, ), &proof.openings.to_fri_openings(), &challenges.fri_challenges, From 9f8696ada55e8b87dbdef8f58a0e6c5183d5166f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 1 Feb 2022 13:57:03 +0100 Subject: [PATCH 07/14] Fix bug --- starky/src/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index f01c72c5..5317cb5c 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -130,7 +130,7 @@ where fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { let n = 1 << log_n; let g = F::primitive_root_of_unity(log_n); - let z_x = x.exp_power_of_2(log_n); + let z_x = x.exp_power_of_2(log_n) - F::ONE; let invs = F::batch_multiplicative_inverse(&[ F::from_canonical_usize(n) * (x - F::ONE), F::from_canonical_usize(n) * (g * x - F::ONE), From 984f44b2817c1cacb513bcf35d2e883613d87625 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 1 Feb 2022 14:41:27 +0100 Subject: [PATCH 08/14] Fix lde -> coset_lde bug --- field/src/polynomial/mod.rs | 5 +++++ starky/src/fibonacci_stark.rs | 6 +++--- starky/src/prover.rs | 5 +++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/field/src/polynomial/mod.rs b/field/src/polynomial/mod.rs index 4264c914..d36757b3 100644 --- a/field/src/polynomial/mod.rs +++ b/field/src/polynomial/mod.rs @@ -57,6 +57,11 @@ impl PolynomialValues { fft_with_options(coeffs, Some(rate_bits), None) } + pub fn coset_lde(self, rate_bits: usize) -> Self { + let coeffs = ifft(self).lde(rate_bits); + coeffs.coset_fft_with_options(F::coset_shift(), Some(rate_bits), None) + } + pub fn degree(&self) -> usize { self.degree_plus_one() .checked_sub(1) diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 488f1d46..1d760455 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -60,9 +60,9 @@ impl, const D: usize> Stark for FibonacciStar P: PackedField, { // Check public inputs. - // yield_constr.one_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); - // yield_constr.one_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); - // yield_constr.one_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); + yield_constr.one_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); + yield_constr.one_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); + yield_constr.one_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); // x0 <- x1 yield_constr.one(vars.next_values[0] - vars.local_values[1]); diff --git a/starky/src/prover.rs b/starky/src/prover.rs index e9c31d98..352b03f7 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -1,6 +1,7 @@ use anyhow::{ensure, Result}; use itertools::Itertools; use plonky2::field::extension_field::Extendable; +use plonky2::field::extension_field::FieldExtension; use plonky2::field::field_types::Field; use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; @@ -169,13 +170,13 @@ where let lagrange_first = { let mut evals = PolynomialValues::new(vec![F::ZERO; degree]); evals.values[0] = F::ONE; - evals.lde(rate_bits) + evals.coset_lde(rate_bits) }; // Evaluation of the last Lagrange polynomial on the LDE domain. let lagrange_last = { let mut evals = PolynomialValues::new(vec![F::ZERO; degree]); evals.values[degree - 1] = F::ONE; - evals.lde(rate_bits) + evals.coset_lde(rate_bits) }; let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, rate_bits); From 8ab4f855b26715d02200588d60f3425390479d31 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 1 Feb 2022 17:02:11 +0100 Subject: [PATCH 09/14] Add `fri_challenges()` to Challenger. --- field/src/field_types.rs | 1 - plonky2/src/fri/proof.rs | 15 +++++++- plonky2/src/fri/verifier.rs | 3 +- plonky2/src/iop/challenger.rs | 56 ++++++++++++++++++++++++++++- plonky2/src/plonk/get_challenges.rs | 52 ++++++--------------------- plonky2/src/plonk/plonk_common.rs | 1 - plonky2/src/plonk/proof.rs | 15 +------- starky/src/get_challenges.rs | 46 +++++------------------- starky/src/proof.rs | 4 +-- starky/src/prover.rs | 1 - starky/src/verifier.rs | 2 -- 11 files changed, 90 insertions(+), 106 deletions(-) diff --git a/field/src/field_types.rs b/field/src/field_types.rs index 845d8e83..0d7b314f 100644 --- a/field/src/field_types.rs +++ b/field/src/field_types.rs @@ -389,7 +389,6 @@ pub trait Field: /// Representative `g` of the coset used in FRI, so that LDEs in FRI are done over `gH`. fn coset_shift() -> Self { Self::MULTIPLICATIVE_GROUP_GENERATOR - // Self::ONE } /// Equivalent to *self + x * y, but may be cheaper. diff --git a/plonky2/src/fri/proof.rs b/plonky2/src/fri/proof.rs index bca7b8db..1f9e6b16 100644 --- a/plonky2/src/fri/proof.rs +++ b/plonky2/src/fri/proof.rs @@ -16,7 +16,7 @@ use crate::iop::ext_target::ExtensionTarget; use crate::iop::target::Target; use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::plonk_common::salt_size; -use crate::plonk::proof::{FriChallenges, FriInferredElements, ProofChallenges}; +use crate::plonk::proof::{FriInferredElements, ProofChallenges}; /// Evaluations and Merkle proof produced by the prover in a FRI query step. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] @@ -362,3 +362,16 @@ impl, H: Hasher, const D: usize> CompressedFriPr } } } + +pub struct FriChallenges, const D: usize> { + // Scaling factor to combine polynomials. + pub fri_alpha: F::Extension, + + // Betas used in the FRI commit phase reductions. + pub fri_betas: Vec, + + pub fri_pow_response: F, + + // Indices at which the oracle is queried in FRI. + pub fri_query_indices: Vec, +} diff --git a/plonky2/src/fri/verifier.rs b/plonky2/src/fri/verifier.rs index 3e70c025..47f10b8a 100644 --- a/plonky2/src/fri/verifier.rs +++ b/plonky2/src/fri/verifier.rs @@ -4,14 +4,13 @@ use plonky2_field::field_types::Field; use plonky2_field::interpolation::{barycentric_weights, interpolate}; use plonky2_util::{log2_strict, reverse_index_bits_in_place}; -use crate::fri::proof::{FriInitialTreeProof, FriProof, FriQueryRound}; +use crate::fri::proof::{FriChallenges, FriInitialTreeProof, FriProof, FriQueryRound}; use crate::fri::structure::{FriBatchInfo, FriInstanceInfo, FriOpenings}; use crate::fri::{FriConfig, FriParams}; use crate::hash::hash_types::RichField; use crate::hash::merkle_proofs::verify_merkle_proof; use crate::hash::merkle_tree::MerkleCap; use crate::plonk::config::{GenericConfig, Hasher}; -use crate::plonk::proof::{FriChallenges, OpeningSet, ProofChallenges}; use crate::util::reducing::ReducingFactor; use crate::util::reverse_bits; diff --git a/plonky2/src/iop/challenger.rs b/plonky2/src/iop/challenger.rs index b8ca4fb7..d7583646 100644 --- a/plonky2/src/iop/challenger.rs +++ b/plonky2/src/iop/challenger.rs @@ -2,7 +2,10 @@ use std::convert::TryInto; use std::marker::PhantomData; use plonky2_field::extension_field::{Extendable, FieldExtension}; +use plonky2_field::polynomial::PolynomialCoeffs; +use crate::fri::proof::FriChallenges; +use crate::fri::FriConfig; use crate::hash::hash_types::RichField; use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget}; use crate::hash::hashing::{PlonkyPermutation, SPONGE_RATE, SPONGE_WIDTH}; @@ -10,7 +13,7 @@ use crate::hash::merkle_tree::MerkleCap; use crate::iop::ext_target::ExtensionTarget; use crate::iop::target::Target; use crate::plonk::circuit_builder::CircuitBuilder; -use crate::plonk::config::{AlgebraicHasher, GenericHashOut, Hasher}; +use crate::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut, Hasher}; use crate::plonk::proof::{OpeningSet, OpeningSetTarget}; /// Observes prover messages, and generates challenges by hashing the transcript, a la Fiat-Shamir. @@ -152,6 +155,57 @@ impl> Challenger { .collect() } + pub fn fri_challenges, const D: usize>( + &mut self, + commit_phase_merkle_caps: &[MerkleCap], + final_poly: &PolynomialCoeffs, + pow_witness: F, + degree_bits: usize, + config: &FriConfig, + ) -> FriChallenges + where + F: RichField + Extendable, + { + let num_fri_queries = config.num_query_rounds; + let lde_size = 1 << (degree_bits + config.rate_bits); + // Scaling factor to combine polynomials. + let fri_alpha = self.get_extension_challenge::(); + + // Recover the random betas used in the FRI reductions. + let fri_betas = commit_phase_merkle_caps + .iter() + .map(|cap| { + self.observe_cap(cap); + self.get_extension_challenge::() + }) + .collect(); + + self.observe_extension_elements(&final_poly.coeffs); + + let fri_pow_response = C::InnerHasher::hash( + &self + .get_hash() + .elements + .iter() + .copied() + .chain(Some(pow_witness)) + .collect::>(), + false, + ) + .elements[0]; + + let fri_query_indices = (0..num_fri_queries) + .map(|_| self.get_challenge().to_canonical_u64() as usize % lde_size) + .collect(); + + FriChallenges { + fri_alpha, + fri_betas, + fri_pow_response, + fri_query_indices, + } + } + /// Absorb any buffered inputs. After calling this, the input buffer will be empty. fn absorb_buffered_inputs(&mut self) { if self.input_buffer.is_empty() { diff --git a/plonky2/src/plonk/get_challenges.rs b/plonky2/src/plonk/get_challenges.rs index 440705ce..3167fef7 100644 --- a/plonky2/src/plonk/get_challenges.rs +++ b/plonky2/src/plonk/get_challenges.rs @@ -1,10 +1,9 @@ use std::collections::HashSet; -use itertools::Itertools; use plonky2_field::extension_field::Extendable; use plonky2_field::polynomial::PolynomialCoeffs; -use crate::fri::proof::{CompressedFriProof, FriProof}; +use crate::fri::proof::{CompressedFriProof, FriChallenges, FriProof}; use crate::fri::verifier::{compute_evaluation, fri_combine_initial, PrecomputedReducedOpenings}; use crate::hash::hash_types::RichField; use crate::hash::merkle_tree::MerkleCap; @@ -12,8 +11,8 @@ use crate::iop::challenger::Challenger; use crate::plonk::circuit_data::CommonCircuitData; use crate::plonk::config::{GenericConfig, Hasher}; use crate::plonk::proof::{ - CompressedProof, CompressedProofWithPublicInputs, FriChallenges, FriInferredElements, - OpeningSet, Proof, ProofChallenges, ProofWithPublicInputs, + CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet, Proof, + ProofChallenges, ProofWithPublicInputs, }; use crate::util::reverse_bits; @@ -30,8 +29,6 @@ fn get_challenges, C: GenericConfig, cons ) -> anyhow::Result> { let config = &common_data.config; let num_challenges = config.num_challenges; - let num_fri_queries = config.fri_config.num_query_rounds; - let lde_size = common_data.lde_size(); let mut challenger = Challenger::::new(); @@ -51,47 +48,18 @@ fn get_challenges, C: GenericConfig, cons challenger.observe_opening_set(openings); - // Scaling factor to combine polynomials. - let fri_alpha = challenger.get_extension_challenge::(); - - // Recover the random betas used in the FRI reductions. - let fri_betas = commit_phase_merkle_caps - .iter() - .map(|cap| { - challenger.observe_cap(cap); - challenger.get_extension_challenge::() - }) - .collect(); - - challenger.observe_extension_elements(&final_poly.coeffs); - - let fri_pow_response = C::InnerHasher::hash( - &challenger - .get_hash() - .elements - .iter() - .copied() - .chain(Some(pow_witness)) - .collect_vec(), - false, - ) - .elements[0]; - - let fri_query_indices = (0..num_fri_queries) - .map(|_| challenger.get_challenge().to_canonical_u64() as usize % lde_size) - .collect(); - Ok(ProofChallenges { plonk_betas, plonk_gammas, plonk_alphas, plonk_zeta, - fri_challenges: FriChallenges { - fri_alpha, - fri_betas, - fri_pow_response, - fri_query_indices, - }, + fri_challenges: challenger.fri_challenges::( + commit_phase_merkle_caps, + final_poly, + pow_witness, + common_data.degree_bits, + &config.fri_config, + ), }) } diff --git a/plonky2/src/plonk/plonk_common.rs b/plonky2/src/plonk/plonk_common.rs index 94279d12..519593b3 100644 --- a/plonky2/src/plonk/plonk_common.rs +++ b/plonky2/src/plonk/plonk_common.rs @@ -1,7 +1,6 @@ use plonky2_field::extension_field::Extendable; use plonky2_field::field_types::Field; use plonky2_field::packed_field::PackedField; -use plonky2_util::log2_strict; use crate::fri::oracle::SALT_SIZE; use crate::fri::structure::FriOracleInfo; diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index 401b9f52..9d9eaaff 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -3,7 +3,7 @@ use rayon::prelude::*; use serde::{Deserialize, Serialize}; use crate::fri::oracle::PolynomialBatch; -use crate::fri::proof::{CompressedFriProof, FriProof, FriProofTarget}; +use crate::fri::proof::{CompressedFriProof, FriChallenges, FriProof, FriProofTarget}; use crate::fri::structure::{ FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget, }; @@ -242,19 +242,6 @@ pub(crate) struct ProofChallenges, const D: usize> pub fri_challenges: FriChallenges, } -pub struct FriChallenges, const D: usize> { - // Scaling factor to combine polynomials. - pub fri_alpha: F::Extension, - - // Betas used in the FRI commit phase reductions. - pub fri_betas: Vec, - - pub fri_pow_response: F, - - // Indices at which the oracle is queried in FRI. - pub fri_query_indices: Vec, -} - /// Coset elements that can be inferred in the FRI reduction steps. pub(crate) struct FriInferredElements, const D: usize>( pub Vec, diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index d6a9b562..9d9b808e 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -5,8 +5,7 @@ use plonky2::fri::proof::FriProof; use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::challenger::Challenger; -use plonky2::plonk::config::{GenericConfig, Hasher}; -use plonky2::plonk::proof::FriChallenges; +use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; @@ -35,45 +34,16 @@ fn get_challenges, C: GenericConfig, cons openings.observe(&mut challenger); - // Scaling factor to combine polynomials. - let fri_alpha = challenger.get_extension_challenge::(); - - // Recover the random betas used in the FRI reductions. - let fri_betas = commit_phase_merkle_caps - .iter() - .map(|cap| { - challenger.observe_cap(cap); - challenger.get_extension_challenge::() - }) - .collect(); - - challenger.observe_extension_elements(&final_poly.coeffs); - - let fri_pow_response = C::InnerHasher::hash( - &challenger - .get_hash() - .elements - .iter() - .copied() - .chain(Some(pow_witness)) - .collect::>(), - false, - ) - .elements[0]; - - let fri_query_indices = (0..num_fri_queries) - .map(|_| challenger.get_challenge().to_canonical_u64() as usize % lde_size) - .collect(); - Ok(StarkProofChallenges { stark_alphas, stark_zeta, - fri_challenges: FriChallenges { - fri_alpha, - fri_betas, - fri_pow_response, - fri_query_indices, - }, + fri_challenges: challenger.fri_challenges::( + commit_phase_merkle_caps, + final_poly, + pow_witness, + degree_bits, + &config.fri_config, + ), }) } diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 2d9597d0..c2d2ac67 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -1,13 +1,11 @@ use plonky2::field::extension_field::Extendable; -use plonky2::field::field_types::Field; use plonky2::fri::oracle::PolynomialBatch; -use plonky2::fri::proof::{CompressedFriProof, FriProof}; +use plonky2::fri::proof::{CompressedFriProof, FriChallenges, FriProof}; use plonky2::fri::structure::{FriOpeningBatch, FriOpenings}; use plonky2::hash::hash_types::RichField; use plonky2::hash::merkle_tree::MerkleCap; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, Hasher}; -use plonky2::plonk::proof::FriChallenges; use rayon::prelude::*; pub struct StarkProof, C: GenericConfig, const D: usize> { diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 352b03f7..1c5310e4 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -1,7 +1,6 @@ use anyhow::{ensure, Result}; use itertools::Itertools; use plonky2::field::extension_field::Extendable; -use plonky2::field::extension_field::FieldExtension; use plonky2::field::field_types::Field; use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 5317cb5c..298e4797 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -3,10 +3,8 @@ use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::fri::verifier::verify_fri_proof; use plonky2::hash::hash_types::RichField; -use plonky2::plonk::circuit_data::CommonCircuitData; use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; -use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2_util::log2_strict; use crate::config::StarkConfig; From 1e04f4f5a437345340110d6107671910100d220d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 1 Feb 2022 17:34:03 +0100 Subject: [PATCH 10/14] Comments --- plonky2/src/fri/verifier.rs | 2 +- starky/src/fibonacci_stark.rs | 2 +- starky/src/get_challenges.rs | 4 +++- starky/src/proof.rs | 2 +- starky/src/prover.rs | 4 +++- starky/src/verifier.rs | 22 +++++++++++----------- system_zero/src/system_zero.rs | 6 ++++-- 7 files changed, 24 insertions(+), 18 deletions(-) diff --git a/plonky2/src/fri/verifier.rs b/plonky2/src/fri/verifier.rs index 47f10b8a..49cfa053 100644 --- a/plonky2/src/fri/verifier.rs +++ b/plonky2/src/fri/verifier.rs @@ -86,7 +86,7 @@ pub fn verify_fri_proof< ); let precomputed_reduced_evals = - PrecomputedReducedOpenings::from_os_and_alpha(&openings, challenges.fri_alpha); + PrecomputedReducedOpenings::from_os_and_alpha(openings, challenges.fri_alpha); for (&x_index, round_proof) in challenges .fri_query_indices .iter() diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index 1d760455..dc6d676a 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -120,6 +120,6 @@ mod tests { &mut TimingTree::default(), )?; - verify(stark, proof, &config, 5) + verify(stark, proof, &config) } } diff --git a/starky/src/get_challenges.rs b/starky/src/get_challenges.rs index 9d9b808e..79e1c032 100644 --- a/starky/src/get_challenges.rs +++ b/starky/src/get_challenges.rs @@ -10,6 +10,7 @@ use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs}; +#[allow(clippy::too_many_arguments)] fn get_challenges, C: GenericConfig, const D: usize>( trace_cap: &MerkleCap, quotient_polys_cap: &MerkleCap, @@ -61,7 +62,7 @@ impl, C: GenericConfig, const D: usize> .fri_query_indices) } - /// Computes all Fiat-Shamir challenges used in the Plonk proof. + /// Computes all Fiat-Shamir challenges used in the STARK proof. pub(crate) fn get_challenges( &self, config: &StarkConfig, @@ -93,6 +94,7 @@ impl, C: GenericConfig, const D: usize> } } +// TODO: Deal with the compressed stuff. // impl, C: GenericConfig, const D: usize> // CompressedProofWithPublicInputs // { diff --git a/starky/src/proof.rs b/starky/src/proof.rs index c2d2ac67..50ef21bc 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -25,7 +25,7 @@ pub struct StarkProofWithPublicInputs< const D: usize, > { pub proof: StarkProof, - // TODO: Maybe make it generic over a `S: Start` and replace with `[F; S::PUBLIC_INPUTS]`. + // TODO: Maybe make it generic over a `S: Stark` and replace with `[F; S::PUBLIC_INPUTS]`. pub public_inputs: Vec, } diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 1c5310e4..d6543dae 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -20,7 +20,6 @@ use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vars::StarkEvaluationVars; -// TODO: Deal with public inputs. pub fn prove( stark: S, config: &StarkConfig, @@ -184,6 +183,7 @@ where let get_at_index = |comm: &PolynomialBatch, i: usize| -> [F; S::COLUMNS] { comm.get_lde_values(i).try_into().unwrap() }; + // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); let coset = F::cyclic_subgroup_coset_known_order( F::primitive_root_of_unity(degree_bits + rate_bits), @@ -211,6 +211,8 @@ where stark.eval_packed_base(vars, &mut consumer); // TODO: Fix this once we use a genuine `PackedField`. let mut constraints_evals = consumer.accumulators(); + // We divide the constraints evaluations by `Z_H(x) / x - last`, i.e., the vanishing + // polynomial of `H` without it's last element. let denominator_inv = z_h_on_coset.eval_inverse(i); let z_last = coset[i] - last; for eval in &mut constraints_evals { diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 298e4797..6a4464e1 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -13,7 +13,7 @@ use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProof use crate::stark::Stark; use crate::vars::StarkEvaluationVars; -pub(crate) fn verify< +pub fn verify< F: RichField + Extendable, C: GenericConfig, S: Stark, @@ -22,14 +22,14 @@ pub(crate) fn verify< stark: S, proof_with_pis: StarkProofWithPublicInputs, config: &StarkConfig, - degree_bits: usize, ) -> Result<()> where [(); S::COLUMNS]:, [(); S::PUBLIC_INPUTS]:, { + let degree_bits = log2_strict(recover_degree(&proof_with_pis.proof, config)); let challenges = proof_with_pis.get_challenges(config, degree_bits)?; - verify_with_challenges(stark, proof_with_pis, challenges, config) + verify_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) } pub(crate) fn verify_with_challenges< @@ -41,6 +41,7 @@ pub(crate) fn verify_with_challenges< stark: S, proof_with_pis: StarkProofWithPublicInputs, challenges: StarkProofChallenges, + degree_bits: usize, config: &StarkConfig, ) -> Result<()> where @@ -51,9 +52,6 @@ where proof, public_inputs, } = proof_with_pis; - let degree = recover_degree(&proof, config); - let degree_bits = log2_strict(degree); - let local_values = &proof.openings.local_values; let next_values = &proof.openings.local_values; let StarkOpeningSet { @@ -80,17 +78,16 @@ where .iter() .map(|&alpha| F::Extension::from_basefield(alpha)) .collect::>(), - l_1.into(), - l_last.into(), + l_1, + l_last, ); stark.eval_ext(vars, &mut consumer); let acc = consumer.accumulators(); - // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. + // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x) / (x - last)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; let zeta_pow_deg = challenges.stark_zeta.exp_power_of_2(degree_bits); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; - let g = F::primitive_root_of_unity(degree_bits + config.fri_config.rate_bits); let last = F::primitive_root_of_unity(degree_bits).inverse(); let z_last = challenges.stark_zeta - last.into(); // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. @@ -124,7 +121,9 @@ where Ok(()) } -/// Evaluate the Lagrange basis `L_1` and `L_n` at a point `x`. +/// Evaluate the Lagrange polynomials `L_1` and `L_n` at a point `x`. +/// `L_1(x) = (x^n - 1)/(n * (x - 1))` +/// `L_n(x) = (x^n - 1)/(n * (g * x - 1))`, with `g` the first element of the subgroup. fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { let n = 1 << log_n; let g = F::primitive_root_of_unity(log_n); @@ -137,6 +136,7 @@ fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { (z_x * invs[0], z_x * invs[1]) } +/// Recover the length of the trace from a STARK proof and a STARK config. fn recover_degree, C: GenericConfig, const D: usize>( proof: &StarkProof, config: &StarkConfig, diff --git a/system_zero/src/system_zero.rs b/system_zero/src/system_zero.rs index 38326b68..31b8434f 100644 --- a/system_zero/src/system_zero.rs +++ b/system_zero/src/system_zero.rs @@ -16,6 +16,7 @@ use crate::public_input_layout::NUM_PUBLIC_INPUTS; /// We require at least 2^16 rows as it helps support efficient 16-bit range checks. const MIN_TRACE_ROWS: usize = 1 << 16; +#[derive(Copy, Clone)] pub struct SystemZero, const D: usize> { _phantom: PhantomData, } @@ -92,6 +93,7 @@ mod tests { use starky::config::StarkConfig; use starky::prover::prove; use starky::stark::Stark; + use starky::verifier::verify; use crate::system_zero::SystemZero; @@ -108,8 +110,8 @@ mod tests { let config = StarkConfig::standard_fast_config(); let mut timing = TimingTree::new("prove", Level::Debug); let trace = system.generate_trace(); - prove::(system, &config, trace, public_inputs, &mut timing)?; + let proof = prove::(system, &config, trace, public_inputs, &mut timing)?; - Ok(()) + verify(system, proof, &config) } } From bff763e3e79135a998fbf6c3fb6cf46bac32427c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Feb 2022 11:23:03 +0100 Subject: [PATCH 11/14] Add distinction between (non-)wrapping constraints --- starky/src/constraint_consumer.rs | 65 +++++++++++++++++------------ starky/src/fibonacci_stark.rs | 13 +++--- starky/src/prover.rs | 4 +- starky/src/verifier.rs | 9 ++-- system_zero/src/core_registers.rs | 10 ++--- system_zero/src/permutation_unit.rs | 4 +- 6 files changed, 61 insertions(+), 44 deletions(-) diff --git a/starky/src/constraint_consumer.rs b/starky/src/constraint_consumer.rs index adb88e41..922475c3 100644 --- a/starky/src/constraint_consumer.rs +++ b/starky/src/constraint_consumer.rs @@ -14,6 +14,9 @@ pub struct ConstraintConsumer { /// Running sums of constraints that have been emitted so far, scaled by powers of alpha. constraint_accs: Vec

, + /// The evaluation of `X - g^(n-1)`. + z_last: P, + /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated /// with the first trace row, and zero at other points in the subgroup. lagrange_basis_first: P, @@ -24,10 +27,16 @@ pub struct ConstraintConsumer { } impl ConstraintConsumer

{ - pub fn new(alphas: Vec, lagrange_basis_first: P, lagrange_basis_last: P) -> Self { + pub fn new( + alphas: Vec, + z_last: P, + lagrange_basis_first: P, + lagrange_basis_last: P, + ) -> Self { Self { constraint_accs: vec![P::ZEROS; alphas.len()], alphas, + z_last, lagrange_basis_first, lagrange_basis_last, } @@ -41,31 +50,29 @@ impl ConstraintConsumer

{ .collect() } - /// Add one constraint. - pub fn one(&mut self, constraint: P) { + /// Add one constraint valid on all rows except the last. + pub fn constraint(&mut self, constraint: P) { + self.constraint_wrapping(constraint * self.z_last); + } + + /// Add one constraint on all rows. + pub fn constraint_wrapping(&mut self, constraint: P) { for (&alpha, acc) in self.alphas.iter().zip(&mut self.constraint_accs) { *acc *= alpha; *acc += constraint; } } - /// Add a series of constraints. - pub fn many(&mut self, constraints: impl IntoIterator) { - constraints - .into_iter() - .for_each(|constraint| self.one(constraint)); - } - /// Add one constraint, but first multiply it by a filter such that it will only apply to the /// first row of the trace. - pub fn one_first_row(&mut self, constraint: P) { - self.one(constraint * self.lagrange_basis_first); + pub fn constraint_first_row(&mut self, constraint: P) { + self.constraint_wrapping(constraint * self.lagrange_basis_first); } /// Add one constraint, but first multiply it by a filter such that it will only apply to the /// last row of the trace. - pub fn one_last_row(&mut self, constraint: P) { - self.one(constraint * self.lagrange_basis_last); + pub fn constraint_last_row(&mut self, constraint: P) { + self.constraint_wrapping(constraint * self.lagrange_basis_last); } } @@ -76,6 +83,9 @@ pub struct RecursiveConstraintConsumer, const D: us /// A running sum of constraints that have been emitted so far, scaled by powers of alpha. constraint_acc: ExtensionTarget, + /// The evaluation of `X - g^(n-1)`. + z_last: ExtensionTarget, + /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated /// with the first trace row, and zero at other points in the subgroup. lagrange_basis_first: ExtensionTarget, @@ -88,42 +98,45 @@ pub struct RecursiveConstraintConsumer, const D: us } impl, const D: usize> RecursiveConstraintConsumer { - /// Add one constraint. - pub fn one(&mut self, builder: &mut CircuitBuilder, constraint: ExtensionTarget) { + /// Add one constraint valid on all rows except the last. + pub fn constraint( + &mut self, + builder: &mut CircuitBuilder, + constraint: ExtensionTarget, + ) { self.constraint_acc = builder.scalar_mul_add_extension(self.alpha, self.constraint_acc, constraint); } - /// Add a series of constraints. - pub fn many( + /// Add one constraint valid on all rows. + pub fn constraint_wrapping( &mut self, builder: &mut CircuitBuilder, - constraints: impl IntoIterator>, + constraint: ExtensionTarget, ) { - constraints - .into_iter() - .for_each(|constraint| self.one(builder, constraint)); + let filtered_constraint = builder.mul_extension(constraint, self.z_last); + self.constraint(builder, filtered_constraint); } /// Add one constraint, but first multiply it by a filter such that it will only apply to the /// first row of the trace. - pub fn one_first_row( + pub fn constraint_first_row( &mut self, builder: &mut CircuitBuilder, constraint: ExtensionTarget, ) { let filtered_constraint = builder.mul_extension(constraint, self.lagrange_basis_first); - self.one(builder, filtered_constraint); + self.constraint(builder, filtered_constraint); } /// Add one constraint, but first multiply it by a filter such that it will only apply to the /// last row of the trace. - pub fn one_last_row( + pub fn constraint_last_row( &mut self, builder: &mut CircuitBuilder, constraint: ExtensionTarget, ) { let filtered_constraint = builder.mul_extension(constraint, self.lagrange_basis_last); - self.one(builder, filtered_constraint); + self.constraint(builder, filtered_constraint); } } diff --git a/starky/src/fibonacci_stark.rs b/starky/src/fibonacci_stark.rs index dc6d676a..f3ffd8a2 100644 --- a/starky/src/fibonacci_stark.rs +++ b/starky/src/fibonacci_stark.rs @@ -60,14 +60,17 @@ impl, const D: usize> Stark for FibonacciStar P: PackedField, { // Check public inputs. - yield_constr.one_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); - yield_constr.one_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); - yield_constr.one_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); + yield_constr + .constraint_first_row(vars.local_values[0] - vars.public_inputs[Self::PI_INDEX_X0]); + yield_constr + .constraint_first_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_X1]); + yield_constr + .constraint_last_row(vars.local_values[1] - vars.public_inputs[Self::PI_INDEX_RES]); // x0 <- x1 - yield_constr.one(vars.next_values[0] - vars.local_values[1]); + yield_constr.constraint(vars.next_values[0] - vars.local_values[1]); // x1 <- x0 + x1 - yield_constr.one(vars.next_values[1] - vars.local_values[0] - vars.local_values[1]); + yield_constr.constraint(vars.next_values[1] - vars.local_values[0] - vars.local_values[1]); } fn eval_ext_recursively( diff --git a/starky/src/prover.rs b/starky/src/prover.rs index d6543dae..4d7f8c3f 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -197,6 +197,7 @@ where // TODO: Set `P` to a genuine `PackedField` here. let mut consumer = ConstraintConsumer::::new( alphas.clone(), + coset[i] - last, lagrange_first.values[i], lagrange_last.values[i], ); @@ -214,9 +215,8 @@ where // We divide the constraints evaluations by `Z_H(x) / x - last`, i.e., the vanishing // polynomial of `H` without it's last element. let denominator_inv = z_h_on_coset.eval_inverse(i); - let z_last = coset[i] - last; for eval in &mut constraints_evals { - *eval *= denominator_inv * z_last; + *eval *= denominator_inv; } constraints_evals }) diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 6a4464e1..5753f21f 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -72,24 +72,25 @@ where }; let (l_1, l_last) = eval_l_1_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(); let mut consumer = ConstraintConsumer::::new( challenges .stark_alphas .iter() .map(|&alpha| F::Extension::from_basefield(alpha)) .collect::>(), + z_last, l_1, l_last, ); stark.eval_ext(vars, &mut consumer); let acc = consumer.accumulators(); - // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x) / (x - last)`, at zeta. + // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; let zeta_pow_deg = challenges.stark_zeta.exp_power_of_2(degree_bits); let z_h_zeta = zeta_pow_deg - F::Extension::ONE; - let last = F::primitive_root_of_unity(degree_bits).inverse(); - let z_last = challenges.stark_zeta - last.into(); // `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations. // Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)` // where the "real" quotient polynomial is `t(X) = t_0(X) + t_1(X)*X^n + t_2(X)*X^{2n} + ...`. @@ -99,7 +100,7 @@ where .chunks(1 << config.fri_config.rate_bits) .enumerate() { - ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg) / z_last); + ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); } let merkle_caps = &[proof.trace_cap, proof.quotient_polys_cap]; diff --git a/system_zero/src/core_registers.rs b/system_zero/src/core_registers.rs index 249c16a3..21faa288 100644 --- a/system_zero/src/core_registers.rs +++ b/system_zero/src/core_registers.rs @@ -55,16 +55,16 @@ impl, const D: usize> SystemZero { let local_clock = vars.local_values[COL_CLOCK]; let next_clock = vars.next_values[COL_CLOCK]; let delta_clock = next_clock - local_clock; - yield_constr.one_first_row(local_clock); - yield_constr.one(delta_clock - FE::ONE); + yield_constr.constraint_first_row(local_clock); + yield_constr.constraint(delta_clock - FE::ONE); // The 16-bit table must start with 0, end with 2^16 - 1, and increment by 0 or 1. let local_range_16 = vars.local_values[COL_RANGE_16]; let next_range_16 = vars.next_values[COL_RANGE_16]; let delta_range_16 = next_range_16 - local_range_16; - yield_constr.one_first_row(local_range_16); - yield_constr.one_last_row(local_range_16 - FE::from_canonical_u64((1 << 16) - 1)); - yield_constr.one(delta_range_16 * (delta_range_16 - FE::ONE)); + yield_constr.constraint_first_row(local_range_16); + yield_constr.constraint_last_row(local_range_16 - FE::from_canonical_u64((1 << 16) - 1)); + yield_constr.constraint(delta_range_16 * (delta_range_16 - FE::ONE)); todo!() } diff --git a/system_zero/src/permutation_unit.rs b/system_zero/src/permutation_unit.rs index a490b49d..43883fca 100644 --- a/system_zero/src/permutation_unit.rs +++ b/system_zero/src/permutation_unit.rs @@ -53,7 +53,7 @@ impl, const D: usize> SystemZero { // Assert that the computed output matches the outputs in the trace. for i in 0..SPONGE_WIDTH { let out = local_values[col_permutation_output(i)]; - yield_constr.one(state[i] - out); + yield_constr.constraint(state[i] - out); } } @@ -80,7 +80,7 @@ impl, const D: usize> SystemZero { for i in 0..SPONGE_WIDTH { let out = local_values[col_permutation_output(i)]; let diff = builder.sub_extension(state[i], out); - yield_constr.one(builder, diff); + yield_constr.constraint(builder, diff); } } } From be44edcd78b8c2b43c0c1140d59b4c7da954f9c6 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 2 Feb 2022 13:20:49 +0100 Subject: [PATCH 12/14] Minor --- starky/src/verifier.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 5753f21f..843bdb63 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -126,13 +126,10 @@ where /// `L_1(x) = (x^n - 1)/(n * (x - 1))` /// `L_n(x) = (x^n - 1)/(n * (g * x - 1))`, with `g` the first element of the subgroup. fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { - let n = 1 << log_n; + let n = F::from_canonical_usize(1 << log_n); let g = F::primitive_root_of_unity(log_n); let z_x = x.exp_power_of_2(log_n) - F::ONE; - let invs = F::batch_multiplicative_inverse(&[ - F::from_canonical_usize(n) * (x - F::ONE), - F::from_canonical_usize(n) * (g * x - F::ONE), - ]); + let invs = F::batch_multiplicative_inverse(&[n * (x - F::ONE), n * (g * x - F::ONE)]); (z_x * invs[0], z_x * invs[1]) } From bc5bc8245d597e20e4c25955c181c70210160e3c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Feb 2022 11:49:44 +0100 Subject: [PATCH 13/14] PR feedback --- field/src/polynomial/mod.rs | 3 ++- plonky2/src/plonk/proof.rs | 8 ++++---- starky/src/constraint_consumer.rs | 8 ++++---- starky/src/proof.rs | 22 ++++++++++++++++++---- starky/src/prover.rs | 7 +++---- starky/src/stark.rs | 1 + starky/src/verifier.rs | 16 ++++++++-------- 7 files changed, 40 insertions(+), 25 deletions(-) diff --git a/field/src/polynomial/mod.rs b/field/src/polynomial/mod.rs index d36757b3..7fccb46e 100644 --- a/field/src/polynomial/mod.rs +++ b/field/src/polynomial/mod.rs @@ -57,7 +57,8 @@ impl PolynomialValues { fft_with_options(coeffs, Some(rate_bits), None) } - pub fn coset_lde(self, rate_bits: usize) -> Self { + /// Low-degree extend `Self` (seen as evaluations over the subgroup) onto a coset. + pub fn lde_onto_coset(self, rate_bits: usize) -> Self { let coeffs = ifft(self).lde(rate_bits); coeffs.coset_fft_with_options(F::coset_shift(), Some(rate_bits), None) } diff --git a/plonky2/src/plonk/proof.rs b/plonky2/src/plonk/proof.rs index 9d9eaaff..803e64d4 100644 --- a/plonky2/src/plonk/proof.rs +++ b/plonky2/src/plonk/proof.rs @@ -227,16 +227,16 @@ impl, C: GenericConfig, const D: usize> } pub(crate) struct ProofChallenges, const D: usize> { - // Random values used in Plonk's permutation argument. + /// Random values used in Plonk's permutation argument. pub plonk_betas: Vec, - // Random values used in Plonk's permutation argument. + /// Random values used in Plonk's permutation argument. pub plonk_gammas: Vec, - // Random values used to combine PLONK constraints. + /// Random values used to combine PLONK constraints. pub plonk_alphas: Vec, - // Point at which the PLONK polynomials are opened. + /// Point at which the PLONK polynomials are opened. pub plonk_zeta: F::Extension, pub fri_challenges: FriChallenges, diff --git a/starky/src/constraint_consumer.rs b/starky/src/constraint_consumer.rs index 922475c3..091215dd 100644 --- a/starky/src/constraint_consumer.rs +++ b/starky/src/constraint_consumer.rs @@ -104,8 +104,8 @@ impl, const D: usize> RecursiveConstraintConsumer, constraint: ExtensionTarget, ) { - self.constraint_acc = - builder.scalar_mul_add_extension(self.alpha, self.constraint_acc, constraint); + let filtered_constraint = builder.mul_extension(constraint, self.z_last); + self.constraint(builder, filtered_constraint); } /// Add one constraint valid on all rows. @@ -114,8 +114,8 @@ impl, const D: usize> RecursiveConstraintConsumer, constraint: ExtensionTarget, ) { - let filtered_constraint = builder.mul_extension(constraint, self.z_last); - self.constraint(builder, filtered_constraint); + self.constraint_acc = + builder.scalar_mul_add_extension(self.alpha, self.constraint_acc, constraint); } /// Add one constraint, but first multiply it by a filter such that it will only apply to the diff --git a/starky/src/proof.rs b/starky/src/proof.rs index 50ef21bc..5f96f1f4 100644 --- a/starky/src/proof.rs +++ b/starky/src/proof.rs @@ -52,10 +52,10 @@ pub struct CompressedStarkProofWithPublicInputs< } pub(crate) struct StarkProofChallenges, const D: usize> { - // Random values used to combine PLONK constraints. + /// Random values used to combine STARK constraints. pub stark_alphas: Vec, - // Point at which the PLONK polynomials are opened. + /// Point at which the STARK polynomials are opened. pub stark_zeta: F::Extension, pub fri_challenges: FriChallenges, @@ -66,6 +66,7 @@ pub struct StarkOpeningSet, const D: usize> { pub local_values: Vec, pub next_values: Vec, pub permutation_zs: Vec, + pub permutation_zs_right: Vec, pub quotient_polys: Vec, } @@ -86,19 +87,28 @@ impl, const D: usize> StarkOpeningSet { local_values: eval_commitment(zeta, trace_commitment), next_values: eval_commitment(zeta * g, trace_commitment), permutation_zs: vec![/*TODO*/], + permutation_zs_right: vec![/*TODO*/], quotient_polys: eval_commitment(zeta, quotient_commitment), } } + // TODO: Replace with a `observe_fri_openings` function. // Note: Can't implement this directly on `Challenger` as it's in a different crate. pub fn observe>(&self, challenger: &mut Challenger) { let StarkOpeningSet { local_values, next_values, permutation_zs, + permutation_zs_right, quotient_polys, } = self; - for v in &[local_values, next_values, permutation_zs, quotient_polys] { + for v in &[ + local_values, + next_values, + permutation_zs, + permutation_zs_right, + quotient_polys, + ] { challenger.observe_extension_elements(v); } } @@ -113,7 +123,11 @@ impl, const D: usize> StarkOpeningSet { .concat(), }; let zeta_right_batch = FriOpeningBatch { - values: self.next_values.to_vec(), + values: [ + self.next_values.as_slice(), + self.permutation_zs_right.as_slice(), + ] + .concat(), }; FriOpenings { batches: vec![zeta_batch, zeta_right_batch], diff --git a/starky/src/prover.rs b/starky/src/prover.rs index 4d7f8c3f..d2c63e02 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -168,13 +168,13 @@ where let lagrange_first = { let mut evals = PolynomialValues::new(vec![F::ZERO; degree]); evals.values[0] = F::ONE; - evals.coset_lde(rate_bits) + evals.lde_onto_coset(rate_bits) }; // Evaluation of the last Lagrange polynomial on the LDE domain. let lagrange_last = { let mut evals = PolynomialValues::new(vec![F::ZERO; degree]); evals.values[degree - 1] = F::ONE; - evals.coset_lde(rate_bits) + evals.lde_onto_coset(rate_bits) }; let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, rate_bits); @@ -212,8 +212,7 @@ where stark.eval_packed_base(vars, &mut consumer); // TODO: Fix this once we use a genuine `PackedField`. let mut constraints_evals = consumer.accumulators(); - // We divide the constraints evaluations by `Z_H(x) / x - last`, i.e., the vanishing - // polynomial of `H` without it's last element. + // We divide the constraints evaluations by `Z_H(x)`. let denominator_inv = z_h_on_coset.eval_inverse(i); for eval in &mut constraints_evals { *eval *= denominator_inv; diff --git a/starky/src/stark.rs b/starky/src/stark.rs index 3b8c795a..00441240 100644 --- a/starky/src/stark.rs +++ b/starky/src/stark.rs @@ -9,6 +9,7 @@ use crate::vars::StarkEvaluationTargets; use crate::vars::StarkEvaluationVars; /// Represents a STARK system. +// TODO: Add a `constraint_degree` fn that returns the maximum constraint degree. pub trait Stark, const D: usize>: Sync { /// The total number of columns in the trace. const COLUMNS: usize; diff --git a/starky/src/verifier.rs b/starky/src/verifier.rs index 843bdb63..b91fe457 100644 --- a/starky/src/verifier.rs +++ b/starky/src/verifier.rs @@ -58,6 +58,7 @@ where local_values, next_values, permutation_zs, + permutation_zs_right, quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { @@ -85,7 +86,7 @@ where l_last, ); stark.eval_ext(vars, &mut consumer); - let acc = consumer.accumulators(); + let vanishing_polys_zeta = consumer.accumulators(); // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. let quotient_polys_zeta = &proof.openings.quotient_polys; @@ -100,9 +101,10 @@ where .chunks(1 << config.fri_config.rate_bits) .enumerate() { - ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); + ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg)); } + // TODO: Permutation polynomials. let merkle_caps = &[proof.trace_cap, proof.quotient_polys_cap]; verify_fri_proof::( @@ -139,12 +141,10 @@ fn recover_degree, C: GenericConfig, cons proof: &StarkProof, config: &StarkConfig, ) -> usize { - 1 << (proof.opening_proof.query_round_proofs[0] + let initial_merkle_proof = &proof.opening_proof.query_round_proofs[0] .initial_trees_proof .evals_proofs[0] - .1 - .siblings - .len() - + config.fri_config.cap_height - - config.fri_config.rate_bits) + .1; + let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len(); + 1 << (lde_bits - config.fri_config.rate_bits) } From 28082e97528a28b784070d92ddd93ca8e3dc1b92 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 3 Feb 2022 11:57:24 +0100 Subject: [PATCH 14/14] Clippy --- starky/src/constraint_consumer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starky/src/constraint_consumer.rs b/starky/src/constraint_consumer.rs index 091215dd..b7c9f399 100644 --- a/starky/src/constraint_consumer.rs +++ b/starky/src/constraint_consumer.rs @@ -105,7 +105,7 @@ impl, const D: usize> RecursiveConstraintConsumer, ) { let filtered_constraint = builder.mul_extension(constraint, self.z_last); - self.constraint(builder, filtered_constraint); + self.constraint_wrapping(builder, filtered_constraint); } /// Add one constraint valid on all rows.