mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 06:43:07 +00:00
Switch permutation argument for logUp in starky (#1496)
* Switch permutation argument for logUp in starky * Apply comments * Refactor check_lookup_options * Comments * Add more visibility * std -> core * Revert "Add more visibility" This reverts commit 2b4e50e0e7fc7676814b1bc1f4071d9ec0ab9d5c. * Add more visibility to lookup items
This commit is contained in:
parent
fb8452de8e
commit
06444eaaf3
@ -612,7 +612,9 @@ where
|
|||||||
local_values: auxiliary_polys_commitment.get_lde_values_packed(i_start, step)
|
local_values: auxiliary_polys_commitment.get_lde_values_packed(i_start, step)
|
||||||
[..num_lookup_columns]
|
[..num_lookup_columns]
|
||||||
.to_vec(),
|
.to_vec(),
|
||||||
next_values: auxiliary_polys_commitment.get_lde_values_packed(i_next_start, step),
|
next_values: auxiliary_polys_commitment.get_lde_values_packed(i_next_start, step)
|
||||||
|
[..num_lookup_columns]
|
||||||
|
.to_vec(),
|
||||||
challenges: challenges.to_vec(),
|
challenges: challenges.to_vec(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,10 @@ timing = ["plonky2/timing"]
|
|||||||
anyhow = { version = "1.0.40", default-features = false }
|
anyhow = { version = "1.0.40", default-features = false }
|
||||||
itertools = { version = "0.11.0", default-features = false }
|
itertools = { version = "0.11.0", default-features = false }
|
||||||
log = { version = "0.4.14", default-features = false }
|
log = { version = "0.4.14", default-features = false }
|
||||||
|
num-bigint = { version = "0.4.3", default-features = false }
|
||||||
plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false }
|
plonky2_maybe_rayon = { path = "../maybe_rayon", default-features = false }
|
||||||
plonky2 = { path = "../plonky2", default-features = false }
|
plonky2 = { path = "../plonky2", default-features = false }
|
||||||
|
plonky2_util = { path = "../util", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = { version = "0.9.0", default-features = false }
|
env_logger = { version = "0.9.0", default-features = false }
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use plonky2::plonk::circuit_builder::CircuitBuilder;
|
|||||||
|
|
||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame};
|
use crate::evaluation_frame::{StarkEvaluationFrame, StarkFrame};
|
||||||
use crate::permutation::PermutationPair;
|
use crate::lookup::{Column, Lookup};
|
||||||
use crate::stark::Stark;
|
use crate::stark::Stark;
|
||||||
use crate::util::trace_rows_to_poly_values;
|
use crate::util::trace_rows_to_poly_values;
|
||||||
|
|
||||||
@ -41,15 +41,16 @@ impl<F: RichField + Extendable<D>, const D: usize> FibonacciStark<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the trace using `x0, x1, 0, 1` as initial state values.
|
/// Generate the trace using `x0, x1, 0, 1, 1` as initial state values.
|
||||||
fn generate_trace(&self, x0: F, x1: F) -> Vec<PolynomialValues<F>> {
|
fn generate_trace(&self, x0: F, x1: F) -> Vec<PolynomialValues<F>> {
|
||||||
let mut trace_rows = (0..self.num_rows)
|
let mut trace_rows = (0..self.num_rows)
|
||||||
.scan([x0, x1, F::ZERO, F::ONE], |acc, _| {
|
.scan([x0, x1, F::ZERO, F::ONE, F::ONE], |acc, _| {
|
||||||
let tmp = *acc;
|
let tmp = *acc;
|
||||||
acc[0] = tmp[1];
|
acc[0] = tmp[1];
|
||||||
acc[1] = tmp[0] + tmp[1];
|
acc[1] = tmp[0] + tmp[1];
|
||||||
acc[2] = tmp[2] + F::ONE;
|
acc[2] = tmp[2] + F::ONE;
|
||||||
acc[3] = tmp[3] + F::ONE;
|
acc[3] = tmp[3] + F::ONE;
|
||||||
|
// acc[4] (i.e. frequency column) remains unchanged, as we're permuting a strictly monotonous sequence.
|
||||||
Some(tmp)
|
Some(tmp)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -58,7 +59,7 @@ impl<F: RichField + Extendable<D>, const D: usize> FibonacciStark<F, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const COLUMNS: usize = 4;
|
const COLUMNS: usize = 5;
|
||||||
const PUBLIC_INPUTS: usize = 3;
|
const PUBLIC_INPUTS: usize = 3;
|
||||||
|
|
||||||
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for FibonacciStark<F, D> {
|
impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for FibonacciStark<F, D> {
|
||||||
@ -127,8 +128,13 @@ impl<F: RichField + Extendable<D>, const D: usize> Stark<F, D> for FibonacciStar
|
|||||||
2
|
2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permutation_pairs(&self) -> Vec<PermutationPair> {
|
fn lookups(&self) -> Vec<Lookup<F>> {
|
||||||
vec![PermutationPair::singletons(2, 3)]
|
vec![Lookup {
|
||||||
|
columns: vec![Column::single(2)],
|
||||||
|
table_column: Column::single(3),
|
||||||
|
frequencies_column: Column::single(4),
|
||||||
|
filter_columns: vec![None; 1],
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,16 +12,13 @@ use plonky2::plonk::circuit_builder::CircuitBuilder;
|
|||||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::permutation::{
|
use crate::lookup::{get_grand_product_challenge_set, get_grand_product_challenge_set_target};
|
||||||
get_n_permutation_challenge_sets, get_n_permutation_challenge_sets_target,
|
|
||||||
};
|
|
||||||
use crate::proof::*;
|
use crate::proof::*;
|
||||||
use crate::stark::Stark;
|
use crate::stark::Stark;
|
||||||
|
|
||||||
fn get_challenges<F, C, S, const D: usize>(
|
fn get_challenges<F, C, const D: usize>(
|
||||||
stark: &S,
|
|
||||||
trace_cap: &MerkleCap<F, C::Hasher>,
|
trace_cap: &MerkleCap<F, C::Hasher>,
|
||||||
permutation_zs_cap: Option<&MerkleCap<F, C::Hasher>>,
|
auxiliary_polys_cap: Option<&MerkleCap<F, C::Hasher>>,
|
||||||
quotient_polys_cap: &MerkleCap<F, C::Hasher>,
|
quotient_polys_cap: &MerkleCap<F, C::Hasher>,
|
||||||
openings: &StarkOpeningSet<F, D>,
|
openings: &StarkOpeningSet<F, D>,
|
||||||
commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
|
||||||
@ -33,7 +30,6 @@ fn get_challenges<F, C, S, const D: usize>(
|
|||||||
where
|
where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
C: GenericConfig<D, F = F>,
|
||||||
S: Stark<F, D>,
|
|
||||||
{
|
{
|
||||||
let num_challenges = config.num_challenges;
|
let num_challenges = config.num_challenges;
|
||||||
|
|
||||||
@ -41,13 +37,9 @@ where
|
|||||||
|
|
||||||
challenger.observe_cap(trace_cap);
|
challenger.observe_cap(trace_cap);
|
||||||
|
|
||||||
let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| {
|
let lookup_challenge_set = auxiliary_polys_cap.map(|auxiliary_polys_cap| {
|
||||||
let tmp = get_n_permutation_challenge_sets(
|
let tmp = get_grand_product_challenge_set(&mut challenger, num_challenges);
|
||||||
&mut challenger,
|
challenger.observe_cap(auxiliary_polys_cap);
|
||||||
num_challenges,
|
|
||||||
stark.permutation_batch_size(),
|
|
||||||
);
|
|
||||||
challenger.observe_cap(permutation_zs_cap);
|
|
||||||
tmp
|
tmp
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,7 +51,7 @@ where
|
|||||||
challenger.observe_openings(&openings.to_fri_openings());
|
challenger.observe_openings(&openings.to_fri_openings());
|
||||||
|
|
||||||
StarkProofChallenges {
|
StarkProofChallenges {
|
||||||
permutation_challenge_sets,
|
lookup_challenge_set,
|
||||||
stark_alphas,
|
stark_alphas,
|
||||||
stark_zeta,
|
stark_zeta,
|
||||||
fri_challenges: challenger.fri_challenges::<C, D>(
|
fri_challenges: challenger.fri_challenges::<C, D>(
|
||||||
@ -79,27 +71,21 @@ where
|
|||||||
{
|
{
|
||||||
// TODO: Should be used later in compression?
|
// TODO: Should be used later in compression?
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
pub(crate) fn fri_query_indices<S: Stark<F, D>>(
|
pub(crate) fn fri_query_indices(&self, config: &StarkConfig, degree_bits: usize) -> Vec<usize> {
|
||||||
&self,
|
self.get_challenges(config, degree_bits)
|
||||||
stark: &S,
|
|
||||||
config: &StarkConfig,
|
|
||||||
degree_bits: usize,
|
|
||||||
) -> Vec<usize> {
|
|
||||||
self.get_challenges(stark, config, degree_bits)
|
|
||||||
.fri_challenges
|
.fri_challenges
|
||||||
.fri_query_indices
|
.fri_query_indices
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
/// Computes all Fiat-Shamir challenges used in the STARK proof.
|
||||||
pub(crate) fn get_challenges<S: Stark<F, D>>(
|
pub(crate) fn get_challenges(
|
||||||
&self,
|
&self,
|
||||||
stark: &S,
|
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
degree_bits: usize,
|
degree_bits: usize,
|
||||||
) -> StarkProofChallenges<F, D> {
|
) -> StarkProofChallenges<F, D> {
|
||||||
let StarkProof {
|
let StarkProof {
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap,
|
auxiliary_polys_cap,
|
||||||
quotient_polys_cap,
|
quotient_polys_cap,
|
||||||
openings,
|
openings,
|
||||||
opening_proof:
|
opening_proof:
|
||||||
@ -111,10 +97,9 @@ where
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
get_challenges::<F, C, S, D>(
|
get_challenges::<F, C, D>(
|
||||||
stark,
|
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap.as_ref(),
|
auxiliary_polys_cap.as_ref(),
|
||||||
quotient_polys_cap,
|
quotient_polys_cap,
|
||||||
openings,
|
openings,
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
@ -130,13 +115,11 @@ where
|
|||||||
pub(crate) fn get_challenges_target<
|
pub(crate) fn get_challenges_target<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
C: GenericConfig<D, F = F>,
|
||||||
S: Stark<F, D>,
|
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
stark: &S,
|
|
||||||
trace_cap: &MerkleCapTarget,
|
trace_cap: &MerkleCapTarget,
|
||||||
permutation_zs_cap: Option<&MerkleCapTarget>,
|
auxiliary_polys_cap: Option<&MerkleCapTarget>,
|
||||||
quotient_polys_cap: &MerkleCapTarget,
|
quotient_polys_cap: &MerkleCapTarget,
|
||||||
openings: &StarkOpeningSetTarget<D>,
|
openings: &StarkOpeningSetTarget<D>,
|
||||||
commit_phase_merkle_caps: &[MerkleCapTarget],
|
commit_phase_merkle_caps: &[MerkleCapTarget],
|
||||||
@ -153,13 +136,8 @@ where
|
|||||||
|
|
||||||
challenger.observe_cap(trace_cap);
|
challenger.observe_cap(trace_cap);
|
||||||
|
|
||||||
let permutation_challenge_sets = permutation_zs_cap.map(|permutation_zs_cap| {
|
let lookup_challenge_set = auxiliary_polys_cap.map(|permutation_zs_cap| {
|
||||||
let tmp = get_n_permutation_challenge_sets_target(
|
let tmp = get_grand_product_challenge_set_target(builder, &mut challenger, num_challenges);
|
||||||
builder,
|
|
||||||
&mut challenger,
|
|
||||||
num_challenges,
|
|
||||||
stark.permutation_batch_size(),
|
|
||||||
);
|
|
||||||
challenger.observe_cap(permutation_zs_cap);
|
challenger.observe_cap(permutation_zs_cap);
|
||||||
tmp
|
tmp
|
||||||
});
|
});
|
||||||
@ -172,7 +150,7 @@ where
|
|||||||
challenger.observe_openings(&openings.to_fri_openings());
|
challenger.observe_openings(&openings.to_fri_openings());
|
||||||
|
|
||||||
StarkProofChallengesTarget {
|
StarkProofChallengesTarget {
|
||||||
permutation_challenge_sets,
|
lookup_challenge_set,
|
||||||
stark_alphas,
|
stark_alphas,
|
||||||
stark_zeta,
|
stark_zeta,
|
||||||
fri_challenges: challenger.fri_challenges(
|
fri_challenges: challenger.fri_challenges(
|
||||||
@ -186,22 +164,19 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
||||||
pub(crate) fn get_challenges<
|
pub(crate) fn get_challenges<F, C>(
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
C: GenericConfig<D, F = F>,
|
|
||||||
S: Stark<F, D>,
|
|
||||||
>(
|
|
||||||
&self,
|
&self,
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
stark: &S,
|
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> StarkProofChallengesTarget<D>
|
) -> StarkProofChallengesTarget<D>
|
||||||
where
|
where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
C: GenericConfig<D, F = F>,
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
{
|
{
|
||||||
let StarkProofTarget {
|
let StarkProofTarget {
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap,
|
auxiliary_polys_cap,
|
||||||
quotient_polys_cap,
|
quotient_polys_cap,
|
||||||
openings,
|
openings,
|
||||||
opening_proof:
|
opening_proof:
|
||||||
@ -213,11 +188,10 @@ impl<const D: usize> StarkProofWithPublicInputsTarget<D> {
|
|||||||
},
|
},
|
||||||
} = &self.proof;
|
} = &self.proof;
|
||||||
|
|
||||||
get_challenges_target::<F, C, S, D>(
|
get_challenges_target::<F, C, D>(
|
||||||
builder,
|
builder,
|
||||||
stark,
|
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap.as_ref(),
|
auxiliary_polys_cap.as_ref(),
|
||||||
quotient_polys_cap,
|
quotient_polys_cap,
|
||||||
openings,
|
openings,
|
||||||
commit_phase_merkle_caps,
|
commit_phase_merkle_caps,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
#![allow(unused)] // TODO: Remove post code migration
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@ -9,7 +10,7 @@ mod get_challenges;
|
|||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod constraint_consumer;
|
pub mod constraint_consumer;
|
||||||
pub mod evaluation_frame;
|
pub mod evaluation_frame;
|
||||||
pub mod permutation;
|
pub mod lookup;
|
||||||
pub mod proof;
|
pub mod proof;
|
||||||
pub mod prover;
|
pub mod prover;
|
||||||
pub mod recursive_verifier;
|
pub mod recursive_verifier;
|
||||||
|
|||||||
1002
starky/src/lookup.rs
Normal file
1002
starky/src/lookup.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,398 +0,0 @@
|
|||||||
//! Permutation arguments.
|
|
||||||
|
|
||||||
use alloc::vec;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use plonky2::field::batch_util::batch_multiply_inplace;
|
|
||||||
use plonky2::field::extension::{Extendable, FieldExtension};
|
|
||||||
use plonky2::field::packed::PackedField;
|
|
||||||
use plonky2::field::polynomial::PolynomialValues;
|
|
||||||
use plonky2::field::types::Field;
|
|
||||||
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, Hasher};
|
|
||||||
use plonky2::util::reducing::{ReducingFactor, ReducingFactorTarget};
|
|
||||||
use plonky2_maybe_rayon::*;
|
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
|
||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
|
||||||
use crate::evaluation_frame::StarkEvaluationFrame;
|
|
||||||
use crate::stark::Stark;
|
|
||||||
|
|
||||||
/// 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<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Randomness for a single instance of a permutation check protocol.
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub(crate) struct PermutationChallenge<T: Copy> {
|
|
||||||
/// 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<T: Copy> {
|
|
||||||
pub(crate) challenges: Vec<PermutationChallenge<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute all Z polynomials (for permutation arguments).
|
|
||||||
pub(crate) fn compute_permutation_z_polys<F, S, const D: usize>(
|
|
||||||
stark: &S,
|
|
||||||
config: &StarkConfig,
|
|
||||||
trace_poly_values: &[PolynomialValues<F>],
|
|
||||||
permutation_challenge_sets: &[PermutationChallengeSet<F>],
|
|
||||||
) -> Vec<PolynomialValues<F>>
|
|
||||||
where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
S: Stark<F, D>,
|
|
||||||
{
|
|
||||||
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<F: Field>(
|
|
||||||
instances: &[PermutationInstance<F>],
|
|
||||||
trace_poly_values: &[PolynomialValues<F>],
|
|
||||||
) -> PolynomialValues<F> {
|
|
||||||
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<F: Field>(
|
|
||||||
instance: &PermutationInstance<F>,
|
|
||||||
trace_poly_values: &[PolynomialValues<F>],
|
|
||||||
degree: usize,
|
|
||||||
) -> (PolynomialValues<F>, PolynomialValues<F>) {
|
|
||||||
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<F: Field>(
|
|
||||||
mut polys: impl Iterator<Item = PolynomialValues<F>>,
|
|
||||||
) -> PolynomialValues<F> {
|
|
||||||
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<F: RichField, H: Hasher<F>>(
|
|
||||||
challenger: &mut Challenger<F, H>,
|
|
||||||
) -> PermutationChallenge<F> {
|
|
||||||
let beta = challenger.get_challenge();
|
|
||||||
let gamma = challenger.get_challenge();
|
|
||||||
PermutationChallenge { beta, gamma }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_permutation_challenge_set<F: RichField, H: Hasher<F>>(
|
|
||||||
challenger: &mut Challenger<F, H>,
|
|
||||||
num_challenges: usize,
|
|
||||||
) -> PermutationChallengeSet<F> {
|
|
||||||
let challenges = (0..num_challenges)
|
|
||||||
.map(|_| get_permutation_challenge(challenger))
|
|
||||||
.collect();
|
|
||||||
PermutationChallengeSet { challenges }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_n_permutation_challenge_sets<F: RichField, H: Hasher<F>>(
|
|
||||||
challenger: &mut Challenger<F, H>,
|
|
||||||
num_challenges: usize,
|
|
||||||
num_sets: usize,
|
|
||||||
) -> Vec<PermutationChallengeSet<F>> {
|
|
||||||
(0..num_sets)
|
|
||||||
.map(|_| get_permutation_challenge_set(challenger, num_challenges))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_permutation_challenge_target<
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
H: AlgebraicHasher<F>,
|
|
||||||
const D: usize,
|
|
||||||
>(
|
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
|
||||||
) -> PermutationChallenge<Target> {
|
|
||||||
let beta = challenger.get_challenge(builder);
|
|
||||||
let gamma = challenger.get_challenge(builder);
|
|
||||||
PermutationChallenge { beta, gamma }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_permutation_challenge_set_target<
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
H: AlgebraicHasher<F>,
|
|
||||||
const D: usize,
|
|
||||||
>(
|
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
|
||||||
num_challenges: usize,
|
|
||||||
) -> PermutationChallengeSet<Target> {
|
|
||||||
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<D>,
|
|
||||||
H: AlgebraicHasher<F>,
|
|
||||||
const D: usize,
|
|
||||||
>(
|
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
|
||||||
challenger: &mut RecursiveChallenger<F, H, D>,
|
|
||||||
num_challenges: usize,
|
|
||||||
num_sets: usize,
|
|
||||||
) -> Vec<PermutationChallengeSet<Target>> {
|
|
||||||
(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<T>],
|
|
||||||
num_challenges: usize,
|
|
||||||
batch_size: usize,
|
|
||||||
) -> Vec<Vec<PermutationInstance<'a, T>>> {
|
|
||||||
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<F, FE, P, const D2: usize>
|
|
||||||
where
|
|
||||||
F: Field,
|
|
||||||
FE: FieldExtension<D2, BaseField = F>,
|
|
||||||
P: PackedField<Scalar = FE>,
|
|
||||||
{
|
|
||||||
pub(crate) local_zs: Vec<P>,
|
|
||||||
pub(crate) next_zs: Vec<P>,
|
|
||||||
pub(crate) permutation_challenge_sets: Vec<PermutationChallengeSet<F>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn eval_permutation_checks<F, FE, P, S, const D: usize, const D2: usize>(
|
|
||||||
stark: &S,
|
|
||||||
config: &StarkConfig,
|
|
||||||
vars: &S::EvaluationFrame<FE, P, D2>,
|
|
||||||
permutation_data: PermutationCheckVars<F, FE, P, D2>,
|
|
||||||
consumer: &mut ConstraintConsumer<P>,
|
|
||||||
) where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
FE: FieldExtension<D2, BaseField = F>,
|
|
||||||
P: PackedField<Scalar = FE>,
|
|
||||||
S: Stark<F, D>,
|
|
||||||
{
|
|
||||||
let local_values = vars.get_local_values();
|
|
||||||
|
|
||||||
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<P>, Vec<P>) = 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)| (local_values[i], 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::<P>()
|
|
||||||
- local_zs[i] * reduced_lhs.into_iter().product::<P>();
|
|
||||||
consumer.constraint(constraint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PermutationCheckDataTarget<const D: usize> {
|
|
||||||
pub(crate) local_zs: Vec<ExtensionTarget<D>>,
|
|
||||||
pub(crate) next_zs: Vec<ExtensionTarget<D>>,
|
|
||||||
pub(crate) permutation_challenge_sets: Vec<PermutationChallengeSet<Target>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn eval_permutation_checks_circuit<F, S, const D: usize>(
|
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
|
||||||
stark: &S,
|
|
||||||
config: &StarkConfig,
|
|
||||||
vars: &S::EvaluationFrameTarget,
|
|
||||||
permutation_data: PermutationCheckDataTarget<D>,
|
|
||||||
consumer: &mut RecursiveConstraintConsumer<F, D>,
|
|
||||||
) where
|
|
||||||
F: RichField + Extendable<D>,
|
|
||||||
S: Stark<F, D>,
|
|
||||||
{
|
|
||||||
let local_values = vars.get_local_values();
|
|
||||||
|
|
||||||
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<ExtensionTarget<D>>, Vec<ExtensionTarget<D>>) =
|
|
||||||
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)| (local_values[i], 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -18,14 +18,14 @@ use plonky2::plonk::config::GenericConfig;
|
|||||||
use plonky2_maybe_rayon::*;
|
use plonky2_maybe_rayon::*;
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::permutation::PermutationChallengeSet;
|
use crate::lookup::GrandProductChallengeSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
pub struct StarkProof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> {
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub trace_cap: MerkleCap<F, C::Hasher>,
|
pub trace_cap: MerkleCap<F, C::Hasher>,
|
||||||
/// Merkle cap of LDEs of permutation Z values.
|
/// Merkle cap of LDEs of permutation Z values.
|
||||||
pub permutation_zs_cap: Option<MerkleCap<F, C::Hasher>>,
|
pub auxiliary_polys_cap: Option<MerkleCap<F, C::Hasher>>,
|
||||||
/// Merkle cap of LDEs of trace values.
|
/// Merkle cap of LDEs of trace values.
|
||||||
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
pub quotient_polys_cap: MerkleCap<F, C::Hasher>,
|
||||||
/// Purported values of each polynomial at the challenge point.
|
/// Purported values of each polynomial at the challenge point.
|
||||||
@ -48,7 +48,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize> S
|
|||||||
|
|
||||||
pub struct StarkProofTarget<const D: usize> {
|
pub struct StarkProofTarget<const D: usize> {
|
||||||
pub trace_cap: MerkleCapTarget,
|
pub trace_cap: MerkleCapTarget,
|
||||||
pub permutation_zs_cap: Option<MerkleCapTarget>,
|
pub auxiliary_polys_cap: Option<MerkleCapTarget>,
|
||||||
pub quotient_polys_cap: MerkleCapTarget,
|
pub quotient_polys_cap: MerkleCapTarget,
|
||||||
pub openings: StarkOpeningSetTarget<D>,
|
pub openings: StarkOpeningSetTarget<D>,
|
||||||
pub opening_proof: FriProofTarget<D>,
|
pub opening_proof: FriProofTarget<D>,
|
||||||
@ -106,7 +106,7 @@ pub struct CompressedStarkProofWithPublicInputs<
|
|||||||
|
|
||||||
pub(crate) struct StarkProofChallenges<F: RichField + Extendable<D>, const D: usize> {
|
pub(crate) struct StarkProofChallenges<F: RichField + Extendable<D>, const D: usize> {
|
||||||
/// Randomness used in any permutation arguments.
|
/// Randomness used in any permutation arguments.
|
||||||
pub permutation_challenge_sets: Option<Vec<PermutationChallengeSet<F>>>,
|
pub lookup_challenge_set: Option<GrandProductChallengeSet<F>>,
|
||||||
|
|
||||||
/// Random values used to combine STARK constraints.
|
/// Random values used to combine STARK constraints.
|
||||||
pub stark_alphas: Vec<F>,
|
pub stark_alphas: Vec<F>,
|
||||||
@ -118,7 +118,7 @@ pub(crate) struct StarkProofChallenges<F: RichField + Extendable<D>, const D: us
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct StarkProofChallengesTarget<const D: usize> {
|
pub(crate) struct StarkProofChallengesTarget<const D: usize> {
|
||||||
pub permutation_challenge_sets: Option<Vec<PermutationChallengeSet<Target>>>,
|
pub lookup_challenge_set: Option<GrandProductChallengeSet<Target>>,
|
||||||
pub stark_alphas: Vec<Target>,
|
pub stark_alphas: Vec<Target>,
|
||||||
pub stark_zeta: ExtensionTarget<D>,
|
pub stark_zeta: ExtensionTarget<D>,
|
||||||
pub fri_challenges: FriChallengesTarget<D>,
|
pub fri_challenges: FriChallengesTarget<D>,
|
||||||
@ -129,8 +129,8 @@ pub(crate) struct StarkProofChallengesTarget<const D: usize> {
|
|||||||
pub struct StarkOpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
pub struct StarkOpeningSet<F: RichField + Extendable<D>, const D: usize> {
|
||||||
pub local_values: Vec<F::Extension>,
|
pub local_values: Vec<F::Extension>,
|
||||||
pub next_values: Vec<F::Extension>,
|
pub next_values: Vec<F::Extension>,
|
||||||
pub permutation_zs: Option<Vec<F::Extension>>,
|
pub auxiliary_polys: Option<Vec<F::Extension>>,
|
||||||
pub permutation_zs_next: Option<Vec<F::Extension>>,
|
pub auxiliary_polys_next: Option<Vec<F::Extension>>,
|
||||||
pub quotient_polys: Vec<F::Extension>,
|
pub quotient_polys: Vec<F::Extension>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
|||||||
zeta: F::Extension,
|
zeta: F::Extension,
|
||||||
g: F,
|
g: F,
|
||||||
trace_commitment: &PolynomialBatch<F, C, D>,
|
trace_commitment: &PolynomialBatch<F, C, D>,
|
||||||
permutation_zs_commitment: Option<&PolynomialBatch<F, C, D>>,
|
auxiliary_polys_commitment: Option<&PolynomialBatch<F, C, D>>,
|
||||||
quotient_commitment: &PolynomialBatch<F, C, D>,
|
quotient_commitment: &PolynomialBatch<F, C, D>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
let eval_commitment = |z: F::Extension, c: &PolynomialBatch<F, C, D>| {
|
||||||
@ -152,8 +152,8 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
|||||||
Self {
|
Self {
|
||||||
local_values: eval_commitment(zeta, trace_commitment),
|
local_values: eval_commitment(zeta, trace_commitment),
|
||||||
next_values: eval_commitment(zeta_next, trace_commitment),
|
next_values: eval_commitment(zeta_next, trace_commitment),
|
||||||
permutation_zs: permutation_zs_commitment.map(|c| eval_commitment(zeta, c)),
|
auxiliary_polys: auxiliary_polys_commitment.map(|c| eval_commitment(zeta, c)),
|
||||||
permutation_zs_next: permutation_zs_commitment.map(|c| eval_commitment(zeta_next, c)),
|
auxiliary_polys_next: auxiliary_polys_commitment.map(|c| eval_commitment(zeta_next, c)),
|
||||||
quotient_polys: eval_commitment(zeta, quotient_commitment),
|
quotient_polys: eval_commitment(zeta, quotient_commitment),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
|||||||
values: self
|
values: self
|
||||||
.local_values
|
.local_values
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.permutation_zs.iter().flatten())
|
.chain(self.auxiliary_polys.iter().flatten())
|
||||||
.chain(&self.quotient_polys)
|
.chain(&self.quotient_polys)
|
||||||
.copied()
|
.copied()
|
||||||
.collect_vec(),
|
.collect_vec(),
|
||||||
@ -172,7 +172,7 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
|||||||
values: self
|
values: self
|
||||||
.next_values
|
.next_values
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.permutation_zs_next.iter().flatten())
|
.chain(self.auxiliary_polys_next.iter().flatten())
|
||||||
.copied()
|
.copied()
|
||||||
.collect_vec(),
|
.collect_vec(),
|
||||||
};
|
};
|
||||||
@ -185,8 +185,8 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
|
|||||||
pub struct StarkOpeningSetTarget<const D: usize> {
|
pub struct StarkOpeningSetTarget<const D: usize> {
|
||||||
pub local_values: Vec<ExtensionTarget<D>>,
|
pub local_values: Vec<ExtensionTarget<D>>,
|
||||||
pub next_values: Vec<ExtensionTarget<D>>,
|
pub next_values: Vec<ExtensionTarget<D>>,
|
||||||
pub permutation_zs: Option<Vec<ExtensionTarget<D>>>,
|
pub auxiliary_polys: Option<Vec<ExtensionTarget<D>>>,
|
||||||
pub permutation_zs_next: Option<Vec<ExtensionTarget<D>>>,
|
pub auxiliary_polys_next: Option<Vec<ExtensionTarget<D>>>,
|
||||||
pub quotient_polys: Vec<ExtensionTarget<D>>,
|
pub quotient_polys: Vec<ExtensionTarget<D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
|||||||
values: self
|
values: self
|
||||||
.local_values
|
.local_values
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.permutation_zs.iter().flatten())
|
.chain(self.auxiliary_polys.iter().flatten())
|
||||||
.chain(&self.quotient_polys)
|
.chain(&self.quotient_polys)
|
||||||
.copied()
|
.copied()
|
||||||
.collect_vec(),
|
.collect_vec(),
|
||||||
@ -205,7 +205,7 @@ impl<const D: usize> StarkOpeningSetTarget<D> {
|
|||||||
values: self
|
values: self
|
||||||
.next_values
|
.next_values
|
||||||
.iter()
|
.iter()
|
||||||
.chain(self.permutation_zs_next.iter().flatten())
|
.chain(self.auxiliary_polys_next.iter().flatten())
|
||||||
.copied()
|
.copied()
|
||||||
.collect_vec(),
|
.collect_vec(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,9 +21,8 @@ use plonky2_maybe_rayon::*;
|
|||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::constraint_consumer::ConstraintConsumer;
|
use crate::constraint_consumer::ConstraintConsumer;
|
||||||
use crate::evaluation_frame::StarkEvaluationFrame;
|
use crate::evaluation_frame::StarkEvaluationFrame;
|
||||||
use crate::permutation::{
|
use crate::lookup::{
|
||||||
compute_permutation_z_polys, get_n_permutation_challenge_sets, PermutationChallengeSet,
|
get_grand_product_challenge_set, lookup_helper_columns, Lookup, LookupCheckVars,
|
||||||
PermutationCheckVars,
|
|
||||||
};
|
};
|
||||||
use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs};
|
use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs};
|
||||||
use crate::stark::Stark;
|
use crate::stark::Stark;
|
||||||
@ -69,25 +68,45 @@ where
|
|||||||
let mut challenger = Challenger::new();
|
let mut challenger = Challenger::new();
|
||||||
challenger.observe_cap(&trace_cap);
|
challenger.observe_cap(&trace_cap);
|
||||||
|
|
||||||
// Permutation arguments.
|
// Lookup argument.
|
||||||
let permutation_zs_commitment_challenges = stark.uses_permutation_args().then(|| {
|
let constraint_degree = stark.constraint_degree();
|
||||||
let permutation_challenge_sets = get_n_permutation_challenge_sets(
|
let lookups = stark.lookups();
|
||||||
&mut challenger,
|
let lookup_challenges = stark.uses_lookups().then(|| {
|
||||||
config.num_challenges,
|
get_grand_product_challenge_set(&mut challenger, config.num_challenges)
|
||||||
stark.permutation_batch_size(),
|
.challenges
|
||||||
);
|
.iter()
|
||||||
let permutation_z_polys = compute_permutation_z_polys::<F, S, D>(
|
.map(|ch| ch.beta)
|
||||||
&stark,
|
.collect::<Vec<_>>()
|
||||||
config,
|
});
|
||||||
&trace_poly_values,
|
|
||||||
&permutation_challenge_sets,
|
|
||||||
);
|
|
||||||
|
|
||||||
let permutation_zs_commitment = timed!(
|
let num_lookup_columns = lookups
|
||||||
|
.iter()
|
||||||
|
.map(|l| l.num_helper_columns(constraint_degree))
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
let auxiliary_polys_commitment = stark.uses_lookups().then(|| {
|
||||||
|
let lookup_helper_columns = timed!(timing, "compute lookup helper columns", {
|
||||||
|
let challenges = lookup_challenges.as_ref().expect("We do have challenges.");
|
||||||
|
let mut columns = Vec::with_capacity(num_lookup_columns);
|
||||||
|
for lookup in &lookups {
|
||||||
|
for &challenge in challenges {
|
||||||
|
columns.extend(lookup_helper_columns(
|
||||||
|
lookup,
|
||||||
|
&trace_poly_values,
|
||||||
|
challenge,
|
||||||
|
constraint_degree,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columns
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the polynomial commitments for all auxiliary polynomials.
|
||||||
|
let auxiliary_polys_commitment = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute permutation Z commitments",
|
"compute permutation Z commitments",
|
||||||
PolynomialBatch::from_values(
|
PolynomialBatch::from_values(
|
||||||
permutation_z_polys,
|
lookup_helper_columns,
|
||||||
rate_bits,
|
rate_bits,
|
||||||
false,
|
false,
|
||||||
config.fri_config.cap_height,
|
config.fri_config.cap_height,
|
||||||
@ -95,38 +114,68 @@ where
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
(permutation_zs_commitment, permutation_challenge_sets)
|
|
||||||
|
auxiliary_polys_commitment
|
||||||
});
|
});
|
||||||
let permutation_zs_commitment = permutation_zs_commitment_challenges
|
|
||||||
.as_ref()
|
let auxiliary_polys_cap = auxiliary_polys_commitment
|
||||||
.map(|(comm, _)| comm);
|
|
||||||
let permutation_zs_cap = permutation_zs_commitment
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|commit| commit.merkle_tree.cap.clone());
|
.map(|commit| commit.merkle_tree.cap.clone());
|
||||||
if let Some(cap) = &permutation_zs_cap {
|
if let Some(cap) = &auxiliary_polys_cap {
|
||||||
challenger.observe_cap(cap);
|
challenger.observe_cap(cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
let alphas = challenger.get_n_challenges(config.num_challenges);
|
let alphas = challenger.get_n_challenges(config.num_challenges);
|
||||||
let quotient_polys = compute_quotient_polys::<F, <F as Packable>::Packing, C, S, D>(
|
|
||||||
&stark,
|
#[cfg(test)]
|
||||||
&trace_commitment,
|
{
|
||||||
&permutation_zs_commitment_challenges,
|
check_constraints(
|
||||||
public_inputs,
|
&stark,
|
||||||
alphas,
|
&trace_commitment,
|
||||||
degree_bits,
|
public_inputs,
|
||||||
config,
|
&auxiliary_polys_commitment,
|
||||||
|
lookup_challenges.as_ref(),
|
||||||
|
&lookups,
|
||||||
|
alphas.clone(),
|
||||||
|
degree_bits,
|
||||||
|
num_lookup_columns,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let quotient_polys = timed!(
|
||||||
|
timing,
|
||||||
|
"compute quotient polys",
|
||||||
|
compute_quotient_polys::<F, <F as Packable>::Packing, C, S, D>(
|
||||||
|
&stark,
|
||||||
|
&trace_commitment,
|
||||||
|
&auxiliary_polys_commitment,
|
||||||
|
lookup_challenges.as_ref(),
|
||||||
|
&lookups,
|
||||||
|
public_inputs,
|
||||||
|
alphas,
|
||||||
|
degree_bits,
|
||||||
|
num_lookup_columns,
|
||||||
|
config,
|
||||||
|
)
|
||||||
);
|
);
|
||||||
let all_quotient_chunks = quotient_polys
|
|
||||||
.into_par_iter()
|
let all_quotient_chunks = timed!(
|
||||||
.flat_map(|mut quotient_poly| {
|
timing,
|
||||||
quotient_poly
|
"split quotient polys",
|
||||||
.trim_to_len(degree * stark.quotient_degree_factor())
|
quotient_polys
|
||||||
.expect("Quotient has failed, the vanishing polynomial is not divisible by Z_H");
|
.into_par_iter()
|
||||||
// Split quotient into degree-n chunks.
|
.flat_map(|mut quotient_poly| {
|
||||||
quotient_poly.chunks(degree)
|
quotient_poly
|
||||||
})
|
.trim_to_len(degree * stark.quotient_degree_factor())
|
||||||
.collect();
|
.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!(
|
let quotient_commitment = timed!(
|
||||||
timing,
|
timing,
|
||||||
"compute quotient commitment",
|
"compute quotient commitment",
|
||||||
@ -139,6 +188,8 @@ where
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Observe the quotient polynomials Merkle cap.
|
||||||
let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone();
|
let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone();
|
||||||
challenger.observe_cap("ient_polys_cap);
|
challenger.observe_cap("ient_polys_cap);
|
||||||
|
|
||||||
@ -151,17 +202,21 @@ where
|
|||||||
zeta.exp_power_of_2(degree_bits) != F::Extension::ONE,
|
zeta.exp_power_of_2(degree_bits) != F::Extension::ONE,
|
||||||
"Opening point is in the subgroup."
|
"Opening point is in the subgroup."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Compute all openings: evaluate all committed polynomials at `zeta` and, when necessary, at `g * zeta`.
|
||||||
let openings = StarkOpeningSet::new(
|
let openings = StarkOpeningSet::new(
|
||||||
zeta,
|
zeta,
|
||||||
g,
|
g,
|
||||||
&trace_commitment,
|
&trace_commitment,
|
||||||
permutation_zs_commitment,
|
auxiliary_polys_commitment.as_ref(),
|
||||||
"ient_commitment,
|
"ient_commitment,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Get the FRI openings and observe them.
|
||||||
challenger.observe_openings(&openings.to_fri_openings());
|
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(&auxiliary_polys_commitment)
|
||||||
.chain(once("ient_commitment))
|
.chain(once("ient_commitment))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
@ -178,7 +233,7 @@ where
|
|||||||
);
|
);
|
||||||
let proof = StarkProof {
|
let proof = StarkProof {
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap,
|
auxiliary_polys_cap,
|
||||||
quotient_polys_cap,
|
quotient_polys_cap,
|
||||||
openings,
|
openings,
|
||||||
opening_proof,
|
opening_proof,
|
||||||
@ -195,13 +250,13 @@ where
|
|||||||
fn compute_quotient_polys<'a, F, P, C, S, const D: usize>(
|
fn compute_quotient_polys<'a, F, P, C, S, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
trace_commitment: &'a PolynomialBatch<F, C, D>,
|
trace_commitment: &'a PolynomialBatch<F, C, D>,
|
||||||
permutation_zs_commitment_challenges: &'a Option<(
|
auxiliary_polys_commitment: &'a Option<PolynomialBatch<F, C, D>>,
|
||||||
PolynomialBatch<F, C, D>,
|
lookup_challenges: Option<&'a Vec<F>>,
|
||||||
Vec<PermutationChallengeSet<F>>,
|
lookups: &[Lookup<F>],
|
||||||
)>,
|
|
||||||
public_inputs: &[F],
|
public_inputs: &[F],
|
||||||
alphas: Vec<F>,
|
alphas: Vec<F>,
|
||||||
degree_bits: usize,
|
degree_bits: usize,
|
||||||
|
num_lookup_columns: usize,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> Vec<PolynomialCoeffs<F>>
|
) -> Vec<PolynomialCoeffs<F>>
|
||||||
where
|
where
|
||||||
@ -263,23 +318,35 @@ where
|
|||||||
lagrange_basis_first,
|
lagrange_basis_first,
|
||||||
lagrange_basis_last,
|
lagrange_basis_last,
|
||||||
);
|
);
|
||||||
|
// Get the local and next row evaluations for the current STARK,
|
||||||
|
// as well as the public inputs.
|
||||||
let vars = S::EvaluationFrame::from_values(
|
let vars = S::EvaluationFrame::from_values(
|
||||||
&get_trace_values_packed(i_start),
|
&get_trace_values_packed(i_start),
|
||||||
&get_trace_values_packed(i_next_start),
|
&get_trace_values_packed(i_next_start),
|
||||||
public_inputs,
|
public_inputs,
|
||||||
);
|
);
|
||||||
let permutation_check_data = permutation_zs_commitment_challenges.as_ref().map(
|
// Get the local and next row evaluations for the permutation argument,
|
||||||
|(permutation_zs_commitment, permutation_challenge_sets)| PermutationCheckVars {
|
// as well as the associated challenges.
|
||||||
local_zs: permutation_zs_commitment.get_lde_values_packed(i_start, step),
|
let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars {
|
||||||
next_zs: permutation_zs_commitment.get_lde_values_packed(i_next_start, step),
|
local_values: auxiliary_polys_commitment
|
||||||
permutation_challenge_sets: permutation_challenge_sets.to_vec(),
|
.as_ref()
|
||||||
},
|
.unwrap()
|
||||||
);
|
.get_lde_values_packed(i_start, step)
|
||||||
|
.to_vec(),
|
||||||
|
next_values: auxiliary_polys_commitment
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get_lde_values_packed(i_next_start, step),
|
||||||
|
challenges: challenges.to_vec(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Evaluate the polynomial combining all constraints, including
|
||||||
|
// those associated to the permutation arguments.
|
||||||
eval_vanishing_poly::<F, F, P, S, D, 1>(
|
eval_vanishing_poly::<F, F, P, S, D, 1>(
|
||||||
stark,
|
stark,
|
||||||
config,
|
|
||||||
&vars,
|
&vars,
|
||||||
permutation_check_data,
|
lookups,
|
||||||
|
lookup_vars,
|
||||||
&mut consumer,
|
&mut consumer,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -307,3 +374,102 @@ where
|
|||||||
.map(|values| values.coset_ifft(F::coset_shift()))
|
.map(|values| values.coset_ifft(F::coset_shift()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
/// 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<F, C, D>,
|
||||||
|
public_inputs: &[F],
|
||||||
|
auxiliary_commitment: &'a Option<PolynomialBatch<F, C, D>>,
|
||||||
|
lookup_challenges: Option<&'a Vec<F>>,
|
||||||
|
lookups: &[Lookup<F>],
|
||||||
|
alphas: Vec<F>,
|
||||||
|
degree_bits: usize,
|
||||||
|
num_lookup_columns: usize,
|
||||||
|
) where
|
||||||
|
F: RichField + Extendable<D>,
|
||||||
|
C: GenericConfig<D, F = F>,
|
||||||
|
S: Stark<F, D>,
|
||||||
|
{
|
||||||
|
let degree = 1 << degree_bits;
|
||||||
|
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.
|
||||||
|
let lagrange_first = PolynomialValues::selector(degree, 0).lde(rate_bits);
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Get the evaluations of a batch of polynomials over our subgroup.
|
||||||
|
let get_subgroup_evals = |comm: &PolynomialBatch<F, C, D>| -> Vec<Vec<F>> {
|
||||||
|
let values = comm
|
||||||
|
.polynomials
|
||||||
|
.par_iter()
|
||||||
|
.map(|coeffs| coeffs.clone().fft().values)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
transpose(&values)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get batch evaluations of the trace and permutation polynomials over our subgroup.
|
||||||
|
let trace_subgroup_evals = get_subgroup_evals(trace_commitment);
|
||||||
|
let auxiliary_subgroup_evals = auxiliary_commitment.as_ref().map(get_subgroup_evals);
|
||||||
|
|
||||||
|
// Last element of the subgroup.
|
||||||
|
let last = F::primitive_root_of_unity(degree_bits).inverse();
|
||||||
|
|
||||||
|
let constraint_values = (0..size)
|
||||||
|
.map(|i| {
|
||||||
|
let i_next = (i + step) % size;
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
// Get the local and next row evaluations for the current STARK's trace.
|
||||||
|
let vars = S::EvaluationFrame::from_values(
|
||||||
|
&trace_subgroup_evals[i],
|
||||||
|
&trace_subgroup_evals[i_next],
|
||||||
|
public_inputs,
|
||||||
|
);
|
||||||
|
// Get the local and next row evaluations for the current STARK's permutation argument.
|
||||||
|
let lookup_vars = lookup_challenges.map(|challenges| LookupCheckVars {
|
||||||
|
local_values: auxiliary_subgroup_evals.as_ref().unwrap()[i].clone(),
|
||||||
|
next_values: auxiliary_subgroup_evals.as_ref().unwrap()[i_next].clone(),
|
||||||
|
challenges: challenges.to_vec(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Evaluate the polynomial combining all constraints, including those associated
|
||||||
|
// to the permutation arguments.
|
||||||
|
eval_vanishing_poly::<F, F, F, S, D, 1>(
|
||||||
|
stark,
|
||||||
|
&vars,
|
||||||
|
lookups,
|
||||||
|
lookup_vars,
|
||||||
|
&mut consumer,
|
||||||
|
);
|
||||||
|
consumer.accumulators()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Assert that all constraints evaluate to 0 over our subgroup.
|
||||||
|
for v in constraint_values {
|
||||||
|
assert!(
|
||||||
|
v.iter().all(|x| x.is_zero()),
|
||||||
|
"Constraint failed in {}",
|
||||||
|
core::any::type_name::<S>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::iter::once;
|
use core::iter::once;
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ use plonky2::with_context;
|
|||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::constraint_consumer::RecursiveConstraintConsumer;
|
use crate::constraint_consumer::RecursiveConstraintConsumer;
|
||||||
use crate::evaluation_frame::StarkEvaluationFrame;
|
use crate::evaluation_frame::StarkEvaluationFrame;
|
||||||
use crate::permutation::PermutationCheckDataTarget;
|
use crate::lookup::LookupCheckVarsTarget;
|
||||||
use crate::proof::{
|
use crate::proof::{
|
||||||
StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget, StarkProofTarget,
|
StarkOpeningSetTarget, StarkProof, StarkProofChallengesTarget, StarkProofTarget,
|
||||||
StarkProofWithPublicInputs, StarkProofWithPublicInputsTarget,
|
StarkProofWithPublicInputs, StarkProofWithPublicInputsTarget,
|
||||||
@ -43,7 +44,7 @@ pub fn verify_stark_proof_circuit<
|
|||||||
let challenges = with_context!(
|
let challenges = with_context!(
|
||||||
builder,
|
builder,
|
||||||
"compute challenges",
|
"compute challenges",
|
||||||
proof_with_pis.get_challenges::<F, C, S>(builder, &stark, inner_config)
|
proof_with_pis.get_challenges::<F, C>(builder, inner_config)
|
||||||
);
|
);
|
||||||
|
|
||||||
verify_stark_proof_with_challenges_circuit::<F, C, S, D>(
|
verify_stark_proof_with_challenges_circuit::<F, C, S, D>(
|
||||||
@ -72,7 +73,7 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
) where
|
) where
|
||||||
C::Hasher: AlgebraicHasher<F>,
|
C::Hasher: AlgebraicHasher<F>,
|
||||||
{
|
{
|
||||||
check_permutation_options(&stark, &proof_with_pis, &challenges).unwrap();
|
check_lookup_options(&stark, &proof_with_pis, &challenges).unwrap();
|
||||||
let one = builder.one_extension();
|
let one = builder.one_extension();
|
||||||
|
|
||||||
let StarkProofWithPublicInputsTarget {
|
let StarkProofWithPublicInputsTarget {
|
||||||
@ -82,8 +83,8 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
let StarkOpeningSetTarget {
|
let StarkOpeningSetTarget {
|
||||||
local_values,
|
local_values,
|
||||||
next_values,
|
next_values,
|
||||||
permutation_zs,
|
auxiliary_polys,
|
||||||
permutation_zs_next,
|
auxiliary_polys_next,
|
||||||
quotient_polys,
|
quotient_polys,
|
||||||
} = &proof.openings;
|
} = &proof.openings;
|
||||||
|
|
||||||
@ -112,25 +113,27 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
l_last,
|
l_last,
|
||||||
);
|
);
|
||||||
|
|
||||||
let permutation_data = stark
|
let num_lookup_columns = stark.num_lookup_helper_columns(inner_config);
|
||||||
.uses_permutation_args()
|
let lookup_challenges = stark.uses_lookups().then(|| {
|
||||||
.then(|| PermutationCheckDataTarget {
|
challenges
|
||||||
local_zs: permutation_zs.as_ref().unwrap().clone(),
|
.lookup_challenge_set
|
||||||
next_zs: permutation_zs_next.as_ref().unwrap().clone(),
|
.unwrap()
|
||||||
permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(),
|
.challenges
|
||||||
});
|
.iter()
|
||||||
|
.map(|ch| ch.beta)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
let lookup_vars = stark.uses_lookups().then(|| LookupCheckVarsTarget {
|
||||||
|
local_values: auxiliary_polys.as_ref().unwrap()[..num_lookup_columns].to_vec(),
|
||||||
|
next_values: auxiliary_polys_next.as_ref().unwrap()[..num_lookup_columns].to_vec(),
|
||||||
|
challenges: lookup_challenges.unwrap(),
|
||||||
|
});
|
||||||
|
|
||||||
with_context!(
|
with_context!(
|
||||||
builder,
|
builder,
|
||||||
"evaluate vanishing polynomial",
|
"evaluate vanishing polynomial",
|
||||||
eval_vanishing_poly_circuit::<F, S, D>(
|
eval_vanishing_poly_circuit::<F, S, D>(builder, &stark, &vars, lookup_vars, &mut consumer)
|
||||||
builder,
|
|
||||||
&stark,
|
|
||||||
inner_config,
|
|
||||||
&vars,
|
|
||||||
permutation_data,
|
|
||||||
&mut consumer,
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
let vanishing_polys_zeta = consumer.accumulators();
|
let vanishing_polys_zeta = consumer.accumulators();
|
||||||
|
|
||||||
@ -146,7 +149,7 @@ fn verify_stark_proof_with_challenges_circuit<
|
|||||||
}
|
}
|
||||||
|
|
||||||
let merkle_caps = once(proof.trace_cap)
|
let merkle_caps = once(proof.trace_cap)
|
||||||
.chain(proof.permutation_zs_cap)
|
.chain(proof.auxiliary_polys_cap)
|
||||||
.chain(once(proof.quotient_polys_cap))
|
.chain(once(proof.quotient_polys_cap))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
@ -212,22 +215,19 @@ pub fn add_virtual_stark_proof<F: RichField + Extendable<D>, S: Stark<F, D>, con
|
|||||||
let fri_params = config.fri_params(degree_bits);
|
let fri_params = config.fri_params(degree_bits);
|
||||||
let cap_height = fri_params.config.cap_height;
|
let cap_height = fri_params.config.cap_height;
|
||||||
|
|
||||||
let num_leaves_per_oracle = once(S::COLUMNS)
|
let num_leaves_per_oracle = vec![
|
||||||
.chain(
|
S::COLUMNS,
|
||||||
stark
|
stark.num_lookup_helper_columns(config),
|
||||||
.uses_permutation_args()
|
stark.quotient_degree_factor() * config.num_challenges,
|
||||||
.then(|| stark.num_permutation_batches(config)),
|
];
|
||||||
)
|
|
||||||
.chain(once(stark.quotient_degree_factor() * config.num_challenges))
|
|
||||||
.collect_vec();
|
|
||||||
|
|
||||||
let permutation_zs_cap = stark
|
let auxiliary_polys_cap = stark
|
||||||
.uses_permutation_args()
|
.uses_lookups()
|
||||||
.then(|| builder.add_virtual_cap(cap_height));
|
.then(|| builder.add_virtual_cap(cap_height));
|
||||||
|
|
||||||
StarkProofTarget {
|
StarkProofTarget {
|
||||||
trace_cap: builder.add_virtual_cap(cap_height),
|
trace_cap: builder.add_virtual_cap(cap_height),
|
||||||
permutation_zs_cap,
|
auxiliary_polys_cap,
|
||||||
quotient_polys_cap: builder.add_virtual_cap(cap_height),
|
quotient_polys_cap: builder.add_virtual_cap(cap_height),
|
||||||
openings: add_stark_opening_set_target::<F, S, D>(builder, stark, config),
|
openings: add_stark_opening_set_target::<F, S, D>(builder, stark, config),
|
||||||
opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params),
|
opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params),
|
||||||
@ -243,12 +243,12 @@ fn add_stark_opening_set_target<F: RichField + Extendable<D>, S: Stark<F, D>, co
|
|||||||
StarkOpeningSetTarget {
|
StarkOpeningSetTarget {
|
||||||
local_values: builder.add_virtual_extension_targets(S::COLUMNS),
|
local_values: builder.add_virtual_extension_targets(S::COLUMNS),
|
||||||
next_values: builder.add_virtual_extension_targets(S::COLUMNS),
|
next_values: builder.add_virtual_extension_targets(S::COLUMNS),
|
||||||
permutation_zs: stark
|
auxiliary_polys: stark.uses_lookups().then(|| {
|
||||||
.uses_permutation_args()
|
builder.add_virtual_extension_targets(stark.num_lookup_helper_columns(config))
|
||||||
.then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))),
|
}),
|
||||||
permutation_zs_next: stark
|
auxiliary_polys_next: stark.uses_lookups().then(|| {
|
||||||
.uses_permutation_args()
|
builder.add_virtual_extension_targets(stark.num_lookup_helper_columns(config))
|
||||||
.then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))),
|
}),
|
||||||
quotient_polys: builder
|
quotient_polys: builder
|
||||||
.add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges),
|
.add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges),
|
||||||
}
|
}
|
||||||
@ -297,33 +297,34 @@ pub fn set_stark_proof_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
|
|||||||
&proof.openings.to_fri_openings(),
|
&proof.openings.to_fri_openings(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let (Some(permutation_zs_cap_target), Some(permutation_zs_cap)) =
|
if let (Some(auxiliary_polys_cap_target), Some(auxiliary_polys_cap)) = (
|
||||||
(&proof_target.permutation_zs_cap, &proof.permutation_zs_cap)
|
&proof_target.auxiliary_polys_cap,
|
||||||
{
|
&proof.auxiliary_polys_cap,
|
||||||
witness.set_cap_target(permutation_zs_cap_target, permutation_zs_cap);
|
) {
|
||||||
|
witness.set_cap_target(auxiliary_polys_cap_target, auxiliary_polys_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof);
|
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
|
/// Utility function to check that all lookups data wrapped in `Option`s are `Some` iff
|
||||||
/// the Stark uses a permutation argument.
|
/// the Stark uses a permutation argument.
|
||||||
fn check_permutation_options<F: RichField + Extendable<D>, S: Stark<F, D>, const D: usize>(
|
fn check_lookup_options<F: RichField + Extendable<D>, S: Stark<F, D>, const D: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
proof_with_pis: &StarkProofWithPublicInputsTarget<D>,
|
proof_with_pis: &StarkProofWithPublicInputsTarget<D>,
|
||||||
challenges: &StarkProofChallengesTarget<D>,
|
challenges: &StarkProofChallengesTarget<D>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let options_is_some = [
|
let options_is_some = [
|
||||||
proof_with_pis.proof.permutation_zs_cap.is_some(),
|
proof_with_pis.proof.auxiliary_polys_cap.is_some(),
|
||||||
proof_with_pis.proof.openings.permutation_zs.is_some(),
|
proof_with_pis.proof.openings.auxiliary_polys.is_some(),
|
||||||
proof_with_pis.proof.openings.permutation_zs_next.is_some(),
|
proof_with_pis.proof.openings.auxiliary_polys_next.is_some(),
|
||||||
challenges.permutation_challenge_sets.is_some(),
|
challenges.lookup_challenge_set.is_some(),
|
||||||
];
|
];
|
||||||
ensure!(
|
ensure!(
|
||||||
options_is_some
|
options_is_some
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.all(|b| b == stark.uses_permutation_args()),
|
.all(|b| b == stark.uses_lookups()),
|
||||||
"Permutation data doesn't match with Stark configuration."
|
"Lookups data doesn't match with Stark configuration."
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use alloc::vec::Vec;
|
|||||||
|
|
||||||
use plonky2::field::extension::{Extendable, FieldExtension};
|
use plonky2::field::extension::{Extendable, FieldExtension};
|
||||||
use plonky2::field::packed::PackedField;
|
use plonky2::field::packed::PackedField;
|
||||||
|
use plonky2::field::types::Field;
|
||||||
use plonky2::fri::structure::{
|
use plonky2::fri::structure::{
|
||||||
FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo,
|
FriBatchInfo, FriBatchInfoTarget, FriInstanceInfo, FriInstanceInfoTarget, FriOracleInfo,
|
||||||
FriPolynomialInfo,
|
FriPolynomialInfo,
|
||||||
@ -10,12 +11,15 @@ use plonky2::fri::structure::{
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::iop::ext_target::ExtensionTarget;
|
use plonky2::iop::ext_target::ExtensionTarget;
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
use plonky2::util::ceil_div_usize;
|
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
use crate::evaluation_frame::StarkEvaluationFrame;
|
use crate::evaluation_frame::StarkEvaluationFrame;
|
||||||
use crate::permutation::PermutationPair;
|
use crate::lookup::Lookup;
|
||||||
|
|
||||||
|
const TRACE_ORACLE_INDEX: usize = 0;
|
||||||
|
const AUXILIARY_ORACLE_INDEX: usize = 1;
|
||||||
|
const QUOTIENT_ORACLE_INDEX: usize = 2;
|
||||||
|
|
||||||
/// Represents a STARK system.
|
/// Represents a STARK system.
|
||||||
pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
||||||
@ -94,49 +98,47 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
|||||||
g: F,
|
g: F,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> FriInstanceInfo<F, D> {
|
) -> FriInstanceInfo<F, D> {
|
||||||
let mut oracles = vec![];
|
let trace_oracle = FriOracleInfo {
|
||||||
|
|
||||||
let trace_info = FriPolynomialInfo::from_range(oracles.len(), 0..Self::COLUMNS);
|
|
||||||
oracles.push(FriOracleInfo {
|
|
||||||
num_polys: Self::COLUMNS,
|
num_polys: Self::COLUMNS,
|
||||||
blinding: false,
|
blinding: false,
|
||||||
});
|
|
||||||
|
|
||||||
let permutation_zs_info = if self.uses_permutation_args() {
|
|
||||||
let num_z_polys = self.num_permutation_batches(config);
|
|
||||||
let polys = FriPolynomialInfo::from_range(oracles.len(), 0..num_z_polys);
|
|
||||||
oracles.push(FriOracleInfo {
|
|
||||||
num_polys: num_z_polys,
|
|
||||||
blinding: false,
|
|
||||||
});
|
|
||||||
polys
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
};
|
||||||
|
let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS);
|
||||||
|
|
||||||
let num_quotient_polys = self.quotient_degree_factor() * config.num_challenges;
|
let num_lookup_columns = self.num_lookup_helper_columns(config);
|
||||||
let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys);
|
let num_auxiliary_polys = num_lookup_columns;
|
||||||
oracles.push(FriOracleInfo {
|
let auxiliary_oracle = FriOracleInfo {
|
||||||
|
num_polys: num_auxiliary_polys,
|
||||||
|
blinding: false,
|
||||||
|
};
|
||||||
|
let auxiliary_polys_info =
|
||||||
|
FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys);
|
||||||
|
|
||||||
|
let num_quotient_polys = self.num_quotient_polys(config);
|
||||||
|
let quotient_oracle = FriOracleInfo {
|
||||||
num_polys: num_quotient_polys,
|
num_polys: num_quotient_polys,
|
||||||
blinding: false,
|
blinding: false,
|
||||||
});
|
};
|
||||||
|
let quotient_info =
|
||||||
|
FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys);
|
||||||
|
|
||||||
let zeta_batch = FriBatchInfo {
|
let zeta_batch = FriBatchInfo {
|
||||||
point: zeta,
|
point: zeta,
|
||||||
polynomials: [
|
polynomials: [
|
||||||
trace_info.clone(),
|
trace_info.clone(),
|
||||||
permutation_zs_info.clone(),
|
auxiliary_polys_info.clone(),
|
||||||
quotient_info,
|
quotient_info,
|
||||||
]
|
]
|
||||||
.concat(),
|
.concat(),
|
||||||
};
|
};
|
||||||
let zeta_next_batch = FriBatchInfo {
|
let zeta_next_batch = FriBatchInfo {
|
||||||
point: zeta.scalar_mul(g),
|
point: zeta.scalar_mul(g),
|
||||||
polynomials: [trace_info, permutation_zs_info].concat(),
|
polynomials: [trace_info, auxiliary_polys_info].concat(),
|
||||||
};
|
};
|
||||||
let batches = vec![zeta_batch, zeta_next_batch];
|
|
||||||
|
|
||||||
FriInstanceInfo { oracles, batches }
|
FriInstanceInfo {
|
||||||
|
oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle],
|
||||||
|
batches: vec![zeta_batch, zeta_next_batch],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the FRI instance used to prove this Stark.
|
/// Computes the FRI instance used to prove this Stark.
|
||||||
@ -147,38 +149,34 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
|||||||
g: F,
|
g: F,
|
||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> FriInstanceInfoTarget<D> {
|
) -> FriInstanceInfoTarget<D> {
|
||||||
let mut oracles = vec![];
|
let trace_oracle = FriOracleInfo {
|
||||||
|
|
||||||
let trace_info = FriPolynomialInfo::from_range(oracles.len(), 0..Self::COLUMNS);
|
|
||||||
oracles.push(FriOracleInfo {
|
|
||||||
num_polys: Self::COLUMNS,
|
num_polys: Self::COLUMNS,
|
||||||
blinding: false,
|
blinding: false,
|
||||||
});
|
|
||||||
|
|
||||||
let permutation_zs_info = if self.uses_permutation_args() {
|
|
||||||
let num_z_polys = self.num_permutation_batches(config);
|
|
||||||
let polys = FriPolynomialInfo::from_range(oracles.len(), 0..num_z_polys);
|
|
||||||
oracles.push(FriOracleInfo {
|
|
||||||
num_polys: num_z_polys,
|
|
||||||
blinding: false,
|
|
||||||
});
|
|
||||||
polys
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
};
|
||||||
|
let trace_info = FriPolynomialInfo::from_range(TRACE_ORACLE_INDEX, 0..Self::COLUMNS);
|
||||||
|
|
||||||
let num_quotient_polys = self.quotient_degree_factor() * config.num_challenges;
|
let num_lookup_columns = self.num_lookup_helper_columns(config);
|
||||||
let quotient_info = FriPolynomialInfo::from_range(oracles.len(), 0..num_quotient_polys);
|
let num_auxiliary_polys = num_lookup_columns;
|
||||||
oracles.push(FriOracleInfo {
|
let auxiliary_oracle = FriOracleInfo {
|
||||||
|
num_polys: num_auxiliary_polys,
|
||||||
|
blinding: false,
|
||||||
|
};
|
||||||
|
let auxiliary_polys_info =
|
||||||
|
FriPolynomialInfo::from_range(AUXILIARY_ORACLE_INDEX, 0..num_auxiliary_polys);
|
||||||
|
|
||||||
|
let num_quotient_polys = self.num_quotient_polys(config);
|
||||||
|
let quotient_oracle = FriOracleInfo {
|
||||||
num_polys: num_quotient_polys,
|
num_polys: num_quotient_polys,
|
||||||
blinding: false,
|
blinding: false,
|
||||||
});
|
};
|
||||||
|
let quotient_info =
|
||||||
|
FriPolynomialInfo::from_range(QUOTIENT_ORACLE_INDEX, 0..num_quotient_polys);
|
||||||
|
|
||||||
let zeta_batch = FriBatchInfoTarget {
|
let zeta_batch = FriBatchInfoTarget {
|
||||||
point: zeta,
|
point: zeta,
|
||||||
polynomials: [
|
polynomials: [
|
||||||
trace_info.clone(),
|
trace_info.clone(),
|
||||||
permutation_zs_info.clone(),
|
auxiliary_polys_info.clone(),
|
||||||
quotient_info,
|
quotient_info,
|
||||||
]
|
]
|
||||||
.concat(),
|
.concat(),
|
||||||
@ -186,40 +184,28 @@ pub trait Stark<F: RichField + Extendable<D>, const D: usize>: Sync {
|
|||||||
let zeta_next = builder.mul_const_extension(g, zeta);
|
let zeta_next = builder.mul_const_extension(g, zeta);
|
||||||
let zeta_next_batch = FriBatchInfoTarget {
|
let zeta_next_batch = FriBatchInfoTarget {
|
||||||
point: zeta_next,
|
point: zeta_next,
|
||||||
polynomials: [trace_info, permutation_zs_info].concat(),
|
polynomials: [trace_info, auxiliary_polys_info].concat(),
|
||||||
};
|
};
|
||||||
let batches = vec![zeta_batch, zeta_next_batch];
|
|
||||||
|
|
||||||
FriInstanceInfoTarget { oracles, batches }
|
FriInstanceInfoTarget {
|
||||||
|
oracles: vec![trace_oracle, auxiliary_oracle, quotient_oracle],
|
||||||
|
batches: vec![zeta_batch, zeta_next_batch],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pairs of lists of columns that should be permutations of one another. A permutation argument
|
fn lookups(&self) -> Vec<Lookup<F>> {
|
||||||
/// will be used for each such pair. Empty by default.
|
|
||||||
fn permutation_pairs(&self) -> Vec<PermutationPair> {
|
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uses_permutation_args(&self) -> bool {
|
fn num_lookup_helper_columns(&self, config: &StarkConfig) -> usize {
|
||||||
!self.permutation_pairs().is_empty()
|
self.lookups()
|
||||||
|
.iter()
|
||||||
|
.map(|lookup| lookup.num_helper_columns(self.constraint_degree()))
|
||||||
|
.sum::<usize>()
|
||||||
|
* config.num_challenges
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of permutation argument instances that can be combined into a single constraint.
|
fn uses_lookups(&self) -> bool {
|
||||||
fn permutation_batch_size(&self) -> usize {
|
!self.lookups().is_empty()
|
||||||
// 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(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,19 +3,18 @@ use plonky2::field::packed::PackedField;
|
|||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
|
||||||
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer};
|
||||||
use crate::permutation::{
|
use crate::lookup::{
|
||||||
eval_permutation_checks, eval_permutation_checks_circuit, PermutationCheckDataTarget,
|
eval_ext_lookups_circuit, eval_packed_lookups_generic, Lookup, LookupCheckVars,
|
||||||
PermutationCheckVars,
|
LookupCheckVarsTarget,
|
||||||
};
|
};
|
||||||
use crate::stark::Stark;
|
use crate::stark::Stark;
|
||||||
|
|
||||||
pub(crate) fn eval_vanishing_poly<F, FE, P, S, const D: usize, const D2: usize>(
|
pub(crate) fn eval_vanishing_poly<F, FE, P, S, const D: usize, const D2: usize>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
config: &StarkConfig,
|
|
||||||
vars: &S::EvaluationFrame<FE, P, D2>,
|
vars: &S::EvaluationFrame<FE, P, D2>,
|
||||||
permutation_data: Option<PermutationCheckVars<F, FE, P, D2>>,
|
lookups: &[Lookup<F>],
|
||||||
|
lookup_vars: Option<LookupCheckVars<F, FE, P, D2>>,
|
||||||
consumer: &mut ConstraintConsumer<P>,
|
consumer: &mut ConstraintConsumer<P>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
@ -24,12 +23,13 @@ pub(crate) fn eval_vanishing_poly<F, FE, P, S, const D: usize, const D2: usize>(
|
|||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
{
|
{
|
||||||
stark.eval_packed_generic(vars, consumer);
|
stark.eval_packed_generic(vars, consumer);
|
||||||
if let Some(permutation_data) = permutation_data {
|
if let Some(lookup_vars) = lookup_vars {
|
||||||
eval_permutation_checks::<F, FE, P, S, D, D2>(
|
// Evaluate the STARK constraints related to the permutation arguments.
|
||||||
|
eval_packed_lookups_generic::<F, FE, P, S, D, D2>(
|
||||||
stark,
|
stark,
|
||||||
config,
|
lookups,
|
||||||
vars,
|
vars,
|
||||||
permutation_data,
|
lookup_vars,
|
||||||
consumer,
|
consumer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -38,23 +38,16 @@ pub(crate) fn eval_vanishing_poly<F, FE, P, S, const D: usize, const D2: usize>(
|
|||||||
pub(crate) fn eval_vanishing_poly_circuit<F, S, const D: usize>(
|
pub(crate) fn eval_vanishing_poly_circuit<F, S, const D: usize>(
|
||||||
builder: &mut CircuitBuilder<F, D>,
|
builder: &mut CircuitBuilder<F, D>,
|
||||||
stark: &S,
|
stark: &S,
|
||||||
config: &StarkConfig,
|
|
||||||
vars: &S::EvaluationFrameTarget,
|
vars: &S::EvaluationFrameTarget,
|
||||||
permutation_data: Option<PermutationCheckDataTarget<D>>,
|
lookup_vars: Option<LookupCheckVarsTarget<D>>,
|
||||||
consumer: &mut RecursiveConstraintConsumer<F, D>,
|
consumer: &mut RecursiveConstraintConsumer<F, D>,
|
||||||
) where
|
) where
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
{
|
{
|
||||||
stark.eval_ext_circuit(builder, vars, consumer);
|
stark.eval_ext_circuit(builder, vars, consumer);
|
||||||
if let Some(permutation_data) = permutation_data {
|
if let Some(lookup_vars) = lookup_vars {
|
||||||
eval_permutation_checks_circuit::<F, S, D>(
|
// Evaluate all of the STARK's constraints related to the permutation argument.
|
||||||
builder,
|
eval_ext_lookups_circuit::<F, S, D>(builder, stark, vars, lookup_vars, consumer);
|
||||||
stark,
|
|
||||||
config,
|
|
||||||
vars,
|
|
||||||
permutation_data,
|
|
||||||
consumer,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,14 @@ use plonky2::field::extension::{Extendable, FieldExtension};
|
|||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use plonky2::fri::verifier::verify_fri_proof;
|
use plonky2::fri::verifier::verify_fri_proof;
|
||||||
use plonky2::hash::hash_types::RichField;
|
use plonky2::hash::hash_types::RichField;
|
||||||
|
use plonky2::hash::merkle_tree::MerkleCap;
|
||||||
use plonky2::plonk::config::GenericConfig;
|
use plonky2::plonk::config::GenericConfig;
|
||||||
use plonky2::plonk::plonk_common::reduce_with_powers;
|
use plonky2::plonk::plonk_common::reduce_with_powers;
|
||||||
|
|
||||||
use crate::config::StarkConfig;
|
use crate::config::StarkConfig;
|
||||||
use crate::constraint_consumer::ConstraintConsumer;
|
use crate::constraint_consumer::ConstraintConsumer;
|
||||||
use crate::evaluation_frame::StarkEvaluationFrame;
|
use crate::evaluation_frame::StarkEvaluationFrame;
|
||||||
use crate::permutation::PermutationCheckVars;
|
use crate::lookup::LookupCheckVars;
|
||||||
use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs};
|
use crate::proof::{StarkOpeningSet, StarkProof, StarkProofChallenges, StarkProofWithPublicInputs};
|
||||||
use crate::stark::Stark;
|
use crate::stark::Stark;
|
||||||
use crate::vanishing_poly::eval_vanishing_poly;
|
use crate::vanishing_poly::eval_vanishing_poly;
|
||||||
@ -30,7 +31,7 @@ pub fn verify_stark_proof<
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS);
|
ensure!(proof_with_pis.public_inputs.len() == S::PUBLIC_INPUTS);
|
||||||
let degree_bits = proof_with_pis.proof.recover_degree_bits(config);
|
let degree_bits = proof_with_pis.proof.recover_degree_bits(config);
|
||||||
let challenges = proof_with_pis.get_challenges(&stark, config, degree_bits);
|
let challenges = proof_with_pis.get_challenges(config, degree_bits);
|
||||||
verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config)
|
verify_stark_proof_with_challenges(stark, proof_with_pis, challenges, degree_bits, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ pub(crate) fn verify_stark_proof_with_challenges<
|
|||||||
config: &StarkConfig,
|
config: &StarkConfig,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
validate_proof_shape(&stark, &proof_with_pis, config)?;
|
validate_proof_shape(&stark, &proof_with_pis, config)?;
|
||||||
check_permutation_options(&stark, &proof_with_pis, &challenges)?;
|
|
||||||
let StarkProofWithPublicInputs {
|
let StarkProofWithPublicInputs {
|
||||||
proof,
|
proof,
|
||||||
public_inputs,
|
public_inputs,
|
||||||
@ -55,8 +56,8 @@ pub(crate) fn verify_stark_proof_with_challenges<
|
|||||||
let StarkOpeningSet {
|
let StarkOpeningSet {
|
||||||
local_values,
|
local_values,
|
||||||
next_values,
|
next_values,
|
||||||
permutation_zs,
|
auxiliary_polys,
|
||||||
permutation_zs_next,
|
auxiliary_polys_next,
|
||||||
quotient_polys,
|
quotient_polys,
|
||||||
} = &proof.openings;
|
} = &proof.openings;
|
||||||
let vars = S::EvaluationFrame::from_values(
|
let vars = S::EvaluationFrame::from_values(
|
||||||
@ -81,16 +82,30 @@ pub(crate) fn verify_stark_proof_with_challenges<
|
|||||||
l_0,
|
l_0,
|
||||||
l_last,
|
l_last,
|
||||||
);
|
);
|
||||||
let permutation_data = stark.uses_permutation_args().then(|| PermutationCheckVars {
|
|
||||||
local_zs: permutation_zs.as_ref().unwrap().clone(),
|
let num_lookup_columns = stark.num_lookup_helper_columns(config);
|
||||||
next_zs: permutation_zs_next.as_ref().unwrap().clone(),
|
let lookup_challenges = (num_lookup_columns > 0).then(|| {
|
||||||
permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(),
|
challenges
|
||||||
|
.lookup_challenge_set
|
||||||
|
.unwrap()
|
||||||
|
.challenges
|
||||||
|
.iter()
|
||||||
|
.map(|ch| ch.beta)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let lookup_vars = stark.uses_lookups().then(|| LookupCheckVars {
|
||||||
|
local_values: auxiliary_polys.as_ref().unwrap().clone(),
|
||||||
|
next_values: auxiliary_polys_next.as_ref().unwrap().clone(),
|
||||||
|
challenges: lookup_challenges.unwrap(),
|
||||||
|
});
|
||||||
|
let lookups = stark.lookups();
|
||||||
|
|
||||||
eval_vanishing_poly::<F, F::Extension, F::Extension, S, D, D>(
|
eval_vanishing_poly::<F, F::Extension, F::Extension, S, D, D>(
|
||||||
&stark,
|
&stark,
|
||||||
config,
|
|
||||||
&vars,
|
&vars,
|
||||||
permutation_data,
|
&lookups,
|
||||||
|
lookup_vars,
|
||||||
&mut consumer,
|
&mut consumer,
|
||||||
);
|
);
|
||||||
let vanishing_polys_zeta = consumer.accumulators();
|
let vanishing_polys_zeta = consumer.accumulators();
|
||||||
@ -114,7 +129,7 @@ pub(crate) fn verify_stark_proof_with_challenges<
|
|||||||
}
|
}
|
||||||
|
|
||||||
let merkle_caps = once(proof.trace_cap)
|
let merkle_caps = once(proof.trace_cap)
|
||||||
.chain(proof.permutation_zs_cap)
|
.chain(proof.auxiliary_polys_cap)
|
||||||
.chain(once(proof.quotient_polys_cap))
|
.chain(once(proof.quotient_polys_cap))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
@ -152,7 +167,7 @@ where
|
|||||||
|
|
||||||
let StarkProof {
|
let StarkProof {
|
||||||
trace_cap,
|
trace_cap,
|
||||||
permutation_zs_cap,
|
auxiliary_polys_cap,
|
||||||
quotient_polys_cap,
|
quotient_polys_cap,
|
||||||
openings,
|
openings,
|
||||||
// The shape of the opening proof will be checked in the FRI verifier (see
|
// The shape of the opening proof will be checked in the FRI verifier (see
|
||||||
@ -163,8 +178,8 @@ where
|
|||||||
let StarkOpeningSet {
|
let StarkOpeningSet {
|
||||||
local_values,
|
local_values,
|
||||||
next_values,
|
next_values,
|
||||||
permutation_zs,
|
auxiliary_polys,
|
||||||
permutation_zs_next,
|
auxiliary_polys_next,
|
||||||
quotient_polys,
|
quotient_polys,
|
||||||
} = openings;
|
} = openings;
|
||||||
|
|
||||||
@ -172,7 +187,8 @@ where
|
|||||||
|
|
||||||
let fri_params = config.fri_params(degree_bits);
|
let fri_params = config.fri_params(degree_bits);
|
||||||
let cap_height = fri_params.config.cap_height;
|
let cap_height = fri_params.config.cap_height;
|
||||||
let num_zs = stark.num_permutation_batches(config);
|
|
||||||
|
let num_auxiliary = stark.num_lookup_helper_columns(config);
|
||||||
|
|
||||||
ensure!(trace_cap.height() == cap_height);
|
ensure!(trace_cap.height() == cap_height);
|
||||||
ensure!(quotient_polys_cap.height() == cap_height);
|
ensure!(quotient_polys_cap.height() == cap_height);
|
||||||
@ -181,25 +197,13 @@ where
|
|||||||
ensure!(next_values.len() == S::COLUMNS);
|
ensure!(next_values.len() == S::COLUMNS);
|
||||||
ensure!(quotient_polys.len() == stark.num_quotient_polys(config));
|
ensure!(quotient_polys.len() == stark.num_quotient_polys(config));
|
||||||
|
|
||||||
if stark.uses_permutation_args() {
|
check_lookup_options::<F, C, S, D>(
|
||||||
let permutation_zs_cap = permutation_zs_cap
|
stark,
|
||||||
.as_ref()
|
auxiliary_polys_cap,
|
||||||
.ok_or_else(|| anyhow!("Missing Zs cap"))?;
|
auxiliary_polys,
|
||||||
let permutation_zs = permutation_zs
|
auxiliary_polys_next,
|
||||||
.as_ref()
|
config,
|
||||||
.ok_or_else(|| anyhow!("Missing permutation_zs"))?;
|
)?;
|
||||||
let permutation_zs_next = permutation_zs_next
|
|
||||||
.as_ref()
|
|
||||||
.ok_or_else(|| anyhow!("Missing permutation_zs_next"))?;
|
|
||||||
|
|
||||||
ensure!(permutation_zs_cap.height() == cap_height);
|
|
||||||
ensure!(permutation_zs.len() == num_zs);
|
|
||||||
ensure!(permutation_zs_next.len() == num_zs);
|
|
||||||
} else {
|
|
||||||
ensure!(permutation_zs_cap.is_none());
|
|
||||||
ensure!(permutation_zs.is_none());
|
|
||||||
ensure!(permutation_zs_next.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -216,30 +220,43 @@ fn eval_l_0_and_l_last<F: Field>(log_n: usize, x: F) -> (F, F) {
|
|||||||
(z_x * invs[0], z_x * invs[1])
|
(z_x * invs[0], z_x * invs[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff
|
/// Utility function to check that all lookups data wrapped in `Option`s are `Some` iff
|
||||||
/// the Stark uses a permutation argument.
|
/// the Stark uses a permutation argument.
|
||||||
fn check_permutation_options<
|
fn check_lookup_options<
|
||||||
F: RichField + Extendable<D>,
|
F: RichField + Extendable<D>,
|
||||||
C: GenericConfig<D, F = F>,
|
C: GenericConfig<D, F = F>,
|
||||||
S: Stark<F, D>,
|
S: Stark<F, D>,
|
||||||
const D: usize,
|
const D: usize,
|
||||||
>(
|
>(
|
||||||
stark: &S,
|
stark: &S,
|
||||||
proof_with_pis: &StarkProofWithPublicInputs<F, C, D>,
|
auxiliary_polys_cap: &Option<MerkleCap<F, <C as GenericConfig<D>>::Hasher>>,
|
||||||
challenges: &StarkProofChallenges<F, D>,
|
auxiliary_polys: &Option<Vec<<F as Extendable<D>>::Extension>>,
|
||||||
|
auxiliary_polys_next: &Option<Vec<<F as Extendable<D>>::Extension>>,
|
||||||
|
config: &StarkConfig,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let options_is_some = [
|
if stark.uses_lookups() {
|
||||||
proof_with_pis.proof.permutation_zs_cap.is_some(),
|
let num_auxiliary = stark.num_lookup_helper_columns(config);
|
||||||
proof_with_pis.proof.openings.permutation_zs.is_some(),
|
let cap_height = config.fri_config.cap_height;
|
||||||
proof_with_pis.proof.openings.permutation_zs_next.is_some(),
|
|
||||||
challenges.permutation_challenge_sets.is_some(),
|
let auxiliary_polys_cap = auxiliary_polys_cap
|
||||||
];
|
.as_ref()
|
||||||
ensure!(
|
.ok_or_else(|| anyhow!("Missing auxiliary_polys_cap"))?;
|
||||||
options_is_some
|
let auxiliary_polys = auxiliary_polys
|
||||||
.into_iter()
|
.as_ref()
|
||||||
.all(|b| b == stark.uses_permutation_args()),
|
.ok_or_else(|| anyhow!("Missing auxiliary_polys"))?;
|
||||||
"Permutation data doesn't match with Stark configuration."
|
let auxiliary_polys_next = auxiliary_polys_next
|
||||||
);
|
.as_ref()
|
||||||
|
.ok_or_else(|| anyhow!("Missing auxiliary_polys_next"))?;
|
||||||
|
|
||||||
|
ensure!(auxiliary_polys_cap.height() == cap_height);
|
||||||
|
ensure!(auxiliary_polys.len() == num_auxiliary);
|
||||||
|
ensure!(auxiliary_polys_next.len() == num_auxiliary);
|
||||||
|
} else {
|
||||||
|
ensure!(auxiliary_polys_cap.is_none());
|
||||||
|
ensure!(auxiliary_polys.is_none());
|
||||||
|
ensure!(auxiliary_polys_next.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user