From 9096b758f4d6cedbe89e7d1fd72209db1f1f812c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 4 May 2022 20:57:07 +0200 Subject: [PATCH 01/33] Start of multi-table STARKs --- Cargo.toml | 2 +- starky2/Cargo.toml | 14 + starky2/src/config.rs | 34 +++ starky2/src/constraint_consumer.rs | 166 ++++++++++++ starky2/src/get_challenges.rs | 335 +++++++++++++++++++++++ starky2/src/lib.rs | 19 ++ starky2/src/mock_stark.rs | 360 +++++++++++++++++++++++++ starky2/src/permutation.rs | 397 +++++++++++++++++++++++++++ starky2/src/proof.rs | 213 +++++++++++++++ starky2/src/prover.rs | 414 +++++++++++++++++++++++++++++ starky2/src/recursive_verifier.rs | 333 +++++++++++++++++++++++ starky2/src/stark.rs | 204 ++++++++++++++ starky2/src/stark_testing.rs | 87 ++++++ starky2/src/util.rs | 16 ++ starky2/src/vanishing_poly.rs | 68 +++++ starky2/src/vars.rs | 26 ++ starky2/src/verifier.rs | 208 +++++++++++++++ 17 files changed, 2895 insertions(+), 1 deletion(-) create mode 100644 starky2/Cargo.toml create mode 100644 starky2/src/config.rs create mode 100644 starky2/src/constraint_consumer.rs create mode 100644 starky2/src/get_challenges.rs create mode 100644 starky2/src/lib.rs create mode 100644 starky2/src/mock_stark.rs create mode 100644 starky2/src/permutation.rs create mode 100644 starky2/src/proof.rs create mode 100644 starky2/src/prover.rs create mode 100644 starky2/src/recursive_verifier.rs create mode 100644 starky2/src/stark.rs create mode 100644 starky2/src/stark_testing.rs create mode 100644 starky2/src/util.rs create mode 100644 starky2/src/vanishing_poly.rs create mode 100644 starky2/src/vars.rs create mode 100644 starky2/src/verifier.rs diff --git a/Cargo.toml b/Cargo.toml index 5f95ea39..f8a59b92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["field", "insertion", "plonky2", "starky", "system_zero", "util", "waksman", "ecdsa", "u32"] +members = ["field", "insertion", "plonky2", "starky", "system_zero", "util", "waksman", "ecdsa", "u32", "starky2"] [profile.release] opt-level = 3 diff --git a/starky2/Cargo.toml b/starky2/Cargo.toml new file mode 100644 index 00000000..feb4cbc3 --- /dev/null +++ b/starky2/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "starky2" +description = "Implementation of STARKs 2" +version = "0.1.0" +edition = "2021" + +[dependencies] +plonky2 = { path = "../plonky2" } +plonky2_util = { path = "../util" } +anyhow = "1.0.40" +env_logger = "0.9.0" +itertools = "0.10.0" +log = "0.4.14" +rayon = "1.5.1" diff --git a/starky2/src/config.rs b/starky2/src/config.rs new file mode 100644 index 00000000..500cd957 --- /dev/null +++ b/starky2/src/config.rs @@ -0,0 +1,34 @@ +use plonky2::fri::reduction_strategies::FriReductionStrategy; +use plonky2::fri::{FriConfig, FriParams}; + +pub struct StarkConfig { + pub security_bits: usize, + + /// The number of challenge points to generate, for IOPs that have soundness errors of (roughly) + /// `degree / |F|`. + pub num_challenges: usize, + + pub fri_config: FriConfig, +} + +impl StarkConfig { + /// A typical configuration with a rate of 2, resulting in fast but large proofs. + /// Targets ~100 bit conjectured security. + pub fn standard_fast_config() -> Self { + Self { + security_bits: 100, + num_challenges: 2, + fri_config: FriConfig { + rate_bits: 1, + cap_height: 4, + proof_of_work_bits: 10, + reduction_strategy: FriReductionStrategy::ConstantArityBits(4, 5), + num_query_rounds: 90, + }, + } + } + + pub(crate) fn fri_params(&self, degree_bits: usize) -> FriParams { + self.fri_config.fri_params(degree_bits, false) + } +} diff --git a/starky2/src/constraint_consumer.rs b/starky2/src/constraint_consumer.rs new file mode 100644 index 00000000..ada28730 --- /dev/null +++ b/starky2/src/constraint_consumer.rs @@ -0,0 +1,166 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::Extendable; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::Target; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +pub struct ConstraintConsumer { + /// Random values used to combine multiple constraints into one. + alphas: Vec, + + /// Running sums of constraints that have been emitted so far, scaled by powers of alpha. + // TODO(JN): This is pub so it can be used in a test. Once we have an API for accessing this + // result, it should be made private. + pub 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, + + /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated + /// with the last trace row, and zero at other points in the subgroup. + lagrange_basis_last: P, +} + +impl ConstraintConsumer

{ + 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, + } + } + + // TODO: Do this correctly. + pub fn accumulators(self) -> Vec { + self.constraint_accs + .into_iter() + .map(|acc| acc.as_slice()[0]) + .collect() + } + + /// Add one constraint valid on all rows except the last. + pub fn constraint_transition(&mut self, constraint: P) { + self.constraint(constraint * self.z_last); + } + + /// Add one constraint on all rows. + pub fn constraint(&mut self, constraint: P) { + for (&alpha, acc) in self.alphas.iter().zip(&mut self.constraint_accs) { + *acc *= alpha; + *acc += 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 constraint_first_row(&mut self, constraint: P) { + self.constraint(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 constraint_last_row(&mut self, constraint: P) { + self.constraint(constraint * self.lagrange_basis_last); + } +} + +pub struct RecursiveConstraintConsumer, const D: usize> { + /// A random value used to combine multiple constraints into one. + alphas: Vec, + + /// A running sum 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: 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, + + /// The evaluation of the Lagrange basis polynomial which is nonzero at the point associated + /// with the last trace row, and zero at other points in the subgroup. + lagrange_basis_last: ExtensionTarget, + + _phantom: PhantomData, +} + +impl, const D: usize> RecursiveConstraintConsumer { + pub fn new( + zero: ExtensionTarget, + alphas: Vec, + z_last: ExtensionTarget, + lagrange_basis_first: ExtensionTarget, + lagrange_basis_last: ExtensionTarget, + ) -> Self { + Self { + constraint_accs: vec![zero; alphas.len()], + alphas, + z_last, + lagrange_basis_first, + lagrange_basis_last, + _phantom: Default::default(), + } + } + + pub fn accumulators(self) -> Vec> { + self.constraint_accs + } + + /// Add one constraint valid on all rows except the last. + pub fn constraint_transition( + &mut self, + builder: &mut CircuitBuilder, + constraint: ExtensionTarget, + ) { + let filtered_constraint = builder.mul_extension(constraint, self.z_last); + self.constraint(builder, filtered_constraint); + } + + /// Add one constraint valid on all rows. + pub fn constraint( + &mut self, + builder: &mut CircuitBuilder, + constraint: ExtensionTarget, + ) { + for (&alpha, acc) in self.alphas.iter().zip(&mut self.constraint_accs) { + *acc = builder.scalar_mul_add_extension(alpha, *acc, 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 constraint_first_row( + &mut self, + builder: &mut CircuitBuilder, + constraint: ExtensionTarget, + ) { + let filtered_constraint = builder.mul_extension(constraint, self.lagrange_basis_first); + 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 constraint_last_row( + &mut self, + builder: &mut CircuitBuilder, + constraint: ExtensionTarget, + ) { + let filtered_constraint = builder.mul_extension(constraint, self.lagrange_basis_last); + self.constraint(builder, filtered_constraint); + } +} diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs new file mode 100644 index 00000000..ceafa297 --- /dev/null +++ b/starky2/src/get_challenges.rs @@ -0,0 +1,335 @@ +use plonky2::field::extension_field::Extendable; +use plonky2::field::polynomial::PolynomialCoeffs; +use plonky2::fri::proof::{FriProof, FriProofTarget}; +use plonky2::gadgets::polynomial::PolynomialCoeffsExtTarget; +use plonky2::hash::hash_types::{MerkleCapTarget, RichField}; +use plonky2::hash::merkle_tree::MerkleCap; +use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; +use plonky2::iop::target::Target; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; + +use crate::config::StarkConfig; +use crate::permutation::{ + get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, +}; +use crate::proof::*; +use crate::stark::Stark; + +fn get_challenges( + stark: &S, + trace_cap: &MerkleCap, + permutation_zs_cap: Option<&MerkleCap>, + quotient_polys_cap: &MerkleCap, + openings: &StarkOpeningSet, + commit_phase_merkle_caps: &[MerkleCap], + final_poly: &PolynomialCoeffs, + pow_witness: F, + config: &StarkConfig, + degree_bits: usize, +) -> StarkProofChallenges +where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, +{ + let num_challenges = config.num_challenges; + + let mut challenger = Challenger::::new(); + + challenger.observe_cap(trace_cap); + + let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { + let tmp = get_n_permutation_challenge_sets( + &mut challenger, + num_challenges, + stark.permutation_batch_size(), + ); + challenger.observe_cap(permutation_zs_cap); + tmp + }); + + let stark_alphas = challenger.get_n_challenges(num_challenges); + + challenger.observe_cap(quotient_polys_cap); + let stark_zeta = challenger.get_extension_challenge::(); + + challenger.observe_openings(&openings.to_fri_openings()); + + StarkProofChallenges { + permutation_challenge_sets, + stark_alphas, + stark_zeta, + fri_challenges: challenger.fri_challenges::( + commit_phase_merkle_caps, + final_poly, + pow_witness, + degree_bits, + &config.fri_config, + ), + } +} + +impl StarkProofWithPublicInputs +where + F: RichField + Extendable, + C: GenericConfig, +{ + // TODO: Should be used later in compression? + #![allow(dead_code)] + pub(crate) fn fri_query_indices>( + &self, + stark: &S, + config: &StarkConfig, + degree_bits: usize, + ) -> Vec { + self.get_challenges(stark, config, degree_bits) + .fri_challenges + .fri_query_indices + } + + /// Computes all Fiat-Shamir challenges used in the STARK proof. + pub(crate) fn get_challenges>( + &self, + stark: &S, + config: &StarkConfig, + degree_bits: usize, + ) -> StarkProofChallenges { + let StarkProof { + trace_cap, + permutation_zs_cap, + quotient_polys_cap, + openings, + opening_proof: + FriProof { + commit_phase_merkle_caps, + final_poly, + pow_witness, + .. + }, + } = &self.proof; + + get_challenges::( + stark, + trace_cap, + permutation_zs_cap.as_ref(), + quotient_polys_cap, + openings, + commit_phase_merkle_caps, + final_poly, + *pow_witness, + config, + degree_bits, + ) + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn get_challenges_target< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + builder: &mut CircuitBuilder, + stark: &S, + trace_cap: &MerkleCapTarget, + permutation_zs_cap: Option<&MerkleCapTarget>, + quotient_polys_cap: &MerkleCapTarget, + openings: &StarkOpeningSetTarget, + commit_phase_merkle_caps: &[MerkleCapTarget], + final_poly: &PolynomialCoeffsExtTarget, + pow_witness: Target, + config: &StarkConfig, +) -> StarkProofChallengesTarget +where + C::Hasher: AlgebraicHasher, +{ + let num_challenges = config.num_challenges; + + let mut challenger = RecursiveChallenger::::new(builder); + + challenger.observe_cap(trace_cap); + + let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { + let tmp = get_n_permutation_challenge_sets_target( + builder, + &mut challenger, + num_challenges, + stark.permutation_batch_size(), + ); + challenger.observe_cap(permutation_zs_cap); + tmp + }); + + let stark_alphas = challenger.get_n_challenges(builder, num_challenges); + + challenger.observe_cap(quotient_polys_cap); + let stark_zeta = challenger.get_extension_challenge(builder); + + challenger.observe_openings(&openings.to_fri_openings()); + + StarkProofChallengesTarget { + permutation_challenge_sets, + stark_alphas, + stark_zeta, + fri_challenges: challenger.fri_challenges::( + builder, + commit_phase_merkle_caps, + final_poly, + pow_witness, + &config.fri_config, + ), + } +} + +impl StarkProofWithPublicInputsTarget { + pub(crate) fn get_challenges< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + >( + &self, + builder: &mut CircuitBuilder, + stark: &S, + config: &StarkConfig, + ) -> StarkProofChallengesTarget + where + C::Hasher: AlgebraicHasher, + { + let StarkProofTarget { + trace_cap, + permutation_zs_cap, + quotient_polys_cap, + openings, + opening_proof: + FriProofTarget { + commit_phase_merkle_caps, + final_poly, + pow_witness, + .. + }, + } = &self.proof; + + get_challenges_target::( + builder, + stark, + trace_cap, + permutation_zs_cap.as_ref(), + quotient_polys_cap, + openings, + commit_phase_merkle_caps, + final_poly, + *pow_witness, + config, + ) + } +} + +// TODO: Deal with the compressed stuff. +// 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/starky2/src/lib.rs b/starky2/src/lib.rs new file mode 100644 index 00000000..0924600d --- /dev/null +++ b/starky2/src/lib.rs @@ -0,0 +1,19 @@ +#![allow(incomplete_features)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::type_complexity)] +#![feature(generic_const_exprs)] + +pub mod config; +pub mod constraint_consumer; +mod get_challenges; +pub mod mock_stark; +pub mod permutation; +pub mod proof; +pub mod prover; +pub mod recursive_verifier; +pub mod stark; +pub mod stark_testing; +pub mod util; +pub mod vanishing_poly; +pub mod vars; +pub mod verifier; diff --git a/starky2/src/mock_stark.rs b/starky2/src/mock_stark.rs new file mode 100644 index 00000000..337c0142 --- /dev/null +++ b/starky2/src/mock_stark.rs @@ -0,0 +1,360 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::PermutationPair; +use crate::stark::Stark; +use crate::util::trace_rows_to_poly_values; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +struct AllStarks, const D: usize> { + fibonacci: FibonacciStark, + multiplications: MultiplicationStark, +} + +/// Toy STARK system used for testing. +/// Computes a Fibonacci sequence with state `[x0, x1, i, j]` using the state transition +/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. +/// Note: The `i, j` columns are only used to test the permutation argument. +#[derive(Copy, Clone)] +struct FibonacciStark, const D: usize> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize> FibonacciStark { + // The first public input is `x0`. + const PI_INDEX_X0: usize = 0; + // The second public input is `x1`. + const PI_INDEX_X1: usize = 1; + // The third public input is the second element of the last row, which should be equal to the + // `num_rows`-th Fibonacci number. + const PI_INDEX_RES: usize = 2; + + fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + /// Generate the trace using `x0, x1, 0, 1` as initial state values. + fn generate_trace(&self, x0: F, x1: F) -> Vec> { + let mut trace_rows = (0..self.num_rows) + .scan([x0, x1, F::ZERO, F::ONE], |acc, _| { + let tmp = *acc; + acc[0] = tmp[1]; + acc[1] = tmp[0] + tmp[1]; + acc[2] = tmp[2] + F::ONE; + acc[3] = tmp[3] + F::ONE; + Some(tmp) + }) + .collect::>(); + trace_rows[self.num_rows - 1][3] = F::ZERO; // So that column 2 and 3 are permutation of one another. + trace_rows_to_poly_values(trace_rows) + } +} + +impl, const D: usize> Stark for FibonacciStark { + const COLUMNS: usize = 4; + const PUBLIC_INPUTS: usize = 3; + + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + // Check public inputs. + 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.constraint_transition(vars.next_values[0] - vars.local_values[1]); + // x1' <- x0 + x1 + yield_constr.constraint_transition( + vars.next_values[1] - vars.local_values[0] - vars.local_values[1], + ); + } + + fn eval_ext_recursively( + &self, + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + // Check public inputs. + let pis_constraints = [ + builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]), + builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_X1]), + builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_RES]), + ]; + yield_constr.constraint_first_row(builder, pis_constraints[0]); + yield_constr.constraint_first_row(builder, pis_constraints[1]); + yield_constr.constraint_last_row(builder, pis_constraints[2]); + + // x0' <- x1 + let first_col_constraint = builder.sub_extension(vars.next_values[0], vars.local_values[1]); + yield_constr.constraint_transition(builder, first_col_constraint); + // x1' <- x0 + x1 + let second_col_constraint = { + let tmp = builder.sub_extension(vars.next_values[1], vars.local_values[0]); + builder.sub_extension(tmp, vars.local_values[1]) + }; + yield_constr.constraint_transition(builder, second_col_constraint); + } + + fn constraint_degree(&self) -> usize { + 2 + } + + fn permutation_pairs(&self) -> Vec { + vec![PermutationPair::singletons(2, 3)] + } +} + +#[derive(Copy, Clone)] +struct MultiplicationStark< + F: RichField + Extendable, + const D: usize, + const NUM_MULTIPLICANDS: usize, +> { + num_rows: usize, + _phantom: PhantomData, +} + +impl, const D: usize, const W: usize> MultiplicationStark { + fn multiplicand(&self, i: usize) -> usize { + debug_assert!(i < W); + i + } + + // Product of the first `i` multiplicands + fn intermediate_product(&self, i: usize) -> usize { + debug_assert!(i < W && i > 0); + W + i - 1 + } + + fn product(&self) -> usize { + 2 * W - 2 + } + + const fn num_columns() -> usize { + 2 * W - 1 + } + + fn new(num_rows: usize) -> Self { + Self { + num_rows, + _phantom: PhantomData, + } + } + + fn generate_trace(&self, multiplicands: &[Vec]) -> Vec> + where + [(); Self::num_columns()]:, + { + debug_assert_eq!(multiplicands.len(), self.num_rows); + let mut trace_rows = multiplicands + .iter() + .map(|row| { + debug_assert_eq!(row.len(), W); + let mut result = [F::ZERO; Self::num_columns()]; + for i in 0..W { + result[self.multiplicand(i)] = row[i]; + } + let mut acc = row[0] * row[1]; + for i in 1..W - 1 { + result[self.intermediate_product(i)] = acc; + acc *= row[i + 1]; + } + result[self.product()] = acc; + result + }) + .collect::>(); + trace_rows_to_poly_values(trace_rows) + } +} + +impl, const D: usize, const W: usize> Stark + for MultiplicationStark +{ + const COLUMNS: usize = 2 * W - 1; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + yield_constr.constraint( + vars.local_values[self.intermediate_product(1)] + - vars.local_values[self.multiplicand(0)] * vars.local_values[self.multiplicand(1)], + ); + for i in 2..W - 1 { + yield_constr.constraint( + vars.local_values[self.intermediate_product(i)] + - vars.local_values[self.intermediate_product(i - 1)] + * vars.local_values[self.multiplicand(i)], + ) + } + } + + fn eval_ext_recursively( + &self, + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + todo!() + } + + fn constraint_degree(&self) -> usize { + 2 + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::extension_field::Extendable; + use plonky2::field::field_types::Field; + use plonky2::hash::hash_types::RichField; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{ + AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, + }; + use plonky2::util::timing::TimingTree; + + use crate::config::StarkConfig; + use crate::mock_stark::FibonacciStark; + use crate::proof::StarkProofWithPublicInputs; + use crate::prover::prove; + use crate::recursive_verifier::{ + add_virtual_stark_proof_with_pis, recursively_verify_stark_proof, + set_stark_proof_with_pis_target, + }; + use crate::stark::Stark; + use crate::stark_testing::test_stark_low_degree; + use crate::verifier::verify_stark_proof; + + fn fibonacci(n: usize, x0: F, x1: F) -> F { + (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 + } + + #[test] + fn test_fibonacci_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + let proof = prove::( + stark, + &config, + trace, + public_inputs, + &mut TimingTree::default(), + )?; + + verify_stark_proof(stark, proof, &config) + } + + #[test] + fn test_fibonacci_stark_degree() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + + let num_rows = 1 << 5; + let stark = S::new(num_rows); + test_stark_low_degree(stark) + } + + #[test] + fn test_recursive_stark_verifier() -> Result<()> { + init_logger(); + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + type S = FibonacciStark; + + let config = StarkConfig::standard_fast_config(); + let num_rows = 1 << 5; + let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; + let stark = S::new(num_rows); + let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); + let proof = prove::( + stark, + &config, + trace, + public_inputs, + &mut TimingTree::default(), + )?; + verify_stark_proof(stark, proof.clone(), &config)?; + + recursive_proof::(stark, proof, &config, true) + } + + fn recursive_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark + Copy, + InnerC: GenericConfig, + const D: usize, + >( + stark: S, + inner_proof: StarkProofWithPublicInputs, + inner_config: &StarkConfig, + print_gate_counts: bool, + ) -> Result<()> + where + InnerC::Hasher: AlgebraicHasher, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); C::Hasher::HASH_SIZE]:, + { + let circuit_config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(circuit_config); + let mut pw = PartialWitness::new(); + let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); + let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); + set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); + + recursively_verify_stark_proof::(&mut builder, stark, pt, inner_config); + + if print_gate_counts { + builder.print_gate_counts(0); + } + + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof) + } + + fn init_logger() { + let _ = env_logger::builder().format_timestamp(None).try_init(); + } +} diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs new file mode 100644 index 00000000..443ff787 --- /dev/null +++ b/starky2/src/permutation.rs @@ -0,0 +1,397 @@ +//! Permutation arguments. + +use itertools::Itertools; +use plonky2::field::batch_util::batch_multiply_inplace; +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::field_types::Field; +use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::Target; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; +use rayon::prelude::*; + +use crate::config::StarkConfig; +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::stark::Stark; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +/// A pair of lists of columns, `lhs` and `rhs`, that should be permutations of one another. +/// In particular, there should exist some permutation `pi` such that for any `i`, +/// `trace[lhs[i]] = pi(trace[rhs[i]])`. Here `trace` denotes the trace in column-major form, so +/// `trace[col]` is a column vector. +pub struct PermutationPair { + /// Each entry contains two column indices, representing two columns which should be + /// permutations of one another. + pub column_pairs: Vec<(usize, usize)>, +} + +impl PermutationPair { + pub fn singletons(lhs: usize, rhs: usize) -> Self { + Self { + column_pairs: vec![(lhs, rhs)], + } + } +} + +/// A single instance of a permutation check protocol. +pub(crate) struct PermutationInstance<'a, T: Copy> { + pub(crate) pair: &'a PermutationPair, + pub(crate) challenge: PermutationChallenge, +} + +/// Randomness for a single instance of a permutation check protocol. +#[derive(Copy, Clone)] +pub(crate) struct PermutationChallenge { + /// Randomness used to combine multiple columns into one. + pub(crate) beta: T, + /// Random offset that's added to the beta-reduced column values. + pub(crate) gamma: T, +} + +/// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. +#[derive(Clone)] +pub(crate) struct PermutationChallengeSet { + pub(crate) challenges: Vec>, +} + +/// Compute all Z polynomials (for permutation arguments). +pub(crate) fn compute_permutation_z_polys( + stark: &S, + config: &StarkConfig, + trace_poly_values: &[PolynomialValues], + permutation_challenge_sets: &[PermutationChallengeSet], +) -> Vec> +where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, +{ + let permutation_pairs = stark.permutation_pairs(); + let permutation_batches = get_permutation_batches( + &permutation_pairs, + permutation_challenge_sets, + config.num_challenges, + stark.permutation_batch_size(), + ); + + permutation_batches + .into_par_iter() + .map(|instances| compute_permutation_z_poly(&instances, trace_poly_values)) + .collect() +} + +/// Compute a single Z polynomial. +fn compute_permutation_z_poly( + instances: &[PermutationInstance], + trace_poly_values: &[PolynomialValues], +) -> PolynomialValues { + let degree = trace_poly_values[0].len(); + let (reduced_lhs_polys, reduced_rhs_polys): (Vec<_>, Vec<_>) = instances + .iter() + .map(|instance| permutation_reduced_polys(instance, trace_poly_values, degree)) + .unzip(); + + let numerator = poly_product_elementwise(reduced_lhs_polys.into_iter()); + let denominator = poly_product_elementwise(reduced_rhs_polys.into_iter()); + + // Compute the quotients. + let denominator_inverses = F::batch_multiplicative_inverse(&denominator.values); + let mut quotients = numerator.values; + batch_multiply_inplace(&mut quotients, &denominator_inverses); + + // Compute Z, which contains partial products of the quotients. + let mut partial_products = Vec::with_capacity(degree); + let mut acc = F::ONE; + for q in quotients { + partial_products.push(acc); + acc *= q; + } + PolynomialValues::new(partial_products) +} + +/// Computes the reduced polynomial, `\sum beta^i f_i(x) + gamma`, for both the "left" and "right" +/// sides of a given `PermutationPair`. +fn permutation_reduced_polys( + instance: &PermutationInstance, + trace_poly_values: &[PolynomialValues], + degree: usize, +) -> (PolynomialValues, PolynomialValues) { + let PermutationInstance { + pair: PermutationPair { column_pairs }, + challenge: PermutationChallenge { beta, gamma }, + } = instance; + + let mut reduced_lhs = PolynomialValues::constant(*gamma, degree); + let mut reduced_rhs = PolynomialValues::constant(*gamma, degree); + for ((lhs, rhs), weight) in column_pairs.iter().zip(beta.powers()) { + reduced_lhs.add_assign_scaled(&trace_poly_values[*lhs], weight); + reduced_rhs.add_assign_scaled(&trace_poly_values[*rhs], weight); + } + (reduced_lhs, reduced_rhs) +} + +/// Computes the elementwise product of a set of polynomials. Assumes that the set is non-empty and +/// that each polynomial has the same length. +fn poly_product_elementwise( + mut polys: impl Iterator>, +) -> PolynomialValues { + let mut product = polys.next().expect("Expected at least one polynomial"); + for poly in polys { + batch_multiply_inplace(&mut product.values, &poly.values) + } + product +} + +fn get_permutation_challenge>( + challenger: &mut Challenger, +) -> PermutationChallenge { + let beta = challenger.get_challenge(); + let gamma = challenger.get_challenge(); + PermutationChallenge { beta, gamma } +} + +fn get_permutation_challenge_set>( + challenger: &mut Challenger, + num_challenges: usize, +) -> PermutationChallengeSet { + let challenges = (0..num_challenges) + .map(|_| get_permutation_challenge(challenger)) + .collect(); + PermutationChallengeSet { challenges } +} + +pub(crate) fn get_n_permutation_challenge_sets>( + challenger: &mut Challenger, + num_challenges: usize, + num_sets: usize, +) -> Vec> { + (0..num_sets) + .map(|_| get_permutation_challenge_set(challenger, num_challenges)) + .collect() +} + +fn get_permutation_challenge_target< + F: RichField + Extendable, + H: AlgebraicHasher, + const D: usize, +>( + builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, +) -> PermutationChallenge { + let beta = challenger.get_challenge(builder); + let gamma = challenger.get_challenge(builder); + PermutationChallenge { beta, gamma } +} + +fn get_permutation_challenge_set_target< + F: RichField + Extendable, + H: AlgebraicHasher, + const D: usize, +>( + builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, + num_challenges: usize, +) -> PermutationChallengeSet { + let challenges = (0..num_challenges) + .map(|_| get_permutation_challenge_target(builder, challenger)) + .collect(); + PermutationChallengeSet { challenges } +} + +pub(crate) fn get_n_permutation_challenge_sets_target< + F: RichField + Extendable, + H: AlgebraicHasher, + const D: usize, +>( + builder: &mut CircuitBuilder, + challenger: &mut RecursiveChallenger, + num_challenges: usize, + num_sets: usize, +) -> Vec> { + (0..num_sets) + .map(|_| get_permutation_challenge_set_target(builder, challenger, num_challenges)) + .collect() +} + +/// Get a list of instances of our batch-permutation argument. These are permutation arguments +/// where the same `Z(x)` polynomial is used to check more than one permutation. +/// Before batching, each permutation pair leads to `num_challenges` permutation arguments, so we +/// start with the cartesian product of `permutation_pairs` and `0..num_challenges`. Then we +/// chunk these arguments based on our batch size. +pub(crate) fn get_permutation_batches<'a, T: Copy>( + permutation_pairs: &'a [PermutationPair], + permutation_challenge_sets: &[PermutationChallengeSet], + num_challenges: usize, + batch_size: usize, +) -> Vec>> { + permutation_pairs + .iter() + .cartesian_product(0..num_challenges) + .chunks(batch_size) + .into_iter() + .map(|batch| { + batch + .enumerate() + .map(|(i, (pair, chal))| { + let challenge = permutation_challenge_sets[i].challenges[chal]; + PermutationInstance { pair, challenge } + }) + .collect_vec() + }) + .collect() +} + +pub struct PermutationCheckVars +where + F: Field, + FE: FieldExtension, + P: PackedField, +{ + pub(crate) local_zs: Vec

, + pub(crate) next_zs: Vec

, + pub(crate) permutation_challenge_sets: Vec>, +} + +pub(crate) fn eval_permutation_checks( + stark: &S, + config: &StarkConfig, + vars: StarkEvaluationVars, + permutation_data: PermutationCheckVars, + consumer: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, + C: GenericConfig, + S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + let PermutationCheckVars { + local_zs, + next_zs, + permutation_challenge_sets, + } = permutation_data; + + // Check that Z(1) = 1; + for &z in &local_zs { + consumer.constraint_first_row(z - FE::ONE); + } + + let permutation_pairs = stark.permutation_pairs(); + + let permutation_batches = get_permutation_batches( + &permutation_pairs, + &permutation_challenge_sets, + config.num_challenges, + stark.permutation_batch_size(), + ); + + // Each zs value corresponds to a permutation batch. + for (i, instances) in permutation_batches.iter().enumerate() { + // Z(gx) * down = Z x * up + let (reduced_lhs, reduced_rhs): (Vec

, Vec

) = instances + .iter() + .map(|instance| { + let PermutationInstance { + pair: PermutationPair { column_pairs }, + challenge: PermutationChallenge { beta, gamma }, + } = instance; + let mut factor = ReducingFactor::new(*beta); + let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs + .iter() + .map(|&(i, j)| (vars.local_values[i], vars.local_values[j])) + .unzip(); + ( + factor.reduce_ext(lhs.into_iter()) + FE::from_basefield(*gamma), + factor.reduce_ext(rhs.into_iter()) + FE::from_basefield(*gamma), + ) + }) + .unzip(); + let constraint = next_zs[i] * reduced_rhs.into_iter().product::

() + - local_zs[i] * reduced_lhs.into_iter().product::

(); + consumer.constraint(constraint); + } +} + +pub struct PermutationCheckDataTarget { + pub(crate) local_zs: Vec>, + pub(crate) next_zs: Vec>, + pub(crate) permutation_challenge_sets: Vec>, +} + +pub(crate) fn eval_permutation_checks_recursively( + builder: &mut CircuitBuilder, + stark: &S, + config: &StarkConfig, + vars: StarkEvaluationTargets, + permutation_data: PermutationCheckDataTarget, + consumer: &mut RecursiveConstraintConsumer, +) where + F: RichField + Extendable, + S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + let PermutationCheckDataTarget { + local_zs, + next_zs, + permutation_challenge_sets, + } = permutation_data; + + let one = builder.one_extension(); + // Check that Z(1) = 1; + for &z in &local_zs { + let z_1 = builder.sub_extension(z, one); + consumer.constraint_first_row(builder, z_1); + } + + let permutation_pairs = stark.permutation_pairs(); + + let permutation_batches = get_permutation_batches( + &permutation_pairs, + &permutation_challenge_sets, + config.num_challenges, + stark.permutation_batch_size(), + ); + + // Each zs value corresponds to a permutation batch. + for (i, instances) in permutation_batches.iter().enumerate() { + let (reduced_lhs, reduced_rhs): (Vec>, Vec>) = + instances + .iter() + .map(|instance| { + let PermutationInstance { + pair: PermutationPair { column_pairs }, + challenge: PermutationChallenge { beta, gamma }, + } = instance; + let beta_ext = builder.convert_to_ext(*beta); + let gamma_ext = builder.convert_to_ext(*gamma); + let mut factor = ReducingFactorTarget::new(beta_ext); + let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs + .iter() + .map(|&(i, j)| (vars.local_values[i], vars.local_values[j])) + .unzip(); + let reduced_lhs = factor.reduce(&lhs, builder); + let reduced_rhs = factor.reduce(&rhs, builder); + ( + builder.add_extension(reduced_lhs, gamma_ext), + builder.add_extension(reduced_rhs, gamma_ext), + ) + }) + .unzip(); + let reduced_lhs_product = builder.mul_many_extension(&reduced_lhs); + let reduced_rhs_product = builder.mul_many_extension(&reduced_rhs); + // constraint = next_zs[i] * reduced_rhs_product - local_zs[i] * reduced_lhs_product + let constraint = { + let tmp = builder.mul_extension(local_zs[i], reduced_lhs_product); + builder.mul_sub_extension(next_zs[i], reduced_rhs_product, tmp) + }; + consumer.constraint(builder, constraint) + } +} diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs new file mode 100644 index 00000000..afefdd96 --- /dev/null +++ b/starky2/src/proof.rs @@ -0,0 +1,213 @@ +use itertools::Itertools; +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::fri::oracle::PolynomialBatch; +use plonky2::fri::proof::{ + CompressedFriProof, FriChallenges, FriChallengesTarget, FriProof, FriProofTarget, +}; +use plonky2::fri::structure::{ + FriOpeningBatch, FriOpeningBatchTarget, FriOpenings, FriOpeningsTarget, +}; +use plonky2::hash::hash_types::{MerkleCapTarget, RichField}; +use plonky2::hash::merkle_tree::MerkleCap; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::target::Target; +use plonky2::plonk::config::GenericConfig; +use rayon::prelude::*; + +use crate::config::StarkConfig; +use crate::permutation::PermutationChallengeSet; + +#[derive(Debug, Clone)] +pub struct StarkProof, C: GenericConfig, const D: usize> { + /// Merkle cap of LDEs of trace values. + pub trace_cap: MerkleCap, + /// Merkle cap of LDEs of permutation Z values. + pub permutation_zs_cap: Option>, + /// 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, +} + +impl, C: GenericConfig, const D: usize> StarkProof { + /// Recover the length of the trace from a STARK proof and a STARK config. + pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { + let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] + .initial_trees_proof + .evals_proofs[0] + .1; + let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len(); + lde_bits - config.fri_config.rate_bits + } +} + +pub struct StarkProofTarget { + pub trace_cap: MerkleCapTarget, + pub permutation_zs_cap: Option, + pub quotient_polys_cap: MerkleCapTarget, + pub openings: StarkOpeningSetTarget, + pub opening_proof: FriProofTarget, +} + +impl StarkProofTarget { + /// Recover the length of the trace from a STARK proof and a STARK config. + pub fn recover_degree_bits(&self, config: &StarkConfig) -> usize { + let initial_merkle_proof = &self.opening_proof.query_round_proofs[0] + .initial_trees_proof + .evals_proofs[0] + .1; + let lde_bits = config.fri_config.cap_height + initial_merkle_proof.siblings.len(); + lde_bits - config.fri_config.rate_bits + } +} + +#[derive(Debug, Clone)] +pub struct StarkProofWithPublicInputs< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +> { + pub proof: StarkProof, + // TODO: Maybe make it generic over a `S: Stark` and replace with `[F; S::PUBLIC_INPUTS]`. + pub public_inputs: Vec, +} + +pub struct StarkProofWithPublicInputsTarget { + pub proof: StarkProofTarget, + pub public_inputs: Vec, +} + +pub struct CompressedStarkProof< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +> { + /// Merkle cap of LDEs of trace values. + pub trace_cap: MerkleCap, + /// Purported values of each polynomial at the challenge point. + pub openings: StarkOpeningSet, + /// A batch FRI argument for all openings. + 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> { + /// Randomness used in any permutation arguments. + pub permutation_challenge_sets: Option>>, + + /// Random values used to combine STARK constraints. + pub stark_alphas: Vec, + + /// Point at which the STARK polynomials are opened. + pub stark_zeta: F::Extension, + + pub fri_challenges: FriChallenges, +} + +pub(crate) struct StarkProofChallengesTarget { + pub permutation_challenge_sets: Option>>, + pub stark_alphas: Vec, + pub stark_zeta: ExtensionTarget, + pub fri_challenges: FriChallengesTarget, +} + +/// Purported values of each polynomial at the challenge point. +#[derive(Debug, Clone)] +pub struct StarkOpeningSet, const D: usize> { + pub local_values: Vec, + pub next_values: Vec, + pub permutation_zs: Option>, + pub permutation_zs_right: Option>, + pub quotient_polys: Vec, +} + +impl, const D: usize> StarkOpeningSet { + pub fn new>( + zeta: F::Extension, + g: F, + trace_commitment: &PolynomialBatch, + permutation_zs_commitment: Option<&PolynomialBatch>, + quotient_commitment: &PolynomialBatch, + ) -> Self { + let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { + c.polynomials + .par_iter() + .map(|p| p.to_extension().eval(z)) + .collect::>() + }; + let zeta_right = zeta.scalar_mul(g); + Self { + local_values: eval_commitment(zeta, trace_commitment), + next_values: eval_commitment(zeta_right, trace_commitment), + permutation_zs: permutation_zs_commitment.map(|c| eval_commitment(zeta, c)), + permutation_zs_right: permutation_zs_commitment.map(|c| eval_commitment(zeta_right, c)), + quotient_polys: eval_commitment(zeta, quotient_commitment), + } + } + + pub(crate) fn to_fri_openings(&self) -> FriOpenings { + let zeta_batch = FriOpeningBatch { + values: self + .local_values + .iter() + .chain(self.permutation_zs.iter().flatten()) + .chain(&self.quotient_polys) + .copied() + .collect_vec(), + }; + let zeta_right_batch = FriOpeningBatch { + values: self + .next_values + .iter() + .chain(self.permutation_zs_right.iter().flatten()) + .copied() + .collect_vec(), + }; + FriOpenings { + batches: vec![zeta_batch, zeta_right_batch], + } + } +} + +pub struct StarkOpeningSetTarget { + pub local_values: Vec>, + pub next_values: Vec>, + pub permutation_zs: Option>>, + pub permutation_zs_right: Option>>, + pub quotient_polys: Vec>, +} + +impl StarkOpeningSetTarget { + pub(crate) fn to_fri_openings(&self) -> FriOpeningsTarget { + let zeta_batch = FriOpeningBatchTarget { + values: self + .local_values + .iter() + .chain(self.permutation_zs.iter().flatten()) + .chain(&self.quotient_polys) + .copied() + .collect_vec(), + }; + let zeta_right_batch = FriOpeningBatchTarget { + values: self + .next_values + .iter() + .chain(self.permutation_zs_right.iter().flatten()) + .copied() + .collect_vec(), + }; + FriOpeningsTarget { + batches: vec![zeta_batch, zeta_right_batch], + } + } +} diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs new file mode 100644 index 00000000..808fb50b --- /dev/null +++ b/starky2/src/prover.rs @@ -0,0 +1,414 @@ +use std::iter::once; +use std::marker::PhantomData; + +use anyhow::{ensure, Result}; +use itertools::Itertools; +use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::Field; +use plonky2::field::packable::Packable; +use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; +use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; +use plonky2::fri::oracle::PolynomialBatch; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::challenger::Challenger; +use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::timed; +use plonky2::util::timing::TimingTree; +use plonky2::util::transpose; +use plonky2_util::{log2_ceil, log2_strict}; +use rayon::prelude::*; + +use crate::config::StarkConfig; +use crate::constraint_consumer::ConstraintConsumer; +use crate::permutation::PermutationCheckVars; +use crate::permutation::{ + compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, +}; +use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; +use crate::stark::Stark; +use crate::vanishing_poly::eval_vanishing_poly; +use crate::vars::StarkEvaluationVars; + +enum Table { + Cpu = 0, + Keccak = 1, +} + +struct CpuStark { + f: PhantomData, +} +struct KeccakStark { + f: PhantomData, +} + +struct AllStarks, const D: usize> { + cpu: CpuStark, + keccak: KeccakStark, +} + +struct CrossTableLookup { + looking_table: Table, + looking_columns: Vec, + looked_table: usize, + looked_columns: Vec, +} + +impl CrossTableLookup { + pub fn new( + looking_table: Table, + looking_columns: Vec, + looked_table: usize, + looked_columns: Vec, + ) -> Self { + assert_eq!(looking_columns.len(), looked_columns.len()); + Self { + looking_table: looking_table, + looking_columns: looking_columns, + looked_table: looked_table, + looked_columns: looked_columns, + } + } +} + +pub fn prove( + all_starks: AllStarks, + config: &StarkConfig, + trace_poly_values: Vec>>, + cross_table_lookups: Vec, + public_inputs: Vec>, + timing: &mut TimingTree, +) -> Result> +where + F: RichField + Extendable, + C: GenericConfig, + [(); <::Packing>::WIDTH]:, + [(); C::Hasher::HASH_SIZE]:, +{ + let num_starks = Table::Keccak as usize + 1; + debug_assert_eq!(num_starks, trace_poly_values.len()); + debug_assert_eq!(num_starks, public_inputs.len()); + + let degree = trace_poly_values[0].len(); + let degree_bits = log2_strict(degree); + let fri_params = config.fri_params(degree_bits); + let rate_bits = config.fri_config.rate_bits; + let cap_height = config.fri_config.cap_height; + assert!( + fri_params.total_arities() <= degree_bits + rate_bits - cap_height, + "FRI total reduction arity is too large.", + ); + + let trace_commitments = timed!( + timing, + "compute trace commitments", + trace_poly_values + .iter() + .map(|trace| { + PolynomialBatch::::from_values( + // TODO: Cloning this isn't great; consider having `from_values` accept a reference, + // or having `compute_permutation_z_polys` read trace values from the `PolynomialBatch`. + trace.clone(), + rate_bits, + false, + cap_height, + timing, + None, + ) + }) + .collect::>() + ); + + let trace_caps = trace_commitments + .iter() + .map(|c| c.merkle_tree.cap) + .collect::>(); + let mut challenger = Challenger::new(); + for cap in &trace_caps { + challenger.observe_cap(cap); + } + + let permutation_zs_commitment_challenges = (0..num_starks) + .map(|i| { + permutation_challenges( + all_starks.stark(i), + &trace_poly_values[i], + config, + &mut challenger, + timing, + ) + }) + .collect::>(); + + let permutation_zs_commitment = permutation_zs_commitment_challenges + .iter() + .map(|pzcc| pzcc.map(|(comm, _)| comm)) + .collect::>(); + let permutation_zs_cap = permutation_zs_commitment + .iter() + .map(|pzc| pzc.as_ref().map(|commit| commit.merkle_tree.cap.clone())) + .collect::>(); + for cap in &permutation_zs_cap { + challenger.observe_cap(cap); + } + + // let alphas = challenger.get_n_challenges(config.num_challenges); + // let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( + // &stark, + // &trace_commitment, + // &permutation_zs_commitment_challenges, + // public_inputs, + // alphas, + // degree_bits, + // config, + // ); + // let all_quotient_chunks = quotient_polys + // .into_par_iter() + // .flat_map(|mut quotient_poly| { + // quotient_poly + // .trim_to_len(degree * stark.quotient_degree_factor()) + // .expect("Quotient has failed, the vanishing polynomial is not divisible by Z_H"); + // // Split quotient into degree-n chunks. + // quotient_poly.chunks(degree) + // }) + // .collect(); + // let quotient_commitment = timed!( + // timing, + // "compute quotient commitment", + // PolynomialBatch::from_coeffs( + // all_quotient_chunks, + // rate_bits, + // false, + // config.fri_config.cap_height, + // timing, + // None, + // ) + // ); + // let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); + // 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 + // // `g * zeta`, are not in our subgroup `H`. It suffices to check `zeta` only, since + // // `(g * zeta)^n = zeta^n`, where `n` is the order of `g`. + // let g = F::primitive_root_of_unity(degree_bits); + // ensure!( + // zeta.exp_power_of_2(degree_bits) != F::Extension::ONE, + // "Opening point is in the subgroup." + // ); + // let openings = StarkOpeningSet::new( + // zeta, + // g, + // &trace_commitment, + // permutation_zs_commitment, + // "ient_commitment, + // ); + // challenger.observe_openings(&openings.to_fri_openings()); + // + // let initial_merkle_trees = once(&trace_commitment) + // .chain(permutation_zs_commitment) + // .chain(once("ient_commitment)) + // .collect_vec(); + // + // let opening_proof = timed!( + // timing, + // "compute openings proof", + // PolynomialBatch::prove_openings( + // &stark.fri_instance(zeta, g, config), + // &initial_merkle_trees, + // &mut challenger, + // &fri_params, + // timing, + // ) + // ); + // let proof = StarkProof { + // trace_cap, + // permutation_zs_cap, + // quotient_polys_cap, + // openings, + // opening_proof, + // }; + // + // Ok(StarkProofWithPublicInputs { + // proof, + // public_inputs: public_inputs.to_vec(), + // }) + todo!() +} + +fn add_cross_table_lookup_columns( + config: &StarkConfig, + trace_poly_values: Vec>>, + cross_table_lookups: Vec, +) { + for cross_table_lookup in cross_table_lookups { + let CrossTableLookup { + looking_table: source_table, + looking_columns: source_columns, + looked_table: target_table, + looked_columns: target_columns, + } = cross_table_lookup; + } +} + +fn permutation_challenges<'a, F, P, C, S, const D: usize>( + stark: &S, + trace_poly_values: &[PolynomialValues], + config: &StarkConfig, + challenger: &mut Challenger, + timing: &mut TimingTree, +) -> Option<(PolynomialBatch, Vec>)> +where + F: RichField + Extendable, + P: PackedField, + C: GenericConfig, + S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); P::WIDTH]:, +{ + // Permutation arguments. + stark.uses_permutation_args().then(|| { + let permutation_challenge_sets = get_n_permutation_challenge_sets( + challenger, + config.num_challenges, + stark.permutation_batch_size(), + ); + let permutation_z_polys = compute_permutation_z_polys::( + &stark, + config, + &trace_poly_values, + &permutation_challenge_sets, + ); + + let permutation_zs_commitment = timed!( + timing, + "compute permutation Z commitments", + PolynomialBatch::from_values( + permutation_z_polys, + rate_bits, + false, + config.fri_config.cap_height, + timing, + None, + ) + ); + (permutation_zs_commitment, permutation_challenge_sets) + }) +} + +/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`, +/// where the `C_i`s are the Stark constraints. +fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( + stark: &S, + trace_commitment: &'a PolynomialBatch, + permutation_zs_commitment_challenges: &'a Option<( + PolynomialBatch, + Vec>, + )>, + public_inputs: [F; S::PUBLIC_INPUTS], + alphas: Vec, + degree_bits: usize, + config: &StarkConfig, +) -> Vec> +where + F: RichField + Extendable, + P: PackedField, + C: GenericConfig, + S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); P::WIDTH]:, +{ + let degree = 1 << degree_bits; + let rate_bits = config.fri_config.rate_bits; + + let quotient_degree_bits = log2_ceil(stark.quotient_degree_factor()); + assert!( + quotient_degree_bits <= rate_bits, + "Having constraints of degree higher than the rate is not supported yet." + ); + let step = 1 << (rate_bits - quotient_degree_bits); + // When opening the `Z`s polys at the "next" point, need to look at the point `next_step` steps away. + let next_step = 1 << quotient_degree_bits; + + // Evaluation of the first Lagrange polynomial on the LDE domain. + let lagrange_first = PolynomialValues::selector(degree, 0).lde_onto_coset(quotient_degree_bits); + // Evaluation of the last Lagrange polynomial on the LDE domain. + let lagrange_last = + PolynomialValues::selector(degree, degree - 1).lde_onto_coset(quotient_degree_bits); + + let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, quotient_degree_bits); + + // Retrieve the LDE values at index `i`. + let get_trace_values_packed = |i_start| -> [P; S::COLUMNS] { + trace_commitment + .get_lde_values_packed(i_start, step) + .try_into() + .unwrap() + }; + + // Last element of the subgroup. + let last = F::primitive_root_of_unity(degree_bits).inverse(); + let size = degree << quotient_degree_bits; + let coset = F::cyclic_subgroup_coset_known_order( + F::primitive_root_of_unity(degree_bits + quotient_degree_bits), + F::coset_shift(), + size, + ); + + // We will step by `P::WIDTH`, and in each iteration, evaluate the quotient polynomial at + // a batch of `P::WIDTH` points. + let quotient_values = (0..size) + .into_par_iter() + .step_by(P::WIDTH) + .map(|i_start| { + let i_next_start = (i_start + next_step) % size; + let i_range = i_start..i_start + P::WIDTH; + + let x = *P::from_slice(&coset[i_range.clone()]); + let z_last = x - last; + let lagrange_basis_first = *P::from_slice(&lagrange_first.values[i_range.clone()]); + let lagrange_basis_last = *P::from_slice(&lagrange_last.values[i_range]); + + let mut consumer = ConstraintConsumer::new( + alphas.clone(), + z_last, + lagrange_basis_first, + lagrange_basis_last, + ); + let vars = StarkEvaluationVars { + local_values: &get_trace_values_packed(i_start), + next_values: &get_trace_values_packed(i_next_start), + public_inputs: &public_inputs, + }; + let permutation_check_data = permutation_zs_commitment_challenges.as_ref().map( + |(permutation_zs_commitment, permutation_challenge_sets)| PermutationCheckVars { + local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step), + next_zs: permutation_zs_commitment.get_lde_values_packed(i_next_start, step), + permutation_challenge_sets: permutation_challenge_sets.to_vec(), + }, + ); + eval_vanishing_poly::( + stark, + config, + vars, + permutation_check_data, + &mut consumer, + ); + let mut constraints_evals = consumer.accumulators(); + // We divide the constraints evaluations by `Z_H(x)`. + let denominator_inv = z_h_on_coset.eval_inverse_packed(i_start); + for eval in &mut constraints_evals { + *eval *= denominator_inv; + } + constraints_evals + }) + .collect::>(); + + transpose("ient_values) + .into_par_iter() + .map(PolynomialValues::new) + .map(|values| values.coset_ifft(F::coset_shift())) + .collect() +} diff --git a/starky2/src/recursive_verifier.rs b/starky2/src/recursive_verifier.rs new file mode 100644 index 00000000..e091d64c --- /dev/null +++ b/starky2/src/recursive_verifier.rs @@ -0,0 +1,333 @@ +use std::iter::once; + +use anyhow::{ensure, Result}; +use itertools::Itertools; +use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::Field; +use plonky2::fri::witness_util::set_fri_proof_target; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::iop::witness::Witness; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; +use plonky2::util::reducing::ReducingFactorTarget; +use plonky2::with_context; + +use crate::config::StarkConfig; +use crate::constraint_consumer::RecursiveConstraintConsumer; +use crate::permutation::PermutationCheckDataTarget; +use crate::proof::{ + StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget, StarkProofTarget, + StarkProofWithPublicInputs, StarkProofWithPublicInputsTarget, +}; +use crate::stark::Stark; +use crate::vanishing_poly::eval_vanishing_poly_recursively; +use crate::vars::StarkEvaluationTargets; + +pub fn recursively_verify_stark_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + builder: &mut CircuitBuilder, + stark: S, + proof_with_pis: StarkProofWithPublicInputsTarget, + inner_config: &StarkConfig, +) where + C::Hasher: AlgebraicHasher, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + assert_eq!(proof_with_pis.public_inputs.len(), S::PUBLIC_INPUTS); + let degree_bits = proof_with_pis.proof.recover_degree_bits(inner_config); + let challenges = with_context!( + builder, + "compute challenges", + proof_with_pis.get_challenges::(builder, &stark, inner_config) + ); + + recursively_verify_stark_proof_with_challenges::( + builder, + stark, + proof_with_pis, + challenges, + inner_config, + degree_bits, + ); +} + +/// Recursively verifies an inner proof. +fn recursively_verify_stark_proof_with_challenges< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + builder: &mut CircuitBuilder, + stark: S, + proof_with_pis: StarkProofWithPublicInputsTarget, + challenges: StarkProofChallengesTarget, + inner_config: &StarkConfig, + degree_bits: usize, +) where + C::Hasher: AlgebraicHasher, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + check_permutation_options(&stark, &proof_with_pis, &challenges).unwrap(); + let one = builder.one_extension(); + + let StarkProofWithPublicInputsTarget { + proof, + public_inputs, + } = proof_with_pis; + let StarkOpeningSetTarget { + local_values, + next_values, + permutation_zs, + permutation_zs_right, + quotient_polys, + } = &proof.openings; + let vars = StarkEvaluationTargets { + 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(|t| builder.convert_to_ext(t)) + .collect::>() + .try_into() + .unwrap(), + }; + + let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits); + let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); + let (l_1, l_last) = + eval_l_1_and_l_last_recursively(builder, degree_bits, challenges.stark_zeta, z_h_zeta); + let last = + builder.constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse()); + let z_last = builder.sub_extension(challenges.stark_zeta, last); + + let mut consumer = RecursiveConstraintConsumer::::new( + builder.zero_extension(), + challenges.stark_alphas, + z_last, + l_1, + l_last, + ); + + let permutation_data = stark + .uses_permutation_args() + .then(|| PermutationCheckDataTarget { + local_zs: permutation_zs.as_ref().unwrap().clone(), + next_zs: permutation_zs_right.as_ref().unwrap().clone(), + permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), + }); + + with_context!( + builder, + "evaluate vanishing polynomial", + eval_vanishing_poly_recursively::( + builder, + &stark, + inner_config, + vars, + permutation_data, + &mut consumer, + ) + ); + let vanishing_polys_zeta = consumer.accumulators(); + + // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. + let mut scale = ReducingFactorTarget::new(zeta_pow_deg); + for (i, chunk) in quotient_polys + .chunks(stark.quotient_degree_factor()) + .enumerate() + { + let recombined_quotient = scale.reduce(chunk, builder); + let computed_vanishing_poly = builder.mul_extension(z_h_zeta, recombined_quotient); + builder.connect_extension(vanishing_polys_zeta[i], computed_vanishing_poly); + } + + let merkle_caps = once(proof.trace_cap) + .chain(proof.permutation_zs_cap) + .chain(once(proof.quotient_polys_cap)) + .collect_vec(); + + let fri_instance = stark.fri_instance_target( + builder, + challenges.stark_zeta, + F::primitive_root_of_unity(degree_bits), + inner_config, + ); + builder.verify_fri_proof::( + &fri_instance, + &proof.openings.to_fri_openings(), + &challenges.fri_challenges, + &merkle_caps, + &proof.opening_proof, + &inner_config.fri_params(degree_bits), + ); +} + +fn eval_l_1_and_l_last_recursively, const D: usize>( + builder: &mut CircuitBuilder, + log_n: usize, + x: ExtensionTarget, + z_x: ExtensionTarget, +) -> (ExtensionTarget, ExtensionTarget) { + let n = builder.constant_extension(F::Extension::from_canonical_usize(1 << log_n)); + let g = builder.constant_extension(F::Extension::primitive_root_of_unity(log_n)); + let one = builder.one_extension(); + let l_1_deno = builder.mul_sub_extension(n, x, n); + let l_last_deno = builder.mul_sub_extension(g, x, one); + let l_last_deno = builder.mul_extension(n, l_last_deno); + + ( + builder.div_extension(z_x, l_1_deno), + builder.div_extension(z_x, l_last_deno), + ) +} + +pub fn add_virtual_stark_proof_with_pis< + F: RichField + Extendable, + S: Stark, + const D: usize, +>( + builder: &mut CircuitBuilder, + stark: S, + config: &StarkConfig, + degree_bits: usize, +) -> StarkProofWithPublicInputsTarget { + let proof = add_virtual_stark_proof::(builder, stark, config, degree_bits); + let public_inputs = builder.add_virtual_targets(S::PUBLIC_INPUTS); + StarkProofWithPublicInputsTarget { + proof, + public_inputs, + } +} + +pub fn add_virtual_stark_proof, S: Stark, const D: usize>( + builder: &mut CircuitBuilder, + stark: S, + config: &StarkConfig, + degree_bits: usize, +) -> StarkProofTarget { + let fri_params = config.fri_params(degree_bits); + let cap_height = fri_params.config.cap_height; + + let num_leaves_per_oracle = once(S::COLUMNS) + .chain( + stark + .uses_permutation_args() + .then(|| stark.num_permutation_batches(config)), + ) + .chain(once(stark.quotient_degree_factor() * config.num_challenges)) + .collect_vec(); + + let permutation_zs_cap = stark + .uses_permutation_args() + .then(|| builder.add_virtual_cap(cap_height)); + + StarkProofTarget { + trace_cap: builder.add_virtual_cap(cap_height), + permutation_zs_cap, + quotient_polys_cap: builder.add_virtual_cap(cap_height), + openings: add_stark_opening_set::(builder, stark, config), + opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params), + } +} + +fn add_stark_opening_set, S: Stark, const D: usize>( + builder: &mut CircuitBuilder, + stark: S, + config: &StarkConfig, +) -> StarkOpeningSetTarget { + let num_challenges = config.num_challenges; + StarkOpeningSetTarget { + local_values: builder.add_virtual_extension_targets(S::COLUMNS), + next_values: builder.add_virtual_extension_targets(S::COLUMNS), + permutation_zs: stark + .uses_permutation_args() + .then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))), + permutation_zs_right: stark + .uses_permutation_args() + .then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))), + quotient_polys: builder + .add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges), + } +} + +pub fn set_stark_proof_with_pis_target, W, const D: usize>( + witness: &mut W, + stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget, + stark_proof_with_pis: &StarkProofWithPublicInputs, +) where + F: RichField + Extendable, + C::Hasher: AlgebraicHasher, + W: Witness, +{ + let StarkProofWithPublicInputs { + proof, + public_inputs, + } = stark_proof_with_pis; + let StarkProofWithPublicInputsTarget { + proof: pt, + public_inputs: pi_targets, + } = stark_proof_with_pis_target; + + // Set public inputs. + for (&pi_t, &pi) in pi_targets.iter().zip_eq(public_inputs) { + witness.set_target(pi_t, pi); + } + + set_stark_proof_target(witness, pt, proof); +} + +pub fn set_stark_proof_target, W, const D: usize>( + witness: &mut W, + proof_target: &StarkProofTarget, + proof: &StarkProof, +) where + F: RichField + Extendable, + C::Hasher: AlgebraicHasher, + W: Witness, +{ + witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap); + witness.set_cap_target(&proof_target.quotient_polys_cap, &proof.quotient_polys_cap); + + witness.set_fri_openings( + &proof_target.openings.to_fri_openings(), + &proof.openings.to_fri_openings(), + ); + + if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = + (&proof_target.permutation_zs_cap, &proof.permutation_zs_cap) + { + witness.set_cap_target(permutation_zs_cap_target, permutation_zs_cap); + } + + set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof); +} + +/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff +/// the Stark uses a permutation argument. +fn check_permutation_options, S: Stark, const D: usize>( + stark: &S, + proof_with_pis: &StarkProofWithPublicInputsTarget, + challenges: &StarkProofChallengesTarget, +) -> Result<()> { + let options_is_some = [ + proof_with_pis.proof.permutation_zs_cap.is_some(), + proof_with_pis.proof.openings.permutation_zs.is_some(), + proof_with_pis.proof.openings.permutation_zs_right.is_some(), + challenges.permutation_challenge_sets.is_some(), + ]; + ensure!( + options_is_some + .into_iter() + .all(|b| b == stark.uses_permutation_args()), + "Permutation data doesn't match with Stark configuration." + ); + Ok(()) +} diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs new file mode 100644 index 00000000..a130d283 --- /dev/null +++ b/starky2/src/stark.rs @@ -0,0 +1,204 @@ +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::fri::structure::{ + FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo, + FriPolynomialInfo, +}; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::ext_target::ExtensionTarget; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2_util::ceil_div_usize; + +use crate::config::StarkConfig; +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::PermutationPair; +use crate::vars::StarkEvaluationTargets; +use crate::vars::StarkEvaluationVars; + +/// Represents a STARK system. +pub trait Stark, const D: usize>: Sync { + /// The total number of columns in the trace. + const COLUMNS: usize; + /// The number of public inputs. + const PUBLIC_INPUTS: usize; + + /// Evaluate constraints at a vector of points. + /// + /// The points are elements of a field `FE`, a degree `D2` extension of `F`. This lets us + /// evaluate constraints over a larger domain if desired. This can also be called with `FE = F` + /// and `D2 = 1`, in which case we are using the trivial extension, i.e. just evaluating + /// constraints over `F`. + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField; + + /// Evaluate constraints at a vector of points from the base field `F`. + fn eval_packed_base>( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) { + self.eval_packed_generic(vars, yield_constr) + } + + /// Evaluate constraints at a single point from the degree `D` extension field. + fn eval_ext( + &self, + vars: StarkEvaluationVars< + F::Extension, + F::Extension, + { Self::COLUMNS }, + { Self::PUBLIC_INPUTS }, + >, + yield_constr: &mut ConstraintConsumer, + ) { + self.eval_packed_generic(vars, yield_constr) + } + + /// Evaluate constraints at a vector of points from the degree `D` extension field. This is like + /// `eval_ext`, except in the context of a recursive circuit. + /// Note: constraints must be added through`yeld_constr.constraint(builder, constraint)` in the + /// same order as they are given in `eval_packed_generic`. + fn eval_ext_recursively( + &self, + builder: &mut CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ); + + /// The maximum constraint degree. + fn constraint_degree(&self) -> usize; + + /// The maximum constraint degree. + fn quotient_degree_factor(&self) -> usize { + 1.max(self.constraint_degree() - 1) + } + + /// Computes the FRI instance used to prove this Stark. + fn fri_instance( + &self, + zeta: F::Extension, + g: F, + config: &StarkConfig, + ) -> FriInstanceInfo { + let no_blinding_oracle = FriOracleInfo { blinding: false }; + let mut oracle_indices = 0..; + + let trace_info = + FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS); + + let permutation_zs_info = if self.uses_permutation_args() { + FriPolynomialInfo::from_range( + oracle_indices.next().unwrap(), + 0..self.num_permutation_batches(config), + ) + } else { + vec![] + }; + + let quotient_info = FriPolynomialInfo::from_range( + oracle_indices.next().unwrap(), + 0..self.quotient_degree_factor() * config.num_challenges, + ); + + let zeta_batch = FriBatchInfo { + point: zeta, + polynomials: [ + trace_info.clone(), + permutation_zs_info.clone(), + quotient_info, + ] + .concat(), + }; + let zeta_right_batch = FriBatchInfo { + point: zeta.scalar_mul(g), + polynomials: [trace_info, permutation_zs_info].concat(), + }; + FriInstanceInfo { + oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()], + batches: vec![zeta_batch, zeta_right_batch], + } + } + + /// Computes the FRI instance used to prove this Stark. + fn fri_instance_target( + &self, + builder: &mut CircuitBuilder, + zeta: ExtensionTarget, + g: F, + config: &StarkConfig, + ) -> FriInstanceInfoTarget { + let no_blinding_oracle = FriOracleInfo { blinding: false }; + let mut oracle_indices = 0..; + + let trace_info = + FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS); + + let permutation_zs_info = if self.uses_permutation_args() { + FriPolynomialInfo::from_range( + oracle_indices.next().unwrap(), + 0..self.num_permutation_batches(config), + ) + } else { + vec![] + }; + + let quotient_info = FriPolynomialInfo::from_range( + oracle_indices.next().unwrap(), + 0..self.quotient_degree_factor() * config.num_challenges, + ); + + let zeta_batch = FriBatchInfoTarget { + point: zeta, + polynomials: [ + trace_info.clone(), + permutation_zs_info.clone(), + quotient_info, + ] + .concat(), + }; + let zeta_right = builder.mul_const_extension(g, zeta); + let zeta_right_batch = FriBatchInfoTarget { + point: zeta_right, + polynomials: [trace_info, permutation_zs_info].concat(), + }; + FriInstanceInfoTarget { + oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()], + batches: vec![zeta_batch, zeta_right_batch], + } + } + + /// Pairs of lists of columns that should be permutations of one another. A permutation argument + /// will be used for each such pair. Empty by default. + fn permutation_pairs(&self) -> Vec { + vec![] + } + + fn uses_permutation_args(&self) -> bool { + !self.permutation_pairs().is_empty() + } + + /// The number of permutation argument instances that can be combined into a single constraint. + fn permutation_batch_size(&self) -> usize { + // The permutation argument constraints look like + // Z(x) \prod(...) = Z(g x) \prod(...) + // where each product has a number of terms equal to the batch size. So our batch size + // should be one less than our constraint degree, which happens to be our quotient degree. + self.quotient_degree_factor() + } + + fn num_permutation_instances(&self, config: &StarkConfig) -> usize { + self.permutation_pairs().len() * config.num_challenges + } + + fn num_permutation_batches(&self, config: &StarkConfig) -> usize { + ceil_div_usize( + self.num_permutation_instances(config), + self.permutation_batch_size(), + ) + } +} diff --git a/starky2/src/stark_testing.rs b/starky2/src/stark_testing.rs new file mode 100644 index 00000000..222ebf39 --- /dev/null +++ b/starky2/src/stark_testing.rs @@ -0,0 +1,87 @@ +use anyhow::{ensure, Result}; +use plonky2::field::extension_field::Extendable; +use plonky2::field::field_types::Field; +use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; +use plonky2::hash::hash_types::RichField; +use plonky2::util::transpose; +use plonky2_util::{log2_ceil, log2_strict}; + +use crate::constraint_consumer::ConstraintConsumer; +use crate::stark::Stark; +use crate::vars::StarkEvaluationVars; + +const WITNESS_SIZE: usize = 1 << 5; + +/// Tests that the constraints imposed by the given STARK are low-degree by applying them to random +/// low-degree witness polynomials. +pub fn test_stark_low_degree, S: Stark, const D: usize>( + stark: S, +) -> Result<()> +where + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + let rate_bits = log2_ceil(stark.constraint_degree() + 1); + + let trace_ldes = random_low_degree_matrix::(S::COLUMNS, rate_bits); + let size = trace_ldes.len(); + let public_inputs = F::rand_arr::<{ S::PUBLIC_INPUTS }>(); + + let lagrange_first = PolynomialValues::selector(WITNESS_SIZE, 0).lde(rate_bits); + let lagrange_last = PolynomialValues::selector(WITNESS_SIZE, WITNESS_SIZE - 1).lde(rate_bits); + + let last = F::primitive_root_of_unity(log2_strict(WITNESS_SIZE)).inverse(); + let subgroup = + F::cyclic_subgroup_known_order(F::primitive_root_of_unity(log2_strict(size)), size); + let alpha = F::rand(); + let constraint_evals = (0..size) + .map(|i| { + let vars = StarkEvaluationVars { + local_values: &trace_ldes[i].clone().try_into().unwrap(), + next_values: &trace_ldes[(i + (1 << rate_bits)) % size] + .clone() + .try_into() + .unwrap(), + public_inputs: &public_inputs, + }; + + let mut consumer = ConstraintConsumer::::new( + vec![alpha], + subgroup[i] - last, + lagrange_first.values[i], + lagrange_last.values[i], + ); + stark.eval_packed_base(vars, &mut consumer); + consumer.accumulators()[0] + }) + .collect::>(); + + let constraint_eval_degree = PolynomialValues::new(constraint_evals).degree(); + let maximum_degree = WITNESS_SIZE * stark.constraint_degree() - 1; + + ensure!( + constraint_eval_degree <= maximum_degree, + "Expected degrees at most {} * {} - 1 = {}, actual {:?}", + WITNESS_SIZE, + stark.constraint_degree(), + maximum_degree, + constraint_eval_degree + ); + + Ok(()) +} + +fn random_low_degree_matrix(num_polys: usize, rate_bits: usize) -> Vec> { + let polys = (0..num_polys) + .map(|_| random_low_degree_values(rate_bits)) + .collect::>(); + + transpose(&polys) +} + +fn random_low_degree_values(rate_bits: usize) -> Vec { + PolynomialCoeffs::new(F::rand_vec(WITNESS_SIZE)) + .lde(rate_bits) + .fft() + .values +} diff --git a/starky2/src/util.rs b/starky2/src/util.rs new file mode 100644 index 00000000..011a1add --- /dev/null +++ b/starky2/src/util.rs @@ -0,0 +1,16 @@ +use itertools::Itertools; +use plonky2::field::field_types::Field; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::util::transpose; + +/// A helper function to transpose a row-wise trace and put it in the format that `prove` expects. +pub fn trace_rows_to_poly_values( + trace_rows: Vec<[F; COLUMNS]>, +) -> Vec> { + let trace_row_vecs = trace_rows.into_iter().map(|row| row.to_vec()).collect_vec(); + let trace_col_vecs: Vec> = transpose(&trace_row_vecs); + trace_col_vecs + .into_iter() + .map(|column| PolynomialValues::new(column)) + .collect() +} diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs new file mode 100644 index 00000000..dc32b800 --- /dev/null +++ b/starky2/src/vanishing_poly.rs @@ -0,0 +1,68 @@ +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::GenericConfig; + +use crate::config::StarkConfig; +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::{ + eval_permutation_checks, eval_permutation_checks_recursively, PermutationCheckDataTarget, + PermutationCheckVars, +}; +use crate::stark::Stark; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +pub(crate) fn eval_vanishing_poly( + stark: &S, + config: &StarkConfig, + vars: StarkEvaluationVars, + permutation_data: Option>, + consumer: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, + C: GenericConfig, + S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + stark.eval_packed_generic(vars, consumer); + if let Some(permutation_data) = permutation_data { + eval_permutation_checks::( + stark, + config, + vars, + permutation_data, + consumer, + ); + } +} + +pub(crate) fn eval_vanishing_poly_recursively( + builder: &mut CircuitBuilder, + stark: &S, + config: &StarkConfig, + vars: StarkEvaluationTargets, + permutation_data: Option>, + consumer: &mut RecursiveConstraintConsumer, +) where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, +{ + stark.eval_ext_recursively(builder, vars, consumer); + if let Some(permutation_data) = permutation_data { + eval_permutation_checks_recursively::( + builder, + stark, + config, + vars, + permutation_data, + consumer, + ); + } +} diff --git a/starky2/src/vars.rs b/starky2/src/vars.rs new file mode 100644 index 00000000..cb83aeb7 --- /dev/null +++ b/starky2/src/vars.rs @@ -0,0 +1,26 @@ +use plonky2::field::field_types::Field; +use plonky2::field::packed_field::PackedField; +use plonky2::iop::ext_target::ExtensionTarget; + +#[derive(Debug, Copy, Clone)] +pub struct StarkEvaluationVars<'a, F, P, const COLUMNS: usize, const PUBLIC_INPUTS: usize> +where + F: Field, + P: PackedField, +{ + pub local_values: &'a [P; COLUMNS], + pub next_values: &'a [P; COLUMNS], + pub public_inputs: &'a [P::Scalar; PUBLIC_INPUTS], +} + +#[derive(Debug, Copy, Clone)] +pub struct StarkEvaluationTargets< + 'a, + const D: usize, + const COLUMNS: usize, + const PUBLIC_INPUTS: usize, +> { + pub local_values: &'a [ExtensionTarget; COLUMNS], + pub next_values: &'a [ExtensionTarget; COLUMNS], + pub public_inputs: &'a [ExtensionTarget; PUBLIC_INPUTS], +} diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs new file mode 100644 index 00000000..ca88ae8b --- /dev/null +++ b/starky2/src/verifier.rs @@ -0,0 +1,208 @@ +use std::iter::once; + +use anyhow::{ensure, Result}; +use itertools::Itertools; +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::config::{GenericConfig, Hasher}; +use plonky2::plonk::plonk_common::reduce_with_powers; + +use crate::config::StarkConfig; +use crate::constraint_consumer::ConstraintConsumer; +use crate::permutation::PermutationCheckVars; +use crate::proof::{StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs}; +use crate::stark::Stark; +use crate::vanishing_poly::eval_vanishing_poly; +use crate::vars::StarkEvaluationVars; + +pub fn verify_stark_proof< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + stark: S, + proof_with_pis: StarkProofWithPublicInputs, + config: &StarkConfig, +) -> Result<()> +where + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); C::Hasher::HASH_SIZE]:, +{ + ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); + let degree_bits = proof_with_pis.proof.recover_degree_bits(config); + let challenges = proof_with_pis.get_challenges(&stark, config, degree_bits); + verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) +} + +pub(crate) fn verify_stark_proof_with_challenges< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + stark: S, + proof_with_pis: StarkProofWithPublicInputs, + challenges: StarkProofChallenges, + degree_bits: usize, + config: &StarkConfig, +) -> Result<()> +where + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); C::Hasher::HASH_SIZE]:, +{ + check_permutation_options(&stark, &proof_with_pis, &challenges)?; + let StarkProofWithPublicInputs { + proof, + public_inputs, + } = proof_with_pis; + let StarkOpeningSet { + local_values, + next_values, + permutation_zs, + permutation_zs_right, + quotient_polys, + } = &proof.openings; + let vars = StarkEvaluationVars { + 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 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, + ); + let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars { + local_zs: permutation_zs.as_ref().unwrap().clone(), + next_zs: permutation_zs_right.as_ref().unwrap().clone(), + permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), + }); + eval_vanishing_poly::( + &stark, + config, + vars, + permutation_data, + &mut consumer, + ); + let vanishing_polys_zeta = consumer.accumulators(); + + // Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta. + 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)` + // 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 + .chunks(stark.quotient_degree_factor()) + .enumerate() + { + ensure!( + vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg), + "Mismatch between evaluation and opening of quotient polynomial" + ); + } + + let merkle_caps = once(proof.trace_cap) + .chain(proof.permutation_zs_cap) + .chain(once(proof.quotient_polys_cap)) + .collect_vec(); + + verify_fri_proof::( + &stark.fri_instance( + challenges.stark_zeta, + F::primitive_root_of_unity(degree_bits), + config, + ), + &proof.openings.to_fri_openings(), + &challenges.fri_challenges, + &merkle_caps, + &proof.opening_proof, + &config.fri_params(degree_bits), + )?; + + Ok(()) +} + +/// 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 = 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(&[n * (x - F::ONE), n * (g * x - F::ONE)]); + + (z_x * invs[0], z_x * invs[1]) +} + +/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff +/// the Stark uses a permutation argument. +fn check_permutation_options< + F: RichField + Extendable, + C: GenericConfig, + S: Stark, + const D: usize, +>( + stark: &S, + proof_with_pis: &StarkProofWithPublicInputs, + challenges: &StarkProofChallenges, +) -> Result<()> { + let options_is_some = [ + proof_with_pis.proof.permutation_zs_cap.is_some(), + proof_with_pis.proof.openings.permutation_zs.is_some(), + proof_with_pis.proof.openings.permutation_zs_right.is_some(), + challenges.permutation_challenge_sets.is_some(), + ]; + ensure!( + options_is_some + .into_iter() + .all(|b| b == stark.uses_permutation_args()), + "Permutation data doesn't match with Stark configuration." + ); + Ok(()) +} + +#[cfg(test)] +mod tests { + use plonky2::field::field_types::Field; + use plonky2::field::goldilocks_field::GoldilocksField; + use plonky2::field::polynomial::PolynomialValues; + + use crate::verifier::eval_l_1_and_l_last; + + #[test] + fn test_eval_l_1_and_l_last() { + type F = GoldilocksField; + let log_n = 5; + let n = 1 << log_n; + + let x = F::rand(); // challenge point + let expected_l_first_x = PolynomialValues::selector(n, 0).ifft().eval(x); + let expected_l_last_x = PolynomialValues::selector(n, n - 1).ifft().eval(x); + + let (l_first_x, l_last_x) = eval_l_1_and_l_last(log_n, x); + assert_eq!(l_first_x, expected_l_first_x); + assert_eq!(l_last_x, expected_l_last_x); + } +} From 9f27849ff165a5ddbbaac606b209e671aab8c2ea Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 4 May 2022 21:29:29 +0200 Subject: [PATCH 02/33] Compiles --- starky2/src/mock_stark.rs | 38 +++--- starky2/src/prover.rs | 269 +++++++++++++++++++------------------- 2 files changed, 152 insertions(+), 155 deletions(-) diff --git a/starky2/src/mock_stark.rs b/starky2/src/mock_stark.rs index 337c0142..767bf580 100644 --- a/starky2/src/mock_stark.rs +++ b/starky2/src/mock_stark.rs @@ -270,15 +270,16 @@ mod tests { let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark = S::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( - stark, - &config, - trace, - public_inputs, - &mut TimingTree::default(), - )?; + // let proof = prove::( + // stark, + // &config, + // trace, + // public_inputs, + // &mut TimingTree::default(), + // )?; - verify_stark_proof(stark, proof, &config) + // verify_stark_proof(stark, proof, &config) + Ok(()) } #[test] @@ -306,16 +307,17 @@ mod tests { let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; let stark = S::new(num_rows); let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - let proof = prove::( - stark, - &config, - trace, - public_inputs, - &mut TimingTree::default(), - )?; - verify_stark_proof(stark, proof.clone(), &config)?; - - recursive_proof::(stark, proof, &config, true) + // let proof = prove::( + // stark, + // &config, + // trace, + // public_inputs, + // &mut TimingTree::default(), + // )?; + // verify_stark_proof(stark, proof.clone(), &config)?; + // + // recursive_proof::(stark, proof, &config, true) + Ok(()) } fn recursive_proof< diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 808fb50b..0e59d79e 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -30,7 +30,7 @@ use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::StarkEvaluationVars; -enum Table { +pub enum Table { Cpu = 0, Keccak = 1, } @@ -42,12 +42,12 @@ struct KeccakStark { f: PhantomData, } -struct AllStarks, const D: usize> { +pub struct AllStarks, const D: usize> { cpu: CpuStark, keccak: KeccakStark, } -struct CrossTableLookup { +pub struct CrossTableLookup { looking_table: Table, looking_columns: Vec, looked_table: usize, @@ -63,10 +63,10 @@ impl CrossTableLookup { ) -> Self { assert_eq!(looking_columns.len(), looked_columns.len()); Self { - looking_table: looking_table, - looking_columns: looking_columns, - looked_table: looked_table, - looked_columns: looked_columns, + looking_table, + looking_columns, + looked_table, + looked_columns, } } } @@ -121,154 +121,43 @@ where let trace_caps = trace_commitments .iter() - .map(|c| c.merkle_tree.cap) + .map(|c| c.merkle_tree.cap.clone()) .collect::>(); - let mut challenger = Challenger::new(); + let mut challenger = Challenger::::new(); for cap in &trace_caps { challenger.observe_cap(cap); } - let permutation_zs_commitment_challenges = (0..num_starks) - .map(|i| { - permutation_challenges( - all_starks.stark(i), - &trace_poly_values[i], - config, - &mut challenger, - timing, - ) - }) - .collect::>(); - - let permutation_zs_commitment = permutation_zs_commitment_challenges - .iter() - .map(|pzcc| pzcc.map(|(comm, _)| comm)) - .collect::>(); - let permutation_zs_cap = permutation_zs_commitment - .iter() - .map(|pzc| pzc.as_ref().map(|commit| commit.merkle_tree.cap.clone())) - .collect::>(); - for cap in &permutation_zs_cap { - challenger.observe_cap(cap); - } - - // let alphas = challenger.get_n_challenges(config.num_challenges); - // let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( - // &stark, - // &trace_commitment, - // &permutation_zs_commitment_challenges, - // public_inputs, - // alphas, - // degree_bits, - // config, - // ); - // let all_quotient_chunks = quotient_polys - // .into_par_iter() - // .flat_map(|mut quotient_poly| { - // quotient_poly - // .trim_to_len(degree * stark.quotient_degree_factor()) - // .expect("Quotient has failed, the vanishing polynomial is not divisible by Z_H"); - // // Split quotient into degree-n chunks. - // quotient_poly.chunks(degree) - // }) - // .collect(); - // let quotient_commitment = timed!( - // timing, - // "compute quotient commitment", - // PolynomialBatch::from_coeffs( - // all_quotient_chunks, - // rate_bits, - // false, - // config.fri_config.cap_height, - // timing, - // None, - // ) - // ); - // let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); - // 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 - // // `g * zeta`, are not in our subgroup `H`. It suffices to check `zeta` only, since - // // `(g * zeta)^n = zeta^n`, where `n` is the order of `g`. - // let g = F::primitive_root_of_unity(degree_bits); - // ensure!( - // zeta.exp_power_of_2(degree_bits) != F::Extension::ONE, - // "Opening point is in the subgroup." - // ); - // let openings = StarkOpeningSet::new( - // zeta, - // g, - // &trace_commitment, - // permutation_zs_commitment, - // "ient_commitment, - // ); - // challenger.observe_openings(&openings.to_fri_openings()); - // - // let initial_merkle_trees = once(&trace_commitment) - // .chain(permutation_zs_commitment) - // .chain(once("ient_commitment)) - // .collect_vec(); - // - // let opening_proof = timed!( - // timing, - // "compute openings proof", - // PolynomialBatch::prove_openings( - // &stark.fri_instance(zeta, g, config), - // &initial_merkle_trees, - // &mut challenger, - // &fri_params, - // timing, - // ) - // ); - // let proof = StarkProof { - // trace_cap, - // permutation_zs_cap, - // quotient_polys_cap, - // openings, - // opening_proof, - // }; - // - // Ok(StarkProofWithPublicInputs { - // proof, - // public_inputs: public_inputs.to_vec(), - // }) todo!() } -fn add_cross_table_lookup_columns( +fn do_rest( + stark: S, config: &StarkConfig, - trace_poly_values: Vec>>, - cross_table_lookups: Vec, -) { - for cross_table_lookup in cross_table_lookups { - let CrossTableLookup { - looking_table: source_table, - looking_columns: source_columns, - looked_table: target_table, - looked_columns: target_columns, - } = cross_table_lookup; - } -} - -fn permutation_challenges<'a, F, P, C, S, const D: usize>( - stark: &S, - trace_poly_values: &[PolynomialValues], - config: &StarkConfig, - challenger: &mut Challenger, + trace_poly_values: Vec>, + trace_commitment: PolynomialBatch, + // lookup info + public_inputs: [F; S::PUBLIC_INPUTS], + challenger: &mut Challenger, timing: &mut TimingTree, -) -> Option<(PolynomialBatch, Vec>)> +) -> Result> where F: RichField + Extendable, - P: PackedField, C: GenericConfig, S: Stark, + [(); <::Packing>::WIDTH]:, + [(); C::Hasher::HASH_SIZE]:, [(); S::COLUMNS]:, [(); S::PUBLIC_INPUTS]:, - [(); P::WIDTH]:, { + let degree = trace_poly_values[0].len(); + let degree_bits = log2_strict(degree); + let fri_params = config.fri_params(degree_bits); + let rate_bits = config.fri_config.rate_bits; + let cap_height = config.fri_config.cap_height; + // Permutation arguments. - stark.uses_permutation_args().then(|| { + let permutation_zs_commitment_challenges = stark.uses_permutation_args().then(|| { let permutation_challenge_sets = get_n_permutation_challenge_sets( challenger, config.num_challenges, @@ -294,9 +183,115 @@ where ) ); (permutation_zs_commitment, permutation_challenge_sets) + }); + let permutation_zs_commitment = permutation_zs_commitment_challenges + .as_ref() + .map(|(comm, _)| comm); + let permutation_zs_cap = permutation_zs_commitment + .as_ref() + .map(|commit| commit.merkle_tree.cap.clone()); + for cap in &permutation_zs_cap { + challenger.observe_cap(cap); + } + + let alphas = challenger.get_n_challenges(config.num_challenges); + let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( + &stark, + &trace_commitment, + &permutation_zs_commitment_challenges, + public_inputs, + alphas, + degree_bits, + config, + ); + let all_quotient_chunks = quotient_polys + .into_par_iter() + .flat_map(|mut quotient_poly| { + quotient_poly + .trim_to_len(degree * stark.quotient_degree_factor()) + .expect("Quotient has failed, the vanishing polynomial is not divisible by Z_H"); + // Split quotient into degree-n chunks. + quotient_poly.chunks(degree) + }) + .collect(); + let quotient_commitment = timed!( + timing, + "compute quotient commitment", + PolynomialBatch::from_coeffs( + all_quotient_chunks, + rate_bits, + false, + config.fri_config.cap_height, + timing, + None, + ) + ); + let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone(); + 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 + // `g * zeta`, are not in our subgroup `H`. It suffices to check `zeta` only, since + // `(g * zeta)^n = zeta^n`, where `n` is the order of `g`. + let g = F::primitive_root_of_unity(degree_bits); + ensure!( + zeta.exp_power_of_2(degree_bits) != F::Extension::ONE, + "Opening point is in the subgroup." + ); + let openings = StarkOpeningSet::new( + zeta, + g, + &trace_commitment, + permutation_zs_commitment, + "ient_commitment, + ); + challenger.observe_openings(&openings.to_fri_openings()); + + let initial_merkle_trees = once(&trace_commitment) + .chain(permutation_zs_commitment) + .chain(once("ient_commitment)) + .collect_vec(); + + let opening_proof = timed!( + timing, + "compute openings proof", + PolynomialBatch::prove_openings( + &stark.fri_instance(zeta, g, config), + &initial_merkle_trees, + challenger, + &fri_params, + timing, + ) + ); + let proof = StarkProof { + trace_cap: trace_commitment.merkle_tree.cap, + permutation_zs_cap, + quotient_polys_cap, + openings, + opening_proof, + }; + + Ok(StarkProofWithPublicInputs { + proof, + public_inputs: public_inputs.to_vec(), }) } +fn add_cross_table_lookup_columns( + config: &StarkConfig, + trace_poly_values: Vec>>, + cross_table_lookups: Vec, +) { + for cross_table_lookup in cross_table_lookups { + let CrossTableLookup { + looking_table: source_table, + looking_columns: source_columns, + looked_table: target_table, + looked_columns: target_columns, + } = cross_table_lookup; + } +} + /// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`, /// where the `C_i`s are the Stark constraints. fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( From d56e3745dda5cc35d5ec86a81b5c505e94b89a39 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 4 May 2022 22:04:11 +0200 Subject: [PATCH 03/33] Progress --- starky2/src/mock_stark.rs | 4 +- starky2/src/permutation.rs | 4 +- starky2/src/prover.rs | 117 +++++++++++++++++++++++++++------- starky2/src/stark.rs | 11 +--- starky2/src/stark_testing.rs | 7 +- starky2/src/vanishing_poly.rs | 4 +- starky2/src/vars.rs | 8 +-- starky2/src/verifier.rs | 8 +-- 8 files changed, 110 insertions(+), 53 deletions(-) diff --git a/starky2/src/mock_stark.rs b/starky2/src/mock_stark.rs index 767bf580..ba415a12 100644 --- a/starky2/src/mock_stark.rs +++ b/starky2/src/mock_stark.rs @@ -66,7 +66,7 @@ impl, const D: usize> Stark for FibonacciStar fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -195,7 +195,7 @@ impl, const D: usize, const W: usize> Stark fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index 443ff787..ad3efd0c 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -260,7 +260,7 @@ where pub(crate) fn eval_permutation_checks( stark: &S, config: &StarkConfig, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, permutation_data: PermutationCheckVars, consumer: &mut ConstraintConsumer

, ) where @@ -269,8 +269,6 @@ pub(crate) fn eval_permutation_checks, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, { let PermutationCheckVars { local_zs, diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 0e59d79e..1ecb8560 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; use anyhow::{ensure, Result}; use itertools::Itertools; -use plonky2::field::extension_field::Extendable; +use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::field::packable::Packable; use plonky2::field::packed_field::PackedField; @@ -20,7 +20,7 @@ use plonky2_util::{log2_ceil, log2_strict}; use rayon::prelude::*; use crate::config::StarkConfig; -use crate::constraint_consumer::ConstraintConsumer; +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, @@ -28,7 +28,7 @@ use crate::permutation::{ use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; -use crate::vars::StarkEvaluationVars; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub enum Table { Cpu = 0, @@ -38,10 +38,69 @@ pub enum Table { struct CpuStark { f: PhantomData, } + +impl, const D: usize> Stark for CpuStark { + const COLUMNS: usize = 0; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + todo!() + } + + fn eval_ext_recursively( + &self, + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + todo!() + } + + fn constraint_degree(&self) -> usize { + todo!() + } +} + struct KeccakStark { f: PhantomData, } +impl, const D: usize> Stark for KeccakStark { + const COLUMNS: usize = 0; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + vars: StarkEvaluationVars, + yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + todo!() + } + + fn eval_ext_recursively( + &self, + builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + vars: StarkEvaluationTargets, + yield_constr: &mut RecursiveConstraintConsumer, + ) { + todo!() + } + + fn constraint_degree(&self) -> usize { + todo!() + } +} + pub struct AllStarks, const D: usize> { cpu: CpuStark, keccak: KeccakStark, @@ -78,7 +137,7 @@ pub fn prove( cross_table_lookups: Vec, public_inputs: Vec>, timing: &mut TimingTree, -) -> Result> +) -> Result>> where F: RichField + Extendable, C: GenericConfig, @@ -128,16 +187,35 @@ where challenger.observe_cap(cap); } - todo!() + let cpu_proof = do_rest( + &all_starks.cpu, + config, + &trace_poly_values[Table::Cpu as usize], + &trace_commitments[Table::Cpu as usize], + &public_inputs[Table::Cpu as usize], + &mut challenger, + timing, + )?; + let keccak_proof = do_rest( + &all_starks.keccak, + config, + &trace_poly_values[Table::Keccak as usize], + &trace_commitments[Table::Keccak as usize], + &public_inputs[Table::Keccak as usize], + &mut challenger, + timing, + )?; + + Ok(vec![cpu_proof, keccak_proof]) } fn do_rest( - stark: S, + stark: &S, config: &StarkConfig, - trace_poly_values: Vec>, - trace_commitment: PolynomialBatch, + trace_poly_values: &[PolynomialValues], + trace_commitment: &PolynomialBatch, // lookup info - public_inputs: [F; S::PUBLIC_INPUTS], + public_inputs: &[F], challenger: &mut Challenger, timing: &mut TimingTree, ) -> Result> @@ -147,8 +225,8 @@ where S: Stark, [(); <::Packing>::WIDTH]:, [(); C::Hasher::HASH_SIZE]:, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, + // [(); S::COLUMNS]:, + // [(); S::PUBLIC_INPUTS]:, { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); @@ -247,7 +325,7 @@ where ); challenger.observe_openings(&openings.to_fri_openings()); - let initial_merkle_trees = once(&trace_commitment) + let initial_merkle_trees = once(trace_commitment) .chain(permutation_zs_commitment) .chain(once("ient_commitment)) .collect_vec(); @@ -264,7 +342,7 @@ where ) ); let proof = StarkProof { - trace_cap: trace_commitment.merkle_tree.cap, + trace_cap: trace_commitment.merkle_tree.cap.clone(), permutation_zs_cap, quotient_polys_cap, openings, @@ -301,7 +379,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( PolynomialBatch, Vec>, )>, - public_inputs: [F; S::PUBLIC_INPUTS], + public_inputs: &[F], alphas: Vec, degree_bits: usize, config: &StarkConfig, @@ -311,9 +389,6 @@ where P: PackedField, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, - [(); P::WIDTH]:, { let degree = 1 << degree_bits; let rate_bits = config.fri_config.rate_bits; @@ -336,12 +411,8 @@ where let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, quotient_degree_bits); // Retrieve the LDE values at index `i`. - let get_trace_values_packed = |i_start| -> [P; S::COLUMNS] { - trace_commitment - .get_lde_values_packed(i_start, step) - .try_into() - .unwrap() - }; + let get_trace_values_packed = + |i_start| -> Vec

{ trace_commitment.get_lde_values_packed(i_start, step) }; // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs index a130d283..c5ec5405 100644 --- a/starky2/src/stark.rs +++ b/starky2/src/stark.rs @@ -30,7 +30,7 @@ pub trait Stark, const D: usize>: Sync { /// constraints over `F`. fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -39,7 +39,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a vector of points from the base field `F`. fn eval_packed_base>( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) { self.eval_packed_generic(vars, yield_constr) @@ -48,12 +48,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a single point from the degree `D` extension field. fn eval_ext( &self, - vars: StarkEvaluationVars< - F::Extension, - F::Extension, - { Self::COLUMNS }, - { Self::PUBLIC_INPUTS }, - >, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer, ) { self.eval_packed_generic(vars, yield_constr) diff --git a/starky2/src/stark_testing.rs b/starky2/src/stark_testing.rs index 222ebf39..10834649 100644 --- a/starky2/src/stark_testing.rs +++ b/starky2/src/stark_testing.rs @@ -37,11 +37,8 @@ where let constraint_evals = (0..size) .map(|i| { let vars = StarkEvaluationVars { - local_values: &trace_ldes[i].clone().try_into().unwrap(), - next_values: &trace_ldes[(i + (1 << rate_bits)) % size] - .clone() - .try_into() - .unwrap(), + local_values: &trace_ldes[i], + next_values: &trace_ldes[(i + (1 << rate_bits)) % size], public_inputs: &public_inputs, }; diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index dc32b800..0a3f4514 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -16,7 +16,7 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub(crate) fn eval_vanishing_poly( stark: &S, config: &StarkConfig, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, permutation_data: Option>, consumer: &mut ConstraintConsumer

, ) where @@ -25,8 +25,6 @@ pub(crate) fn eval_vanishing_poly, C: GenericConfig, S: Stark, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, { stark.eval_packed_generic(vars, consumer); if let Some(permutation_data) = permutation_data { diff --git a/starky2/src/vars.rs b/starky2/src/vars.rs index cb83aeb7..bef8557b 100644 --- a/starky2/src/vars.rs +++ b/starky2/src/vars.rs @@ -3,14 +3,14 @@ use plonky2::field::packed_field::PackedField; use plonky2::iop::ext_target::ExtensionTarget; #[derive(Debug, Copy, Clone)] -pub struct StarkEvaluationVars<'a, F, P, const COLUMNS: usize, const PUBLIC_INPUTS: usize> +pub struct StarkEvaluationVars<'a, F, P> where F: Field, P: PackedField, { - pub local_values: &'a [P; COLUMNS], - pub next_values: &'a [P; COLUMNS], - pub public_inputs: &'a [P::Scalar; PUBLIC_INPUTS], + pub local_values: &'a [P], + pub next_values: &'a [P], + pub public_inputs: &'a [P::Scalar], } #[derive(Debug, Copy, Clone)] diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index ca88ae8b..d5344a44 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -68,14 +68,12 @@ where quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { - local_values: &local_values.to_vec().try_into().unwrap(), - next_values: &next_values.to_vec().try_into().unwrap(), + local_values: &local_values, + next_values: &next_values, public_inputs: &public_inputs .into_iter() .map(F::Extension::from_basefield) - .collect::>() - .try_into() - .unwrap(), + .collect::>(), }; let (l_1, l_last) = eval_l_1_and_l_last(degree_bits, challenges.stark_zeta); From 4e18cad0f1400cc996db8b39f2dc86b615aa4c47 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 5 May 2022 16:30:21 +0200 Subject: [PATCH 04/33] Lookup argument --- starky2/src/prover.rs | 76 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 15 deletions(-) diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 1ecb8560..58f91192 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -13,6 +13,7 @@ use plonky2::fri::oracle::PolynomialBatch; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, Hasher}; +use plonky2::plonk::plonk_common::reduce_with_powers; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; @@ -30,6 +31,7 @@ use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; +#[derive(Copy, Clone)] pub enum Table { Cpu = 0, Keccak = 1, @@ -106,19 +108,21 @@ pub struct AllStarks, const D: usize> { keccak: KeccakStark, } -pub struct CrossTableLookup { +pub struct CrossTableLookup { looking_table: Table, looking_columns: Vec, looked_table: usize, looked_columns: Vec, + default: F, } -impl CrossTableLookup { +impl CrossTableLookup { pub fn new( looking_table: Table, looking_columns: Vec, looked_table: usize, looked_columns: Vec, + default: F, ) -> Self { assert_eq!(looking_columns.len(), looked_columns.len()); Self { @@ -126,6 +130,7 @@ impl CrossTableLookup { looking_columns, looked_table, looked_columns, + default, } } } @@ -134,7 +139,7 @@ pub fn prove( all_starks: AllStarks, config: &StarkConfig, trace_poly_values: Vec>>, - cross_table_lookups: Vec, + cross_table_lookups: Vec>, public_inputs: Vec>, timing: &mut TimingTree, ) -> Result>> @@ -187,6 +192,9 @@ where challenger.observe_cap(cap); } + let lookup_zs_commitments = + cross_table_lookup_commitments(&trace_poly_values, &cross_table_lookups); + let cpu_proof = do_rest( &all_starks.cpu, config, @@ -355,19 +363,57 @@ where }) } -fn add_cross_table_lookup_columns( - config: &StarkConfig, - trace_poly_values: Vec>>, - cross_table_lookups: Vec, -) { - for cross_table_lookup in cross_table_lookups { - let CrossTableLookup { - looking_table: source_table, - looking_columns: source_columns, - looked_table: target_table, - looked_columns: target_columns, - } = cross_table_lookup; +fn cross_table_lookup_commitments( + trace_poly_values: &[Vec>], + cross_table_lookups: &[CrossTableLookup], +) -> Vec>> { + cross_table_lookups.iter().fold( + vec![vec![]; trace_poly_values.len()], + |mut acc, cross_table_lookup| { + let CrossTableLookup { + looking_table, + looking_columns, + looked_table, + looked_columns, + default, + } = cross_table_lookup; + + let beta = F::ONE; // TODO + let gamma = F::ONE; // TODO + let z_looking = partial_products( + &trace_poly_values[*looking_table as usize], + &looking_columns, + beta, + gamma, + ); + let z_looked = partial_products( + &trace_poly_values[*looked_table as usize], + &looked_columns, + beta, + gamma, + ); + + acc[*looking_table as usize].push(z_looking); + acc[*looked_table as usize].push(z_looked); + acc + }, + ) +} + +fn partial_products( + trace: &[PolynomialValues], + columns: &[usize], + beta: F, + gamma: F, +) -> PolynomialValues { + let mut partial_prod = F::ONE; + let mut res = Vec::new(); + for i in 0..trace[0].len() { + partial_prod *= + gamma + reduce_with_powers(columns.iter().map(|&j| &trace[i].values[j]), beta); + res.push(partial_prod); } + res.into() } /// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`, From e40276ef195eafe6f7784c4b933fb2b53fa34d82 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 5 May 2022 17:14:21 +0200 Subject: [PATCH 05/33] Compiles somehow --- starky/src/prover.rs | 2 +- starky2/src/prover.rs | 104 ++++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/starky/src/prover.rs b/starky/src/prover.rs index f7422c24..04b34241 100644 --- a/starky/src/prover.rs +++ b/starky/src/prover.rs @@ -108,7 +108,7 @@ where let permutation_zs_cap = permutation_zs_commitment .as_ref() .map(|commit| commit.merkle_tree.cap.clone()); - for cap in &permutation_zs_cap { + if let Some(cap) = &permutation_zs_cap { challenger.observe_cap(cap); } diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 58f91192..9b100001 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -192,14 +192,14 @@ where challenger.observe_cap(cap); } - let lookup_zs_commitments = - cross_table_lookup_commitments(&trace_poly_values, &cross_table_lookups); + let lookup_zs = cross_table_lookup_commitments(&trace_poly_values, &cross_table_lookups); let cpu_proof = do_rest( &all_starks.cpu, config, &trace_poly_values[Table::Cpu as usize], &trace_commitments[Table::Cpu as usize], + &lookup_zs[Table::Cpu as usize], &public_inputs[Table::Cpu as usize], &mut challenger, timing, @@ -209,6 +209,7 @@ where config, &trace_poly_values[Table::Keccak as usize], &trace_commitments[Table::Keccak as usize], + &lookup_zs[Table::Keccak as usize], &public_inputs[Table::Keccak as usize], &mut challenger, timing, @@ -222,7 +223,7 @@ fn do_rest( config: &StarkConfig, trace_poly_values: &[PolynomialValues], trace_commitment: &PolynomialBatch, - // lookup info + lookup_zs: &[PolynomialValues], public_inputs: &[F], challenger: &mut Challenger, timing: &mut TimingTree, @@ -243,48 +244,72 @@ where let cap_height = config.fri_config.cap_height; // Permutation arguments. - let permutation_zs_commitment_challenges = stark.uses_permutation_args().then(|| { - let permutation_challenge_sets = get_n_permutation_challenge_sets( + let permutation_challenges = stark.uses_permutation_args().then(|| { + get_n_permutation_challenge_sets( challenger, config.num_challenges, stark.permutation_batch_size(), - ); - let permutation_z_polys = compute_permutation_z_polys::( - &stark, - config, - &trace_poly_values, - &permutation_challenge_sets, - ); - - let permutation_zs_commitment = timed!( - timing, - "compute permutation Z commitments", - PolynomialBatch::from_values( - permutation_z_polys, - rate_bits, - false, - config.fri_config.cap_height, - timing, - None, - ) - ); - (permutation_zs_commitment, permutation_challenge_sets) + ) }); - let permutation_zs_commitment = permutation_zs_commitment_challenges - .as_ref() - .map(|(comm, _)| comm); - let permutation_zs_cap = permutation_zs_commitment + let permutation_zs = permutation_challenges.as_ref().map(|challenges| { + compute_permutation_z_polys::(&stark, config, &trace_poly_values, challenges) + }); + + // if let Some((permutation_z_polys, _)) = permutation_zs_challenges { + // if lookup_zs.is_empty() { + // PolynomialBatch::from_values( + // permutation_z_polys, + // rate_bits, + // false, + // config.fri_config.cap_height, + // timing, + // None, + // ) + // } else { + // } + // } + + let z_polys = match (permutation_zs, lookup_zs.is_empty()) { + (None, true) => lookup_zs.to_vec(), + (None, false) => vec![], + (Some(mut permutation_zs), true) => { + permutation_zs.extend(lookup_zs.to_vec()); + permutation_zs + } + (Some(permutation_zs), false) => permutation_zs, + }; + + let permutation_lookup_zs_commitment = (!z_polys.is_empty()).then(|| { + PolynomialBatch::from_values( + z_polys, + rate_bits, + false, + config.fri_config.cap_height, + timing, + None, + ) + }); + let permutation_zs_cap = permutation_lookup_zs_commitment .as_ref() .map(|commit| commit.merkle_tree.cap.clone()); - for cap in &permutation_zs_cap { + if let Some(cap) = &permutation_zs_cap { challenger.observe_cap(cap); } + let zipped = if let (Some(x), Some(y)) = ( + permutation_lookup_zs_commitment.as_ref(), + permutation_challenges.as_ref(), + ) { + Some((x, y)) + } else { + None + }; + let alphas = challenger.get_n_challenges(config.num_challenges); let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( &stark, &trace_commitment, - &permutation_zs_commitment_challenges, + zipped, public_inputs, alphas, degree_bits, @@ -324,17 +349,18 @@ where zeta.exp_power_of_2(degree_bits) != F::Extension::ONE, "Opening point is in the subgroup." ); + let openings = StarkOpeningSet::new( zeta, g, &trace_commitment, - permutation_zs_commitment, + permutation_lookup_zs_commitment.as_ref(), "ient_commitment, ); challenger.observe_openings(&openings.to_fri_openings()); let initial_merkle_trees = once(trace_commitment) - .chain(permutation_zs_commitment) + .chain(&permutation_lookup_zs_commitment) .chain(once("ient_commitment)) .collect_vec(); @@ -378,8 +404,8 @@ fn cross_table_lookup_commitments( default, } = cross_table_lookup; - let beta = F::ONE; // TODO - let gamma = F::ONE; // TODO + let beta = F::ONE; // TODO num_challenges times + let gamma = F::ONE; // TODO num_challenges times let z_looking = partial_products( &trace_poly_values[*looking_table as usize], &looking_columns, @@ -421,9 +447,9 @@ fn partial_products( fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, - permutation_zs_commitment_challenges: &'a Option<( - PolynomialBatch, - Vec>, + permutation_zs_commitment_challenges: Option<( + &'a PolynomialBatch, + &'a Vec>, )>, public_inputs: &[F], alphas: Vec, From d659e759e96f8ad43fbcd8fe3bac475dd839c2e5 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 5 May 2022 19:12:58 +0200 Subject: [PATCH 06/33] Clean lookup stuff --- starky2/src/prover.rs | 106 ++++++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 39 deletions(-) diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 9b100001..cc19edbc 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -192,7 +192,12 @@ where challenger.observe_cap(cap); } - let lookup_zs = cross_table_lookup_commitments(&trace_poly_values, &cross_table_lookups); + let lookup_zs = cross_table_lookup_zs::( + config, + &trace_poly_values, + &cross_table_lookups, + &mut challenger, + ); let cpu_proof = do_rest( &all_starks.cpu, @@ -223,7 +228,7 @@ fn do_rest( config: &StarkConfig, trace_poly_values: &[PolynomialValues], trace_commitment: &PolynomialBatch, - lookup_zs: &[PolynomialValues], + lookup_data: &LookupData, public_inputs: &[F], challenger: &mut Challenger, timing: &mut TimingTree, @@ -255,25 +260,11 @@ where compute_permutation_z_polys::(&stark, config, &trace_poly_values, challenges) }); - // if let Some((permutation_z_polys, _)) = permutation_zs_challenges { - // if lookup_zs.is_empty() { - // PolynomialBatch::from_values( - // permutation_z_polys, - // rate_bits, - // false, - // config.fri_config.cap_height, - // timing, - // None, - // ) - // } else { - // } - // } - - let z_polys = match (permutation_zs, lookup_zs.is_empty()) { - (None, true) => lookup_zs.to_vec(), + let z_polys = match (permutation_zs, lookup_data.is_empty()) { + (None, true) => lookup_data.z_polys(), (None, false) => vec![], (Some(mut permutation_zs), true) => { - permutation_zs.extend(lookup_zs.to_vec()); + permutation_zs.extend(lookup_data.z_polys()); permutation_zs } (Some(permutation_zs), false) => permutation_zs, @@ -310,6 +301,7 @@ where &stark, &trace_commitment, zipped, + lookup_data, public_inputs, alphas, degree_bits, @@ -389,38 +381,73 @@ where }) } -fn cross_table_lookup_commitments( +/// Lookup data for one table. +#[derive(Clone)] +struct LookupData { + zs_beta_gammas: Vec<(PolynomialValues, F, F)>, +} + +impl Default for LookupData { + fn default() -> Self { + Self { + zs_beta_gammas: Vec::new(), + } + } +} + +impl LookupData { + fn is_empty(&self) -> bool { + self.zs_beta_gammas.is_empty() + } + + fn z_polys(&self) -> Vec> { + self.zs_beta_gammas + .iter() + .map(|(p, _, _)| p.clone()) + .collect() + } +} + +fn cross_table_lookup_zs, const D: usize>( + config: &StarkConfig, trace_poly_values: &[Vec>], cross_table_lookups: &[CrossTableLookup], -) -> Vec>> { + challenger: &mut Challenger, +) -> Vec> { cross_table_lookups.iter().fold( - vec![vec![]; trace_poly_values.len()], + vec![LookupData::default(); trace_poly_values.len()], |mut acc, cross_table_lookup| { let CrossTableLookup { looking_table, looking_columns, looked_table, looked_columns, - default, + .. } = cross_table_lookup; - let beta = F::ONE; // TODO num_challenges times - let gamma = F::ONE; // TODO num_challenges times - let z_looking = partial_products( - &trace_poly_values[*looking_table as usize], - &looking_columns, - beta, - gamma, - ); - let z_looked = partial_products( - &trace_poly_values[*looked_table as usize], - &looked_columns, - beta, - gamma, - ); + for _ in 0..config.num_challenges { + let beta = challenger.get_challenge(); + let gamma = challenger.get_challenge(); + let z_looking = partial_products( + &trace_poly_values[*looking_table as usize], + &looking_columns, + beta, + gamma, + ); + let z_looked = partial_products( + &trace_poly_values[*looked_table as usize], + &looked_columns, + beta, + gamma, + ); - acc[*looking_table as usize].push(z_looking); - acc[*looked_table as usize].push(z_looked); + acc[*looking_table as usize] + .zs_beta_gammas + .push((z_looking, beta, gamma)); + acc[*looked_table as usize] + .zs_beta_gammas + .push((z_looked, beta, gamma)); + } acc }, ) @@ -451,6 +478,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( &'a PolynomialBatch, &'a Vec>, )>, + lookup_data: &LookupData, public_inputs: &[F], alphas: Vec, degree_bits: usize, From d4a6b35436355c033c85e43468e337db6521ccf2 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 5 May 2022 22:21:09 +0200 Subject: [PATCH 07/33] More cleaning --- starky2/src/cross_table_lookups.rs | 170 +++++++++++++++++++++++++++++ starky2/src/lib.rs | 1 + starky2/src/prover.rs | 89 +-------------- 3 files changed, 172 insertions(+), 88 deletions(-) create mode 100644 starky2/src/cross_table_lookups.rs diff --git a/starky2/src/cross_table_lookups.rs b/starky2/src/cross_table_lookups.rs new file mode 100644 index 00000000..b35733a2 --- /dev/null +++ b/starky2/src/cross_table_lookups.rs @@ -0,0 +1,170 @@ +use plonky2::field::extension_field::FieldExtension; +use plonky2::field::field_types::Field; +use plonky2::field::packed_field::PackedField; +use plonky2::field::polynomial::PolynomialValues; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::challenger::Challenger; +use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::plonk_common::reduce_with_powers; + +use crate::config::StarkConfig; +use crate::prover::CrossTableLookup; + +/// Lookup data for one table. +#[derive(Clone)] +pub struct LookupData { + zs_beta_gammas: Vec<(PolynomialValues, F, F)>, +} + +impl Default for LookupData { + fn default() -> Self { + Self { + zs_beta_gammas: Vec::new(), + } + } +} + +impl LookupData { + pub fn is_empty(&self) -> bool { + self.zs_beta_gammas.is_empty() + } + + pub fn z_polys(&self) -> Vec> { + self.zs_beta_gammas + .iter() + .map(|(p, _, _)| p.clone()) + .collect() + } +} + +pub fn cross_table_lookup_zs, const D: usize>( + config: &StarkConfig, + trace_poly_values: &[Vec>], + cross_table_lookups: &[CrossTableLookup], + challenger: &mut Challenger, +) -> Vec> { + cross_table_lookups.iter().fold( + vec![LookupData::default(); trace_poly_values.len()], + |mut acc, cross_table_lookup| { + let CrossTableLookup { + looking_table, + looking_columns, + looked_table, + looked_columns, + .. + } = cross_table_lookup; + + for _ in 0..config.num_challenges { + let beta = challenger.get_challenge(); + let gamma = challenger.get_challenge(); + let z_looking = partial_products( + &trace_poly_values[*looking_table as usize], + &looking_columns, + beta, + gamma, + ); + let z_looked = partial_products( + &trace_poly_values[*looked_table as usize], + &looked_columns, + beta, + gamma, + ); + + acc[*looking_table as usize] + .zs_beta_gammas + .push((z_looking, beta, gamma)); + acc[*looked_table as usize] + .zs_beta_gammas + .push((z_looked, beta, gamma)); + } + acc + }, + ) +} + +fn partial_products( + trace: &[PolynomialValues], + columns: &[usize], + beta: F, + gamma: F, +) -> PolynomialValues { + let mut partial_prod = F::ONE; + let mut res = Vec::new(); + for i in 0..trace[0].len() { + partial_prod *= + gamma + reduce_with_powers(columns.iter().map(|&j| &trace[i].values[j]), beta); + res.push(partial_prod); + } + res.into() +} + +pub struct CTLCheckVars +where + F: Field, + FE: FieldExtension, + P: PackedField, +{ + pub(crate) local_zs: Vec

, + pub(crate) next_zs: Vec

, + pub(crate) permutation_challenge_sets: Vec>, +} + +pub(crate) fn eval_permutation_checks( + stark: &S, + config: &StarkConfig, + vars: StarkEvaluationVars, + permutation_data: PermutationCheckVars, + consumer: &mut ConstraintConsumer

, +) where + F: RichField + Extendable, + FE: FieldExtension, + P: PackedField, + C: GenericConfig, + S: Stark, +{ + let PermutationCheckVars { + local_zs, + next_zs, + permutation_challenge_sets, + } = permutation_data; + + // Check that Z(1) = 1; + for &z in &local_zs { + consumer.constraint_first_row(z - FE::ONE); + } + + let permutation_pairs = stark.permutation_pairs(); + + let permutation_batches = get_permutation_batches( + &permutation_pairs, + &permutation_challenge_sets, + config.num_challenges, + stark.permutation_batch_size(), + ); + + // Each zs value corresponds to a permutation batch. + for (i, instances) in permutation_batches.iter().enumerate() { + // Z(gx) * down = Z x * up + let (reduced_lhs, reduced_rhs): (Vec

, Vec

) = instances + .iter() + .map(|instance| { + let PermutationInstance { + pair: PermutationPair { column_pairs }, + challenge: PermutationChallenge { beta, gamma }, + } = instance; + let mut factor = ReducingFactor::new(*beta); + let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs + .iter() + .map(|&(i, j)| (vars.local_values[i], vars.local_values[j])) + .unzip(); + ( + factor.reduce_ext(lhs.into_iter()) + FE::from_basefield(*gamma), + factor.reduce_ext(rhs.into_iter()) + FE::from_basefield(*gamma), + ) + }) + .unzip(); + let constraint = next_zs[i] * reduced_rhs.into_iter().product::

() + - local_zs[i] * reduced_lhs.into_iter().product::

(); + consumer.constraint(constraint); + } +} diff --git a/starky2/src/lib.rs b/starky2/src/lib.rs index 0924600d..03ce7076 100644 --- a/starky2/src/lib.rs +++ b/starky2/src/lib.rs @@ -5,6 +5,7 @@ pub mod config; pub mod constraint_consumer; +pub mod cross_table_lookups; mod get_challenges; pub mod mock_stark; pub mod permutation; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index cc19edbc..d998b074 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -22,6 +22,7 @@ use rayon::prelude::*; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookups::{cross_table_lookup_zs, LookupData}; use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, @@ -381,94 +382,6 @@ where }) } -/// Lookup data for one table. -#[derive(Clone)] -struct LookupData { - zs_beta_gammas: Vec<(PolynomialValues, F, F)>, -} - -impl Default for LookupData { - fn default() -> Self { - Self { - zs_beta_gammas: Vec::new(), - } - } -} - -impl LookupData { - fn is_empty(&self) -> bool { - self.zs_beta_gammas.is_empty() - } - - fn z_polys(&self) -> Vec> { - self.zs_beta_gammas - .iter() - .map(|(p, _, _)| p.clone()) - .collect() - } -} - -fn cross_table_lookup_zs, const D: usize>( - config: &StarkConfig, - trace_poly_values: &[Vec>], - cross_table_lookups: &[CrossTableLookup], - challenger: &mut Challenger, -) -> Vec> { - cross_table_lookups.iter().fold( - vec![LookupData::default(); trace_poly_values.len()], - |mut acc, cross_table_lookup| { - let CrossTableLookup { - looking_table, - looking_columns, - looked_table, - looked_columns, - .. - } = cross_table_lookup; - - for _ in 0..config.num_challenges { - let beta = challenger.get_challenge(); - let gamma = challenger.get_challenge(); - let z_looking = partial_products( - &trace_poly_values[*looking_table as usize], - &looking_columns, - beta, - gamma, - ); - let z_looked = partial_products( - &trace_poly_values[*looked_table as usize], - &looked_columns, - beta, - gamma, - ); - - acc[*looking_table as usize] - .zs_beta_gammas - .push((z_looking, beta, gamma)); - acc[*looked_table as usize] - .zs_beta_gammas - .push((z_looked, beta, gamma)); - } - acc - }, - ) -} - -fn partial_products( - trace: &[PolynomialValues], - columns: &[usize], - beta: F, - gamma: F, -) -> PolynomialValues { - let mut partial_prod = F::ONE; - let mut res = Vec::new(); - for i in 0..trace[0].len() { - partial_prod *= - gamma + reduce_with_powers(columns.iter().map(|&j| &trace[i].values[j]), beta); - res.push(partial_prod); - } - res.into() -} - /// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`, /// where the `C_i`s are the Stark constraints. fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( From e744c640e033b530023446e02f5e8f29ba5ca071 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 6 May 2022 14:55:54 +0200 Subject: [PATCH 08/33] Add eval for cross table lookups --- starky2/src/cross_table_lookups.rs | 100 ++++++++++++----------------- starky2/src/lib.rs | 2 +- starky2/src/prover.rs | 82 +++++++++++++++-------- starky2/src/vanishing_poly.rs | 3 + starky2/src/verifier.rs | 1 + 5 files changed, 102 insertions(+), 86 deletions(-) diff --git a/starky2/src/cross_table_lookups.rs b/starky2/src/cross_table_lookups.rs index b35733a2..5ad5680e 100644 --- a/starky2/src/cross_table_lookups.rs +++ b/starky2/src/cross_table_lookups.rs @@ -1,4 +1,4 @@ -use plonky2::field::extension_field::FieldExtension; +use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::field::packed_field::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -6,14 +6,19 @@ use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; +use plonky2::util::reducing::ReducingFactor; use crate::config::StarkConfig; +use crate::constraint_consumer::ConstraintConsumer; +use crate::permutation::PermutationChallenge; use crate::prover::CrossTableLookup; +use crate::stark::Stark; +use crate::vars::StarkEvaluationVars; /// Lookup data for one table. #[derive(Clone)] pub struct LookupData { - zs_beta_gammas: Vec<(PolynomialValues, F, F)>, + pub zs_beta_gammas: Vec<(PolynomialValues, F, F, Vec)>, } impl Default for LookupData { @@ -32,7 +37,7 @@ impl LookupData { pub fn z_polys(&self) -> Vec> { self.zs_beta_gammas .iter() - .map(|(p, _, _)| p.clone()) + .map(|(p, _, _, _)| p.clone()) .collect() } } @@ -70,12 +75,18 @@ pub fn cross_table_lookup_zs, const D: gamma, ); - acc[*looking_table as usize] - .zs_beta_gammas - .push((z_looking, beta, gamma)); - acc[*looked_table as usize] - .zs_beta_gammas - .push((z_looked, beta, gamma)); + acc[*looking_table as usize].zs_beta_gammas.push(( + z_looking, + beta, + gamma, + looking_columns.clone(), + )); + acc[*looked_table as usize].zs_beta_gammas.push(( + z_looked, + beta, + gamma, + looked_columns.clone(), + )); } acc }, @@ -104,16 +115,15 @@ where FE: FieldExtension, P: PackedField, { - pub(crate) local_zs: Vec

, - pub(crate) next_zs: Vec

, - pub(crate) permutation_challenge_sets: Vec>, + pub(crate) local_z: P, + pub(crate) next_z: P, + pub(crate) challenges: PermutationChallenge, + pub(crate) columns: Vec, } -pub(crate) fn eval_permutation_checks( - stark: &S, - config: &StarkConfig, +pub(crate) fn eval_cross_table_lookup_checks( vars: StarkEvaluationVars, - permutation_data: PermutationCheckVars, + lookup_data: &[CTLCheckVars], consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -122,49 +132,21 @@ pub(crate) fn eval_permutation_checks, S: Stark, { - let PermutationCheckVars { - local_zs, - next_zs, - permutation_challenge_sets, - } = permutation_data; + for lookup_datum in lookup_data { + let CTLCheckVars { + local_z, + next_z, + challenges, + columns, + } = lookup_datum; + let mut factor = ReducingFactor::new(challenges.beta); + let mut combine = |v: &[P]| -> P { + factor.reduce_ext(columns.iter().map(|&i| v[i])) + FE::from_basefield(challenges.gamma) + }; - // Check that Z(1) = 1; - for &z in &local_zs { - consumer.constraint_first_row(z - FE::ONE); - } - - let permutation_pairs = stark.permutation_pairs(); - - let permutation_batches = get_permutation_batches( - &permutation_pairs, - &permutation_challenge_sets, - config.num_challenges, - stark.permutation_batch_size(), - ); - - // Each zs value corresponds to a permutation batch. - for (i, instances) in permutation_batches.iter().enumerate() { - // Z(gx) * down = Z x * up - let (reduced_lhs, reduced_rhs): (Vec

, Vec

) = instances - .iter() - .map(|instance| { - let PermutationInstance { - pair: PermutationPair { column_pairs }, - challenge: PermutationChallenge { beta, gamma }, - } = instance; - let mut factor = ReducingFactor::new(*beta); - let (lhs, rhs): (Vec<_>, Vec<_>) = column_pairs - .iter() - .map(|&(i, j)| (vars.local_values[i], vars.local_values[j])) - .unzip(); - ( - factor.reduce_ext(lhs.into_iter()) + FE::from_basefield(*gamma), - factor.reduce_ext(rhs.into_iter()) + FE::from_basefield(*gamma), - ) - }) - .unzip(); - let constraint = next_zs[i] * reduced_rhs.into_iter().product::

() - - local_zs[i] * reduced_lhs.into_iter().product::

(); - consumer.constraint(constraint); + // Check value of `Z(1)` + consumer.constraint_first_row(*local_z - combine(vars.local_values)); + // Check `Z(gw) = combination * Z(w)` + consumer.constraint_transition(*next_z - *local_z * combine(vars.next_values)); } } diff --git a/starky2/src/lib.rs b/starky2/src/lib.rs index 03ce7076..a545b2c0 100644 --- a/starky2/src/lib.rs +++ b/starky2/src/lib.rs @@ -7,7 +7,7 @@ pub mod config; pub mod constraint_consumer; pub mod cross_table_lookups; mod get_challenges; -pub mod mock_stark; +// pub mod mock_stark; pub mod permutation; pub mod proof; pub mod prover; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index d998b074..7413dcba 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -13,7 +13,6 @@ use plonky2::fri::oracle::PolynomialBatch; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::{GenericConfig, Hasher}; -use plonky2::plonk::plonk_common::reduce_with_powers; use plonky2::timed; use plonky2::util::timing::TimingTree; use plonky2::util::transpose; @@ -22,11 +21,11 @@ use rayon::prelude::*; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookups::{cross_table_lookup_zs, LookupData}; -use crate::permutation::PermutationCheckVars; +use crate::cross_table_lookups::{cross_table_lookup_zs, CTLCheckVars, LookupData}; use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, }; +use crate::permutation::{PermutationChallenge, PermutationCheckVars}; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; @@ -48,8 +47,8 @@ impl, const D: usize> Stark for CpuStark( &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, + _vars: StarkEvaluationVars, + _yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, @@ -59,9 +58,9 @@ impl, const D: usize> Stark for CpuStark, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, + _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _vars: StarkEvaluationTargets, + _yield_constr: &mut RecursiveConstraintConsumer, ) { todo!() } @@ -81,8 +80,8 @@ impl, const D: usize> Stark for KeccakStark( &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, + _vars: StarkEvaluationVars, + _yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, P: PackedField, @@ -92,9 +91,9 @@ impl, const D: usize> Stark for KeccakStark, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, + _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _vars: StarkEvaluationTargets, + _yield_constr: &mut RecursiveConstraintConsumer, ) { todo!() } @@ -110,11 +109,11 @@ pub struct AllStarks, const D: usize> { } pub struct CrossTableLookup { - looking_table: Table, - looking_columns: Vec, - looked_table: usize, - looked_columns: Vec, - default: F, + pub looking_table: Table, + pub looking_columns: Vec, + pub looked_table: usize, + pub looked_columns: Vec, + pub default: F, } impl CrossTableLookup { @@ -247,7 +246,7 @@ where let degree_bits = log2_strict(degree); let fri_params = config.fri_params(degree_bits); let rate_bits = config.fri_config.rate_bits; - let cap_height = config.fri_config.cap_height; + let _cap_height = config.fri_config.cap_height; // Permutation arguments. let permutation_challenges = stark.uses_permutation_args().then(|| { @@ -258,8 +257,9 @@ where ) }); let permutation_zs = permutation_challenges.as_ref().map(|challenges| { - compute_permutation_z_polys::(&stark, config, &trace_poly_values, challenges) + compute_permutation_z_polys::(stark, config, trace_poly_values, challenges) }); + let num_permutation_zs = permutation_zs.as_ref().map(|v| v.len()).unwrap_or(0); let z_polys = match (permutation_zs, lookup_data.is_empty()) { (None, true) => lookup_data.z_polys(), @@ -288,6 +288,7 @@ where challenger.observe_cap(cap); } + // TODO: if no permutation but lookup, this is wrong. let zipped = if let (Some(x), Some(y)) = ( permutation_lookup_zs_commitment.as_ref(), permutation_challenges.as_ref(), @@ -299,13 +300,14 @@ where let alphas = challenger.get_n_challenges(config.num_challenges); let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( - &stark, - &trace_commitment, + stark, + trace_commitment, zipped, lookup_data, public_inputs, alphas, degree_bits, + num_permutation_zs, config, ); let all_quotient_chunks = quotient_polys @@ -346,7 +348,7 @@ where let openings = StarkOpeningSet::new( zeta, g, - &trace_commitment, + trace_commitment, permutation_lookup_zs_commitment.as_ref(), "ient_commitment, ); @@ -395,6 +397,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( public_inputs: &[F], alphas: Vec, degree_bits: usize, + num_permutation_zs: usize, config: &StarkConfig, ) -> Vec> where @@ -459,20 +462,47 @@ where let vars = StarkEvaluationVars { local_values: &get_trace_values_packed(i_start), next_values: &get_trace_values_packed(i_next_start), - public_inputs: &public_inputs, + public_inputs, }; let permutation_check_data = permutation_zs_commitment_challenges.as_ref().map( |(permutation_zs_commitment, permutation_challenge_sets)| PermutationCheckVars { - local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step), - next_zs: permutation_zs_commitment.get_lde_values_packed(i_next_start, step), + local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step) + [..num_permutation_zs] + .to_vec(), + next_zs: permutation_zs_commitment.get_lde_values_packed(i_next_start, step) + [..num_permutation_zs] + .to_vec(), permutation_challenge_sets: permutation_challenge_sets.to_vec(), }, ); + let lookup_check_data = lookup_data + .zs_beta_gammas + .iter() + .enumerate() + .map( + |(i, (_, beta, gamma, columns))| CTLCheckVars:: { + local_z: permutation_zs_commitment_challenges + .unwrap() + .0 + .get_lde_values_packed(i_start, step)[num_permutation_zs + i], + next_z: permutation_zs_commitment_challenges + .unwrap() + .0 + .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], + challenges: PermutationChallenge { + beta: *beta, + gamma: *gamma, + }, + columns: columns.to_vec(), + }, + ) + .collect::>(); eval_vanishing_poly::( stark, config, vars, permutation_check_data, + &lookup_check_data, &mut consumer, ); let mut constraints_evals = consumer.accumulators(); diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index 0a3f4514..7d43feb3 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -6,6 +6,7 @@ use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookups::{eval_cross_table_lookup_checks, CTLCheckVars}; use crate::permutation::{ eval_permutation_checks, eval_permutation_checks_recursively, PermutationCheckDataTarget, PermutationCheckVars, @@ -18,6 +19,7 @@ pub(crate) fn eval_vanishing_poly, permutation_data: Option>, + lookup_data: &[CTLCheckVars], consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -36,6 +38,7 @@ pub(crate) fn eval_vanishing_poly(vars, lookup_data, consumer); } pub(crate) fn eval_vanishing_poly_recursively( diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index d5344a44..9ac05d96 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -99,6 +99,7 @@ where config, vars, permutation_data, + &[/*TODO*/], &mut consumer, ); let vanishing_polys_zeta = consumer.accumulators(); From 99b6ac4ff23ca99076b2557649c1ec7c9c3c3f0a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 6 May 2022 16:59:25 +0200 Subject: [PATCH 09/33] Minor --- starky2/src/cross_table_lookups.rs | 6 +++--- starky2/src/prover.rs | 1 + starky2/src/verifier.rs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/starky2/src/cross_table_lookups.rs b/starky2/src/cross_table_lookups.rs index 5ad5680e..69d76811 100644 --- a/starky2/src/cross_table_lookups.rs +++ b/starky2/src/cross_table_lookups.rs @@ -64,13 +64,13 @@ pub fn cross_table_lookup_zs, const D: let gamma = challenger.get_challenge(); let z_looking = partial_products( &trace_poly_values[*looking_table as usize], - &looking_columns, + looking_columns, beta, gamma, ); let z_looked = partial_products( &trace_poly_values[*looked_table as usize], - &looked_columns, + looked_columns, beta, gamma, ); @@ -103,7 +103,7 @@ fn partial_products( let mut res = Vec::new(); for i in 0..trace[0].len() { partial_prod *= - gamma + reduce_with_powers(columns.iter().map(|&j| &trace[i].values[j]), beta); + gamma + reduce_with_powers(columns.iter().map(|&j| &trace[j].values[i]), beta); res.push(partial_prod); } res.into() diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 7413dcba..74c036af 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -345,6 +345,7 @@ where "Opening point is in the subgroup." ); + // TODO: Add openings of lookup Z polynomials. let openings = StarkOpeningSet::new( zeta, g, diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 9ac05d96..05718ee0 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -68,8 +68,8 @@ where quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { - local_values: &local_values, - next_values: &next_values, + local_values, + next_values, public_inputs: &public_inputs .into_iter() .map(F::Extension::from_basefield) From 3005f7bf7baab9e188a5407d0fdf37181b439f0c Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 6 May 2022 17:22:30 +0200 Subject: [PATCH 10/33] Cleaning --- starky2/src/all_starks.rs | 86 ++++++++++++++++++++ starky2/src/cross_table_lookups.rs | 28 ++++++- starky2/src/lib.rs | 1 + starky2/src/prover.rs | 124 +++-------------------------- 4 files changed, 124 insertions(+), 115 deletions(-) create mode 100644 starky2/src/all_starks.rs diff --git a/starky2/src/all_starks.rs b/starky2/src/all_starks.rs new file mode 100644 index 00000000..b8514568 --- /dev/null +++ b/starky2/src/all_starks.rs @@ -0,0 +1,86 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::stark::Stark; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +pub struct AllStarks, const D: usize> { + pub cpu: CpuStark, + pub keccak: KeccakStark, +} + +pub struct CpuStark { + f: PhantomData, +} + +pub struct KeccakStark { + f: PhantomData, +} + +#[derive(Copy, Clone)] +pub enum Table { + Cpu = 0, + Keccak = 1, +} + +impl, const D: usize> Stark for CpuStark { + const COLUMNS: usize = 0; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + _vars: StarkEvaluationVars, + _yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + todo!() + } + + fn eval_ext_recursively( + &self, + _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _vars: StarkEvaluationTargets, + _yield_constr: &mut RecursiveConstraintConsumer, + ) { + todo!() + } + + fn constraint_degree(&self) -> usize { + todo!() + } +} + +impl, const D: usize> Stark for KeccakStark { + const COLUMNS: usize = 0; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + _vars: StarkEvaluationVars, + _yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + todo!() + } + + fn eval_ext_recursively( + &self, + _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _vars: StarkEvaluationTargets, + _yield_constr: &mut RecursiveConstraintConsumer, + ) { + todo!() + } + + fn constraint_degree(&self) -> usize { + todo!() + } +} diff --git a/starky2/src/cross_table_lookups.rs b/starky2/src/cross_table_lookups.rs index 69d76811..090ab15c 100644 --- a/starky2/src/cross_table_lookups.rs +++ b/starky2/src/cross_table_lookups.rs @@ -8,13 +8,37 @@ use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; use plonky2::util::reducing::ReducingFactor; +use crate::all_starks::Table; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::permutation::PermutationChallenge; -use crate::prover::CrossTableLookup; use crate::stark::Stark; use crate::vars::StarkEvaluationVars; +pub struct CrossTableLookup { + pub looking_table: Table, + pub looking_columns: Vec, + pub looked_table: usize, + pub looked_columns: Vec, +} + +impl CrossTableLookup { + pub fn new( + looking_table: Table, + looking_columns: Vec, + looked_table: usize, + looked_columns: Vec, + ) -> Self { + assert_eq!(looking_columns.len(), looked_columns.len()); + Self { + looking_table, + looking_columns, + looked_table, + looked_columns, + } + } +} + /// Lookup data for one table. #[derive(Clone)] pub struct LookupData { @@ -45,7 +69,7 @@ impl LookupData { pub fn cross_table_lookup_zs, const D: usize>( config: &StarkConfig, trace_poly_values: &[Vec>], - cross_table_lookups: &[CrossTableLookup], + cross_table_lookups: &[CrossTableLookup], challenger: &mut Challenger, ) -> Vec> { cross_table_lookups.iter().fold( diff --git a/starky2/src/lib.rs b/starky2/src/lib.rs index a545b2c0..ab452240 100644 --- a/starky2/src/lib.rs +++ b/starky2/src/lib.rs @@ -8,6 +8,7 @@ pub mod constraint_consumer; pub mod cross_table_lookups; mod get_challenges; // pub mod mock_stark; +pub mod all_starks; pub mod permutation; pub mod proof; pub mod prover; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 74c036af..afc0bfb2 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -1,9 +1,8 @@ use std::iter::once; -use std::marker::PhantomData; use anyhow::{ensure, Result}; use itertools::Itertools; -use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::extension_field::Extendable; use plonky2::field::field_types::Field; use plonky2::field::packable::Packable; use plonky2::field::packed_field::PackedField; @@ -19,9 +18,12 @@ use plonky2::util::transpose; use plonky2_util::{log2_ceil, log2_strict}; use rayon::prelude::*; +use crate::all_starks::{AllStarks, Table}; use crate::config::StarkConfig; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookups::{cross_table_lookup_zs, CTLCheckVars, LookupData}; +use crate::constraint_consumer::ConstraintConsumer; +use crate::cross_table_lookups::{ + cross_table_lookup_zs, CTLCheckVars, CrossTableLookup, LookupData, +}; use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, }; @@ -29,117 +31,13 @@ use crate::permutation::{PermutationChallenge, PermutationCheckVars}; use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; - -#[derive(Copy, Clone)] -pub enum Table { - Cpu = 0, - Keccak = 1, -} - -struct CpuStark { - f: PhantomData, -} - -impl, const D: usize> Stark for CpuStark { - const COLUMNS: usize = 0; - const PUBLIC_INPUTS: usize = 0; - - fn eval_packed_generic( - &self, - _vars: StarkEvaluationVars, - _yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - todo!() - } - - fn eval_ext_recursively( - &self, - _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - _vars: StarkEvaluationTargets, - _yield_constr: &mut RecursiveConstraintConsumer, - ) { - todo!() - } - - fn constraint_degree(&self) -> usize { - todo!() - } -} - -struct KeccakStark { - f: PhantomData, -} - -impl, const D: usize> Stark for KeccakStark { - const COLUMNS: usize = 0; - const PUBLIC_INPUTS: usize = 0; - - fn eval_packed_generic( - &self, - _vars: StarkEvaluationVars, - _yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - todo!() - } - - fn eval_ext_recursively( - &self, - _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - _vars: StarkEvaluationTargets, - _yield_constr: &mut RecursiveConstraintConsumer, - ) { - todo!() - } - - fn constraint_degree(&self) -> usize { - todo!() - } -} - -pub struct AllStarks, const D: usize> { - cpu: CpuStark, - keccak: KeccakStark, -} - -pub struct CrossTableLookup { - pub looking_table: Table, - pub looking_columns: Vec, - pub looked_table: usize, - pub looked_columns: Vec, - pub default: F, -} - -impl CrossTableLookup { - pub fn new( - looking_table: Table, - looking_columns: Vec, - looked_table: usize, - looked_columns: Vec, - default: F, - ) -> Self { - assert_eq!(looking_columns.len(), looked_columns.len()); - Self { - looking_table, - looking_columns, - looked_table, - looked_columns, - default, - } - } -} +use crate::vars::StarkEvaluationVars; pub fn prove( all_starks: AllStarks, config: &StarkConfig, trace_poly_values: Vec>>, - cross_table_lookups: Vec>, + cross_table_lookups: Vec, public_inputs: Vec>, timing: &mut TimingTree, ) -> Result>> @@ -199,7 +97,7 @@ where &mut challenger, ); - let cpu_proof = do_rest( + let cpu_proof = prove_single_table( &all_starks.cpu, config, &trace_poly_values[Table::Cpu as usize], @@ -209,7 +107,7 @@ where &mut challenger, timing, )?; - let keccak_proof = do_rest( + let keccak_proof = prove_single_table( &all_starks.keccak, config, &trace_poly_values[Table::Keccak as usize], @@ -223,7 +121,7 @@ where Ok(vec![cpu_proof, keccak_proof]) } -fn do_rest( +fn prove_single_table( stark: &S, config: &StarkConfig, trace_poly_values: &[PolynomialValues], From 44b9237c08735f599b504c86c527e548bccdf9ae Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 6 May 2022 17:35:25 +0200 Subject: [PATCH 11/33] Rename --- starky2/src/{all_starks.rs => all_stark.rs} | 2 +- starky2/src/cross_table_lookups.rs | 2 +- starky2/src/lib.rs | 2 +- starky2/src/prover.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename starky2/src/{all_starks.rs => all_stark.rs} (97%) diff --git a/starky2/src/all_starks.rs b/starky2/src/all_stark.rs similarity index 97% rename from starky2/src/all_starks.rs rename to starky2/src/all_stark.rs index b8514568..c8b7fa76 100644 --- a/starky2/src/all_starks.rs +++ b/starky2/src/all_stark.rs @@ -8,7 +8,7 @@ use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; -pub struct AllStarks, const D: usize> { +pub struct AllStark, const D: usize> { pub cpu: CpuStark, pub keccak: KeccakStark, } diff --git a/starky2/src/cross_table_lookups.rs b/starky2/src/cross_table_lookups.rs index 090ab15c..d49cf1b9 100644 --- a/starky2/src/cross_table_lookups.rs +++ b/starky2/src/cross_table_lookups.rs @@ -8,7 +8,7 @@ use plonky2::plonk::config::GenericConfig; use plonky2::plonk::plonk_common::reduce_with_powers; use plonky2::util::reducing::ReducingFactor; -use crate::all_starks::Table; +use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::permutation::PermutationChallenge; diff --git a/starky2/src/lib.rs b/starky2/src/lib.rs index ab452240..52d7a6c1 100644 --- a/starky2/src/lib.rs +++ b/starky2/src/lib.rs @@ -8,7 +8,7 @@ pub mod constraint_consumer; pub mod cross_table_lookups; mod get_challenges; // pub mod mock_stark; -pub mod all_starks; +pub mod all_stark; pub mod permutation; pub mod proof; pub mod prover; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index afc0bfb2..4b2ad23c 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -18,7 +18,7 @@ use plonky2::util::transpose; use plonky2_util::{log2_ceil, log2_strict}; use rayon::prelude::*; -use crate::all_starks::{AllStarks, Table}; +use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::cross_table_lookups::{ @@ -34,7 +34,7 @@ use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::StarkEvaluationVars; pub fn prove( - all_starks: AllStarks, + all_starks: AllStark, config: &StarkConfig, trace_poly_values: Vec>>, cross_table_lookups: Vec, From 79dc28c7e14409f5e3178f96f49afdb7b9044995 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 6 May 2022 17:36:32 +0200 Subject: [PATCH 12/33] Rename --- starky2/src/{cross_table_lookups.rs => cross_table_lookup.rs} | 0 starky2/src/lib.rs | 2 +- starky2/src/prover.rs | 2 +- starky2/src/vanishing_poly.rs | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename starky2/src/{cross_table_lookups.rs => cross_table_lookup.rs} (100%) diff --git a/starky2/src/cross_table_lookups.rs b/starky2/src/cross_table_lookup.rs similarity index 100% rename from starky2/src/cross_table_lookups.rs rename to starky2/src/cross_table_lookup.rs diff --git a/starky2/src/lib.rs b/starky2/src/lib.rs index 52d7a6c1..aa14ac73 100644 --- a/starky2/src/lib.rs +++ b/starky2/src/lib.rs @@ -5,7 +5,7 @@ pub mod config; pub mod constraint_consumer; -pub mod cross_table_lookups; +pub mod cross_table_lookup; mod get_challenges; // pub mod mock_stark; pub mod all_stark; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 4b2ad23c..02de8560 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -21,7 +21,7 @@ use rayon::prelude::*; use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cross_table_lookups::{ +use crate::cross_table_lookup::{ cross_table_lookup_zs, CTLCheckVars, CrossTableLookup, LookupData, }; use crate::permutation::{ diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index 7d43feb3..baa9aeaa 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -6,7 +6,7 @@ use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookups::{eval_cross_table_lookup_checks, CTLCheckVars}; +use crate::cross_table_lookup::{eval_cross_table_lookup_checks, CTLCheckVars}; use crate::permutation::{ eval_permutation_checks, eval_permutation_checks_recursively, PermutationCheckDataTarget, PermutationCheckVars, From feb00bd197d35da4f83ca15ef3a05d25f8dfd35f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 10 May 2022 07:45:42 +0200 Subject: [PATCH 13/33] Fix `looked_table: Table` --- starky2/src/cross_table_lookup.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index d49cf1b9..50ee8817 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -18,7 +18,7 @@ use crate::vars::StarkEvaluationVars; pub struct CrossTableLookup { pub looking_table: Table, pub looking_columns: Vec, - pub looked_table: usize, + pub looked_table: Table, pub looked_columns: Vec, } @@ -26,7 +26,7 @@ impl CrossTableLookup { pub fn new( looking_table: Table, looking_columns: Vec, - looked_table: usize, + looked_table: Table, looked_columns: Vec, ) -> Self { assert_eq!(looking_columns.len(), looked_columns.len()); From 9de8c2c73b1e31146445809e3ac73d66bb2ad400 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 10 May 2022 15:08:08 +0200 Subject: [PATCH 14/33] Open lookup polys --- starky2/src/cross_table_lookup.rs | 4 ++++ starky2/src/prover.rs | 10 ++++------ starky2/src/stark.rs | 31 ++++++++++++++++++++----------- starky2/src/verifier.rs | 2 ++ 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 50ee8817..3d689b50 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -54,6 +54,10 @@ impl Default for LookupData { } impl LookupData { + pub fn len(&self) -> usize { + self.zs_beta_gammas.len() + } + pub fn is_empty(&self) -> bool { self.zs_beta_gammas.is_empty() } diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 02de8560..4ab821e4 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -159,14 +159,12 @@ where }); let num_permutation_zs = permutation_zs.as_ref().map(|v| v.len()).unwrap_or(0); - let z_polys = match (permutation_zs, lookup_data.is_empty()) { - (None, true) => lookup_data.z_polys(), - (None, false) => vec![], - (Some(mut permutation_zs), true) => { + let z_polys = match permutation_zs { + None => lookup_data.z_polys(), + Some(mut permutation_zs) => { permutation_zs.extend(lookup_data.z_polys()); permutation_zs } - (Some(permutation_zs), false) => permutation_zs, }; let permutation_lookup_zs_commitment = (!z_polys.is_empty()).then(|| { @@ -262,7 +260,7 @@ where timing, "compute openings proof", PolynomialBatch::prove_openings( - &stark.fri_instance(zeta, g, config), + &stark.fri_instance(zeta, g, degree_bits, lookup_data.len(), config), &initial_merkle_trees, challenger, &fri_params, diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs index c5ec5405..35abb8ad 100644 --- a/starky2/src/stark.rs +++ b/starky2/src/stark.rs @@ -1,4 +1,5 @@ use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::field_types::Field; use plonky2::field::packed_field::PackedField; use plonky2::fri::structure::{ FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo, @@ -78,6 +79,8 @@ pub trait Stark, const D: usize>: Sync { &self, zeta: F::Extension, g: F, + degree_bits: usize, + num_ctl_zs: usize, config: &StarkConfig, ) -> FriInstanceInfo { let no_blinding_oracle = FriOracleInfo { blinding: false }; @@ -86,14 +89,16 @@ pub trait Stark, const D: usize>: Sync { let trace_info = FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS); - let permutation_zs_info = if self.uses_permutation_args() { - FriPolynomialInfo::from_range( - oracle_indices.next().unwrap(), - 0..self.num_permutation_batches(config), - ) - } else { - vec![] - }; + let num_permutation_batches = self.num_permutation_batches(config); + let permutation_lookup_index = oracle_indices.next().unwrap(); + let permutation_lookup_zs_info = FriPolynomialInfo::from_range( + permutation_lookup_index, + 0..num_permutation_batches + num_ctl_zs, + ); + let lookup_zs_info = FriPolynomialInfo::from_range( + permutation_lookup_index, + num_permutation_batches..num_permutation_batches + num_ctl_zs, + ); let quotient_info = FriPolynomialInfo::from_range( oracle_indices.next().unwrap(), @@ -104,18 +109,22 @@ pub trait Stark, const D: usize>: Sync { point: zeta, polynomials: [ trace_info.clone(), - permutation_zs_info.clone(), + permutation_lookup_zs_info.clone(), quotient_info, ] .concat(), }; let zeta_right_batch = FriBatchInfo { point: zeta.scalar_mul(g), - polynomials: [trace_info, permutation_zs_info].concat(), + polynomials: [trace_info, permutation_lookup_zs_info].concat(), + }; + let lookup_batch = FriBatchInfo { + point: F::Extension::primitive_root_of_unity(degree_bits).inverse(), + polynomials: lookup_zs_info, }; FriInstanceInfo { oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()], - batches: vec![zeta_batch, zeta_right_batch], + batches: vec![zeta_batch, zeta_right_batch, lookup_batch], } } diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 05718ee0..886d7512 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -131,6 +131,8 @@ where &stark.fri_instance( challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), + degree_bits, + todo!(), config, ), &proof.openings.to_fri_openings(), From 5b146d558f9b1d1d77da66cb0c160fdeee9136d9 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 10 May 2022 15:21:09 +0200 Subject: [PATCH 15/33] Change StarkOpeningSet --- starky2/src/proof.rs | 39 ++++++++++++++++++++++++++++++--------- starky2/src/prover.rs | 2 ++ starky2/src/verifier.rs | 21 +++++++++++++++------ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index afefdd96..3fa51a51 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -1,5 +1,6 @@ use itertools::Itertools; use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::field_types::Field; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{ CompressedFriProof, FriChallenges, FriChallengesTarget, FriProof, FriProofTarget, @@ -126,8 +127,9 @@ pub(crate) struct StarkProofChallengesTarget { pub struct StarkOpeningSet, const D: usize> { pub local_values: Vec, pub next_values: Vec, - pub permutation_zs: Option>, - pub permutation_zs_right: Option>, + pub permutation_lookup_zs: Option>, + pub permutation_lookup_zs_right: Option>, + pub lookup_zs_last: Vec, pub quotient_polys: Vec, } @@ -136,8 +138,10 @@ impl, const D: usize> StarkOpeningSet { zeta: F::Extension, g: F, trace_commitment: &PolynomialBatch, - permutation_zs_commitment: Option<&PolynomialBatch>, + permutation_lookup_zs_commitment: Option<&PolynomialBatch>, quotient_commitment: &PolynomialBatch, + degree_bits: usize, + num_permutation_zs: usize, ) -> Self { let eval_commitment = |z: F::Extension, c: &PolynomialBatch| { c.polynomials @@ -149,8 +153,19 @@ impl, const D: usize> StarkOpeningSet { Self { local_values: eval_commitment(zeta, trace_commitment), next_values: eval_commitment(zeta_right, trace_commitment), - permutation_zs: permutation_zs_commitment.map(|c| eval_commitment(zeta, c)), - permutation_zs_right: permutation_zs_commitment.map(|c| eval_commitment(zeta_right, c)), + permutation_lookup_zs: permutation_lookup_zs_commitment + .map(|c| eval_commitment(zeta, c)), + permutation_lookup_zs_right: permutation_lookup_zs_commitment + .map(|c| eval_commitment(zeta_right, c)), + lookup_zs_last: permutation_lookup_zs_commitment + .map(|c| { + eval_commitment( + F::Extension::primitive_root_of_unity(degree_bits).inverse(), + c, + )[num_permutation_zs..] + .to_vec() + }) + .unwrap_or_default(), quotient_polys: eval_commitment(zeta, quotient_commitment), } } @@ -160,7 +175,7 @@ impl, const D: usize> StarkOpeningSet { values: self .local_values .iter() - .chain(self.permutation_zs.iter().flatten()) + .chain(self.permutation_lookup_zs.iter().flatten()) .chain(&self.quotient_polys) .copied() .collect_vec(), @@ -169,13 +184,19 @@ impl, const D: usize> StarkOpeningSet { values: self .next_values .iter() - .chain(self.permutation_zs_right.iter().flatten()) + .chain(self.permutation_lookup_zs_right.iter().flatten()) .copied() .collect_vec(), }; - FriOpenings { - batches: vec![zeta_batch, zeta_right_batch], + let mut batches = vec![zeta_batch, zeta_right_batch]; + + if !self.lookup_zs_last.is_empty() { + batches.push(FriOpeningBatch { + values: self.lookup_zs_last.clone(), + }); } + + FriOpenings { batches } } } diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 4ab821e4..aba1f830 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -248,6 +248,8 @@ where trace_commitment, permutation_lookup_zs_commitment.as_ref(), "ient_commitment, + degree_bits, + stark.num_permutation_batches(config), ); challenger.observe_openings(&openings.to_fri_openings()); diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 886d7512..9375c9f4 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -63,8 +63,9 @@ where let StarkOpeningSet { local_values, next_values, - permutation_zs, - permutation_zs_right, + permutation_lookup_zs, + permutation_lookup_zs_right, + lookup_zs_last, quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { @@ -90,8 +91,8 @@ where l_last, ); let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars { - local_zs: permutation_zs.as_ref().unwrap().clone(), - next_zs: permutation_zs_right.as_ref().unwrap().clone(), + local_zs: permutation_lookup_zs.as_ref().unwrap().clone(), + next_zs: permutation_lookup_zs_right.as_ref().unwrap().clone(), permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), }); eval_vanishing_poly::( @@ -171,8 +172,16 @@ fn check_permutation_options< ) -> Result<()> { let options_is_some = [ proof_with_pis.proof.permutation_zs_cap.is_some(), - proof_with_pis.proof.openings.permutation_zs.is_some(), - proof_with_pis.proof.openings.permutation_zs_right.is_some(), + proof_with_pis + .proof + .openings + .permutation_lookup_zs + .is_some(), + proof_with_pis + .proof + .openings + .permutation_lookup_zs_right + .is_some(), challenges.permutation_challenge_sets.is_some(), ]; ensure!( From d0fb76c8db0f64650fc446e779ad64bc4adb8d06 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 11 May 2022 14:35:33 +0200 Subject: [PATCH 16/33] Progress on verifier --- starky2/src/all_stark.rs | 6 ++- starky2/src/cross_table_lookup.rs | 54 +++++++++++-------------- starky2/src/get_challenges.rs | 53 +++++++++++++++++++++---- starky2/src/proof.rs | 20 +++++++++- starky2/src/prover.rs | 50 +++++++++++------------ starky2/src/verifier.rs | 66 +++++++++++++++++++++++-------- 6 files changed, 167 insertions(+), 82 deletions(-) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index c8b7fa76..67445856 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -5,12 +5,14 @@ use plonky2::field::packed_field::PackedField; use plonky2::hash::hash_types::RichField; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cross_table_lookup::CrossTableLookup; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub struct AllStark, const D: usize> { - pub cpu: CpuStark, - pub keccak: KeccakStark, + pub cpu_stark: CpuStark, + pub keccak_stark: KeccakStark, + pub cross_table_lookups: Vec, } pub struct CpuStark { diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 3d689b50..9e485d07 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -42,31 +42,30 @@ impl CrossTableLookup { /// Lookup data for one table. #[derive(Clone)] pub struct LookupData { - pub zs_beta_gammas: Vec<(PolynomialValues, F, F, Vec)>, -} - -impl Default for LookupData { - fn default() -> Self { - Self { - zs_beta_gammas: Vec::new(), - } - } + pub beta: F, + pub gamma: F, + pub zs_columns: Vec<(PolynomialValues, Vec)>, } impl LookupData { + pub fn new(beta: F, gamma: F) -> Self { + Self { + beta, + gamma, + zs_columns: vec![], + } + } + pub fn len(&self) -> usize { - self.zs_beta_gammas.len() + self.zs_columns.len() } pub fn is_empty(&self) -> bool { - self.zs_beta_gammas.is_empty() + self.zs_columns.is_empty() } pub fn z_polys(&self) -> Vec> { - self.zs_beta_gammas - .iter() - .map(|(p, _, _, _)| p.clone()) - .collect() + self.zs_columns.iter().map(|(p, _)| p.clone()).collect() } } @@ -76,20 +75,19 @@ pub fn cross_table_lookup_zs, const D: cross_table_lookups: &[CrossTableLookup], challenger: &mut Challenger, ) -> Vec> { + let beta = challenger.get_challenge(); + let gamma = challenger.get_challenge(); cross_table_lookups.iter().fold( - vec![LookupData::default(); trace_poly_values.len()], + vec![LookupData::new(beta, gamma); trace_poly_values.len()], |mut acc, cross_table_lookup| { let CrossTableLookup { looking_table, looking_columns, looked_table, looked_columns, - .. } = cross_table_lookup; for _ in 0..config.num_challenges { - let beta = challenger.get_challenge(); - let gamma = challenger.get_challenge(); let z_looking = partial_products( &trace_poly_values[*looking_table as usize], looking_columns, @@ -103,18 +101,12 @@ pub fn cross_table_lookup_zs, const D: gamma, ); - acc[*looking_table as usize].zs_beta_gammas.push(( - z_looking, - beta, - gamma, - looking_columns.clone(), - )); - acc[*looked_table as usize].zs_beta_gammas.push(( - z_looked, - beta, - gamma, - looked_columns.clone(), - )); + acc[*looking_table as usize] + .zs_columns + .push((z_looking, looking_columns.clone())); + acc[*looked_table as usize] + .zs_columns + .push((z_looked, looked_columns.clone())); } acc }, diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index ceafa297..dc69d857 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -9,14 +9,16 @@ use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; +use crate::all_stark::AllStark; use crate::config::StarkConfig; use crate::permutation::{ - get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, + get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, PermutationChallenge, }; use crate::proof::*; use crate::stark::Stark; fn get_challenges( + challenger: &mut Challenger, stark: &S, trace_cap: &MerkleCap, permutation_zs_cap: Option<&MerkleCap>, @@ -35,13 +37,11 @@ where { let num_challenges = config.num_challenges; - let mut challenger = Challenger::::new(); - challenger.observe_cap(trace_cap); let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { let tmp = get_n_permutation_challenge_sets( - &mut challenger, + challenger, num_challenges, stark.permutation_batch_size(), ); @@ -70,6 +70,42 @@ where } } +impl, C: GenericConfig, const D: usize> AllProof { + /// Computes all Fiat-Shamir challenges used in the STARK proof. + pub(crate) fn get_challenges( + &self, + all_stark: &AllStark, + config: &StarkConfig, + ) -> AllProofChallenges { + let num_challenges = config.num_challenges; + + let mut challenger = Challenger::::new(); + + for proof in self.proofs() { + challenger.observe_cap(&proof.proof.trace_cap); + } + + let ctl_challenges = PermutationChallenge { + beta: challenger.get_challenge(), + gamma: challenger.get_challenge(), + }; + + AllProofChallenges { + cpu_challenges: self.cpu_proof.get_challenges( + &mut challenger, + &all_stark.cpu_stark, + config, + ), + keccak_challenges: self.keccak_proof.get_challenges( + &mut challenger, + &all_stark.keccak_stark, + config, + ), + ctl_challenges, + } + } +} + impl StarkProofWithPublicInputs where F: RichField + Extendable, @@ -81,9 +117,9 @@ where &self, stark: &S, config: &StarkConfig, - degree_bits: usize, ) -> Vec { - self.get_challenges(stark, config, degree_bits) + let mut challenger = Challenger::new(); + self.get_challenges(&mut challenger, stark, config) .fri_challenges .fri_query_indices } @@ -91,10 +127,12 @@ where /// Computes all Fiat-Shamir challenges used in the STARK proof. pub(crate) fn get_challenges>( &self, + challenger: &mut Challenger, stark: &S, config: &StarkConfig, - degree_bits: usize, ) -> StarkProofChallenges { + let degree_bits = self.proof.recover_degree_bits(config); + let StarkProof { trace_cap, permutation_zs_cap, @@ -110,6 +148,7 @@ where } = &self.proof; get_challenges::( + challenger, stark, trace_cap, permutation_zs_cap.as_ref(), diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index 3fa51a51..89c116df 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -16,7 +16,25 @@ use plonky2::plonk::config::GenericConfig; use rayon::prelude::*; use crate::config::StarkConfig; -use crate::permutation::PermutationChallengeSet; +use crate::permutation::{PermutationChallenge, PermutationChallengeSet}; + +#[derive(Debug, Clone)] +pub struct AllProof, C: GenericConfig, const D: usize> { + pub cpu_proof: StarkProofWithPublicInputs, + pub keccak_proof: StarkProofWithPublicInputs, +} + +impl, C: GenericConfig, const D: usize> AllProof { + pub fn proofs(&self) -> [&StarkProofWithPublicInputs; 2] { + [&self.cpu_proof, &self.keccak_proof] + } +} + +pub(crate) struct AllProofChallenges, const D: usize> { + pub cpu_challenges: StarkProofChallenges, + pub keccak_challenges: StarkProofChallenges, + pub ctl_challenges: PermutationChallenge, +} #[derive(Debug, Clone)] pub struct StarkProof, C: GenericConfig, const D: usize> { diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index aba1f830..0f5013a4 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -28,19 +28,18 @@ use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, }; use crate::permutation::{PermutationChallenge, PermutationCheckVars}; -use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; +use crate::proof::{AllProof, StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::StarkEvaluationVars; pub fn prove( - all_starks: AllStark, + all_stark: AllStark, config: &StarkConfig, trace_poly_values: Vec>>, - cross_table_lookups: Vec, public_inputs: Vec>, timing: &mut TimingTree, -) -> Result>> +) -> Result> where F: RichField + Extendable, C: GenericConfig, @@ -93,12 +92,12 @@ where let lookup_zs = cross_table_lookup_zs::( config, &trace_poly_values, - &cross_table_lookups, + &all_stark.cross_table_lookups, &mut challenger, ); let cpu_proof = prove_single_table( - &all_starks.cpu, + &all_stark.cpu_stark, config, &trace_poly_values[Table::Cpu as usize], &trace_commitments[Table::Cpu as usize], @@ -108,7 +107,7 @@ where timing, )?; let keccak_proof = prove_single_table( - &all_starks.keccak, + &all_stark.keccak_stark, config, &trace_poly_values[Table::Keccak as usize], &trace_commitments[Table::Keccak as usize], @@ -118,7 +117,10 @@ where timing, )?; - Ok(vec![cpu_proof, keccak_proof]) + Ok(AllProof { + cpu_proof, + keccak_proof, + }) } fn prove_single_table( @@ -375,26 +377,24 @@ where }, ); let lookup_check_data = lookup_data - .zs_beta_gammas + .zs_columns .iter() .enumerate() - .map( - |(i, (_, beta, gamma, columns))| CTLCheckVars:: { - local_z: permutation_zs_commitment_challenges - .unwrap() - .0 - .get_lde_values_packed(i_start, step)[num_permutation_zs + i], - next_z: permutation_zs_commitment_challenges - .unwrap() - .0 - .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], - challenges: PermutationChallenge { - beta: *beta, - gamma: *gamma, - }, - columns: columns.to_vec(), + .map(|(i, (_, columns))| CTLCheckVars:: { + local_z: permutation_zs_commitment_challenges + .unwrap() + .0 + .get_lde_values_packed(i_start, step)[num_permutation_zs + i], + next_z: permutation_zs_commitment_challenges + .unwrap() + .0 + .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], + challenges: PermutationChallenge { + beta: lookup_data.beta, + gamma: lookup_data.gamma, }, - ) + columns: columns.to_vec(), + }) .collect::>(); eval_vanishing_poly::( stark, diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 9375c9f4..b5f144fb 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -9,35 +9,69 @@ use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::{GenericConfig, Hasher}; use plonky2::plonk::plonk_common::reduce_with_powers; +use crate::all_stark::{AllStark, KeccakStark}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::permutation::PermutationCheckVars; -use crate::proof::{StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs}; +use crate::proof::{ + AllProof, AllProofChallenges, StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs, +}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::StarkEvaluationVars; -pub fn verify_stark_proof< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - stark: S, - proof_with_pis: StarkProofWithPublicInputs, +pub fn verify_proof, C: GenericConfig, const D: usize>( + all_stark: AllStark, + all_proof: AllProof, config: &StarkConfig, ) -> Result<()> where - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, + [(); KeccakStark::::COLUMNS]:, [(); C::Hasher::HASH_SIZE]:, { - ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); - let degree_bits = proof_with_pis.proof.recover_degree_bits(config); - let challenges = proof_with_pis.get_challenges(&stark, config, degree_bits); - verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) + let AllProofChallenges { + cpu_challenges, + keccak_challenges, + ctl_challenges, + } = all_proof.get_challenges(&all_stark, config); + + // Verify CTL + + verify_stark_proof_with_challenges( + all_stark.cpu_stark, + all_proof.cpu_proof, + cpu_challenges, + config, + )?; + verify_stark_proof_with_challenges( + all_stark.keccak_stark, + all_proof.keccak_proof, + keccak_challenges, + config, + ) } +// pub fn verify_stark_proof< +// F: RichField + Extendable, +// C: GenericConfig, +// S: Stark, +// const D: usize, +// >( +// stark: S, +// proof_with_pis: StarkProofWithPublicInputs, +// config: &StarkConfig, +// ) -> Result<()> +// where +// [(); S::COLUMNS]:, +// [(); S::PUBLIC_INPUTS]:, +// [(); C::Hasher::HASH_SIZE]:, +// { +// ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); +// let degree_bits = proof_with_pis.proof.recover_degree_bits(config); +// let challenges = proof_with_pis.get_challenges(&stark, config, degree_bits); +// verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) +// } + pub(crate) fn verify_stark_proof_with_challenges< F: RichField + Extendable, C: GenericConfig, @@ -47,7 +81,6 @@ pub(crate) fn verify_stark_proof_with_challenges< stark: S, proof_with_pis: StarkProofWithPublicInputs, challenges: StarkProofChallenges, - degree_bits: usize, config: &StarkConfig, ) -> Result<()> where @@ -77,6 +110,7 @@ where .collect::>(), }; + let degree_bits = proof.recover_degree_bits(config); 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(); From b9e921f6400578bfe5822a7a74e63fef6a1d9924 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 11 May 2022 16:09:12 +0200 Subject: [PATCH 17/33] CTL verification --- starky2/src/cross_table_lookup.rs | 39 +++++++++++++++++++++++++++++++ starky2/src/get_challenges.rs | 2 -- starky2/src/proof.rs | 22 +++++++++++------ starky2/src/prover.rs | 4 +--- starky2/src/verifier.rs | 19 ++++++++++----- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 9e485d07..63f41c6b 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -1,3 +1,4 @@ +use anyhow::{ensure, Result}; use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::field::packed_field::PackedField; @@ -12,6 +13,7 @@ use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::permutation::PermutationChallenge; +use crate::proof::StarkProofWithPublicInputs; use crate::stark::Stark; use crate::vars::StarkEvaluationVars; @@ -170,3 +172,40 @@ pub(crate) fn eval_cross_table_lookup_checks, + C: GenericConfig, + const D: usize, +>( + cross_table_lookups: Vec, + proofs: &[&StarkProofWithPublicInputs], + challenges: PermutationChallenge, + config: &StarkConfig, +) -> Result<()> { + let degrees_bits = proofs + .iter() + .map(|p| p.proof.recover_degree_bits(config)) + .collect::>(); + let mut lookup_zs_openings = proofs + .iter() + .map(|p| p.proof.openings.lookup_zs_last.iter()) + .collect::>(); + for CrossTableLookup { + looking_table, + looked_table, + .. + } in cross_table_lookups + { + let looking_degree = 1 << degrees_bits[looking_table as usize]; + let looked_degree = 1 << degrees_bits[looked_table as usize]; + let looking_z = *lookup_zs_openings[looking_table as usize].next().unwrap(); + let looked_z = *lookup_zs_openings[looked_table as usize].next().unwrap(); + ensure!( + looking_z == looked_z * challenges.gamma.exp_u64(looking_degree - looked_degree), + "Cross-table lookup verification failed." + ); + } + + Ok(()) +} diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index dc69d857..a6e3506d 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -77,8 +77,6 @@ impl, C: GenericConfig, const D: usize> A all_stark: &AllStark, config: &StarkConfig, ) -> AllProofChallenges { - let num_challenges = config.num_challenges; - let mut challenger = Challenger::::new(); for proof in self.proofs() { diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index 89c116df..a7a31fa4 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -1,6 +1,5 @@ use itertools::Itertools; use plonky2::field::extension_field::{Extendable, FieldExtension}; -use plonky2::field::field_types::Field; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{ CompressedFriProof, FriChallenges, FriChallengesTarget, FriProof, FriProofTarget, @@ -147,7 +146,7 @@ pub struct StarkOpeningSet, const D: usize> { pub next_values: Vec, pub permutation_lookup_zs: Option>, pub permutation_lookup_zs_right: Option>, - pub lookup_zs_last: Vec, + pub lookup_zs_last: Vec, pub quotient_polys: Vec, } @@ -167,6 +166,12 @@ impl, const D: usize> StarkOpeningSet { .map(|p| p.to_extension().eval(z)) .collect::>() }; + let eval_commitment_base = |z: F, c: &PolynomialBatch| { + c.polynomials + .par_iter() + .map(|p| p.eval(z)) + .collect::>() + }; let zeta_right = zeta.scalar_mul(g); Self { local_values: eval_commitment(zeta, trace_commitment), @@ -177,10 +182,8 @@ impl, const D: usize> StarkOpeningSet { .map(|c| eval_commitment(zeta_right, c)), lookup_zs_last: permutation_lookup_zs_commitment .map(|c| { - eval_commitment( - F::Extension::primitive_root_of_unity(degree_bits).inverse(), - c, - )[num_permutation_zs..] + eval_commitment_base(F::primitive_root_of_unity(degree_bits).inverse(), c) + [num_permutation_zs..] .to_vec() }) .unwrap_or_default(), @@ -210,7 +213,12 @@ impl, const D: usize> StarkOpeningSet { if !self.lookup_zs_last.is_empty() { batches.push(FriOpeningBatch { - values: self.lookup_zs_last.clone(), + values: self + .lookup_zs_last + .iter() + .copied() + .map(F::Extension::from_basefield) + .collect(), }); } diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 0f5013a4..ebebd915 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -21,9 +21,7 @@ use rayon::prelude::*; use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cross_table_lookup::{ - cross_table_lookup_zs, CTLCheckVars, CrossTableLookup, LookupData, -}; +use crate::cross_table_lookup::{cross_table_lookup_zs, CTLCheckVars, LookupData}; use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, }; diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index b5f144fb..6e60e601 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -12,6 +12,7 @@ use plonky2::plonk::plonk_common::reduce_with_powers; use crate::all_stark::{AllStark, KeccakStark}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; +use crate::cross_table_lookup::verify_cross_table_lookups; use crate::permutation::PermutationCheckVars; use crate::proof::{ AllProof, AllProofChallenges, StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs, @@ -35,16 +36,22 @@ where ctl_challenges, } = all_proof.get_challenges(&all_stark, config); - // Verify CTL + let AllStark { + cpu_stark, + keccak_stark, + cross_table_lookups, + } = all_stark; - verify_stark_proof_with_challenges( - all_stark.cpu_stark, - all_proof.cpu_proof, - cpu_challenges, + verify_cross_table_lookups( + cross_table_lookups, + &all_proof.proofs(), + ctl_challenges, config, )?; + + verify_stark_proof_with_challenges(cpu_stark, all_proof.cpu_proof, cpu_challenges, config)?; verify_stark_proof_with_challenges( - all_stark.keccak_stark, + keccak_stark, all_proof.keccak_proof, keccak_challenges, config, From 2b8c3de10ec9d31d715a5ec9843ba5cedff2ebdb Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 13:47:55 +0200 Subject: [PATCH 18/33] Finish verifier --- starky2/src/cross_table_lookup.rs | 107 ++++++++++++++++++++++++------ starky2/src/get_challenges.rs | 8 +-- starky2/src/permutation.rs | 2 +- starky2/src/proof.rs | 4 +- starky2/src/prover.rs | 9 +-- starky2/src/verifier.rs | 46 ++++++++----- 6 files changed, 126 insertions(+), 50 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 63f41c6b..6dfec2d2 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -12,11 +12,14 @@ use plonky2::util::reducing::ReducingFactor; use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::permutation::PermutationChallenge; +use crate::permutation::{ + get_permutation_challenge_set, PermutationChallenge, PermutationChallengeSet, +}; use crate::proof::StarkProofWithPublicInputs; use crate::stark::Stark; use crate::vars::StarkEvaluationVars; +#[derive(Clone)] pub struct CrossTableLookup { pub looking_table: Table, pub looking_columns: Vec, @@ -44,16 +47,14 @@ impl CrossTableLookup { /// Lookup data for one table. #[derive(Clone)] pub struct LookupData { - pub beta: F, - pub gamma: F, + pub(crate) challenges: PermutationChallengeSet, pub zs_columns: Vec<(PolynomialValues, Vec)>, } impl LookupData { - pub fn new(beta: F, gamma: F) -> Self { + pub(crate) fn new(challenges: PermutationChallengeSet) -> Self { Self { - beta, - gamma, + challenges, zs_columns: vec![], } } @@ -77,10 +78,9 @@ pub fn cross_table_lookup_zs, const D: cross_table_lookups: &[CrossTableLookup], challenger: &mut Challenger, ) -> Vec> { - let beta = challenger.get_challenge(); - let gamma = challenger.get_challenge(); + let challenges = get_permutation_challenge_set(challenger, config.num_challenges); cross_table_lookups.iter().fold( - vec![LookupData::new(beta, gamma); trace_poly_values.len()], + vec![LookupData::new(challenges.clone()); trace_poly_values.len()], |mut acc, cross_table_lookup| { let CrossTableLookup { looking_table, @@ -89,7 +89,7 @@ pub fn cross_table_lookup_zs, const D: looked_columns, } = cross_table_lookup; - for _ in 0..config.num_challenges { + for &PermutationChallenge { beta, gamma } in &challenges.challenges { let z_looking = partial_products( &trace_poly_values[*looking_table as usize], looking_columns, @@ -131,7 +131,8 @@ fn partial_products( res.into() } -pub struct CTLCheckVars +#[derive(Clone)] +pub struct CTLCheckVars<'a, F, FE, P, const D2: usize> where F: Field, FE: FieldExtension, @@ -140,7 +141,68 @@ where pub(crate) local_z: P, pub(crate) next_z: P, pub(crate) challenges: PermutationChallenge, - pub(crate) columns: Vec, + pub(crate) columns: &'a [usize], +} + +impl<'a, F: RichField + Extendable, const D: usize> + CTLCheckVars<'a, F, F::Extension, F::Extension, D> +{ + pub(crate) fn from_proofs>( + proofs: &[&StarkProofWithPublicInputs], + cross_table_lookups: &'a [CrossTableLookup], + ctl_challenges: &'a PermutationChallengeSet, + ) -> Vec> { + let mut ctl_zs = proofs + .iter() + .map(|p| { + p.proof + .openings + .permutation_lookup_zs + .as_ref() + .unwrap() // TODO: fix unwrap + .iter() + .zip( + p.proof + .openings + .permutation_lookup_zs_right + .as_ref() + .unwrap() + .iter(), + ) + }) + .collect::>(); + + cross_table_lookups + .iter() + .fold(vec![vec![]; proofs.len()], |mut acc, ctl| { + let CrossTableLookup { + looking_table, + looking_columns, + looked_table, + looked_columns, + } = ctl; + + for &challenges in &ctl_challenges.challenges { + let (looking_z, looking_z_next) = + ctl_zs[*looking_table as usize].next().unwrap(); + acc[*looking_table as usize].push(Self { + local_z: *looking_z, + next_z: *looking_z_next, + challenges, + columns: &looking_columns, + }); + + let (looked_z, looked_z_next) = ctl_zs[*looked_table as usize].next().unwrap(); + acc[*looked_table as usize].push(Self { + local_z: *looked_z, + next_z: *looked_z_next, + challenges, + columns: &looked_columns, + }); + } + acc + }) + } } pub(crate) fn eval_cross_table_lookup_checks( @@ -180,7 +242,7 @@ pub(crate) fn verify_cross_table_lookups< >( cross_table_lookups: Vec, proofs: &[&StarkProofWithPublicInputs], - challenges: PermutationChallenge, + challenges: PermutationChallengeSet, config: &StarkConfig, ) -> Result<()> { let degrees_bits = proofs @@ -191,18 +253,25 @@ pub(crate) fn verify_cross_table_lookups< .iter() .map(|p| p.proof.openings.lookup_zs_last.iter()) .collect::>(); - for CrossTableLookup { - looking_table, - looked_table, - .. - } in cross_table_lookups + for ( + i, + CrossTableLookup { + looking_table, + looked_table, + .. + }, + ) in cross_table_lookups.into_iter().enumerate() { let looking_degree = 1 << degrees_bits[looking_table as usize]; let looked_degree = 1 << degrees_bits[looked_table as usize]; let looking_z = *lookup_zs_openings[looking_table as usize].next().unwrap(); let looked_z = *lookup_zs_openings[looked_table as usize].next().unwrap(); ensure!( - looking_z == looked_z * challenges.gamma.exp_u64(looking_degree - looked_degree), + looking_z + == looked_z + * challenges.challenges[i % config.num_challenges] + .gamma + .exp_u64(looking_degree - looked_degree), "Cross-table lookup verification failed." ); } diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index a6e3506d..acc0bbc0 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -12,7 +12,8 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::all_stark::AllStark; use crate::config::StarkConfig; use crate::permutation::{ - get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, PermutationChallenge, + get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, + get_permutation_challenge_set, }; use crate::proof::*; use crate::stark::Stark; @@ -83,10 +84,7 @@ impl, C: GenericConfig, const D: usize> A challenger.observe_cap(&proof.proof.trace_cap); } - let ctl_challenges = PermutationChallenge { - beta: challenger.get_challenge(), - gamma: challenger.get_challenge(), - }; + let ctl_challenges = get_permutation_challenge_set(&mut challenger, config.num_challenges); AllProofChallenges { cpu_challenges: self.cpu_proof.get_challenges( diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index ad3efd0c..2f0b5378 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -155,7 +155,7 @@ fn get_permutation_challenge>( PermutationChallenge { beta, gamma } } -fn get_permutation_challenge_set>( +pub(crate) fn get_permutation_challenge_set>( challenger: &mut Challenger, num_challenges: usize, ) -> PermutationChallengeSet { diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index a7a31fa4..1dd40d3f 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -15,7 +15,7 @@ use plonky2::plonk::config::GenericConfig; use rayon::prelude::*; use crate::config::StarkConfig; -use crate::permutation::{PermutationChallenge, PermutationChallengeSet}; +use crate::permutation::PermutationChallengeSet; #[derive(Debug, Clone)] pub struct AllProof, C: GenericConfig, const D: usize> { @@ -32,7 +32,7 @@ impl, C: GenericConfig, const D: usize> A pub(crate) struct AllProofChallenges, const D: usize> { pub cpu_challenges: StarkProofChallenges, pub keccak_challenges: StarkProofChallenges, - pub ctl_challenges: PermutationChallenge, + pub ctl_challenges: PermutationChallengeSet, } #[derive(Debug, Clone)] diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index ebebd915..f6a412c6 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -22,10 +22,10 @@ use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::cross_table_lookup::{cross_table_lookup_zs, CTLCheckVars, LookupData}; +use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, }; -use crate::permutation::{PermutationChallenge, PermutationCheckVars}; use crate::proof::{AllProof, StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; @@ -387,11 +387,8 @@ where .unwrap() .0 .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], - challenges: PermutationChallenge { - beta: lookup_data.beta, - gamma: lookup_data.gamma, - }, - columns: columns.to_vec(), + challenges: lookup_data.challenges.challenges[i % config.num_challenges], + columns: &columns, }) .collect::>(); eval_vanishing_poly::( diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 6e60e601..723b9e78 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -9,10 +9,10 @@ use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::{GenericConfig, Hasher}; use plonky2::plonk::plonk_common::reduce_with_powers; -use crate::all_stark::{AllStark, KeccakStark}; +use crate::all_stark::{AllStark, KeccakStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cross_table_lookup::verify_cross_table_lookups; +use crate::cross_table_lookup::{verify_cross_table_lookups, CTLCheckVars}; use crate::permutation::PermutationCheckVars; use crate::proof::{ AllProof, AllProofChallenges, StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs, @@ -42,19 +42,29 @@ where cross_table_lookups, } = all_stark; + let ctl_vars_per_table = + CTLCheckVars::from_proofs(&all_proof.proofs(), &cross_table_lookups, &ctl_challenges); + + verify_stark_proof_with_challenges( + cpu_stark, + &all_proof.cpu_proof, + cpu_challenges, + &ctl_vars_per_table[Table::Cpu as usize], + config, + )?; + verify_stark_proof_with_challenges( + keccak_stark, + &all_proof.keccak_proof, + keccak_challenges, + &ctl_vars_per_table[Table::Keccak as usize], + config, + )?; + verify_cross_table_lookups( cross_table_lookups, &all_proof.proofs(), ctl_challenges, config, - )?; - - verify_stark_proof_with_challenges(cpu_stark, all_proof.cpu_proof, cpu_challenges, config)?; - verify_stark_proof_with_challenges( - keccak_stark, - all_proof.keccak_proof, - keccak_challenges, - config, ) } @@ -86,8 +96,9 @@ pub(crate) fn verify_stark_proof_with_challenges< const D: usize, >( stark: S, - proof_with_pis: StarkProofWithPublicInputs, + proof_with_pis: &StarkProofWithPublicInputs, challenges: StarkProofChallenges, + lookup_data: &[CTLCheckVars], config: &StarkConfig, ) -> Result<()> where @@ -112,7 +123,8 @@ where local_values, next_values, public_inputs: &public_inputs - .into_iter() + .iter() + .copied() .map(F::Extension::from_basefield) .collect::>(), }; @@ -141,7 +153,7 @@ where config, vars, permutation_data, - &[/*TODO*/], + lookup_data, &mut consumer, ); let vanishing_polys_zeta = consumer.accumulators(); @@ -164,9 +176,9 @@ where ); } - let merkle_caps = once(proof.trace_cap) - .chain(proof.permutation_zs_cap) - .chain(once(proof.quotient_polys_cap)) + let merkle_caps = once(proof.trace_cap.clone()) + .chain(proof.permutation_zs_cap.clone()) + .chain(once(proof.quotient_polys_cap.clone())) .collect_vec(); verify_fri_proof::( @@ -174,7 +186,7 @@ where challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), degree_bits, - todo!(), + lookup_zs_last.len(), config, ), &proof.openings.to_fri_openings(), From c4c3533cff77ace80c49c22f62266fd2f8b376a4 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 13:51:02 +0200 Subject: [PATCH 19/33] PermutationChallenge -> GrandProductChallenge since it's also used for cross-table lookups --- starky2/src/cross_table_lookup.rs | 16 +++++----- starky2/src/get_challenges.rs | 9 +++--- starky2/src/permutation.rs | 52 +++++++++++++++---------------- starky2/src/proof.rs | 8 ++--- starky2/src/prover.rs | 6 ++-- 5 files changed, 46 insertions(+), 45 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 6dfec2d2..da141a52 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -13,7 +13,7 @@ use crate::all_stark::Table; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::permutation::{ - get_permutation_challenge_set, PermutationChallenge, PermutationChallengeSet, + get_grand_product_challenge_set, GrandProductChallenge, GrandProductChallengeSet, }; use crate::proof::StarkProofWithPublicInputs; use crate::stark::Stark; @@ -47,12 +47,12 @@ impl CrossTableLookup { /// Lookup data for one table. #[derive(Clone)] pub struct LookupData { - pub(crate) challenges: PermutationChallengeSet, + pub(crate) challenges: GrandProductChallengeSet, pub zs_columns: Vec<(PolynomialValues, Vec)>, } impl LookupData { - pub(crate) fn new(challenges: PermutationChallengeSet) -> Self { + pub(crate) fn new(challenges: GrandProductChallengeSet) -> Self { Self { challenges, zs_columns: vec![], @@ -78,7 +78,7 @@ pub fn cross_table_lookup_zs, const D: cross_table_lookups: &[CrossTableLookup], challenger: &mut Challenger, ) -> Vec> { - let challenges = get_permutation_challenge_set(challenger, config.num_challenges); + let challenges = get_grand_product_challenge_set(challenger, config.num_challenges); cross_table_lookups.iter().fold( vec![LookupData::new(challenges.clone()); trace_poly_values.len()], |mut acc, cross_table_lookup| { @@ -89,7 +89,7 @@ pub fn cross_table_lookup_zs, const D: looked_columns, } = cross_table_lookup; - for &PermutationChallenge { beta, gamma } in &challenges.challenges { + for &GrandProductChallenge { beta, gamma } in &challenges.challenges { let z_looking = partial_products( &trace_poly_values[*looking_table as usize], looking_columns, @@ -140,7 +140,7 @@ where { pub(crate) local_z: P, pub(crate) next_z: P, - pub(crate) challenges: PermutationChallenge, + pub(crate) challenges: GrandProductChallenge, pub(crate) columns: &'a [usize], } @@ -150,7 +150,7 @@ impl<'a, F: RichField + Extendable, const D: usize> pub(crate) fn from_proofs>( proofs: &[&StarkProofWithPublicInputs], cross_table_lookups: &'a [CrossTableLookup], - ctl_challenges: &'a PermutationChallengeSet, + ctl_challenges: &'a GrandProductChallengeSet, ) -> Vec> { let mut ctl_zs = proofs .iter() @@ -242,7 +242,7 @@ pub(crate) fn verify_cross_table_lookups< >( cross_table_lookups: Vec, proofs: &[&StarkProofWithPublicInputs], - challenges: PermutationChallengeSet, + challenges: GrandProductChallengeSet, config: &StarkConfig, ) -> Result<()> { let degrees_bits = proofs diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index acc0bbc0..9851c787 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -12,8 +12,8 @@ use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use crate::all_stark::AllStark; use crate::config::StarkConfig; use crate::permutation::{ - get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target, - get_permutation_challenge_set, + get_grand_product_challenge_set, get_n_grand_product_challenge_sets, + get_n_permutation_challenge_sets_target, }; use crate::proof::*; use crate::stark::Stark; @@ -41,7 +41,7 @@ where challenger.observe_cap(trace_cap); let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { - let tmp = get_n_permutation_challenge_sets( + let tmp = get_n_grand_product_challenge_sets( challenger, num_challenges, stark.permutation_batch_size(), @@ -84,7 +84,8 @@ impl, C: GenericConfig, const D: usize> A challenger.observe_cap(&proof.proof.trace_cap); } - let ctl_challenges = get_permutation_challenge_set(&mut challenger, config.num_challenges); + let ctl_challenges = + get_grand_product_challenge_set(&mut challenger, config.num_challenges); AllProofChallenges { cpu_challenges: self.cpu_proof.get_challenges( diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index 2f0b5378..a5601a7b 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -41,12 +41,12 @@ impl PermutationPair { /// A single instance of a permutation check protocol. pub(crate) struct PermutationInstance<'a, T: Copy> { pub(crate) pair: &'a PermutationPair, - pub(crate) challenge: PermutationChallenge, + pub(crate) challenge: GrandProductChallenge, } /// Randomness for a single instance of a permutation check protocol. #[derive(Copy, Clone)] -pub(crate) struct PermutationChallenge { +pub(crate) struct GrandProductChallenge { /// Randomness used to combine multiple columns into one. pub(crate) beta: T, /// Random offset that's added to the beta-reduced column values. @@ -55,8 +55,8 @@ pub(crate) struct PermutationChallenge { /// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. #[derive(Clone)] -pub(crate) struct PermutationChallengeSet { - pub(crate) challenges: Vec>, +pub(crate) struct GrandProductChallengeSet { + pub(crate) challenges: Vec>, } /// Compute all Z polynomials (for permutation arguments). @@ -64,7 +64,7 @@ pub(crate) fn compute_permutation_z_polys( stark: &S, config: &StarkConfig, trace_poly_values: &[PolynomialValues], - permutation_challenge_sets: &[PermutationChallengeSet], + permutation_challenge_sets: &[GrandProductChallengeSet], ) -> Vec> where F: RichField + Extendable, @@ -123,7 +123,7 @@ fn permutation_reduced_polys( ) -> (PolynomialValues, PolynomialValues) { let PermutationInstance { pair: PermutationPair { column_pairs }, - challenge: PermutationChallenge { beta, gamma }, + challenge: GrandProductChallenge { beta, gamma }, } = instance; let mut reduced_lhs = PolynomialValues::constant(*gamma, degree); @@ -147,31 +147,31 @@ fn poly_product_elementwise( product } -fn get_permutation_challenge>( +fn get_grand_product_challenge>( challenger: &mut Challenger, -) -> PermutationChallenge { +) -> GrandProductChallenge { let beta = challenger.get_challenge(); let gamma = challenger.get_challenge(); - PermutationChallenge { beta, gamma } + GrandProductChallenge { beta, gamma } } -pub(crate) fn get_permutation_challenge_set>( +pub(crate) fn get_grand_product_challenge_set>( challenger: &mut Challenger, num_challenges: usize, -) -> PermutationChallengeSet { +) -> GrandProductChallengeSet { let challenges = (0..num_challenges) - .map(|_| get_permutation_challenge(challenger)) + .map(|_| get_grand_product_challenge(challenger)) .collect(); - PermutationChallengeSet { challenges } + GrandProductChallengeSet { challenges } } -pub(crate) fn get_n_permutation_challenge_sets>( +pub(crate) fn get_n_grand_product_challenge_sets>( challenger: &mut Challenger, num_challenges: usize, num_sets: usize, -) -> Vec> { +) -> Vec> { (0..num_sets) - .map(|_| get_permutation_challenge_set(challenger, num_challenges)) + .map(|_| get_grand_product_challenge_set(challenger, num_challenges)) .collect() } @@ -182,10 +182,10 @@ fn get_permutation_challenge_target< >( builder: &mut CircuitBuilder, challenger: &mut RecursiveChallenger, -) -> PermutationChallenge { +) -> GrandProductChallenge { let beta = challenger.get_challenge(builder); let gamma = challenger.get_challenge(builder); - PermutationChallenge { beta, gamma } + GrandProductChallenge { beta, gamma } } fn get_permutation_challenge_set_target< @@ -196,11 +196,11 @@ fn get_permutation_challenge_set_target< builder: &mut CircuitBuilder, challenger: &mut RecursiveChallenger, num_challenges: usize, -) -> PermutationChallengeSet { +) -> GrandProductChallengeSet { let challenges = (0..num_challenges) .map(|_| get_permutation_challenge_target(builder, challenger)) .collect(); - PermutationChallengeSet { challenges } + GrandProductChallengeSet { challenges } } pub(crate) fn get_n_permutation_challenge_sets_target< @@ -212,7 +212,7 @@ pub(crate) fn get_n_permutation_challenge_sets_target< challenger: &mut RecursiveChallenger, num_challenges: usize, num_sets: usize, -) -> Vec> { +) -> Vec> { (0..num_sets) .map(|_| get_permutation_challenge_set_target(builder, challenger, num_challenges)) .collect() @@ -225,7 +225,7 @@ pub(crate) fn get_n_permutation_challenge_sets_target< /// chunk these arguments based on our batch size. pub(crate) fn get_permutation_batches<'a, T: Copy>( permutation_pairs: &'a [PermutationPair], - permutation_challenge_sets: &[PermutationChallengeSet], + permutation_challenge_sets: &[GrandProductChallengeSet], num_challenges: usize, batch_size: usize, ) -> Vec>> { @@ -254,7 +254,7 @@ where { pub(crate) local_zs: Vec

, pub(crate) next_zs: Vec

, - pub(crate) permutation_challenge_sets: Vec>, + pub(crate) permutation_challenge_sets: Vec>, } pub(crate) fn eval_permutation_checks( @@ -298,7 +298,7 @@ pub(crate) fn eval_permutation_checks, Vec<_>) = column_pairs @@ -320,7 +320,7 @@ pub(crate) fn eval_permutation_checks { pub(crate) local_zs: Vec>, pub(crate) next_zs: Vec>, - pub(crate) permutation_challenge_sets: Vec>, + pub(crate) permutation_challenge_sets: Vec>, } pub(crate) fn eval_permutation_checks_recursively( @@ -366,7 +366,7 @@ pub(crate) fn eval_permutation_checks_recursively( .map(|instance| { let PermutationInstance { pair: PermutationPair { column_pairs }, - challenge: PermutationChallenge { beta, gamma }, + challenge: GrandProductChallenge { beta, gamma }, } = instance; let beta_ext = builder.convert_to_ext(*beta); let gamma_ext = builder.convert_to_ext(*gamma); diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index 1dd40d3f..b8ede54f 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -15,7 +15,7 @@ use plonky2::plonk::config::GenericConfig; use rayon::prelude::*; use crate::config::StarkConfig; -use crate::permutation::PermutationChallengeSet; +use crate::permutation::GrandProductChallengeSet; #[derive(Debug, Clone)] pub struct AllProof, C: GenericConfig, const D: usize> { @@ -32,7 +32,7 @@ impl, C: GenericConfig, const D: usize> A pub(crate) struct AllProofChallenges, const D: usize> { pub cpu_challenges: StarkProofChallenges, pub keccak_challenges: StarkProofChallenges, - pub ctl_challenges: PermutationChallengeSet, + pub ctl_challenges: GrandProductChallengeSet, } #[derive(Debug, Clone)] @@ -121,7 +121,7 @@ pub struct CompressedStarkProofWithPublicInputs< pub(crate) struct StarkProofChallenges, const D: usize> { /// Randomness used in any permutation arguments. - pub permutation_challenge_sets: Option>>, + pub permutation_challenge_sets: Option>>, /// Random values used to combine STARK constraints. pub stark_alphas: Vec, @@ -133,7 +133,7 @@ pub(crate) struct StarkProofChallenges, const D: us } pub(crate) struct StarkProofChallengesTarget { - pub permutation_challenge_sets: Option>>, + pub permutation_challenge_sets: Option>>, pub stark_alphas: Vec, pub stark_zeta: ExtensionTarget, pub fri_challenges: FriChallengesTarget, diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index f6a412c6..25a05b69 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -24,7 +24,7 @@ use crate::constraint_consumer::ConstraintConsumer; use crate::cross_table_lookup::{cross_table_lookup_zs, CTLCheckVars, LookupData}; use crate::permutation::PermutationCheckVars; use crate::permutation::{ - compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet, + compute_permutation_z_polys, get_n_grand_product_challenge_sets, GrandProductChallengeSet, }; use crate::proof::{AllProof, StarkOpeningSet, StarkProof, StarkProofWithPublicInputs}; use crate::stark::Stark; @@ -148,7 +148,7 @@ where // Permutation arguments. let permutation_challenges = stark.uses_permutation_args().then(|| { - get_n_permutation_challenge_sets( + get_n_grand_product_challenge_sets( challenger, config.num_challenges, stark.permutation_batch_size(), @@ -290,7 +290,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( trace_commitment: &'a PolynomialBatch, permutation_zs_commitment_challenges: Option<( &'a PolynomialBatch, - &'a Vec>, + &'a Vec>, )>, lookup_data: &LookupData, public_inputs: &[F], From 3359ee708c74badca3d1b9848c268b26ec06320f Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 14:07:03 +0200 Subject: [PATCH 20/33] Clippy --- starky2/src/cross_table_lookup.rs | 4 ++-- starky2/src/prover.rs | 2 +- starky2/src/verifier.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index da141a52..980513e7 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -189,7 +189,7 @@ impl<'a, F: RichField + Extendable, const D: usize> local_z: *looking_z, next_z: *looking_z_next, challenges, - columns: &looking_columns, + columns: looking_columns, }); let (looked_z, looked_z_next) = ctl_zs[*looked_table as usize].next().unwrap(); @@ -197,7 +197,7 @@ impl<'a, F: RichField + Extendable, const D: usize> local_z: *looked_z, next_z: *looked_z_next, challenges, - columns: &looked_columns, + columns: looked_columns, }); } acc diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 25a05b69..5938e61d 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -388,7 +388,7 @@ where .0 .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], challenges: lookup_data.challenges.challenges[i % config.num_challenges], - columns: &columns, + columns, }) .collect::>(); eval_vanishing_poly::( diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 723b9e78..be6d96d4 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -106,7 +106,7 @@ where [(); S::PUBLIC_INPUTS]:, [(); C::Hasher::HASH_SIZE]:, { - check_permutation_options(&stark, &proof_with_pis, &challenges)?; + check_permutation_options(&stark, proof_with_pis, &challenges)?; let StarkProofWithPublicInputs { proof, public_inputs, From 17ba468e3a8a723009a1988014a1c1fdc772545a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 20:38:11 +0200 Subject: [PATCH 21/33] Add mock test (doesn't work) --- starky2/Cargo.toml | 1 + starky2/src/all_stark.rs | 97 +++++++++++++-- starky2/src/config.rs | 2 +- starky2/src/cross_table_lookup.rs | 5 +- starky2/src/permutation.rs | 2 + starky2/src/prover.rs | 190 +++++++++++++++++++++++------- starky2/src/verifier.rs | 7 +- 7 files changed, 247 insertions(+), 57 deletions(-) diff --git a/starky2/Cargo.toml b/starky2/Cargo.toml index feb4cbc3..70a6a2cf 100644 --- a/starky2/Cargo.toml +++ b/starky2/Cargo.toml @@ -12,3 +12,4 @@ env_logger = "0.9.0" itertools = "0.10.0" log = "0.4.14" rayon = "1.5.1" +rand = "0.8.5" diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index 67445856..0b5b0316 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -9,17 +9,22 @@ use crate::cross_table_lookup::CrossTableLookup; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; +#[derive(Clone)] pub struct AllStark, const D: usize> { pub cpu_stark: CpuStark, pub keccak_stark: KeccakStark, pub cross_table_lookups: Vec, } +#[derive(Copy, Clone)] pub struct CpuStark { + num_rows: usize, f: PhantomData, } +#[derive(Copy, Clone)] pub struct KeccakStark { + num_rows: usize, f: PhantomData, } @@ -30,7 +35,7 @@ pub enum Table { } impl, const D: usize> Stark for CpuStark { - const COLUMNS: usize = 0; + const COLUMNS: usize = 10; const PUBLIC_INPUTS: usize = 0; fn eval_packed_generic( @@ -41,7 +46,6 @@ impl, const D: usize> Stark for CpuStark, P: PackedField, { - todo!() } fn eval_ext_recursively( @@ -50,16 +54,15 @@ impl, const D: usize> Stark for CpuStark, _yield_constr: &mut RecursiveConstraintConsumer, ) { - todo!() } fn constraint_degree(&self) -> usize { - todo!() + 3 } } impl, const D: usize> Stark for KeccakStark { - const COLUMNS: usize = 0; + const COLUMNS: usize = 7; const PUBLIC_INPUTS: usize = 0; fn eval_packed_generic( @@ -70,7 +73,6 @@ impl, const D: usize> Stark for KeccakStark, P: PackedField, { - todo!() } fn eval_ext_recursively( @@ -79,10 +81,89 @@ impl, const D: usize> Stark for KeccakStark, _yield_constr: &mut RecursiveConstraintConsumer, ) { - todo!() } fn constraint_degree(&self) -> usize { - todo!() + 3 + } +} + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::field_types::Field; + use plonky2::field::goldilocks_field::GoldilocksField; + use plonky2::field::polynomial::PolynomialValues; + use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + use plonky2::util::timing::TimingTree; + use rand::{thread_rng, Rng}; + + use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; + use crate::config::StarkConfig; + use crate::cross_table_lookup::CrossTableLookup; + use crate::prover::prove; + use crate::stark::Stark; + use crate::verifier::verify_proof; + + #[test] + fn test_all_stark() -> Result<()> { + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = StarkConfig::standard_fast_config(); + + let cpu_stark = CpuStark:: { + num_rows: 1 << 4, + f: Default::default(), + }; + let keccak_stark = KeccakStark:: { + num_rows: 1 << 3, + f: Default::default(), + }; + + // let mut cpu_trace = vec![PolynomialValues::zero(cpu_stark.num_rows); CpuStark::COLUMNS]; + let mut cpu_trace = vec![PolynomialValues::zero(cpu_stark.num_rows); 10]; + // let mut keccak_trace = + // vec![PolynomialValues::zero(keccak_stark.num_rows); KeccakStark::COLUMNS]; + let mut keccak_trace = vec![PolynomialValues::zero(keccak_stark.num_rows); 7]; + + let vs0 = (0..keccak_stark.num_rows) + .map(F::from_canonical_usize) + .collect::>(); + let vs1 = (1..=keccak_stark.num_rows) + .map(F::from_canonical_usize) + .collect::>(); + let start = thread_rng().gen_range(0..cpu_stark.num_rows - keccak_stark.num_rows); + let start = 0; + + cpu_trace[2].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs0); + cpu_trace[4].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs1); + + keccak_trace[3].values[..].copy_from_slice(&vs0); + keccak_trace[5].values[..].copy_from_slice(&vs1); + + let cross_table_lookups = vec![CrossTableLookup { + looking_table: Table::Cpu, + looking_columns: vec![2, 4], + looked_table: Table::Keccak, + looked_columns: vec![3, 5], + }]; + + let all_stark = AllStark { + cpu_stark, + keccak_stark, + cross_table_lookups, + }; + + let proof = prove::( + &all_stark, + &config, + vec![cpu_trace, keccak_trace], + vec![vec![]; 2], + &mut TimingTree::default(), + )?; + + verify_proof(all_stark, proof, &config) } } diff --git a/starky2/src/config.rs b/starky2/src/config.rs index 500cd957..00d13bd9 100644 --- a/starky2/src/config.rs +++ b/starky2/src/config.rs @@ -17,7 +17,7 @@ impl StarkConfig { pub fn standard_fast_config() -> Self { Self { security_bits: 100, - num_challenges: 2, + num_challenges: 1, fri_config: FriConfig { rate_bits: 1, cap_height: 4, diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 980513e7..124294b2 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -122,8 +122,9 @@ fn partial_products( gamma: F, ) -> PolynomialValues { let mut partial_prod = F::ONE; - let mut res = Vec::new(); - for i in 0..trace[0].len() { + let degree = trace[0].len(); + let mut res = Vec::with_capacity(degree); + for i in 0..degree { partial_prod *= gamma + reduce_with_powers(columns.iter().map(|&j| &trace[j].values[i]), beta); res.push(partial_prod); diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index a5601a7b..881a67ed 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -1,5 +1,7 @@ //! Permutation arguments. +use std::fmt::Debug; + use itertools::Itertools; use plonky2::field::batch_util::batch_multiply_inplace; use plonky2::field::extension_field::{Extendable, FieldExtension}; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 5938e61d..cf9472e4 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -31,8 +31,8 @@ use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::StarkEvaluationVars; -pub fn prove( - all_stark: AllStark, +pub fn prove( + all_stark: &AllStark, config: &StarkConfig, trace_poly_values: Vec>>, public_inputs: Vec>, @@ -48,15 +48,8 @@ where debug_assert_eq!(num_starks, trace_poly_values.len()); debug_assert_eq!(num_starks, public_inputs.len()); - let degree = trace_poly_values[0].len(); - let degree_bits = log2_strict(degree); - let fri_params = config.fri_params(degree_bits); let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; - assert!( - fri_params.total_arities() <= degree_bits + rate_bits - cap_height, - "FRI total reduction arity is too large.", - ); let trace_commitments = timed!( timing, @@ -144,7 +137,11 @@ where let degree_bits = log2_strict(degree); let fri_params = config.fri_params(degree_bits); let rate_bits = config.fri_config.rate_bits; - let _cap_height = config.fri_config.cap_height; + let cap_height = config.fri_config.cap_height; + assert!( + fri_params.total_arities() <= degree_bits + rate_bits - cap_height, + "FRI total reduction arity is too large.", + ); // Permutation arguments. let permutation_challenges = stark.uses_permutation_args().then(|| { @@ -167,7 +164,7 @@ where } }; - let permutation_lookup_zs_commitment = (!z_polys.is_empty()).then(|| { + let permutation_ctl_zs_commitment = (!z_polys.is_empty()).then(|| { PolynomialBatch::from_values( z_polys, rate_bits, @@ -177,28 +174,31 @@ where None, ) }); - let permutation_zs_cap = permutation_lookup_zs_commitment + let permutation_zs_cap = permutation_ctl_zs_commitment .as_ref() .map(|commit| commit.merkle_tree.cap.clone()); if let Some(cap) = &permutation_zs_cap { challenger.observe_cap(cap); } - // TODO: if no permutation but lookup, this is wrong. - let zipped = if let (Some(x), Some(y)) = ( - permutation_lookup_zs_commitment.as_ref(), - permutation_challenges.as_ref(), - ) { - Some((x, y)) - } else { - None - }; - let alphas = challenger.get_n_challenges(config.num_challenges); + test_it( + stark, + trace_commitment, + permutation_ctl_zs_commitment.as_ref(), + permutation_challenges.as_ref(), + lookup_data, + public_inputs, + alphas.clone(), + degree_bits, + num_permutation_zs, + config, + ); let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( stark, trace_commitment, - zipped, + permutation_ctl_zs_commitment.as_ref(), + permutation_challenges.as_ref(), lookup_data, public_inputs, alphas, @@ -246,7 +246,7 @@ where zeta, g, trace_commitment, - permutation_lookup_zs_commitment.as_ref(), + permutation_ctl_zs_commitment.as_ref(), "ient_commitment, degree_bits, stark.num_permutation_batches(config), @@ -254,7 +254,7 @@ where challenger.observe_openings(&openings.to_fri_openings()); let initial_merkle_trees = once(trace_commitment) - .chain(&permutation_lookup_zs_commitment) + .chain(&permutation_ctl_zs_commitment) .chain(once("ient_commitment)) .collect_vec(); @@ -288,10 +288,8 @@ where fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, - permutation_zs_commitment_challenges: Option<( - &'a PolynomialBatch, - &'a Vec>, - )>, + permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, + permutation_challenges: Option<&'a Vec>>, lookup_data: &LookupData, public_inputs: &[F], alphas: Vec, @@ -363,29 +361,32 @@ where next_values: &get_trace_values_packed(i_next_start), public_inputs, }; - let permutation_check_data = permutation_zs_commitment_challenges.as_ref().map( - |(permutation_zs_commitment, permutation_challenge_sets)| PermutationCheckVars { - local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step) - [..num_permutation_zs] - .to_vec(), - next_zs: permutation_zs_commitment.get_lde_values_packed(i_next_start, step) - [..num_permutation_zs] - .to_vec(), - permutation_challenge_sets: permutation_challenge_sets.to_vec(), - }, - ); + let permutation_check_data = + if let (Some(permutation_zs_commitment), Some(permutation_challenge_sets)) = + (permutation_ctl_zs_commitment, permutation_challenges) + { + Some(PermutationCheckVars { + local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step) + [..num_permutation_zs] + .to_vec(), + next_zs: permutation_zs_commitment + .get_lde_values_packed(i_next_start, step)[..num_permutation_zs] + .to_vec(), + permutation_challenge_sets: permutation_challenge_sets.to_vec(), + }) + } else { + None + }; let lookup_check_data = lookup_data .zs_columns .iter() .enumerate() .map(|(i, (_, columns))| CTLCheckVars:: { - local_z: permutation_zs_commitment_challenges + local_z: permutation_ctl_zs_commitment .unwrap() - .0 .get_lde_values_packed(i_start, step)[num_permutation_zs + i], - next_z: permutation_zs_commitment_challenges + next_z: permutation_ctl_zs_commitment .unwrap() - .0 .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], challenges: lookup_data.challenges.challenges[i % config.num_challenges], columns, @@ -415,3 +416,104 @@ where .map(|values| values.coset_ifft(F::coset_shift())) .collect() } + +fn test_it<'a, F, C, S, const D: usize>( + stark: &S, + trace_commitment: &'a PolynomialBatch, + permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, + permutation_challenges: Option<&'a Vec>>, + lookup_data: &LookupData, + public_inputs: &[F], + alphas: Vec, + degree_bits: usize, + num_permutation_zs: usize, + config: &StarkConfig, +) where + F: RichField + Extendable, + C: GenericConfig, + S: Stark, +{ + let degree = 1 << degree_bits; + + // Evaluation of the first Lagrange polynomial on the LDE domain. + let lagrange_first = PolynomialValues::selector(degree, 0); + // Evaluation of the last Lagrange polynomial on the LDE domain. + let lagrange_last = PolynomialValues::selector(degree, degree - 1); + + let subgroup = F::two_adic_subgroup(degree_bits); + + // Retrieve the LDE values at index `i`. + let get_comm_values = |comm: &PolynomialBatch, i| -> Vec { + comm.polynomials + .iter() + .map(|poly| poly.eval(subgroup[i])) + .collect() + }; + + // Last element of the subgroup. + let last = F::primitive_root_of_unity(degree_bits).inverse(); + + let constraint_values = (0..degree) + .map(|i| { + let i_next = (i + 1) % degree; + + let x = subgroup[i]; + let z_last = x - last; + let lagrange_basis_first = lagrange_first.values[i]; + let lagrange_basis_last = lagrange_last.values[i]; + + let mut consumer = ConstraintConsumer::new( + alphas.clone(), + z_last, + lagrange_basis_first, + lagrange_basis_last, + ); + let vars = StarkEvaluationVars { + local_values: &get_comm_values(trace_commitment, i), + next_values: &get_comm_values(trace_commitment, i_next), + public_inputs, + }; + let permutation_check_data = + if let (Some(permutation_zs_commitment), Some(permutation_challenge_sets)) = + (permutation_ctl_zs_commitment, permutation_challenges) + { + Some(PermutationCheckVars { + local_zs: get_comm_values(permutation_zs_commitment, i) + [..num_permutation_zs] + .to_vec(), + next_zs: get_comm_values(permutation_zs_commitment, i_next) + [..num_permutation_zs] + .to_vec(), + permutation_challenge_sets: permutation_challenge_sets.to_vec(), + }) + } else { + None + }; + let lookup_check_data = lookup_data + .zs_columns + .iter() + .enumerate() + .map(|(iii, (_, columns))| CTLCheckVars:: { + local_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i) + [num_permutation_zs + iii], + next_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i_next) + [num_permutation_zs + iii], + challenges: lookup_data.challenges.challenges[iii % config.num_challenges], + columns, + }) + .collect::>(); + eval_vanishing_poly::( + stark, + config, + vars, + permutation_check_data, + &lookup_check_data, + &mut consumer, + ); + let mut constraints_evals = consumer.accumulators(); + constraints_evals + }) + .collect::>(); + + dbg!(constraint_values); +} diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index be6d96d4..7ef9381e 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -9,7 +9,7 @@ use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::{GenericConfig, Hasher}; use plonky2::plonk::plonk_common::reduce_with_powers; -use crate::all_stark::{AllStark, KeccakStark, Table}; +use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::cross_table_lookup::{verify_cross_table_lookups, CTLCheckVars}; @@ -27,6 +27,8 @@ pub fn verify_proof, C: GenericConfig, co config: &StarkConfig, ) -> Result<()> where + [(); CpuStark::::COLUMNS]:, + [(); CpuStark::::PUBLIC_INPUTS]:, [(); KeccakStark::::COLUMNS]:, [(); C::Hasher::HASH_SIZE]:, { @@ -106,7 +108,8 @@ where [(); S::PUBLIC_INPUTS]:, [(); C::Hasher::HASH_SIZE]:, { - check_permutation_options(&stark, proof_with_pis, &challenges)?; + // TODO: Fix this to take CTLs into account + // check_permutation_options(&stark, proof_with_pis, &challenges)?; let StarkProofWithPublicInputs { proof, public_inputs, From fe3811e8ed82d7abfb4a05095b9d2913c6d175cc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 22:29:10 +0200 Subject: [PATCH 22/33] Test works --- starky2/src/constraint_consumer.rs | 2 +- starky2/src/cross_table_lookup.rs | 5 ++++- starky2/src/get_challenges.rs | 20 ++++++++++---------- starky2/src/proof.rs | 2 +- starky2/src/prover.rs | 25 ++++++++++++++++--------- starky2/src/recursive_verifier.rs | 7 ++++--- starky2/src/verifier.rs | 12 ++++++++---- 7 files changed, 44 insertions(+), 29 deletions(-) diff --git a/starky2/src/constraint_consumer.rs b/starky2/src/constraint_consumer.rs index ada28730..fd21e366 100644 --- a/starky2/src/constraint_consumer.rs +++ b/starky2/src/constraint_consumer.rs @@ -9,7 +9,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder; pub struct ConstraintConsumer { /// Random values used to combine multiple constraints into one. - alphas: Vec, + pub alphas: Vec, /// Running sums of constraints that have been emitted so far, scaled by powers of alpha. // TODO(JN): This is pub so it can be used in a test. Once we have an API for accessing this diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 124294b2..4a8423d5 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -152,6 +152,7 @@ impl<'a, F: RichField + Extendable, const D: usize> proofs: &[&StarkProofWithPublicInputs], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, + num_permutation_zs: usize, ) -> Vec> { let mut ctl_zs = proofs .iter() @@ -162,13 +163,15 @@ impl<'a, F: RichField + Extendable, const D: usize> .as_ref() .unwrap() // TODO: fix unwrap .iter() + .skip(num_permutation_zs) .zip( p.proof .openings .permutation_lookup_zs_right .as_ref() .unwrap() - .iter(), + .iter() + .skip(num_permutation_zs), ) }) .collect::>(); diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index 9851c787..7e65a432 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -22,7 +22,7 @@ fn get_challenges( challenger: &mut Challenger, stark: &S, trace_cap: &MerkleCap, - permutation_zs_cap: Option<&MerkleCap>, + permutation_ctl_zs_cap: Option<&MerkleCap>, quotient_polys_cap: &MerkleCap, openings: &StarkOpeningSet, commit_phase_merkle_caps: &[MerkleCap], @@ -38,18 +38,18 @@ where { let num_challenges = config.num_challenges; - challenger.observe_cap(trace_cap); - - let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { - let tmp = get_n_grand_product_challenge_sets( + let permutation_challenge_sets = stark.uses_permutation_args().then(|| { + get_n_grand_product_challenge_sets( challenger, num_challenges, stark.permutation_batch_size(), - ); - challenger.observe_cap(permutation_zs_cap); - tmp + ) }); + if let Some(cap) = permutation_ctl_zs_cap { + challenger.observe_cap(cap); + } + let stark_alphas = challenger.get_n_challenges(num_challenges); challenger.observe_cap(quotient_polys_cap); @@ -132,7 +132,7 @@ where let StarkProof { trace_cap, - permutation_zs_cap, + permutation_ctl_zs_cap, quotient_polys_cap, openings, opening_proof: @@ -148,7 +148,7 @@ where challenger, stark, trace_cap, - permutation_zs_cap.as_ref(), + permutation_ctl_zs_cap.as_ref(), quotient_polys_cap, openings, commit_phase_merkle_caps, diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index b8ede54f..a30f3a9b 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -40,7 +40,7 @@ pub struct StarkProof, C: GenericConfig, /// Merkle cap of LDEs of trace values. pub trace_cap: MerkleCap, /// Merkle cap of LDEs of permutation Z values. - pub permutation_zs_cap: Option>, + pub permutation_ctl_zs_cap: Option>, /// Merkle cap of LDEs of trace values. pub quotient_polys_cap: MerkleCap, /// Purported values of each polynomial at the challenge point. diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index cf9472e4..6dc94657 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -174,10 +174,10 @@ where None, ) }); - let permutation_zs_cap = permutation_ctl_zs_commitment + let permutation_ctl_zs_cap = permutation_ctl_zs_commitment .as_ref() .map(|commit| commit.merkle_tree.cap.clone()); - if let Some(cap) = &permutation_zs_cap { + if let Some(cap) = &permutation_ctl_zs_cap { challenger.observe_cap(cap); } @@ -271,7 +271,7 @@ where ); let proof = StarkProof { trace_cap: trace_commitment.merkle_tree.cap.clone(), - permutation_zs_cap, + permutation_ctl_zs_cap, quotient_polys_cap, openings, opening_proof, @@ -434,13 +434,17 @@ fn test_it<'a, F, C, S, const D: usize>( S: Stark, { let degree = 1 << degree_bits; + let rate_bits = 0; + + let size = degree << rate_bits; + let step = 1 << rate_bits; // Evaluation of the first Lagrange polynomial on the LDE domain. - let lagrange_first = PolynomialValues::selector(degree, 0); + let lagrange_first = PolynomialValues::selector(degree, 0).lde(rate_bits); // Evaluation of the last Lagrange polynomial on the LDE domain. - let lagrange_last = PolynomialValues::selector(degree, degree - 1); + let lagrange_last = PolynomialValues::selector(degree, degree - 1).lde(rate_bits); - let subgroup = F::two_adic_subgroup(degree_bits); + let subgroup = F::two_adic_subgroup(degree_bits + rate_bits); // Retrieve the LDE values at index `i`. let get_comm_values = |comm: &PolynomialBatch, i| -> Vec { @@ -453,9 +457,9 @@ fn test_it<'a, F, C, S, const D: usize>( // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); - let constraint_values = (0..degree) + let constraint_values = (0..size) .map(|i| { - let i_next = (i + 1) % degree; + let i_next = (i + step) % size; let x = subgroup[i]; let z_last = x - last; @@ -515,5 +519,8 @@ fn test_it<'a, F, C, S, const D: usize>( }) .collect::>(); - dbg!(constraint_values); + let values = transpose(&constraint_values); + for v in values { + assert!(v.iter().all(|x| x.is_zero())); + } } diff --git a/starky2/src/recursive_verifier.rs b/starky2/src/recursive_verifier.rs index e091d64c..6dcc7a7e 100644 --- a/starky2/src/recursive_verifier.rs +++ b/starky2/src/recursive_verifier.rs @@ -301,9 +301,10 @@ pub fn set_stark_proof_target, W, const D: usize>( &proof.openings.to_fri_openings(), ); - if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = - (&proof_target.permutation_zs_cap, &proof.permutation_zs_cap) - { + if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = ( + &proof_target.permutation_zs_cap, + &proof.permutation_ctl_zs_cap, + ) { witness.set_cap_target(permutation_zs_cap_target, permutation_zs_cap); } diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 7ef9381e..2f7a628e 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -44,8 +44,12 @@ where cross_table_lookups, } = all_stark; - let ctl_vars_per_table = - CTLCheckVars::from_proofs(&all_proof.proofs(), &cross_table_lookups, &ctl_challenges); + let ctl_vars_per_table = CTLCheckVars::from_proofs( + &all_proof.proofs(), + &cross_table_lookups, + &ctl_challenges, + 0, // TODO: Fix 0 + ); verify_stark_proof_with_challenges( cpu_stark, @@ -180,7 +184,7 @@ where } let merkle_caps = once(proof.trace_cap.clone()) - .chain(proof.permutation_zs_cap.clone()) + .chain(proof.permutation_ctl_zs_cap.clone()) .chain(once(proof.quotient_polys_cap.clone())) .collect_vec(); @@ -227,7 +231,7 @@ fn check_permutation_options< challenges: &StarkProofChallenges, ) -> Result<()> { let options_is_some = [ - proof_with_pis.proof.permutation_zs_cap.is_some(), + proof_with_pis.proof.permutation_ctl_zs_cap.is_some(), proof_with_pis .proof .openings From 0c8178e286096480ed3808d1ddb1a4ea7a35ba36 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 22:29:10 +0200 Subject: [PATCH 23/33] Test works --- starky2/src/config.rs | 2 +- starky2/src/cross_table_lookup.rs | 5 ++++- starky2/src/get_challenges.rs | 20 ++++++++++---------- starky2/src/proof.rs | 2 +- starky2/src/prover.rs | 25 ++++++++++++++++--------- starky2/src/recursive_verifier.rs | 7 ++++--- starky2/src/verifier.rs | 12 ++++++++---- 7 files changed, 44 insertions(+), 29 deletions(-) diff --git a/starky2/src/config.rs b/starky2/src/config.rs index 00d13bd9..500cd957 100644 --- a/starky2/src/config.rs +++ b/starky2/src/config.rs @@ -17,7 +17,7 @@ impl StarkConfig { pub fn standard_fast_config() -> Self { Self { security_bits: 100, - num_challenges: 1, + num_challenges: 2, fri_config: FriConfig { rate_bits: 1, cap_height: 4, diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 124294b2..4a8423d5 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -152,6 +152,7 @@ impl<'a, F: RichField + Extendable, const D: usize> proofs: &[&StarkProofWithPublicInputs], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, + num_permutation_zs: usize, ) -> Vec> { let mut ctl_zs = proofs .iter() @@ -162,13 +163,15 @@ impl<'a, F: RichField + Extendable, const D: usize> .as_ref() .unwrap() // TODO: fix unwrap .iter() + .skip(num_permutation_zs) .zip( p.proof .openings .permutation_lookup_zs_right .as_ref() .unwrap() - .iter(), + .iter() + .skip(num_permutation_zs), ) }) .collect::>(); diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index 9851c787..7e65a432 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -22,7 +22,7 @@ fn get_challenges( challenger: &mut Challenger, stark: &S, trace_cap: &MerkleCap, - permutation_zs_cap: Option<&MerkleCap>, + permutation_ctl_zs_cap: Option<&MerkleCap>, quotient_polys_cap: &MerkleCap, openings: &StarkOpeningSet, commit_phase_merkle_caps: &[MerkleCap], @@ -38,18 +38,18 @@ where { let num_challenges = config.num_challenges; - challenger.observe_cap(trace_cap); - - let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { - let tmp = get_n_grand_product_challenge_sets( + let permutation_challenge_sets = stark.uses_permutation_args().then(|| { + get_n_grand_product_challenge_sets( challenger, num_challenges, stark.permutation_batch_size(), - ); - challenger.observe_cap(permutation_zs_cap); - tmp + ) }); + if let Some(cap) = permutation_ctl_zs_cap { + challenger.observe_cap(cap); + } + let stark_alphas = challenger.get_n_challenges(num_challenges); challenger.observe_cap(quotient_polys_cap); @@ -132,7 +132,7 @@ where let StarkProof { trace_cap, - permutation_zs_cap, + permutation_ctl_zs_cap, quotient_polys_cap, openings, opening_proof: @@ -148,7 +148,7 @@ where challenger, stark, trace_cap, - permutation_zs_cap.as_ref(), + permutation_ctl_zs_cap.as_ref(), quotient_polys_cap, openings, commit_phase_merkle_caps, diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index b8ede54f..a30f3a9b 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -40,7 +40,7 @@ pub struct StarkProof, C: GenericConfig, /// Merkle cap of LDEs of trace values. pub trace_cap: MerkleCap, /// Merkle cap of LDEs of permutation Z values. - pub permutation_zs_cap: Option>, + pub permutation_ctl_zs_cap: Option>, /// Merkle cap of LDEs of trace values. pub quotient_polys_cap: MerkleCap, /// Purported values of each polynomial at the challenge point. diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index cf9472e4..6dc94657 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -174,10 +174,10 @@ where None, ) }); - let permutation_zs_cap = permutation_ctl_zs_commitment + let permutation_ctl_zs_cap = permutation_ctl_zs_commitment .as_ref() .map(|commit| commit.merkle_tree.cap.clone()); - if let Some(cap) = &permutation_zs_cap { + if let Some(cap) = &permutation_ctl_zs_cap { challenger.observe_cap(cap); } @@ -271,7 +271,7 @@ where ); let proof = StarkProof { trace_cap: trace_commitment.merkle_tree.cap.clone(), - permutation_zs_cap, + permutation_ctl_zs_cap, quotient_polys_cap, openings, opening_proof, @@ -434,13 +434,17 @@ fn test_it<'a, F, C, S, const D: usize>( S: Stark, { let degree = 1 << degree_bits; + let rate_bits = 0; + + let size = degree << rate_bits; + let step = 1 << rate_bits; // Evaluation of the first Lagrange polynomial on the LDE domain. - let lagrange_first = PolynomialValues::selector(degree, 0); + let lagrange_first = PolynomialValues::selector(degree, 0).lde(rate_bits); // Evaluation of the last Lagrange polynomial on the LDE domain. - let lagrange_last = PolynomialValues::selector(degree, degree - 1); + let lagrange_last = PolynomialValues::selector(degree, degree - 1).lde(rate_bits); - let subgroup = F::two_adic_subgroup(degree_bits); + let subgroup = F::two_adic_subgroup(degree_bits + rate_bits); // Retrieve the LDE values at index `i`. let get_comm_values = |comm: &PolynomialBatch, i| -> Vec { @@ -453,9 +457,9 @@ fn test_it<'a, F, C, S, const D: usize>( // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); - let constraint_values = (0..degree) + let constraint_values = (0..size) .map(|i| { - let i_next = (i + 1) % degree; + let i_next = (i + step) % size; let x = subgroup[i]; let z_last = x - last; @@ -515,5 +519,8 @@ fn test_it<'a, F, C, S, const D: usize>( }) .collect::>(); - dbg!(constraint_values); + let values = transpose(&constraint_values); + for v in values { + assert!(v.iter().all(|x| x.is_zero())); + } } diff --git a/starky2/src/recursive_verifier.rs b/starky2/src/recursive_verifier.rs index e091d64c..6dcc7a7e 100644 --- a/starky2/src/recursive_verifier.rs +++ b/starky2/src/recursive_verifier.rs @@ -301,9 +301,10 @@ pub fn set_stark_proof_target, W, const D: usize>( &proof.openings.to_fri_openings(), ); - if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = - (&proof_target.permutation_zs_cap, &proof.permutation_zs_cap) - { + if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = ( + &proof_target.permutation_zs_cap, + &proof.permutation_ctl_zs_cap, + ) { witness.set_cap_target(permutation_zs_cap_target, permutation_zs_cap); } diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 7ef9381e..2f7a628e 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -44,8 +44,12 @@ where cross_table_lookups, } = all_stark; - let ctl_vars_per_table = - CTLCheckVars::from_proofs(&all_proof.proofs(), &cross_table_lookups, &ctl_challenges); + let ctl_vars_per_table = CTLCheckVars::from_proofs( + &all_proof.proofs(), + &cross_table_lookups, + &ctl_challenges, + 0, // TODO: Fix 0 + ); verify_stark_proof_with_challenges( cpu_stark, @@ -180,7 +184,7 @@ where } let merkle_caps = once(proof.trace_cap.clone()) - .chain(proof.permutation_zs_cap.clone()) + .chain(proof.permutation_ctl_zs_cap.clone()) .chain(once(proof.quotient_polys_cap.clone())) .collect_vec(); @@ -227,7 +231,7 @@ fn check_permutation_options< challenges: &StarkProofChallenges, ) -> Result<()> { let options_is_some = [ - proof_with_pis.proof.permutation_zs_cap.is_some(), + proof_with_pis.proof.permutation_ctl_zs_cap.is_some(), proof_with_pis .proof .openings From d421bd3593973c6cedaff6aa9fcea9ce7281e8e4 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 12 May 2022 22:35:13 +0200 Subject: [PATCH 24/33] Lints --- starky2/src/all_stark.rs | 3 --- starky2/src/get_challenges.rs | 4 +--- starky2/src/permutation.rs | 2 -- starky2/src/prover.rs | 3 +-- starky2/src/verifier.rs | 1 + 5 files changed, 3 insertions(+), 10 deletions(-) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index 0b5b0316..7542d3bf 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -92,7 +92,6 @@ impl, const D: usize> Stark for KeccakStark>(); let start = thread_rng().gen_range(0..cpu_stark.num_rows - keccak_stark.num_rows); - let start = 0; cpu_trace[2].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs0); cpu_trace[4].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs1); diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index 7e65a432..c7931ebf 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -21,7 +21,6 @@ use crate::stark::Stark; fn get_challenges( challenger: &mut Challenger, stark: &S, - trace_cap: &MerkleCap, permutation_ctl_zs_cap: Option<&MerkleCap>, quotient_polys_cap: &MerkleCap, openings: &StarkOpeningSet, @@ -131,7 +130,6 @@ where let degree_bits = self.proof.recover_degree_bits(config); let StarkProof { - trace_cap, permutation_ctl_zs_cap, quotient_polys_cap, openings, @@ -142,12 +140,12 @@ where pow_witness, .. }, + .. } = &self.proof; get_challenges::( challenger, stark, - trace_cap, permutation_ctl_zs_cap.as_ref(), quotient_polys_cap, openings, diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index 881a67ed..a5601a7b 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -1,7 +1,5 @@ //! Permutation arguments. -use std::fmt::Debug; - use itertools::Itertools; use plonky2::field::batch_util::batch_multiply_inplace; use plonky2::field::extension_field::{Extendable, FieldExtension}; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 6dc94657..2ab1886d 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -514,8 +514,7 @@ fn test_it<'a, F, C, S, const D: usize>( &lookup_check_data, &mut consumer, ); - let mut constraints_evals = consumer.accumulators(); - constraints_evals + consumer.accumulators() }) .collect::>(); diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 2f7a628e..2c4a7fba 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -220,6 +220,7 @@ fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { /// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff /// the Stark uses a permutation argument. +#[allow(dead_code)] fn check_permutation_options< F: RichField + Extendable, C: GenericConfig, From f9e929a0a91e134c5990926c586774d83ceb27e4 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 13 May 2022 10:35:59 +0200 Subject: [PATCH 25/33] Fix bugs when no ctls --- starky2/src/cross_table_lookup.rs | 28 ++++++++++------ starky2/src/stark.rs | 56 ++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 31 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 4a8423d5..fc0e6f33 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -156,23 +156,29 @@ impl<'a, F: RichField + Extendable, const D: usize> ) -> Vec> { let mut ctl_zs = proofs .iter() - .map(|p| { - p.proof - .openings - .permutation_lookup_zs - .as_ref() - .unwrap() // TODO: fix unwrap - .iter() - .skip(num_permutation_zs) - .zip( + .map(|p| -> Box> { + if p.proof.openings.permutation_lookup_zs.is_some() { + Box::new( p.proof .openings - .permutation_lookup_zs_right + .permutation_lookup_zs .as_ref() .unwrap() .iter() - .skip(num_permutation_zs), + .skip(num_permutation_zs) + .zip( + p.proof + .openings + .permutation_lookup_zs_right + .as_ref() + .unwrap() + .iter() + .skip(num_permutation_zs), + ), ) + } else { + Box::new(std::iter::empty()) + } }) .collect::>(); diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs index 35abb8ad..3f713477 100644 --- a/starky2/src/stark.rs +++ b/starky2/src/stark.rs @@ -1,3 +1,5 @@ +use std::iter::once; + use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::field::packed_field::PackedField; @@ -90,15 +92,24 @@ pub trait Stark, const D: usize>: Sync { FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS); let num_permutation_batches = self.num_permutation_batches(config); - let permutation_lookup_index = oracle_indices.next().unwrap(); - let permutation_lookup_zs_info = FriPolynomialInfo::from_range( - permutation_lookup_index, - 0..num_permutation_batches + num_ctl_zs, - ); - let lookup_zs_info = FriPolynomialInfo::from_range( - permutation_lookup_index, - num_permutation_batches..num_permutation_batches + num_ctl_zs, - ); + let permutation_lookup_zs_info = (num_permutation_batches + num_ctl_zs > 0).then(|| { + let permutation_lookup_index = oracle_indices.next().unwrap(); + FriPolynomialInfo::from_range( + permutation_lookup_index, + 0..num_permutation_batches + num_ctl_zs, + ) + }); + + let lookup_zs_info = (num_ctl_zs > 0).then(|| { + let index = permutation_lookup_zs_info + .as_ref() + .map(|info| info[0].oracle_index) + .unwrap_or_else(|| oracle_indices.next().unwrap()); + FriPolynomialInfo::from_range( + index, + num_permutation_batches..num_permutation_batches + num_ctl_zs, + ) + }); let quotient_info = FriPolynomialInfo::from_range( oracle_indices.next().unwrap(), @@ -107,24 +118,29 @@ pub trait Stark, const D: usize>: Sync { let zeta_batch = FriBatchInfo { point: zeta, - polynomials: [ - trace_info.clone(), - permutation_lookup_zs_info.clone(), - quotient_info, - ] - .concat(), + polynomials: once(trace_info.clone()) + .chain(permutation_lookup_zs_info.clone()) + .chain(once(quotient_info)) + .collect::>() + .concat(), }; let zeta_right_batch = FriBatchInfo { point: zeta.scalar_mul(g), - polynomials: [trace_info, permutation_lookup_zs_info].concat(), + polynomials: once(trace_info.clone()) + .chain(permutation_lookup_zs_info.clone()) + .collect::>() + .concat(), }; - let lookup_batch = FriBatchInfo { + let lookup_batch = lookup_zs_info.map(|info| FriBatchInfo { point: F::Extension::primitive_root_of_unity(degree_bits).inverse(), - polynomials: lookup_zs_info, - }; + polynomials: info, + }); FriInstanceInfo { oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()], - batches: vec![zeta_batch, zeta_right_batch, lookup_batch], + batches: once(zeta_batch) + .chain(once(zeta_right_batch)) + .chain(lookup_batch) + .collect::>(), } } From a38c19f952acdfca4c5825eb8ff340a740c1f8bc Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 13 May 2022 10:48:56 +0200 Subject: [PATCH 26/33] Fix num_permutation_zs --- starky2/src/all_stark.rs | 10 ++++++++++ starky2/src/cross_table_lookup.rs | 10 ++++++---- starky2/src/verifier.rs | 9 ++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index 7542d3bf..89eff627 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -4,6 +4,7 @@ use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::packed_field::PackedField; use plonky2::hash::hash_types::RichField; +use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::CrossTableLookup; use crate::stark::Stark; @@ -16,6 +17,15 @@ pub struct AllStark, const D: usize> { pub cross_table_lookups: Vec, } +impl, const D: usize> AllStark { + pub(crate) fn nums_permutation_zs(&self, config: &StarkConfig) -> Vec { + vec![ + self.cpu_stark.num_permutation_batches(config), + self.keccak_stark.num_permutation_batches(config), + ] + } +} + #[derive(Copy, Clone)] pub struct CpuStark { num_rows: usize, diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index fc0e6f33..8072a6c9 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -152,11 +152,13 @@ impl<'a, F: RichField + Extendable, const D: usize> proofs: &[&StarkProofWithPublicInputs], cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, - num_permutation_zs: usize, + num_permutation_zs: &[usize], ) -> Vec> { + debug_assert_eq!(proofs.len(), num_permutation_zs.len()); let mut ctl_zs = proofs .iter() - .map(|p| -> Box> { + .zip(num_permutation_zs) + .map(|(p, &num_permutation)| -> Box> { if p.proof.openings.permutation_lookup_zs.is_some() { Box::new( p.proof @@ -165,7 +167,7 @@ impl<'a, F: RichField + Extendable, const D: usize> .as_ref() .unwrap() .iter() - .skip(num_permutation_zs) + .skip(num_permutation) .zip( p.proof .openings @@ -173,7 +175,7 @@ impl<'a, F: RichField + Extendable, const D: usize> .as_ref() .unwrap() .iter() - .skip(num_permutation_zs), + .skip(num_permutation), ), ) } else { diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 2c4a7fba..78763fd2 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -38,6 +38,8 @@ where ctl_challenges, } = all_proof.get_challenges(&all_stark, config); + let nums_permutation_zs = all_stark.nums_permutation_zs(config); + let AllStark { cpu_stark, keccak_stark, @@ -48,7 +50,7 @@ where &all_proof.proofs(), &cross_table_lookups, &ctl_challenges, - 0, // TODO: Fix 0 + &nums_permutation_zs, ); verify_stark_proof_with_challenges( @@ -150,9 +152,10 @@ where l_1, l_last, ); + let num_permutation_zs = stark.num_permutation_batches(config); let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars { - local_zs: permutation_lookup_zs.as_ref().unwrap().clone(), - next_zs: permutation_lookup_zs_right.as_ref().unwrap().clone(), + local_zs: permutation_lookup_zs.as_ref().unwrap()[..num_permutation_zs].to_vec(), + next_zs: permutation_lookup_zs_right.as_ref().unwrap()[..num_permutation_zs].to_vec(), permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), }); eval_vanishing_poly::( From 8cd27939377e00714f4b622ae3e0a683e87ceee0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 13 May 2022 11:20:29 +0200 Subject: [PATCH 27/33] Some renaming + cleaning --- starky2/src/all_stark.rs | 2 + starky2/src/cross_table_lookup.rs | 32 +++++++------ starky2/src/permutation.rs | 4 +- starky2/src/proof.rs | 23 +++++---- starky2/src/prover.rs | 77 ++++++++++++++++--------------- starky2/src/stark.rs | 20 ++++---- starky2/src/vanishing_poly.rs | 10 ++-- starky2/src/verifier.rs | 45 ++++-------------- 8 files changed, 98 insertions(+), 115 deletions(-) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index 89eff627..0aa080d8 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -28,12 +28,14 @@ impl, const D: usize> AllStark { #[derive(Copy, Clone)] pub struct CpuStark { + #[allow(dead_code)] num_rows: usize, f: PhantomData, } #[derive(Copy, Clone)] pub struct KeccakStark { + #[allow(dead_code)] num_rows: usize, f: PhantomData, } diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 8072a6c9..af8f5c5d 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -46,12 +46,14 @@ impl CrossTableLookup { /// Lookup data for one table. #[derive(Clone)] -pub struct LookupData { +pub struct CtlData { + // Challenges used in the lookup argument. pub(crate) challenges: GrandProductChallengeSet, + // Vector of `(Z, columns)` where `Z` is a Z-polynomial for a lookup on columns `columns`. pub zs_columns: Vec<(PolynomialValues, Vec)>, } -impl LookupData { +impl CtlData { pub(crate) fn new(challenges: GrandProductChallengeSet) -> Self { Self { challenges, @@ -72,15 +74,15 @@ impl LookupData { } } -pub fn cross_table_lookup_zs, const D: usize>( +pub fn cross_table_lookup_data, const D: usize>( config: &StarkConfig, trace_poly_values: &[Vec>], cross_table_lookups: &[CrossTableLookup], challenger: &mut Challenger, -) -> Vec> { +) -> Vec> { let challenges = get_grand_product_challenge_set(challenger, config.num_challenges); cross_table_lookups.iter().fold( - vec![LookupData::new(challenges.clone()); trace_poly_values.len()], + vec![CtlData::new(challenges.clone()); trace_poly_values.len()], |mut acc, cross_table_lookup| { let CrossTableLookup { looking_table, @@ -159,11 +161,11 @@ impl<'a, F: RichField + Extendable, const D: usize> .iter() .zip(num_permutation_zs) .map(|(p, &num_permutation)| -> Box> { - if p.proof.openings.permutation_lookup_zs.is_some() { + if p.proof.openings.permutation_ctl_zs.is_some() { Box::new( p.proof .openings - .permutation_lookup_zs + .permutation_ctl_zs .as_ref() .unwrap() .iter() @@ -171,7 +173,7 @@ impl<'a, F: RichField + Extendable, const D: usize> .zip( p.proof .openings - .permutation_lookup_zs_right + .permutation_ctl_zs_right .as_ref() .unwrap() .iter() @@ -219,7 +221,7 @@ impl<'a, F: RichField + Extendable, const D: usize> pub(crate) fn eval_cross_table_lookup_checks( vars: StarkEvaluationVars, - lookup_data: &[CTLCheckVars], + ctl_vars: &[CTLCheckVars], consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -228,13 +230,13 @@ pub(crate) fn eval_cross_table_lookup_checks, S: Stark, { - for lookup_datum in lookup_data { + for lookup_vars in ctl_vars { let CTLCheckVars { local_z, next_z, challenges, columns, - } = lookup_datum; + } = lookup_vars; let mut factor = ReducingFactor::new(challenges.beta); let mut combine = |v: &[P]| -> P { factor.reduce_ext(columns.iter().map(|&i| v[i])) + FE::from_basefield(challenges.gamma) @@ -261,9 +263,9 @@ pub(crate) fn verify_cross_table_lookups< .iter() .map(|p| p.proof.recover_degree_bits(config)) .collect::>(); - let mut lookup_zs_openings = proofs + let mut ctl_zs_openings = proofs .iter() - .map(|p| p.proof.openings.lookup_zs_last.iter()) + .map(|p| p.proof.openings.ctl_zs_last.iter()) .collect::>(); for ( i, @@ -276,8 +278,8 @@ pub(crate) fn verify_cross_table_lookups< { let looking_degree = 1 << degrees_bits[looking_table as usize]; let looked_degree = 1 << degrees_bits[looked_table as usize]; - let looking_z = *lookup_zs_openings[looking_table as usize].next().unwrap(); - let looked_z = *lookup_zs_openings[looked_table as usize].next().unwrap(); + let looking_z = *ctl_zs_openings[looking_table as usize].next().unwrap(); + let looked_z = *ctl_zs_openings[looked_table as usize].next().unwrap(); ensure!( looking_z == looked_z diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index a5601a7b..51142724 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -261,7 +261,7 @@ pub(crate) fn eval_permutation_checks, - permutation_data: PermutationCheckVars, + permutation_vars: PermutationCheckVars, consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -274,7 +274,7 @@ pub(crate) fn eval_permutation_checks { pub struct StarkOpeningSet, const D: usize> { pub local_values: Vec, pub next_values: Vec, - pub permutation_lookup_zs: Option>, - pub permutation_lookup_zs_right: Option>, - pub lookup_zs_last: Vec, + pub permutation_ctl_zs: Option>, + pub permutation_ctl_zs_right: Option>, + pub ctl_zs_last: Vec, pub quotient_polys: Vec, } @@ -155,7 +155,7 @@ impl, const D: usize> StarkOpeningSet { zeta: F::Extension, g: F, trace_commitment: &PolynomialBatch, - permutation_lookup_zs_commitment: Option<&PolynomialBatch>, + permutation_ctl_zs_commitment: Option<&PolynomialBatch>, quotient_commitment: &PolynomialBatch, degree_bits: usize, num_permutation_zs: usize, @@ -176,11 +176,10 @@ impl, const D: usize> StarkOpeningSet { Self { local_values: eval_commitment(zeta, trace_commitment), next_values: eval_commitment(zeta_right, trace_commitment), - permutation_lookup_zs: permutation_lookup_zs_commitment - .map(|c| eval_commitment(zeta, c)), - permutation_lookup_zs_right: permutation_lookup_zs_commitment + permutation_ctl_zs: permutation_ctl_zs_commitment.map(|c| eval_commitment(zeta, c)), + permutation_ctl_zs_right: permutation_ctl_zs_commitment .map(|c| eval_commitment(zeta_right, c)), - lookup_zs_last: permutation_lookup_zs_commitment + ctl_zs_last: permutation_ctl_zs_commitment .map(|c| { eval_commitment_base(F::primitive_root_of_unity(degree_bits).inverse(), c) [num_permutation_zs..] @@ -196,7 +195,7 @@ impl, const D: usize> StarkOpeningSet { values: self .local_values .iter() - .chain(self.permutation_lookup_zs.iter().flatten()) + .chain(self.permutation_ctl_zs.iter().flatten()) .chain(&self.quotient_polys) .copied() .collect_vec(), @@ -205,16 +204,16 @@ impl, const D: usize> StarkOpeningSet { values: self .next_values .iter() - .chain(self.permutation_lookup_zs_right.iter().flatten()) + .chain(self.permutation_ctl_zs_right.iter().flatten()) .copied() .collect_vec(), }; let mut batches = vec![zeta_batch, zeta_right_batch]; - if !self.lookup_zs_last.is_empty() { + if !self.ctl_zs_last.is_empty() { batches.push(FriOpeningBatch { values: self - .lookup_zs_last + .ctl_zs_last .iter() .copied() .map(F::Extension::from_basefield) diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 2ab1886d..4120275f 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -21,7 +21,7 @@ use rayon::prelude::*; use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cross_table_lookup::{cross_table_lookup_zs, CTLCheckVars, LookupData}; +use crate::cross_table_lookup::{cross_table_lookup_data, CTLCheckVars, CtlData}; use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_grand_product_challenge_sets, GrandProductChallengeSet, @@ -31,6 +31,7 @@ use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; use crate::vars::StarkEvaluationVars; +/// Compute all STARK proofs. pub fn prove( all_stark: &AllStark, config: &StarkConfig, @@ -80,7 +81,7 @@ where challenger.observe_cap(cap); } - let lookup_zs = cross_table_lookup_zs::( + let ctl_data_per_table = cross_table_lookup_data::( config, &trace_poly_values, &all_stark.cross_table_lookups, @@ -92,7 +93,7 @@ where config, &trace_poly_values[Table::Cpu as usize], &trace_commitments[Table::Cpu as usize], - &lookup_zs[Table::Cpu as usize], + &ctl_data_per_table[Table::Cpu as usize], &public_inputs[Table::Cpu as usize], &mut challenger, timing, @@ -102,7 +103,7 @@ where config, &trace_poly_values[Table::Keccak as usize], &trace_commitments[Table::Keccak as usize], - &lookup_zs[Table::Keccak as usize], + &ctl_data_per_table[Table::Keccak as usize], &public_inputs[Table::Keccak as usize], &mut challenger, timing, @@ -114,12 +115,13 @@ where }) } +/// Compute proof for a single STARK table. fn prove_single_table( stark: &S, config: &StarkConfig, trace_poly_values: &[PolynomialValues], trace_commitment: &PolynomialBatch, - lookup_data: &LookupData, + ctl_data: &CtlData, public_inputs: &[F], challenger: &mut Challenger, timing: &mut TimingTree, @@ -157,9 +159,9 @@ where let num_permutation_zs = permutation_zs.as_ref().map(|v| v.len()).unwrap_or(0); let z_polys = match permutation_zs { - None => lookup_data.z_polys(), + None => ctl_data.z_polys(), Some(mut permutation_zs) => { - permutation_zs.extend(lookup_data.z_polys()); + permutation_zs.extend(ctl_data.z_polys()); permutation_zs } }; @@ -182,24 +184,26 @@ where } let alphas = challenger.get_n_challenges(config.num_challenges); - test_it( - stark, - trace_commitment, - permutation_ctl_zs_commitment.as_ref(), - permutation_challenges.as_ref(), - lookup_data, - public_inputs, - alphas.clone(), - degree_bits, - num_permutation_zs, - config, - ); + if cfg!(test) { + check_constraints( + stark, + trace_commitment, + permutation_ctl_zs_commitment.as_ref(), + permutation_challenges.as_ref(), + ctl_data, + public_inputs, + alphas.clone(), + degree_bits, + num_permutation_zs, + config, + ); + } let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( stark, trace_commitment, permutation_ctl_zs_commitment.as_ref(), permutation_challenges.as_ref(), - lookup_data, + ctl_data, public_inputs, alphas, degree_bits, @@ -241,7 +245,6 @@ where "Opening point is in the subgroup." ); - // TODO: Add openings of lookup Z polynomials. let openings = StarkOpeningSet::new( zeta, g, @@ -262,7 +265,7 @@ where timing, "compute openings proof", PolynomialBatch::prove_openings( - &stark.fri_instance(zeta, g, degree_bits, lookup_data.len(), config), + &stark.fri_instance(zeta, g, degree_bits, ctl_data.len(), config), &initial_merkle_trees, challenger, &fri_params, @@ -290,7 +293,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( trace_commitment: &'a PolynomialBatch, permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, permutation_challenges: Option<&'a Vec>>, - lookup_data: &LookupData, + ctl_data: &CtlData, public_inputs: &[F], alphas: Vec, degree_bits: usize, @@ -377,7 +380,7 @@ where } else { None }; - let lookup_check_data = lookup_data + let ctl_vars = ctl_data .zs_columns .iter() .enumerate() @@ -388,7 +391,7 @@ where next_z: permutation_ctl_zs_commitment .unwrap() .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], - challenges: lookup_data.challenges.challenges[i % config.num_challenges], + challenges: ctl_data.challenges.challenges[i % config.num_challenges], columns, }) .collect::>(); @@ -397,7 +400,7 @@ where config, vars, permutation_check_data, - &lookup_check_data, + &ctl_vars, &mut consumer, ); let mut constraints_evals = consumer.accumulators(); @@ -417,12 +420,14 @@ where .collect() } -fn test_it<'a, F, C, S, const D: usize>( +/// Check that all constraints evaluate to zero on `H`. +/// Can also be used to check the degree of the constraints by evaluating on a larger subgroup. +fn check_constraints<'a, F, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, permutation_challenges: Option<&'a Vec>>, - lookup_data: &LookupData, + ctl_data: &CtlData, public_inputs: &[F], alphas: Vec, degree_bits: usize, @@ -434,23 +439,23 @@ fn test_it<'a, F, C, S, const D: usize>( S: Stark, { let degree = 1 << degree_bits; - let rate_bits = 0; + let rate_bits = 0; // Set this to higher value to check constraint degree. let size = degree << rate_bits; let step = 1 << rate_bits; - // Evaluation of the first Lagrange polynomial on the LDE domain. + // Evaluation of the first Lagrange polynomial. let lagrange_first = PolynomialValues::selector(degree, 0).lde(rate_bits); - // Evaluation of the last Lagrange polynomial on the LDE domain. + // Evaluation of the last Lagrange polynomial. let lagrange_last = PolynomialValues::selector(degree, degree - 1).lde(rate_bits); let subgroup = F::two_adic_subgroup(degree_bits + rate_bits); - // Retrieve the LDE values at index `i`. + // Retrieve the polynomials values at index `i`. let get_comm_values = |comm: &PolynomialBatch, i| -> Vec { comm.polynomials .iter() - .map(|poly| poly.eval(subgroup[i])) + .map(|poly| poly.eval(subgroup[i])) // O(n^2) FTW .collect() }; @@ -493,7 +498,7 @@ fn test_it<'a, F, C, S, const D: usize>( } else { None }; - let lookup_check_data = lookup_data + let ctl_vars = ctl_data .zs_columns .iter() .enumerate() @@ -502,7 +507,7 @@ fn test_it<'a, F, C, S, const D: usize>( [num_permutation_zs + iii], next_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i_next) [num_permutation_zs + iii], - challenges: lookup_data.challenges.challenges[iii % config.num_challenges], + challenges: ctl_data.challenges.challenges[iii % config.num_challenges], columns, }) .collect::>(); @@ -511,7 +516,7 @@ fn test_it<'a, F, C, S, const D: usize>( config, vars, permutation_check_data, - &lookup_check_data, + &ctl_vars, &mut consumer, ); consumer.accumulators() diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs index 3f713477..bb8429cd 100644 --- a/starky2/src/stark.rs +++ b/starky2/src/stark.rs @@ -92,16 +92,16 @@ pub trait Stark, const D: usize>: Sync { FriPolynomialInfo::from_range(oracle_indices.next().unwrap(), 0..Self::COLUMNS); let num_permutation_batches = self.num_permutation_batches(config); - let permutation_lookup_zs_info = (num_permutation_batches + num_ctl_zs > 0).then(|| { - let permutation_lookup_index = oracle_indices.next().unwrap(); + let permutation_ctl_zs_info = (num_permutation_batches + num_ctl_zs > 0).then(|| { + let permutation_ctl_index = oracle_indices.next().unwrap(); FriPolynomialInfo::from_range( - permutation_lookup_index, + permutation_ctl_index, 0..num_permutation_batches + num_ctl_zs, ) }); - let lookup_zs_info = (num_ctl_zs > 0).then(|| { - let index = permutation_lookup_zs_info + let ctl_zs_info = (num_ctl_zs > 0).then(|| { + let index = permutation_ctl_zs_info .as_ref() .map(|info| info[0].oracle_index) .unwrap_or_else(|| oracle_indices.next().unwrap()); @@ -119,19 +119,19 @@ pub trait Stark, const D: usize>: Sync { let zeta_batch = FriBatchInfo { point: zeta, polynomials: once(trace_info.clone()) - .chain(permutation_lookup_zs_info.clone()) + .chain(permutation_ctl_zs_info.clone()) .chain(once(quotient_info)) .collect::>() .concat(), }; let zeta_right_batch = FriBatchInfo { point: zeta.scalar_mul(g), - polynomials: once(trace_info.clone()) - .chain(permutation_lookup_zs_info.clone()) + polynomials: once(trace_info) + .chain(permutation_ctl_zs_info) .collect::>() .concat(), }; - let lookup_batch = lookup_zs_info.map(|info| FriBatchInfo { + let ctl_last_batch = ctl_zs_info.map(|info| FriBatchInfo { point: F::Extension::primitive_root_of_unity(degree_bits).inverse(), polynomials: info, }); @@ -139,7 +139,7 @@ pub trait Stark, const D: usize>: Sync { oracles: vec![no_blinding_oracle; oracle_indices.next().unwrap()], batches: once(zeta_batch) .chain(once(zeta_right_batch)) - .chain(lookup_batch) + .chain(ctl_last_batch) .collect::>(), } } diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index baa9aeaa..c23462c1 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -18,8 +18,8 @@ pub(crate) fn eval_vanishing_poly, - permutation_data: Option>, - lookup_data: &[CTLCheckVars], + permutation_vars: Option>, + ctl_vars: &[CTLCheckVars], consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -29,16 +29,16 @@ pub(crate) fn eval_vanishing_poly, { stark.eval_packed_generic(vars, consumer); - if let Some(permutation_data) = permutation_data { + if let Some(permutation_vars) = permutation_vars { eval_permutation_checks::( stark, config, vars, - permutation_data, + permutation_vars, consumer, ); } - eval_cross_table_lookup_checks::(vars, lookup_data, consumer); + eval_cross_table_lookup_checks::(vars, ctl_vars, consumer); } pub(crate) fn eval_vanishing_poly_recursively( diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index 78763fd2..bb156128 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -76,27 +76,6 @@ where ) } -// pub fn verify_stark_proof< -// F: RichField + Extendable, -// C: GenericConfig, -// S: Stark, -// const D: usize, -// >( -// stark: S, -// proof_with_pis: StarkProofWithPublicInputs, -// config: &StarkConfig, -// ) -> Result<()> -// where -// [(); S::COLUMNS]:, -// [(); S::PUBLIC_INPUTS]:, -// [(); C::Hasher::HASH_SIZE]:, -// { -// ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS); -// let degree_bits = proof_with_pis.proof.recover_degree_bits(config); -// let challenges = proof_with_pis.get_challenges(&stark, config, degree_bits); -// verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config) -// } - pub(crate) fn verify_stark_proof_with_challenges< F: RichField + Extendable, C: GenericConfig, @@ -106,7 +85,7 @@ pub(crate) fn verify_stark_proof_with_challenges< stark: S, proof_with_pis: &StarkProofWithPublicInputs, challenges: StarkProofChallenges, - lookup_data: &[CTLCheckVars], + ctl_vars: &[CTLCheckVars], config: &StarkConfig, ) -> Result<()> where @@ -123,9 +102,9 @@ where let StarkOpeningSet { local_values, next_values, - permutation_lookup_zs, - permutation_lookup_zs_right, - lookup_zs_last, + permutation_ctl_zs, + permutation_ctl_zs_right, + ctl_zs_last, quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { @@ -154,8 +133,8 @@ where ); let num_permutation_zs = stark.num_permutation_batches(config); let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars { - local_zs: permutation_lookup_zs.as_ref().unwrap()[..num_permutation_zs].to_vec(), - next_zs: permutation_lookup_zs_right.as_ref().unwrap()[..num_permutation_zs].to_vec(), + local_zs: permutation_ctl_zs.as_ref().unwrap()[..num_permutation_zs].to_vec(), + next_zs: permutation_ctl_zs_right.as_ref().unwrap()[..num_permutation_zs].to_vec(), permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), }); eval_vanishing_poly::( @@ -163,7 +142,7 @@ where config, vars, permutation_data, - lookup_data, + ctl_vars, &mut consumer, ); let vanishing_polys_zeta = consumer.accumulators(); @@ -196,7 +175,7 @@ where challenges.stark_zeta, F::primitive_root_of_unity(degree_bits), degree_bits, - lookup_zs_last.len(), + ctl_zs_last.len(), config, ), &proof.openings.to_fri_openings(), @@ -236,15 +215,11 @@ fn check_permutation_options< ) -> Result<()> { let options_is_some = [ proof_with_pis.proof.permutation_ctl_zs_cap.is_some(), + proof_with_pis.proof.openings.permutation_ctl_zs.is_some(), proof_with_pis .proof .openings - .permutation_lookup_zs - .is_some(), - proof_with_pis - .proof - .openings - .permutation_lookup_zs_right + .permutation_ctl_zs_right .is_some(), challenges.permutation_challenge_sets.is_some(), ]; From aa77660bfe4d48db1f5f362b464a82085342685b Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 13 May 2022 14:16:28 +0200 Subject: [PATCH 28/33] Back to const generic + arrays --- starky2/src/all_stark.rs | 4 +-- starky2/src/cross_table_lookup.rs | 2 +- starky2/src/permutation.rs | 2 +- starky2/src/prover.rs | 49 ++++++++++++++++++++++--------- starky2/src/stark.rs | 11 +++++-- starky2/src/stark_testing.rs | 7 +++-- starky2/src/vanishing_poly.rs | 2 +- starky2/src/vars.rs | 8 ++--- starky2/src/verifier.rs | 8 +++-- 9 files changed, 62 insertions(+), 31 deletions(-) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index 0aa080d8..bcfe9935 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -52,7 +52,7 @@ impl, const D: usize> Stark for CpuStark( &self, - _vars: StarkEvaluationVars, + _vars: StarkEvaluationVars, _yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -79,7 +79,7 @@ impl, const D: usize> Stark for KeccakStark( &self, - _vars: StarkEvaluationVars, + _vars: StarkEvaluationVars, _yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index af8f5c5d..14932ea2 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -220,7 +220,7 @@ impl<'a, F: RichField + Extendable, const D: usize> } pub(crate) fn eval_cross_table_lookup_checks( - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, ctl_vars: &[CTLCheckVars], consumer: &mut ConstraintConsumer

, ) where diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index 51142724..56784f8f 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -260,7 +260,7 @@ where pub(crate) fn eval_permutation_checks( stark: &S, config: &StarkConfig, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, permutation_vars: PermutationCheckVars, consumer: &mut ConstraintConsumer

, ) where diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 4120275f..a5f1c6fd 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -18,7 +18,7 @@ use plonky2::util::transpose; use plonky2_util::{log2_ceil, log2_strict}; use rayon::prelude::*; -use crate::all_stark::{AllStark, Table}; +use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; use crate::cross_table_lookup::{cross_table_lookup_data, CTLCheckVars, CtlData}; @@ -44,6 +44,10 @@ where C: GenericConfig, [(); <::Packing>::WIDTH]:, [(); C::Hasher::HASH_SIZE]:, + [(); CpuStark::::COLUMNS]:, + [(); CpuStark::::PUBLIC_INPUTS]:, + [(); KeccakStark::::COLUMNS]:, + [(); KeccakStark::::PUBLIC_INPUTS]:, { let num_starks = Table::Keccak as usize + 1; debug_assert_eq!(num_starks, trace_poly_values.len()); @@ -94,7 +98,10 @@ where &trace_poly_values[Table::Cpu as usize], &trace_commitments[Table::Cpu as usize], &ctl_data_per_table[Table::Cpu as usize], - &public_inputs[Table::Cpu as usize], + public_inputs[Table::Cpu as usize] + .clone() + .try_into() + .unwrap(), &mut challenger, timing, )?; @@ -104,7 +111,10 @@ where &trace_poly_values[Table::Keccak as usize], &trace_commitments[Table::Keccak as usize], &ctl_data_per_table[Table::Keccak as usize], - &public_inputs[Table::Keccak as usize], + public_inputs[Table::Keccak as usize] + .clone() + .try_into() + .unwrap(), &mut challenger, timing, )?; @@ -122,7 +132,7 @@ fn prove_single_table( trace_poly_values: &[PolynomialValues], trace_commitment: &PolynomialBatch, ctl_data: &CtlData, - public_inputs: &[F], + public_inputs: [F; S::PUBLIC_INPUTS], challenger: &mut Challenger, timing: &mut TimingTree, ) -> Result> @@ -132,8 +142,8 @@ where S: Stark, [(); <::Packing>::WIDTH]:, [(); C::Hasher::HASH_SIZE]:, - // [(); S::COLUMNS]:, - // [(); S::PUBLIC_INPUTS]:, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, { let degree = trace_poly_values[0].len(); let degree_bits = log2_strict(degree); @@ -294,7 +304,7 @@ fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, permutation_challenges: Option<&'a Vec>>, ctl_data: &CtlData, - public_inputs: &[F], + public_inputs: [F; S::PUBLIC_INPUTS], alphas: Vec, degree_bits: usize, num_permutation_zs: usize, @@ -305,6 +315,9 @@ where P: PackedField, C: GenericConfig, S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, + [(); P::WIDTH]:, { let degree = 1 << degree_bits; let rate_bits = config.fri_config.rate_bits; @@ -327,8 +340,12 @@ where let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, quotient_degree_bits); // Retrieve the LDE values at index `i`. - let get_trace_values_packed = - |i_start| -> Vec

{ trace_commitment.get_lde_values_packed(i_start, step) }; + let get_trace_values_packed = |i_start| -> [P; S::COLUMNS] { + trace_commitment + .get_lde_values_packed(i_start, step) + .try_into() + .unwrap() + }; // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); @@ -362,7 +379,7 @@ where let vars = StarkEvaluationVars { local_values: &get_trace_values_packed(i_start), next_values: &get_trace_values_packed(i_next_start), - public_inputs, + public_inputs: &public_inputs, }; let permutation_check_data = if let (Some(permutation_zs_commitment), Some(permutation_challenge_sets)) = @@ -428,7 +445,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, permutation_challenges: Option<&'a Vec>>, ctl_data: &CtlData, - public_inputs: &[F], + public_inputs: [F; S::PUBLIC_INPUTS], alphas: Vec, degree_bits: usize, num_permutation_zs: usize, @@ -437,6 +454,8 @@ fn check_constraints<'a, F, C, S, const D: usize>( F: RichField + Extendable, C: GenericConfig, S: Stark, + [(); S::COLUMNS]:, + [(); S::PUBLIC_INPUTS]:, { let degree = 1 << degree_bits; let rate_bits = 0; // Set this to higher value to check constraint degree. @@ -478,9 +497,11 @@ fn check_constraints<'a, F, C, S, const D: usize>( lagrange_basis_last, ); let vars = StarkEvaluationVars { - local_values: &get_comm_values(trace_commitment, i), - next_values: &get_comm_values(trace_commitment, i_next), - public_inputs, + local_values: &get_comm_values(trace_commitment, i).try_into().unwrap(), + next_values: &get_comm_values(trace_commitment, i_next) + .try_into() + .unwrap(), + public_inputs: &public_inputs, }; let permutation_check_data = if let (Some(permutation_zs_commitment), Some(permutation_challenge_sets)) = diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs index bb8429cd..dcf1a73f 100644 --- a/starky2/src/stark.rs +++ b/starky2/src/stark.rs @@ -33,7 +33,7 @@ pub trait Stark, const D: usize>: Sync { /// constraints over `F`. fn eval_packed_generic( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) where FE: FieldExtension, @@ -42,7 +42,7 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a vector of points from the base field `F`. fn eval_packed_base>( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, yield_constr: &mut ConstraintConsumer

, ) { self.eval_packed_generic(vars, yield_constr) @@ -51,7 +51,12 @@ pub trait Stark, const D: usize>: Sync { /// Evaluate constraints at a single point from the degree `D` extension field. fn eval_ext( &self, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars< + F::Extension, + F::Extension, + { Self::COLUMNS }, + { Self::PUBLIC_INPUTS }, + >, yield_constr: &mut ConstraintConsumer, ) { self.eval_packed_generic(vars, yield_constr) diff --git a/starky2/src/stark_testing.rs b/starky2/src/stark_testing.rs index 10834649..222ebf39 100644 --- a/starky2/src/stark_testing.rs +++ b/starky2/src/stark_testing.rs @@ -37,8 +37,11 @@ where let constraint_evals = (0..size) .map(|i| { let vars = StarkEvaluationVars { - local_values: &trace_ldes[i], - next_values: &trace_ldes[(i + (1 << rate_bits)) % size], + local_values: &trace_ldes[i].clone().try_into().unwrap(), + next_values: &trace_ldes[(i + (1 << rate_bits)) % size] + .clone() + .try_into() + .unwrap(), public_inputs: &public_inputs, }; diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index c23462c1..459c27de 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -17,7 +17,7 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub(crate) fn eval_vanishing_poly( stark: &S, config: &StarkConfig, - vars: StarkEvaluationVars, + vars: StarkEvaluationVars, permutation_vars: Option>, ctl_vars: &[CTLCheckVars], consumer: &mut ConstraintConsumer

, diff --git a/starky2/src/vars.rs b/starky2/src/vars.rs index bef8557b..cb83aeb7 100644 --- a/starky2/src/vars.rs +++ b/starky2/src/vars.rs @@ -3,14 +3,14 @@ use plonky2::field::packed_field::PackedField; use plonky2::iop::ext_target::ExtensionTarget; #[derive(Debug, Copy, Clone)] -pub struct StarkEvaluationVars<'a, F, P> +pub struct StarkEvaluationVars<'a, F, P, const COLUMNS: usize, const PUBLIC_INPUTS: usize> where F: Field, P: PackedField, { - pub local_values: &'a [P], - pub next_values: &'a [P], - pub public_inputs: &'a [P::Scalar], + pub local_values: &'a [P; COLUMNS], + pub next_values: &'a [P; COLUMNS], + pub public_inputs: &'a [P::Scalar; PUBLIC_INPUTS], } #[derive(Debug, Copy, Clone)] diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index bb156128..c219dbe4 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -108,13 +108,15 @@ where quotient_polys, } = &proof.openings; let vars = StarkEvaluationVars { - local_values, - next_values, + local_values: &local_values.to_vec().try_into().unwrap(), + next_values: &next_values.to_vec().try_into().unwrap(), public_inputs: &public_inputs .iter() .copied() .map(F::Extension::from_basefield) - .collect::>(), + .collect::>() + .try_into() + .unwrap(), }; let degree_bits = proof.recover_degree_bits(config); From 02db6c93ecfd12e4d088b8a1d229d2ae40473f80 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 13 May 2022 14:20:59 +0200 Subject: [PATCH 29/33] Add permutation in mock STARKs for good measure --- starky2/src/all_stark.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index bcfe9935..262b3fb2 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -7,6 +7,7 @@ use plonky2::hash::hash_types::RichField; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::CrossTableLookup; +use crate::permutation::PermutationPair; use crate::stark::Stark; use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; @@ -71,6 +72,10 @@ impl, const D: usize> Stark for CpuStark usize { 3 } + + fn permutation_pairs(&self) -> Vec { + vec![PermutationPair::singletons(8, 9)] + } } impl, const D: usize> Stark for KeccakStark { @@ -98,6 +103,10 @@ impl, const D: usize> Stark for KeccakStark usize { 3 } + + fn permutation_pairs(&self) -> Vec { + vec![PermutationPair::singletons(0, 6)] + } } #[cfg(test)] From 863d9a863c962b841364bc7a9c30736c73a3df70 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 16 May 2022 20:45:30 +0200 Subject: [PATCH 30/33] PR comments --- starky2/src/cross_table_lookup.rs | 12 ++++++------ starky2/src/proof.rs | 6 ++++++ starky2/src/prover.rs | 6 +++--- starky2/src/vanishing_poly.rs | 4 ++-- starky2/src/verifier.rs | 6 +++--- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 14932ea2..57d84b90 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -44,10 +44,10 @@ impl CrossTableLookup { } } -/// Lookup data for one table. +/// Cross-table lookup data for one table. #[derive(Clone)] pub struct CtlData { - // Challenges used in the lookup argument. + // Challenges used in the argument. pub(crate) challenges: GrandProductChallengeSet, // Vector of `(Z, columns)` where `Z` is a Z-polynomial for a lookup on columns `columns`. pub zs_columns: Vec<(PolynomialValues, Vec)>, @@ -135,7 +135,7 @@ fn partial_products( } #[derive(Clone)] -pub struct CTLCheckVars<'a, F, FE, P, const D2: usize> +pub struct CtlCheckVars<'a, F, FE, P, const D2: usize> where F: Field, FE: FieldExtension, @@ -148,7 +148,7 @@ where } impl<'a, F: RichField + Extendable, const D: usize> - CTLCheckVars<'a, F, F::Extension, F::Extension, D> + CtlCheckVars<'a, F, F::Extension, F::Extension, D> { pub(crate) fn from_proofs>( proofs: &[&StarkProofWithPublicInputs], @@ -221,7 +221,7 @@ impl<'a, F: RichField + Extendable, const D: usize> pub(crate) fn eval_cross_table_lookup_checks( vars: StarkEvaluationVars, - ctl_vars: &[CTLCheckVars], + ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, @@ -231,7 +231,7 @@ pub(crate) fn eval_cross_table_lookup_checks, { for lookup_vars in ctl_vars { - let CTLCheckVars { + let CtlCheckVars { local_z, next_z, challenges, diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index 6cd4fde1..f206f23f 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -142,11 +142,17 @@ pub(crate) struct StarkProofChallengesTarget { /// Purported values of each polynomial at the challenge point. #[derive(Debug, Clone)] pub struct StarkOpeningSet, const D: usize> { + /// Openings of trace polynomials at `zeta`. pub local_values: Vec, + /// Openings of trace polynomials at `g * zeta`. pub next_values: Vec, + /// Openings of permutations and cross-table lookups `Z` polynomials at `zeta`. pub permutation_ctl_zs: Option>, + /// Openings of permutations and cross-table lookups `Z` polynomials at `g * zeta`. pub permutation_ctl_zs_right: Option>, + /// Openings of cross-table lookups `Z` polynomials at `g^-1`. pub ctl_zs_last: Vec, + /// Openings of quotient polynomials at `zeta`. pub quotient_polys: Vec, } diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index a5f1c6fd..4fc602c1 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -21,7 +21,7 @@ use rayon::prelude::*; use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cross_table_lookup::{cross_table_lookup_data, CTLCheckVars, CtlData}; +use crate::cross_table_lookup::{cross_table_lookup_data, CtlCheckVars, CtlData}; use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_grand_product_challenge_sets, GrandProductChallengeSet, @@ -401,7 +401,7 @@ where .zs_columns .iter() .enumerate() - .map(|(i, (_, columns))| CTLCheckVars:: { + .map(|(i, (_, columns))| CtlCheckVars:: { local_z: permutation_ctl_zs_commitment .unwrap() .get_lde_values_packed(i_start, step)[num_permutation_zs + i], @@ -523,7 +523,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( .zs_columns .iter() .enumerate() - .map(|(iii, (_, columns))| CTLCheckVars:: { + .map(|(iii, (_, columns))| CtlCheckVars:: { local_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i) [num_permutation_zs + iii], next_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i_next) diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index 459c27de..aac8fc80 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -6,7 +6,7 @@ use plonky2::plonk::config::GenericConfig; use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::cross_table_lookup::{eval_cross_table_lookup_checks, CTLCheckVars}; +use crate::cross_table_lookup::{eval_cross_table_lookup_checks, CtlCheckVars}; use crate::permutation::{ eval_permutation_checks, eval_permutation_checks_recursively, PermutationCheckDataTarget, PermutationCheckVars, @@ -19,7 +19,7 @@ pub(crate) fn eval_vanishing_poly, permutation_vars: Option>, - ctl_vars: &[CTLCheckVars], + ctl_vars: &[CtlCheckVars], consumer: &mut ConstraintConsumer

, ) where F: RichField + Extendable, diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index c219dbe4..a5411386 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -12,7 +12,7 @@ use plonky2::plonk::plonk_common::reduce_with_powers; use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; -use crate::cross_table_lookup::{verify_cross_table_lookups, CTLCheckVars}; +use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars}; use crate::permutation::PermutationCheckVars; use crate::proof::{ AllProof, AllProofChallenges, StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs, @@ -46,7 +46,7 @@ where cross_table_lookups, } = all_stark; - let ctl_vars_per_table = CTLCheckVars::from_proofs( + let ctl_vars_per_table = CtlCheckVars::from_proofs( &all_proof.proofs(), &cross_table_lookups, &ctl_challenges, @@ -85,7 +85,7 @@ pub(crate) fn verify_stark_proof_with_challenges< stark: S, proof_with_pis: &StarkProofWithPublicInputs, challenges: StarkProofChallenges, - ctl_vars: &[CTLCheckVars], + ctl_vars: &[CtlCheckVars], config: &StarkConfig, ) -> Result<()> where From db2aae515f4974571b15cbb4b561c7f2c7f70e4a Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 17 May 2022 09:24:22 +0200 Subject: [PATCH 31/33] Add default value to CTL --- starky2/src/all_stark.rs | 7 +++++- starky2/src/cross_table_lookup.rs | 39 +++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index 262b3fb2..d00f5b8c 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -15,7 +15,7 @@ use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; pub struct AllStark, const D: usize> { pub cpu_stark: CpuStark, pub keccak_stark: KeccakStark, - pub cross_table_lookups: Vec, + pub cross_table_lookups: Vec>, } impl, const D: usize> AllStark { @@ -155,7 +155,11 @@ mod tests { .collect::>(); let start = thread_rng().gen_range(0..cpu_stark.num_rows - keccak_stark.num_rows); + let default = vec![F::ONE; 2]; + + cpu_trace[2].values = vec![default[0]; cpu_stark.num_rows]; cpu_trace[2].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs0); + cpu_trace[4].values = vec![default[1]; cpu_stark.num_rows]; cpu_trace[4].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs1); keccak_trace[3].values[..].copy_from_slice(&vs0); @@ -166,6 +170,7 @@ mod tests { looking_columns: vec![2, 4], looked_table: Table::Keccak, looked_columns: vec![3, 5], + default: vec![F::ONE; 2], }]; let all_stark = AllStark { diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 57d84b90..9c7b8e03 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -20,19 +20,21 @@ use crate::stark::Stark; use crate::vars::StarkEvaluationVars; #[derive(Clone)] -pub struct CrossTableLookup { +pub struct CrossTableLookup { pub looking_table: Table, pub looking_columns: Vec, pub looked_table: Table, pub looked_columns: Vec, + pub default: Vec, } -impl CrossTableLookup { +impl CrossTableLookup { pub fn new( looking_table: Table, looking_columns: Vec, looked_table: Table, looked_columns: Vec, + default: Vec, ) -> Self { assert_eq!(looking_columns.len(), looked_columns.len()); Self { @@ -40,6 +42,7 @@ impl CrossTableLookup { looking_columns, looked_table, looked_columns, + default, } } } @@ -47,9 +50,9 @@ impl CrossTableLookup { /// Cross-table lookup data for one table. #[derive(Clone)] pub struct CtlData { - // Challenges used in the argument. + /// Challenges used in the argument. pub(crate) challenges: GrandProductChallengeSet, - // Vector of `(Z, columns)` where `Z` is a Z-polynomial for a lookup on columns `columns`. + /// Vector of `(Z, columns)` where `Z` is a Z-polynomial for a lookup on columns `columns`. pub zs_columns: Vec<(PolynomialValues, Vec)>, } @@ -77,7 +80,7 @@ impl CtlData { pub fn cross_table_lookup_data, const D: usize>( config: &StarkConfig, trace_poly_values: &[Vec>], - cross_table_lookups: &[CrossTableLookup], + cross_table_lookups: &[CrossTableLookup], challenger: &mut Challenger, ) -> Vec> { let challenges = get_grand_product_challenge_set(challenger, config.num_challenges); @@ -89,6 +92,7 @@ pub fn cross_table_lookup_data, const D looking_columns, looked_table, looked_columns, + default, } = cross_table_lookup; for &GrandProductChallenge { beta, gamma } in &challenges.challenges { @@ -105,6 +109,15 @@ pub fn cross_table_lookup_data, const D gamma, ); + debug_assert_eq!( + *z_looking.values.last().unwrap(), + *z_looked.values.last().unwrap() + * (gamma + reduce_with_powers(default.iter(), beta)).exp_u64( + trace_poly_values[*looking_table as usize][0].len() as u64 + - trace_poly_values[*looked_table as usize][0].len() as u64 + ) + ); + acc[*looking_table as usize] .zs_columns .push((z_looking, looking_columns.clone())); @@ -152,7 +165,7 @@ impl<'a, F: RichField + Extendable, const D: usize> { pub(crate) fn from_proofs>( proofs: &[&StarkProofWithPublicInputs], - cross_table_lookups: &'a [CrossTableLookup], + cross_table_lookups: &'a [CrossTableLookup], ctl_challenges: &'a GrandProductChallengeSet, num_permutation_zs: &[usize], ) -> Vec> { @@ -194,6 +207,7 @@ impl<'a, F: RichField + Extendable, const D: usize> looking_columns, looked_table, looked_columns, + .. } = ctl; for &challenges in &ctl_challenges.challenges { @@ -254,7 +268,7 @@ pub(crate) fn verify_cross_table_lookups< C: GenericConfig, const D: usize, >( - cross_table_lookups: Vec, + cross_table_lookups: Vec>, proofs: &[&StarkProofWithPublicInputs], challenges: GrandProductChallengeSet, config: &StarkConfig, @@ -272,6 +286,7 @@ pub(crate) fn verify_cross_table_lookups< CrossTableLookup { looking_table, looked_table, + default, .. }, ) in cross_table_lookups.into_iter().enumerate() @@ -280,12 +295,12 @@ pub(crate) fn verify_cross_table_lookups< let looked_degree = 1 << degrees_bits[looked_table as usize]; let looking_z = *ctl_zs_openings[looking_table as usize].next().unwrap(); let looked_z = *ctl_zs_openings[looked_table as usize].next().unwrap(); + let GrandProductChallenge { beta, gamma } = + challenges.challenges[i % config.num_challenges]; + let combined_default = gamma + reduce_with_powers(default.iter(), beta); + ensure!( - looking_z - == looked_z - * challenges.challenges[i % config.num_challenges] - .gamma - .exp_u64(looking_degree - looked_degree), + looking_z == looked_z * combined_default.exp_u64(looking_degree - looked_degree), "Cross-table lookup verification failed." ); } From 2ffd22acf5a21c8b1c26c20cd0c8ff4a48af57f2 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 17 May 2022 09:41:06 +0200 Subject: [PATCH 32/33] Assume every STARK in AllStark is part of a CTL --- starky2/src/cross_table_lookup.rs | 28 +++---- starky2/src/get_challenges.rs | 8 +- starky2/src/proof.rs | 29 ++++--- starky2/src/prover.rs | 121 +++++++++++++----------------- starky2/src/recursive_verifier.rs | 7 +- starky2/src/verifier.rs | 50 ++---------- 6 files changed, 85 insertions(+), 158 deletions(-) diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index 9c7b8e03..de58273f 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -173,29 +173,19 @@ impl<'a, F: RichField + Extendable, const D: usize> let mut ctl_zs = proofs .iter() .zip(num_permutation_zs) - .map(|(p, &num_permutation)| -> Box> { - if p.proof.openings.permutation_ctl_zs.is_some() { - Box::new( + .map(|(p, &num_permutation)| { + p.proof + .openings + .permutation_ctl_zs + .iter() + .skip(num_permutation) + .zip( p.proof .openings - .permutation_ctl_zs - .as_ref() - .unwrap() + .permutation_ctl_zs_right .iter() - .skip(num_permutation) - .zip( - p.proof - .openings - .permutation_ctl_zs_right - .as_ref() - .unwrap() - .iter() - .skip(num_permutation), - ), + .skip(num_permutation), ) - } else { - Box::new(std::iter::empty()) - } }) .collect::>(); diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index c7931ebf..ecde68ee 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -21,7 +21,7 @@ use crate::stark::Stark; fn get_challenges( challenger: &mut Challenger, stark: &S, - permutation_ctl_zs_cap: Option<&MerkleCap>, + permutation_ctl_zs_cap: &MerkleCap, quotient_polys_cap: &MerkleCap, openings: &StarkOpeningSet, commit_phase_merkle_caps: &[MerkleCap], @@ -45,9 +45,7 @@ where ) }); - if let Some(cap) = permutation_ctl_zs_cap { - challenger.observe_cap(cap); - } + challenger.observe_cap(permutation_ctl_zs_cap); let stark_alphas = challenger.get_n_challenges(num_challenges); @@ -146,7 +144,7 @@ where get_challenges::( challenger, stark, - permutation_ctl_zs_cap.as_ref(), + permutation_ctl_zs_cap, quotient_polys_cap, openings, commit_phase_merkle_caps, diff --git a/starky2/src/proof.rs b/starky2/src/proof.rs index f206f23f..c04ff9dc 100644 --- a/starky2/src/proof.rs +++ b/starky2/src/proof.rs @@ -40,7 +40,7 @@ pub struct StarkProof, C: GenericConfig, /// Merkle cap of LDEs of trace values. pub trace_cap: MerkleCap, /// Merkle cap of LDEs of permutation Z values. - pub permutation_ctl_zs_cap: Option>, + pub permutation_ctl_zs_cap: MerkleCap, /// Merkle cap of LDEs of trace values. pub quotient_polys_cap: MerkleCap, /// Purported values of each polynomial at the challenge point. @@ -147,9 +147,9 @@ pub struct StarkOpeningSet, const D: usize> { /// Openings of trace polynomials at `g * zeta`. pub next_values: Vec, /// Openings of permutations and cross-table lookups `Z` polynomials at `zeta`. - pub permutation_ctl_zs: Option>, + pub permutation_ctl_zs: Vec, /// Openings of permutations and cross-table lookups `Z` polynomials at `g * zeta`. - pub permutation_ctl_zs_right: Option>, + pub permutation_ctl_zs_right: Vec, /// Openings of cross-table lookups `Z` polynomials at `g^-1`. pub ctl_zs_last: Vec, /// Openings of quotient polynomials at `zeta`. @@ -161,7 +161,7 @@ impl, const D: usize> StarkOpeningSet { zeta: F::Extension, g: F, trace_commitment: &PolynomialBatch, - permutation_ctl_zs_commitment: Option<&PolynomialBatch>, + permutation_ctl_zs_commitment: &PolynomialBatch, quotient_commitment: &PolynomialBatch, degree_bits: usize, num_permutation_zs: usize, @@ -182,16 +182,13 @@ impl, const D: usize> StarkOpeningSet { Self { local_values: eval_commitment(zeta, trace_commitment), next_values: eval_commitment(zeta_right, trace_commitment), - permutation_ctl_zs: permutation_ctl_zs_commitment.map(|c| eval_commitment(zeta, c)), - permutation_ctl_zs_right: permutation_ctl_zs_commitment - .map(|c| eval_commitment(zeta_right, c)), - ctl_zs_last: permutation_ctl_zs_commitment - .map(|c| { - eval_commitment_base(F::primitive_root_of_unity(degree_bits).inverse(), c) - [num_permutation_zs..] - .to_vec() - }) - .unwrap_or_default(), + permutation_ctl_zs: eval_commitment(zeta, permutation_ctl_zs_commitment), + permutation_ctl_zs_right: eval_commitment(zeta_right, permutation_ctl_zs_commitment), + ctl_zs_last: eval_commitment_base( + F::primitive_root_of_unity(degree_bits).inverse(), + permutation_ctl_zs_commitment, + )[num_permutation_zs..] + .to_vec(), quotient_polys: eval_commitment(zeta, quotient_commitment), } } @@ -201,7 +198,7 @@ impl, const D: usize> StarkOpeningSet { values: self .local_values .iter() - .chain(self.permutation_ctl_zs.iter().flatten()) + .chain(&self.permutation_ctl_zs) .chain(&self.quotient_polys) .copied() .collect_vec(), @@ -210,7 +207,7 @@ impl, const D: usize> StarkOpeningSet { values: self .next_values .iter() - .chain(self.permutation_ctl_zs_right.iter().flatten()) + .chain(&self.permutation_ctl_zs_right) .copied() .collect_vec(), }; diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index 4fc602c1..c9f2f0ab 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -1,7 +1,4 @@ -use std::iter::once; - use anyhow::{ensure, Result}; -use itertools::Itertools; use plonky2::field::extension_field::Extendable; use plonky2::field::field_types::Field; use plonky2::field::packable::Packable; @@ -175,30 +172,26 @@ where permutation_zs } }; + assert!(!z_polys.is_empty(), "No CTL?"); - let permutation_ctl_zs_commitment = (!z_polys.is_empty()).then(|| { - PolynomialBatch::from_values( - z_polys, - rate_bits, - false, - config.fri_config.cap_height, - timing, - None, - ) - }); - let permutation_ctl_zs_cap = permutation_ctl_zs_commitment - .as_ref() - .map(|commit| commit.merkle_tree.cap.clone()); - if let Some(cap) = &permutation_ctl_zs_cap { - challenger.observe_cap(cap); - } + let permutation_ctl_zs_commitment = PolynomialBatch::from_values( + z_polys, + rate_bits, + false, + config.fri_config.cap_height, + timing, + None, + ); + + let permutation_ctl_zs_cap = permutation_ctl_zs_commitment.merkle_tree.cap.clone(); + challenger.observe_cap(&permutation_ctl_zs_cap); let alphas = challenger.get_n_challenges(config.num_challenges); if cfg!(test) { check_constraints( stark, trace_commitment, - permutation_ctl_zs_commitment.as_ref(), + &permutation_ctl_zs_commitment, permutation_challenges.as_ref(), ctl_data, public_inputs, @@ -211,7 +204,7 @@ where let quotient_polys = compute_quotient_polys::::Packing, C, S, D>( stark, trace_commitment, - permutation_ctl_zs_commitment.as_ref(), + &permutation_ctl_zs_commitment, permutation_challenges.as_ref(), ctl_data, public_inputs, @@ -259,17 +252,18 @@ where zeta, g, trace_commitment, - permutation_ctl_zs_commitment.as_ref(), + &permutation_ctl_zs_commitment, "ient_commitment, degree_bits, stark.num_permutation_batches(config), ); challenger.observe_openings(&openings.to_fri_openings()); - let initial_merkle_trees = once(trace_commitment) - .chain(&permutation_ctl_zs_commitment) - .chain(once("ient_commitment)) - .collect_vec(); + let initial_merkle_trees = vec![ + trace_commitment, + &permutation_ctl_zs_commitment, + "ient_commitment, + ]; let opening_proof = timed!( timing, @@ -301,7 +295,7 @@ where fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, - permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, + permutation_ctl_zs_commitment: &'a PolynomialBatch, permutation_challenges: Option<&'a Vec>>, ctl_data: &CtlData, public_inputs: [F; S::PUBLIC_INPUTS], @@ -381,33 +375,25 @@ where next_values: &get_trace_values_packed(i_next_start), public_inputs: &public_inputs, }; - let permutation_check_data = - if let (Some(permutation_zs_commitment), Some(permutation_challenge_sets)) = - (permutation_ctl_zs_commitment, permutation_challenges) - { - Some(PermutationCheckVars { - local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step) - [..num_permutation_zs] - .to_vec(), - next_zs: permutation_zs_commitment - .get_lde_values_packed(i_next_start, step)[..num_permutation_zs] - .to_vec(), - permutation_challenge_sets: permutation_challenge_sets.to_vec(), - }) - } else { - None - }; + let permutation_check_vars = + permutation_challenges.map(|permutation_challenge_sets| PermutationCheckVars { + local_zs: permutation_ctl_zs_commitment.get_lde_values_packed(i_start, step) + [..num_permutation_zs] + .to_vec(), + next_zs: permutation_ctl_zs_commitment + .get_lde_values_packed(i_next_start, step)[..num_permutation_zs] + .to_vec(), + permutation_challenge_sets: permutation_challenge_sets.to_vec(), + }); let ctl_vars = ctl_data .zs_columns .iter() .enumerate() .map(|(i, (_, columns))| CtlCheckVars:: { - local_z: permutation_ctl_zs_commitment - .unwrap() - .get_lde_values_packed(i_start, step)[num_permutation_zs + i], - next_z: permutation_ctl_zs_commitment - .unwrap() - .get_lde_values_packed(i_next_start, step)[num_permutation_zs + i], + local_z: permutation_ctl_zs_commitment.get_lde_values_packed(i_start, step) + [num_permutation_zs + i], + next_z: permutation_ctl_zs_commitment.get_lde_values_packed(i_next_start, step) + [num_permutation_zs + i], challenges: ctl_data.challenges.challenges[i % config.num_challenges], columns, }) @@ -416,7 +402,7 @@ where stark, config, vars, - permutation_check_data, + permutation_check_vars, &ctl_vars, &mut consumer, ); @@ -442,7 +428,7 @@ where fn check_constraints<'a, F, C, S, const D: usize>( stark: &S, trace_commitment: &'a PolynomialBatch, - permutation_ctl_zs_commitment: Option<&'a PolynomialBatch>, + permutation_ctl_zs_commitment: &'a PolynomialBatch, permutation_challenges: Option<&'a Vec>>, ctl_data: &CtlData, public_inputs: [F; S::PUBLIC_INPUTS], @@ -503,30 +489,25 @@ fn check_constraints<'a, F, C, S, const D: usize>( .unwrap(), public_inputs: &public_inputs, }; - let permutation_check_data = - if let (Some(permutation_zs_commitment), Some(permutation_challenge_sets)) = - (permutation_ctl_zs_commitment, permutation_challenges) - { - Some(PermutationCheckVars { - local_zs: get_comm_values(permutation_zs_commitment, i) - [..num_permutation_zs] - .to_vec(), - next_zs: get_comm_values(permutation_zs_commitment, i_next) - [..num_permutation_zs] - .to_vec(), - permutation_challenge_sets: permutation_challenge_sets.to_vec(), - }) - } else { - None - }; + let permutation_check_vars = + permutation_challenges.map(|permutation_challenge_sets| PermutationCheckVars { + local_zs: permutation_ctl_zs_commitment.get_lde_values_packed(i, step) + [..num_permutation_zs] + .to_vec(), + next_zs: permutation_ctl_zs_commitment.get_lde_values_packed(i_next, step) + [..num_permutation_zs] + .to_vec(), + permutation_challenge_sets: permutation_challenge_sets.to_vec(), + }); + let ctl_vars = ctl_data .zs_columns .iter() .enumerate() .map(|(iii, (_, columns))| CtlCheckVars:: { - local_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i) + local_z: get_comm_values(permutation_ctl_zs_commitment, i) [num_permutation_zs + iii], - next_z: get_comm_values(permutation_ctl_zs_commitment.unwrap(), i_next) + next_z: get_comm_values(permutation_ctl_zs_commitment, i_next) [num_permutation_zs + iii], challenges: ctl_data.challenges.challenges[iii % config.num_challenges], columns, @@ -536,7 +517,7 @@ fn check_constraints<'a, F, C, S, const D: usize>( stark, config, vars, - permutation_check_data, + permutation_check_vars, &ctl_vars, &mut consumer, ); diff --git a/starky2/src/recursive_verifier.rs b/starky2/src/recursive_verifier.rs index 6dcc7a7e..b0021ac6 100644 --- a/starky2/src/recursive_verifier.rs +++ b/starky2/src/recursive_verifier.rs @@ -301,11 +301,8 @@ pub fn set_stark_proof_target, W, const D: usize>( &proof.openings.to_fri_openings(), ); - if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) = ( - &proof_target.permutation_zs_cap, - &proof.permutation_ctl_zs_cap, - ) { - witness.set_cap_target(permutation_zs_cap_target, permutation_zs_cap); + if let Some(permutation_zs_cap_target) = &proof_target.permutation_zs_cap { + witness.set_cap_target(permutation_zs_cap_target, &proof.permutation_ctl_zs_cap); } set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof); diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index a5411386..d8f24e97 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -1,7 +1,4 @@ -use std::iter::once; - use anyhow::{ensure, Result}; -use itertools::Itertools; use plonky2::field::extension_field::{Extendable, FieldExtension}; use plonky2::field::field_types::Field; use plonky2::fri::verifier::verify_fri_proof; @@ -93,8 +90,6 @@ where [(); S::PUBLIC_INPUTS]:, [(); C::Hasher::HASH_SIZE]:, { - // TODO: Fix this to take CTLs into account - // check_permutation_options(&stark, proof_with_pis, &challenges)?; let StarkProofWithPublicInputs { proof, public_inputs, @@ -135,8 +130,8 @@ where ); let num_permutation_zs = stark.num_permutation_batches(config); let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars { - local_zs: permutation_ctl_zs.as_ref().unwrap()[..num_permutation_zs].to_vec(), - next_zs: permutation_ctl_zs_right.as_ref().unwrap()[..num_permutation_zs].to_vec(), + local_zs: permutation_ctl_zs[..num_permutation_zs].to_vec(), + next_zs: permutation_ctl_zs_right[..num_permutation_zs].to_vec(), permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(), }); eval_vanishing_poly::( @@ -167,10 +162,11 @@ where ); } - let merkle_caps = once(proof.trace_cap.clone()) - .chain(proof.permutation_ctl_zs_cap.clone()) - .chain(once(proof.quotient_polys_cap.clone())) - .collect_vec(); + let merkle_caps = vec![ + proof.trace_cap.clone(), + proof.permutation_ctl_zs_cap.clone(), + proof.quotient_polys_cap.clone(), + ]; verify_fri_proof::( &stark.fri_instance( @@ -202,38 +198,6 @@ fn eval_l_1_and_l_last(log_n: usize, x: F) -> (F, F) { (z_x * invs[0], z_x * invs[1]) } -/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff -/// the Stark uses a permutation argument. -#[allow(dead_code)] -fn check_permutation_options< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - stark: &S, - proof_with_pis: &StarkProofWithPublicInputs, - challenges: &StarkProofChallenges, -) -> Result<()> { - let options_is_some = [ - proof_with_pis.proof.permutation_ctl_zs_cap.is_some(), - proof_with_pis.proof.openings.permutation_ctl_zs.is_some(), - proof_with_pis - .proof - .openings - .permutation_ctl_zs_right - .is_some(), - challenges.permutation_challenge_sets.is_some(), - ]; - ensure!( - options_is_some - .into_iter() - .all(|b| b == stark.uses_permutation_args()), - "Permutation data doesn't match with Stark configuration." - ); - Ok(()) -} - #[cfg(test)] mod tests { use plonky2::field::field_types::Field; From f8b51743a951fec762fba80ed539bb4288b0d6ed Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Wed, 18 May 2022 09:22:58 +0200 Subject: [PATCH 33/33] PR feedback --- starky2/src/all_stark.rs | 116 ++------- starky2/src/cpu/cpu_stark.rs | 46 ++++ starky2/src/cpu/mod.rs | 1 + starky2/src/cross_table_lookup.rs | 168 ++++++------- starky2/src/get_challenges.rs | 327 +++++--------------------- starky2/src/keccak/keccak_stark.rs | 46 ++++ starky2/src/keccak/mod.rs | 1 + starky2/src/lib.rs | 5 +- starky2/src/mock_stark.rs | 362 ----------------------------- starky2/src/permutation.rs | 17 +- starky2/src/prover.rs | 4 +- starky2/src/recursive_verifier.rs | 14 +- starky2/src/stark.rs | 2 +- starky2/src/vanishing_poly.rs | 8 +- starky2/src/verifier.rs | 4 +- 15 files changed, 280 insertions(+), 841 deletions(-) create mode 100644 starky2/src/cpu/cpu_stark.rs create mode 100644 starky2/src/cpu/mod.rs create mode 100644 starky2/src/keccak/keccak_stark.rs create mode 100644 starky2/src/keccak/mod.rs delete mode 100644 starky2/src/mock_stark.rs diff --git a/starky2/src/all_stark.rs b/starky2/src/all_stark.rs index d00f5b8c..b4687a89 100644 --- a/starky2/src/all_stark.rs +++ b/starky2/src/all_stark.rs @@ -1,15 +1,11 @@ -use std::marker::PhantomData; - -use plonky2::field::extension_field::{Extendable, FieldExtension}; -use plonky2::field::packed_field::PackedField; +use plonky2::field::extension_field::Extendable; use plonky2::hash::hash_types::RichField; use crate::config::StarkConfig; -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::CrossTableLookup; -use crate::permutation::PermutationPair; +use crate::keccak::keccak_stark::KeccakStark; use crate::stark::Stark; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; #[derive(Clone)] pub struct AllStark, const D: usize> { @@ -27,88 +23,12 @@ impl, const D: usize> AllStark { } } -#[derive(Copy, Clone)] -pub struct CpuStark { - #[allow(dead_code)] - num_rows: usize, - f: PhantomData, -} - -#[derive(Copy, Clone)] -pub struct KeccakStark { - #[allow(dead_code)] - num_rows: usize, - f: PhantomData, -} - #[derive(Copy, Clone)] pub enum Table { Cpu = 0, Keccak = 1, } -impl, const D: usize> Stark for CpuStark { - const COLUMNS: usize = 10; - const PUBLIC_INPUTS: usize = 0; - - fn eval_packed_generic( - &self, - _vars: StarkEvaluationVars, - _yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - } - - fn eval_ext_recursively( - &self, - _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - _vars: StarkEvaluationTargets, - _yield_constr: &mut RecursiveConstraintConsumer, - ) { - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn permutation_pairs(&self) -> Vec { - vec![PermutationPair::singletons(8, 9)] - } -} - -impl, const D: usize> Stark for KeccakStark { - const COLUMNS: usize = 7; - const PUBLIC_INPUTS: usize = 0; - - fn eval_packed_generic( - &self, - _vars: StarkEvaluationVars, - _yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - } - - fn eval_ext_recursively( - &self, - _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, - _vars: StarkEvaluationTargets, - _yield_constr: &mut RecursiveConstraintConsumer, - ) { - } - - fn constraint_degree(&self) -> usize { - 3 - } - - fn permutation_pairs(&self) -> Vec { - vec![PermutationPair::singletons(0, 6)] - } -} - #[cfg(test)] mod tests { use anyhow::Result; @@ -118,9 +38,11 @@ mod tests { use plonky2::util::timing::TimingTree; use rand::{thread_rng, Rng}; - use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; + use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; + use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::CrossTableLookup; + use crate::keccak::keccak_stark::KeccakStark; use crate::prover::prove; use crate::verifier::verify_proof; @@ -133,34 +55,32 @@ mod tests { let config = StarkConfig::standard_fast_config(); let cpu_stark = CpuStark:: { - num_rows: 1 << 4, f: Default::default(), }; + let cpu_rows = 1 << 4; + let keccak_stark = KeccakStark:: { - num_rows: 1 << 3, f: Default::default(), }; + let keccak_rows = 1 << 3; - // let mut cpu_trace = vec![PolynomialValues::zero(cpu_stark.num_rows); CpuStark::COLUMNS]; - let mut cpu_trace = vec![PolynomialValues::zero(cpu_stark.num_rows); 10]; - // let mut keccak_trace = - // vec![PolynomialValues::zero(keccak_stark.num_rows); KeccakStark::COLUMNS]; - let mut keccak_trace = vec![PolynomialValues::zero(keccak_stark.num_rows); 7]; + let mut cpu_trace = vec![PolynomialValues::zero(cpu_rows); 10]; + let mut keccak_trace = vec![PolynomialValues::zero(keccak_rows); 7]; - let vs0 = (0..keccak_stark.num_rows) + let vs0 = (0..keccak_rows) .map(F::from_canonical_usize) .collect::>(); - let vs1 = (1..=keccak_stark.num_rows) + let vs1 = (1..=keccak_rows) .map(F::from_canonical_usize) .collect::>(); - let start = thread_rng().gen_range(0..cpu_stark.num_rows - keccak_stark.num_rows); + let start = thread_rng().gen_range(0..cpu_rows - keccak_rows); let default = vec![F::ONE; 2]; - cpu_trace[2].values = vec![default[0]; cpu_stark.num_rows]; - cpu_trace[2].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs0); - cpu_trace[4].values = vec![default[1]; cpu_stark.num_rows]; - cpu_trace[4].values[start..start + keccak_stark.num_rows].copy_from_slice(&vs1); + cpu_trace[2].values = vec![default[0]; cpu_rows]; + cpu_trace[2].values[start..start + keccak_rows].copy_from_slice(&vs0); + cpu_trace[4].values = vec![default[1]; cpu_rows]; + cpu_trace[4].values[start..start + keccak_rows].copy_from_slice(&vs1); keccak_trace[3].values[..].copy_from_slice(&vs0); keccak_trace[5].values[..].copy_from_slice(&vs1); diff --git a/starky2/src/cpu/cpu_stark.rs b/starky2/src/cpu/cpu_stark.rs new file mode 100644 index 00000000..710c9df5 --- /dev/null +++ b/starky2/src/cpu/cpu_stark.rs @@ -0,0 +1,46 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::PermutationPair; +use crate::stark::Stark; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +#[derive(Copy, Clone)] +pub struct CpuStark { + pub f: PhantomData, +} + +impl, const D: usize> Stark for CpuStark { + const COLUMNS: usize = 10; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + _vars: StarkEvaluationVars, + _yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + } + + fn eval_ext_circuit( + &self, + _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _vars: StarkEvaluationTargets, + _yield_constr: &mut RecursiveConstraintConsumer, + ) { + } + + fn constraint_degree(&self) -> usize { + 3 + } + + fn permutation_pairs(&self) -> Vec { + vec![PermutationPair::singletons(8, 9)] + } +} diff --git a/starky2/src/cpu/mod.rs b/starky2/src/cpu/mod.rs new file mode 100644 index 00000000..fe991a17 --- /dev/null +++ b/starky2/src/cpu/mod.rs @@ -0,0 +1 @@ +pub mod cpu_stark; diff --git a/starky2/src/cross_table_lookup.rs b/starky2/src/cross_table_lookup.rs index de58273f..2f6ac84c 100644 --- a/starky2/src/cross_table_lookup.rs +++ b/starky2/src/cross_table_lookup.rs @@ -6,8 +6,6 @@ use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::Challenger; use plonky2::plonk::config::GenericConfig; -use plonky2::plonk::plonk_common::reduce_with_powers; -use plonky2::util::reducing::ReducingFactor; use crate::all_stark::Table; use crate::config::StarkConfig; @@ -84,64 +82,57 @@ pub fn cross_table_lookup_data, const D challenger: &mut Challenger, ) -> Vec> { let challenges = get_grand_product_challenge_set(challenger, config.num_challenges); - cross_table_lookups.iter().fold( - vec![CtlData::new(challenges.clone()); trace_poly_values.len()], - |mut acc, cross_table_lookup| { - let CrossTableLookup { - looking_table, + let mut ctl_data_per_table = vec![CtlData::new(challenges.clone()); trace_poly_values.len()]; + for CrossTableLookup { + looking_table, + looking_columns, + looked_table, + looked_columns, + default, + } in cross_table_lookups + { + for &challenge in &challenges.challenges { + let z_looking = partial_products( + &trace_poly_values[*looking_table as usize], looking_columns, - looked_table, + challenge, + ); + let z_looked = partial_products( + &trace_poly_values[*looked_table as usize], looked_columns, - default, - } = cross_table_lookup; + challenge, + ); - for &GrandProductChallenge { beta, gamma } in &challenges.challenges { - let z_looking = partial_products( - &trace_poly_values[*looking_table as usize], - looking_columns, - beta, - gamma, - ); - let z_looked = partial_products( - &trace_poly_values[*looked_table as usize], - looked_columns, - beta, - gamma, - ); + debug_assert_eq!( + *z_looking.values.last().unwrap(), + *z_looked.values.last().unwrap() + * challenge.combine(default).exp_u64( + trace_poly_values[*looking_table as usize][0].len() as u64 + - trace_poly_values[*looked_table as usize][0].len() as u64 + ) + ); - debug_assert_eq!( - *z_looking.values.last().unwrap(), - *z_looked.values.last().unwrap() - * (gamma + reduce_with_powers(default.iter(), beta)).exp_u64( - trace_poly_values[*looking_table as usize][0].len() as u64 - - trace_poly_values[*looked_table as usize][0].len() as u64 - ) - ); - - acc[*looking_table as usize] - .zs_columns - .push((z_looking, looking_columns.clone())); - acc[*looked_table as usize] - .zs_columns - .push((z_looked, looked_columns.clone())); - } - acc - }, - ) + ctl_data_per_table[*looking_table as usize] + .zs_columns + .push((z_looking, looking_columns.clone())); + ctl_data_per_table[*looked_table as usize] + .zs_columns + .push((z_looked, looked_columns.clone())); + } + } + ctl_data_per_table } fn partial_products( trace: &[PolynomialValues], columns: &[usize], - beta: F, - gamma: F, + challenge: GrandProductChallenge, ) -> PolynomialValues { let mut partial_prod = F::ONE; let degree = trace[0].len(); let mut res = Vec::with_capacity(degree); for i in 0..degree { - partial_prod *= - gamma + reduce_with_powers(columns.iter().map(|&j| &trace[j].values[i]), beta); + partial_prod *= challenge.combine(columns.iter().map(|&j| &trace[j].values[i])); res.push(partial_prod); } res.into() @@ -173,53 +164,42 @@ impl<'a, F: RichField + Extendable, const D: usize> let mut ctl_zs = proofs .iter() .zip(num_permutation_zs) - .map(|(p, &num_permutation)| { - p.proof - .openings - .permutation_ctl_zs - .iter() - .skip(num_permutation) - .zip( - p.proof - .openings - .permutation_ctl_zs_right - .iter() - .skip(num_permutation), - ) + .map(|(p, &num_perms)| { + let openings = &p.proof.openings; + let ctl_zs = openings.permutation_ctl_zs.iter().skip(num_perms); + let ctl_zs_right = openings.permutation_ctl_zs_right.iter().skip(num_perms); + ctl_zs.zip(ctl_zs_right) }) .collect::>(); - cross_table_lookups - .iter() - .fold(vec![vec![]; proofs.len()], |mut acc, ctl| { - let CrossTableLookup { - looking_table, - looking_columns, - looked_table, - looked_columns, - .. - } = ctl; + let mut ctl_vars_per_table = vec![vec![]; proofs.len()]; + for CrossTableLookup { + looking_table, + looking_columns, + looked_table, + looked_columns, + .. + } in cross_table_lookups + { + for &challenges in &ctl_challenges.challenges { + let (looking_z, looking_z_next) = ctl_zs[*looking_table as usize].next().unwrap(); + ctl_vars_per_table[*looking_table as usize].push(Self { + local_z: *looking_z, + next_z: *looking_z_next, + challenges, + columns: looking_columns, + }); - for &challenges in &ctl_challenges.challenges { - let (looking_z, looking_z_next) = - ctl_zs[*looking_table as usize].next().unwrap(); - acc[*looking_table as usize].push(Self { - local_z: *looking_z, - next_z: *looking_z_next, - challenges, - columns: looking_columns, - }); - - let (looked_z, looked_z_next) = ctl_zs[*looked_table as usize].next().unwrap(); - acc[*looked_table as usize].push(Self { - local_z: *looked_z, - next_z: *looked_z_next, - challenges, - columns: looked_columns, - }); - } - acc - }) + let (looked_z, looked_z_next) = ctl_zs[*looked_table as usize].next().unwrap(); + ctl_vars_per_table[*looked_table as usize].push(Self { + local_z: *looked_z, + next_z: *looked_z_next, + challenges, + columns: looked_columns, + }); + } + } + ctl_vars_per_table } } @@ -241,10 +221,7 @@ pub(crate) fn eval_cross_table_lookup_checks P { - factor.reduce_ext(columns.iter().map(|&i| v[i])) + FE::from_basefield(challenges.gamma) - }; + let combine = |v: &[P]| -> P { challenges.combine(columns.iter().map(|&i| &v[i])) }; // Check value of `Z(1)` consumer.constraint_first_row(*local_z - combine(vars.local_values)); @@ -285,9 +262,8 @@ pub(crate) fn verify_cross_table_lookups< let looked_degree = 1 << degrees_bits[looked_table as usize]; let looking_z = *ctl_zs_openings[looking_table as usize].next().unwrap(); let looked_z = *ctl_zs_openings[looked_table as usize].next().unwrap(); - let GrandProductChallenge { beta, gamma } = - challenges.challenges[i % config.num_challenges]; - let combined_default = gamma + reduce_with_powers(default.iter(), beta); + let challenge = challenges.challenges[i % config.num_challenges]; + let combined_default = challenge.combine(default.iter()); ensure!( looking_z == looked_z * combined_default.exp_u64(looking_degree - looked_degree), diff --git a/starky2/src/get_challenges.rs b/starky2/src/get_challenges.rs index ecde68ee..64eaeb53 100644 --- a/starky2/src/get_challenges.rs +++ b/starky2/src/get_challenges.rs @@ -1,11 +1,7 @@ use plonky2::field::extension_field::Extendable; -use plonky2::field::polynomial::PolynomialCoeffs; -use plonky2::fri::proof::{FriProof, FriProofTarget}; -use plonky2::gadgets::polynomial::PolynomialCoeffsExtTarget; -use plonky2::hash::hash_types::{MerkleCapTarget, RichField}; -use plonky2::hash::merkle_tree::MerkleCap; +use plonky2::fri::proof::FriProof; +use plonky2::hash::hash_types::RichField; use plonky2::iop::challenger::{Challenger, RecursiveChallenger}; -use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; @@ -18,56 +14,6 @@ use crate::permutation::{ use crate::proof::*; use crate::stark::Stark; -fn get_challenges( - challenger: &mut Challenger, - stark: &S, - permutation_ctl_zs_cap: &MerkleCap, - quotient_polys_cap: &MerkleCap, - openings: &StarkOpeningSet, - commit_phase_merkle_caps: &[MerkleCap], - final_poly: &PolynomialCoeffs, - pow_witness: F, - config: &StarkConfig, - degree_bits: usize, -) -> StarkProofChallenges -where - F: RichField + Extendable, - C: GenericConfig, - S: Stark, -{ - let num_challenges = config.num_challenges; - - let permutation_challenge_sets = stark.uses_permutation_args().then(|| { - get_n_grand_product_challenge_sets( - challenger, - num_challenges, - stark.permutation_batch_size(), - ) - }); - - challenger.observe_cap(permutation_ctl_zs_cap); - - let stark_alphas = challenger.get_n_challenges(num_challenges); - - challenger.observe_cap(quotient_polys_cap); - let stark_zeta = challenger.get_extension_challenge::(); - - challenger.observe_openings(&openings.to_fri_openings()); - - StarkProofChallenges { - permutation_challenge_sets, - stark_alphas, - stark_zeta, - fri_challenges: challenger.fri_challenges::( - commit_phase_merkle_caps, - final_poly, - pow_witness, - degree_bits, - &config.fri_config, - ), - } -} - impl, C: GenericConfig, const D: usize> AllProof { /// Computes all Fiat-Shamir challenges used in the STARK proof. pub(crate) fn get_challenges( @@ -105,19 +51,6 @@ where F: RichField + Extendable, C: GenericConfig, { - // TODO: Should be used later in compression? - #![allow(dead_code)] - pub(crate) fn fri_query_indices>( - &self, - stark: &S, - config: &StarkConfig, - ) -> Vec { - let mut challenger = Challenger::new(); - self.get_challenges(&mut challenger, stark, config) - .fri_challenges - .fri_query_indices - } - /// Computes all Fiat-Shamir challenges used in the STARK proof. pub(crate) fn get_challenges>( &self, @@ -141,77 +74,37 @@ where .. } = &self.proof; - get_challenges::( - challenger, - stark, - permutation_ctl_zs_cap, - quotient_polys_cap, - openings, - commit_phase_merkle_caps, - final_poly, - *pow_witness, - config, - degree_bits, - ) - } -} + let num_challenges = config.num_challenges; -#[allow(clippy::too_many_arguments)] -pub(crate) fn get_challenges_target< - F: RichField + Extendable, - C: GenericConfig, - S: Stark, - const D: usize, ->( - builder: &mut CircuitBuilder, - stark: &S, - trace_cap: &MerkleCapTarget, - permutation_zs_cap: Option<&MerkleCapTarget>, - quotient_polys_cap: &MerkleCapTarget, - openings: &StarkOpeningSetTarget, - commit_phase_merkle_caps: &[MerkleCapTarget], - final_poly: &PolynomialCoeffsExtTarget, - pow_witness: Target, - config: &StarkConfig, -) -> StarkProofChallengesTarget -where - C::Hasher: AlgebraicHasher, -{ - let num_challenges = config.num_challenges; + let permutation_challenge_sets = stark.uses_permutation_args().then(|| { + get_n_grand_product_challenge_sets( + challenger, + num_challenges, + stark.permutation_batch_size(), + ) + }); - let mut challenger = RecursiveChallenger::::new(builder); + challenger.observe_cap(permutation_ctl_zs_cap); - challenger.observe_cap(trace_cap); + let stark_alphas = challenger.get_n_challenges(num_challenges); - let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| { - let tmp = get_n_permutation_challenge_sets_target( - builder, - &mut challenger, - num_challenges, - stark.permutation_batch_size(), - ); - challenger.observe_cap(permutation_zs_cap); - tmp - }); + challenger.observe_cap(quotient_polys_cap); + let stark_zeta = challenger.get_extension_challenge::(); - let stark_alphas = challenger.get_n_challenges(builder, num_challenges); + challenger.observe_openings(&openings.to_fri_openings()); - challenger.observe_cap(quotient_polys_cap); - let stark_zeta = challenger.get_extension_challenge(builder); - - challenger.observe_openings(&openings.to_fri_openings()); - - StarkProofChallengesTarget { - permutation_challenge_sets, - stark_alphas, - stark_zeta, - fri_challenges: challenger.fri_challenges::( - builder, - commit_phase_merkle_caps, - final_poly, - pow_witness, - &config.fri_config, - ), + StarkProofChallenges { + permutation_challenge_sets, + stark_alphas, + stark_zeta, + fri_challenges: challenger.fri_challenges::( + commit_phase_merkle_caps, + final_poly, + *pow_witness, + degree_bits, + &config.fri_config, + ), + } } } @@ -229,139 +122,37 @@ impl StarkProofWithPublicInputsTarget { where C::Hasher: AlgebraicHasher, { - let StarkProofTarget { - trace_cap, - permutation_zs_cap, - quotient_polys_cap, - openings, - opening_proof: - FriProofTarget { - commit_phase_merkle_caps, - final_poly, - pow_witness, - .. - }, - } = &self.proof; - - get_challenges_target::( - builder, - stark, - trace_cap, - permutation_zs_cap.as_ref(), - quotient_polys_cap, - openings, - commit_phase_merkle_caps, - final_poly, - *pow_witness, - config, - ) + let proof = &self.proof; + let opening_proof = &proof.opening_proof; + let num_challenges = config.num_challenges; + let mut challenger = RecursiveChallenger::::new(builder); + challenger.observe_cap(&proof.trace_cap); + let permutation_challenge_sets = + proof.permutation_zs_cap.as_ref().map(|permutation_zs_cap| { + let tmp = get_n_permutation_challenge_sets_target( + builder, + &mut challenger, + num_challenges, + stark.permutation_batch_size(), + ); + challenger.observe_cap(permutation_zs_cap); + tmp + }); + let stark_alphas = challenger.get_n_challenges(builder, num_challenges); + challenger.observe_cap(&proof.quotient_polys_cap); + let stark_zeta = challenger.get_extension_challenge(builder); + challenger.observe_openings(&proof.openings.to_fri_openings()); + StarkProofChallengesTarget { + permutation_challenge_sets, + stark_alphas, + stark_zeta, + fri_challenges: challenger.fri_challenges::( + builder, + &opening_proof.commit_phase_merkle_caps, + &opening_proof.final_poly, + opening_proof.pow_witness, + &config.fri_config, + ), + } } } - -// TODO: Deal with the compressed stuff. -// 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/starky2/src/keccak/keccak_stark.rs b/starky2/src/keccak/keccak_stark.rs new file mode 100644 index 00000000..cf75bf96 --- /dev/null +++ b/starky2/src/keccak/keccak_stark.rs @@ -0,0 +1,46 @@ +use std::marker::PhantomData; + +use plonky2::field::extension_field::{Extendable, FieldExtension}; +use plonky2::field::packed_field::PackedField; +use plonky2::hash::hash_types::RichField; + +use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; +use crate::permutation::PermutationPair; +use crate::stark::Stark; +use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; + +#[derive(Copy, Clone)] +pub struct KeccakStark { + pub(crate) f: PhantomData, +} + +impl, const D: usize> Stark for KeccakStark { + const COLUMNS: usize = 7; + const PUBLIC_INPUTS: usize = 0; + + fn eval_packed_generic( + &self, + _vars: StarkEvaluationVars, + _yield_constr: &mut ConstraintConsumer

, + ) where + FE: FieldExtension, + P: PackedField, + { + } + + fn eval_ext_circuit( + &self, + _builder: &mut plonky2::plonk::circuit_builder::CircuitBuilder, + _vars: StarkEvaluationTargets, + _yield_constr: &mut RecursiveConstraintConsumer, + ) { + } + + fn constraint_degree(&self) -> usize { + 3 + } + + fn permutation_pairs(&self) -> Vec { + vec![PermutationPair::singletons(0, 6)] + } +} diff --git a/starky2/src/keccak/mod.rs b/starky2/src/keccak/mod.rs new file mode 100644 index 00000000..4c31c32b --- /dev/null +++ b/starky2/src/keccak/mod.rs @@ -0,0 +1 @@ +pub mod keccak_stark; diff --git a/starky2/src/lib.rs b/starky2/src/lib.rs index aa14ac73..bd8ca5f3 100644 --- a/starky2/src/lib.rs +++ b/starky2/src/lib.rs @@ -3,12 +3,13 @@ #![allow(clippy::type_complexity)] #![feature(generic_const_exprs)] +pub mod all_stark; pub mod config; pub mod constraint_consumer; +pub mod cpu; pub mod cross_table_lookup; mod get_challenges; -// pub mod mock_stark; -pub mod all_stark; +pub mod keccak; pub mod permutation; pub mod proof; pub mod prover; diff --git a/starky2/src/mock_stark.rs b/starky2/src/mock_stark.rs deleted file mode 100644 index ba415a12..00000000 --- a/starky2/src/mock_stark.rs +++ /dev/null @@ -1,362 +0,0 @@ -use std::marker::PhantomData; - -use plonky2::field::extension_field::{Extendable, FieldExtension}; -use plonky2::field::packed_field::PackedField; -use plonky2::field::polynomial::PolynomialValues; -use plonky2::hash::hash_types::RichField; -use plonky2::plonk::circuit_builder::CircuitBuilder; - -use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; -use crate::permutation::PermutationPair; -use crate::stark::Stark; -use crate::util::trace_rows_to_poly_values; -use crate::vars::{StarkEvaluationTargets, StarkEvaluationVars}; - -struct AllStarks, const D: usize> { - fibonacci: FibonacciStark, - multiplications: MultiplicationStark, -} - -/// Toy STARK system used for testing. -/// Computes a Fibonacci sequence with state `[x0, x1, i, j]` using the state transition -/// `x0' <- x1, x1' <- x0 + x1, i' <- i+1, j' <- j+1`. -/// Note: The `i, j` columns are only used to test the permutation argument. -#[derive(Copy, Clone)] -struct FibonacciStark, const D: usize> { - num_rows: usize, - _phantom: PhantomData, -} - -impl, const D: usize> FibonacciStark { - // The first public input is `x0`. - const PI_INDEX_X0: usize = 0; - // The second public input is `x1`. - const PI_INDEX_X1: usize = 1; - // The third public input is the second element of the last row, which should be equal to the - // `num_rows`-th Fibonacci number. - const PI_INDEX_RES: usize = 2; - - fn new(num_rows: usize) -> Self { - Self { - num_rows, - _phantom: PhantomData, - } - } - - /// Generate the trace using `x0, x1, 0, 1` as initial state values. - fn generate_trace(&self, x0: F, x1: F) -> Vec> { - let mut trace_rows = (0..self.num_rows) - .scan([x0, x1, F::ZERO, F::ONE], |acc, _| { - let tmp = *acc; - acc[0] = tmp[1]; - acc[1] = tmp[0] + tmp[1]; - acc[2] = tmp[2] + F::ONE; - acc[3] = tmp[3] + F::ONE; - Some(tmp) - }) - .collect::>(); - trace_rows[self.num_rows - 1][3] = F::ZERO; // So that column 2 and 3 are permutation of one another. - trace_rows_to_poly_values(trace_rows) - } -} - -impl, const D: usize> Stark for FibonacciStark { - const COLUMNS: usize = 4; - const PUBLIC_INPUTS: usize = 3; - - fn eval_packed_generic( - &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - // Check public inputs. - 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.constraint_transition(vars.next_values[0] - vars.local_values[1]); - // x1' <- x0 + x1 - yield_constr.constraint_transition( - vars.next_values[1] - vars.local_values[0] - vars.local_values[1], - ); - } - - fn eval_ext_recursively( - &self, - builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - // Check public inputs. - let pis_constraints = [ - builder.sub_extension(vars.local_values[0], vars.public_inputs[Self::PI_INDEX_X0]), - builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_X1]), - builder.sub_extension(vars.local_values[1], vars.public_inputs[Self::PI_INDEX_RES]), - ]; - yield_constr.constraint_first_row(builder, pis_constraints[0]); - yield_constr.constraint_first_row(builder, pis_constraints[1]); - yield_constr.constraint_last_row(builder, pis_constraints[2]); - - // x0' <- x1 - let first_col_constraint = builder.sub_extension(vars.next_values[0], vars.local_values[1]); - yield_constr.constraint_transition(builder, first_col_constraint); - // x1' <- x0 + x1 - let second_col_constraint = { - let tmp = builder.sub_extension(vars.next_values[1], vars.local_values[0]); - builder.sub_extension(tmp, vars.local_values[1]) - }; - yield_constr.constraint_transition(builder, second_col_constraint); - } - - fn constraint_degree(&self) -> usize { - 2 - } - - fn permutation_pairs(&self) -> Vec { - vec![PermutationPair::singletons(2, 3)] - } -} - -#[derive(Copy, Clone)] -struct MultiplicationStark< - F: RichField + Extendable, - const D: usize, - const NUM_MULTIPLICANDS: usize, -> { - num_rows: usize, - _phantom: PhantomData, -} - -impl, const D: usize, const W: usize> MultiplicationStark { - fn multiplicand(&self, i: usize) -> usize { - debug_assert!(i < W); - i - } - - // Product of the first `i` multiplicands - fn intermediate_product(&self, i: usize) -> usize { - debug_assert!(i < W && i > 0); - W + i - 1 - } - - fn product(&self) -> usize { - 2 * W - 2 - } - - const fn num_columns() -> usize { - 2 * W - 1 - } - - fn new(num_rows: usize) -> Self { - Self { - num_rows, - _phantom: PhantomData, - } - } - - fn generate_trace(&self, multiplicands: &[Vec]) -> Vec> - where - [(); Self::num_columns()]:, - { - debug_assert_eq!(multiplicands.len(), self.num_rows); - let mut trace_rows = multiplicands - .iter() - .map(|row| { - debug_assert_eq!(row.len(), W); - let mut result = [F::ZERO; Self::num_columns()]; - for i in 0..W { - result[self.multiplicand(i)] = row[i]; - } - let mut acc = row[0] * row[1]; - for i in 1..W - 1 { - result[self.intermediate_product(i)] = acc; - acc *= row[i + 1]; - } - result[self.product()] = acc; - result - }) - .collect::>(); - trace_rows_to_poly_values(trace_rows) - } -} - -impl, const D: usize, const W: usize> Stark - for MultiplicationStark -{ - const COLUMNS: usize = 2 * W - 1; - const PUBLIC_INPUTS: usize = 0; - - fn eval_packed_generic( - &self, - vars: StarkEvaluationVars, - yield_constr: &mut ConstraintConsumer

, - ) where - FE: FieldExtension, - P: PackedField, - { - yield_constr.constraint( - vars.local_values[self.intermediate_product(1)] - - vars.local_values[self.multiplicand(0)] * vars.local_values[self.multiplicand(1)], - ); - for i in 2..W - 1 { - yield_constr.constraint( - vars.local_values[self.intermediate_product(i)] - - vars.local_values[self.intermediate_product(i - 1)] - * vars.local_values[self.multiplicand(i)], - ) - } - } - - fn eval_ext_recursively( - &self, - builder: &mut CircuitBuilder, - vars: StarkEvaluationTargets, - yield_constr: &mut RecursiveConstraintConsumer, - ) { - todo!() - } - - fn constraint_degree(&self) -> usize { - 2 - } -} - -#[cfg(test)] -mod tests { - use anyhow::Result; - use plonky2::field::extension_field::Extendable; - use plonky2::field::field_types::Field; - use plonky2::hash::hash_types::RichField; - use plonky2::iop::witness::PartialWitness; - use plonky2::plonk::circuit_builder::CircuitBuilder; - use plonky2::plonk::circuit_data::CircuitConfig; - use plonky2::plonk::config::{ - AlgebraicHasher, GenericConfig, Hasher, PoseidonGoldilocksConfig, - }; - use plonky2::util::timing::TimingTree; - - use crate::config::StarkConfig; - use crate::mock_stark::FibonacciStark; - use crate::proof::StarkProofWithPublicInputs; - use crate::prover::prove; - use crate::recursive_verifier::{ - add_virtual_stark_proof_with_pis, recursively_verify_stark_proof, - set_stark_proof_with_pis_target, - }; - use crate::stark::Stark; - use crate::stark_testing::test_stark_low_degree; - use crate::verifier::verify_stark_proof; - - fn fibonacci(n: usize, x0: F, x1: F) -> F { - (0..n).fold((x0, x1), |x, _| (x.1, x.0 + x.1)).1 - } - - #[test] - fn test_fibonacci_stark() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - - let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 5; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - // let proof = prove::( - // stark, - // &config, - // trace, - // public_inputs, - // &mut TimingTree::default(), - // )?; - - // verify_stark_proof(stark, proof, &config) - Ok(()) - } - - #[test] - fn test_fibonacci_stark_degree() -> Result<()> { - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - - let num_rows = 1 << 5; - let stark = S::new(num_rows); - test_stark_low_degree(stark) - } - - #[test] - fn test_recursive_stark_verifier() -> Result<()> { - init_logger(); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - type S = FibonacciStark; - - let config = StarkConfig::standard_fast_config(); - let num_rows = 1 << 5; - let public_inputs = [F::ZERO, F::ONE, fibonacci(num_rows - 1, F::ZERO, F::ONE)]; - let stark = S::new(num_rows); - let trace = stark.generate_trace(public_inputs[0], public_inputs[1]); - // let proof = prove::( - // stark, - // &config, - // trace, - // public_inputs, - // &mut TimingTree::default(), - // )?; - // verify_stark_proof(stark, proof.clone(), &config)?; - // - // recursive_proof::(stark, proof, &config, true) - Ok(()) - } - - fn recursive_proof< - F: RichField + Extendable, - C: GenericConfig, - S: Stark + Copy, - InnerC: GenericConfig, - const D: usize, - >( - stark: S, - inner_proof: StarkProofWithPublicInputs, - inner_config: &StarkConfig, - print_gate_counts: bool, - ) -> Result<()> - where - InnerC::Hasher: AlgebraicHasher, - [(); S::COLUMNS]:, - [(); S::PUBLIC_INPUTS]:, - [(); C::Hasher::HASH_SIZE]:, - { - let circuit_config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(circuit_config); - let mut pw = PartialWitness::new(); - let degree_bits = inner_proof.proof.recover_degree_bits(inner_config); - let pt = add_virtual_stark_proof_with_pis(&mut builder, stark, inner_config, degree_bits); - set_stark_proof_with_pis_target(&mut pw, &pt, &inner_proof); - - recursively_verify_stark_proof::(&mut builder, stark, pt, inner_config); - - if print_gate_counts { - builder.print_gate_counts(0); - } - - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof) - } - - fn init_logger() { - let _ = env_logger::builder().format_timestamp(None).try_init(); - } -} diff --git a/starky2/src/permutation.rs b/starky2/src/permutation.rs index 56784f8f..6f4c6ca2 100644 --- a/starky2/src/permutation.rs +++ b/starky2/src/permutation.rs @@ -12,6 +12,7 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher}; +use plonky2::plonk::plonk_common::reduce_with_powers; use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget}; use rayon::prelude::*; @@ -53,6 +54,20 @@ pub(crate) struct GrandProductChallenge { pub(crate) gamma: T, } +impl GrandProductChallenge { + pub(crate) fn combine<'a, FE, P, T: IntoIterator, const D2: usize>( + &self, + terms: T, + ) -> P + where + FE: FieldExtension, + P: PackedField, + T::IntoIter: DoubleEndedIterator, + { + reduce_with_powers(terms, FE::from_basefield(self.beta)) + FE::from_basefield(self.gamma) + } +} + /// Like `PermutationChallenge`, but with `num_challenges` copies to boost soundness. #[derive(Clone)] pub(crate) struct GrandProductChallengeSet { @@ -323,7 +338,7 @@ pub struct PermutationCheckDataTarget { pub(crate) permutation_challenge_sets: Vec>, } -pub(crate) fn eval_permutation_checks_recursively( +pub(crate) fn eval_permutation_checks_circuit( builder: &mut CircuitBuilder, stark: &S, config: &StarkConfig, diff --git a/starky2/src/prover.rs b/starky2/src/prover.rs index c9f2f0ab..bc6cd99b 100644 --- a/starky2/src/prover.rs +++ b/starky2/src/prover.rs @@ -15,10 +15,12 @@ use plonky2::util::transpose; use plonky2_util::{log2_ceil, log2_strict}; use rayon::prelude::*; -use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; +use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; +use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{cross_table_lookup_data, CtlCheckVars, CtlData}; +use crate::keccak::keccak_stark::KeccakStark; use crate::permutation::PermutationCheckVars; use crate::permutation::{ compute_permutation_z_polys, get_n_grand_product_challenge_sets, GrandProductChallengeSet, diff --git a/starky2/src/recursive_verifier.rs b/starky2/src/recursive_verifier.rs index b0021ac6..af395fdb 100644 --- a/starky2/src/recursive_verifier.rs +++ b/starky2/src/recursive_verifier.rs @@ -21,10 +21,10 @@ use crate::proof::{ StarkProofWithPublicInputs, StarkProofWithPublicInputsTarget, }; use crate::stark::Stark; -use crate::vanishing_poly::eval_vanishing_poly_recursively; +use crate::vanishing_poly::eval_vanishing_poly_circuit; use crate::vars::StarkEvaluationTargets; -pub fn recursively_verify_stark_proof< +pub fn verify_stark_proof_circuit< F: RichField + Extendable, C: GenericConfig, S: Stark, @@ -47,7 +47,7 @@ pub fn recursively_verify_stark_proof< proof_with_pis.get_challenges::(builder, &stark, inner_config) ); - recursively_verify_stark_proof_with_challenges::( + verify_stark_proof_with_challenges_circuit::( builder, stark, proof_with_pis, @@ -58,7 +58,7 @@ pub fn recursively_verify_stark_proof< } /// Recursively verifies an inner proof. -fn recursively_verify_stark_proof_with_challenges< +fn verify_stark_proof_with_challenges_circuit< F: RichField + Extendable, C: GenericConfig, S: Stark, @@ -103,7 +103,7 @@ fn recursively_verify_stark_proof_with_challenges< let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits); let z_h_zeta = builder.sub_extension(zeta_pow_deg, one); let (l_1, l_last) = - eval_l_1_and_l_last_recursively(builder, degree_bits, challenges.stark_zeta, z_h_zeta); + eval_l_1_and_l_last_circuit(builder, degree_bits, challenges.stark_zeta, z_h_zeta); let last = builder.constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse()); let z_last = builder.sub_extension(challenges.stark_zeta, last); @@ -127,7 +127,7 @@ fn recursively_verify_stark_proof_with_challenges< with_context!( builder, "evaluate vanishing polynomial", - eval_vanishing_poly_recursively::( + eval_vanishing_poly_circuit::( builder, &stark, inner_config, @@ -170,7 +170,7 @@ fn recursively_verify_stark_proof_with_challenges< ); } -fn eval_l_1_and_l_last_recursively, const D: usize>( +fn eval_l_1_and_l_last_circuit, const D: usize>( builder: &mut CircuitBuilder, log_n: usize, x: ExtensionTarget, diff --git a/starky2/src/stark.rs b/starky2/src/stark.rs index dcf1a73f..20404341 100644 --- a/starky2/src/stark.rs +++ b/starky2/src/stark.rs @@ -66,7 +66,7 @@ pub trait Stark, const D: usize>: Sync { /// `eval_ext`, except in the context of a recursive circuit. /// Note: constraints must be added through`yeld_constr.constraint(builder, constraint)` in the /// same order as they are given in `eval_packed_generic`. - fn eval_ext_recursively( + fn eval_ext_circuit( &self, builder: &mut CircuitBuilder, vars: StarkEvaluationTargets, diff --git a/starky2/src/vanishing_poly.rs b/starky2/src/vanishing_poly.rs index aac8fc80..37b1b5ab 100644 --- a/starky2/src/vanishing_poly.rs +++ b/starky2/src/vanishing_poly.rs @@ -8,7 +8,7 @@ use crate::config::StarkConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cross_table_lookup::{eval_cross_table_lookup_checks, CtlCheckVars}; use crate::permutation::{ - eval_permutation_checks, eval_permutation_checks_recursively, PermutationCheckDataTarget, + eval_permutation_checks, eval_permutation_checks_circuit, PermutationCheckDataTarget, PermutationCheckVars, }; use crate::stark::Stark; @@ -41,7 +41,7 @@ pub(crate) fn eval_vanishing_poly(vars, ctl_vars, consumer); } -pub(crate) fn eval_vanishing_poly_recursively( +pub(crate) fn eval_vanishing_poly_circuit( builder: &mut CircuitBuilder, stark: &S, config: &StarkConfig, @@ -55,9 +55,9 @@ pub(crate) fn eval_vanishing_poly_recursively( [(); S::COLUMNS]:, [(); S::PUBLIC_INPUTS]:, { - stark.eval_ext_recursively(builder, vars, consumer); + stark.eval_ext_circuit(builder, vars, consumer); if let Some(permutation_data) = permutation_data { - eval_permutation_checks_recursively::( + eval_permutation_checks_circuit::( builder, stark, config, diff --git a/starky2/src/verifier.rs b/starky2/src/verifier.rs index d8f24e97..52882488 100644 --- a/starky2/src/verifier.rs +++ b/starky2/src/verifier.rs @@ -6,10 +6,12 @@ use plonky2::hash::hash_types::RichField; use plonky2::plonk::config::{GenericConfig, Hasher}; use plonky2::plonk::plonk_common::reduce_with_powers; -use crate::all_stark::{AllStark, CpuStark, KeccakStark, Table}; +use crate::all_stark::{AllStark, Table}; use crate::config::StarkConfig; use crate::constraint_consumer::ConstraintConsumer; +use crate::cpu::cpu_stark::CpuStark; use crate::cross_table_lookup::{verify_cross_table_lookups, CtlCheckVars}; +use crate::keccak::keccak_stark::KeccakStark; use crate::permutation::PermutationCheckVars; use crate::proof::{ AllProof, AllProofChallenges, StarkOpeningSet, StarkProofChallenges, StarkProofWithPublicInputs,