Add FRI challenges

This commit is contained in:
wborgeaud 2022-01-31 16:19:30 +01:00
parent 851455a26a
commit d24d26e5c0
11 changed files with 107 additions and 58 deletions

View File

@ -16,7 +16,7 @@ use crate::iop::ext_target::ExtensionTarget;
use crate::iop::target::Target;
use crate::plonk::config::{GenericConfig, Hasher};
use crate::plonk::plonk_common::salt_size;
use crate::plonk::proof::{FriInferredElements, ProofChallenges};
use crate::plonk::proof::{FriChallenges, FriInferredElements, ProofChallenges};
/// Evaluations and Merkle proof produced by the prover in a FRI query step.
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
@ -253,10 +253,10 @@ impl<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize> CompressedFriPr
pow_witness,
..
} = self;
let ProofChallenges {
let FriChallenges {
fri_query_indices: indices,
..
} = challenges;
} = &challenges.fri_challenges;
let mut fri_inferred_elements = fri_inferred_elements.0.into_iter();
let cap_height = params.config.cap_height;
let reduction_arity_bits = &params.reduction_arity_bits;

View File

@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
use crate::hash::merkle_proofs::verify_merkle_proof;
use crate::hash::merkle_tree::MerkleCap;
use crate::plonk::config::{GenericConfig, Hasher};
use crate::plonk::proof::{OpeningSet, ProofChallenges};
use crate::plonk::proof::{FriChallenges, OpeningSet, ProofChallenges};
use crate::util::reducing::ReducingFactor;
use crate::util::reverse_bits;
@ -57,7 +57,7 @@ pub(crate) fn fri_verify_proof_of_work<F: RichField + Extendable<D>, const D: us
Ok(())
}
pub(crate) fn verify_fri_proof<
pub fn verify_fri_proof<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
@ -65,7 +65,7 @@ pub(crate) fn verify_fri_proof<
instance: &FriInstanceInfo<F, D>,
// Openings of the PLONK polynomials.
os: &OpeningSet<F, D>,
challenges: &ProofChallenges<F, D>,
challenges: &FriChallenges<F, D>,
initial_merkle_caps: &[MerkleCap<F, C::Hasher>],
proof: &FriProof<F, C::Hasher, D>,
params: &FriParams,
@ -171,7 +171,7 @@ fn fri_verifier_query_round<
const D: usize,
>(
instance: &FriInstanceInfo<F, D>,
challenges: &ProofChallenges<F, D>,
challenges: &FriChallenges<F, D>,
precomputed_reduced_evals: &PrecomputedReducedOpenings<F, D>,
initial_merkle_caps: &[MerkleCap<F, C::Hasher>],
proof: &FriProof<F, C::Hasher, D>,

View File

@ -12,8 +12,8 @@ use crate::iop::challenger::Challenger;
use crate::plonk::circuit_data::CommonCircuitData;
use crate::plonk::config::{GenericConfig, Hasher};
use crate::plonk::proof::{
CompressedProof, CompressedProofWithPublicInputs, FriInferredElements, OpeningSet, Proof,
ProofChallenges, ProofWithPublicInputs,
CompressedProof, CompressedProofWithPublicInputs, FriChallenges, FriInferredElements,
OpeningSet, Proof, ProofChallenges, ProofWithPublicInputs,
};
use crate::util::reverse_bits;
@ -86,10 +86,12 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
plonk_gammas,
plonk_alphas,
plonk_zeta,
fri_alpha,
fri_betas,
fri_pow_response,
fri_query_indices,
fri_challenges: FriChallenges {
fri_alpha,
fri_betas,
fri_pow_response,
fri_query_indices,
},
})
}
@ -100,7 +102,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
&self,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<Vec<usize>> {
Ok(self.get_challenges(common_data)?.fri_query_indices)
Ok(self
.get_challenges(common_data)?
.fri_challenges
.fri_query_indices)
}
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
@ -179,9 +184,13 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
) -> FriInferredElements<F, D> {
let ProofChallenges {
plonk_zeta,
fri_alpha,
fri_betas,
fri_query_indices,
fri_challenges:
FriChallenges {
fri_alpha,
fri_betas,
fri_query_indices,
..
},
..
} = challenges;
let mut fri_inferred_elements = Vec::new();

View File

@ -4,7 +4,7 @@ pub mod config;
pub(crate) mod copy_constraint;
mod get_challenges;
pub(crate) mod permutation_argument;
pub(crate) mod plonk_common;
pub mod plonk_common;
pub mod proof;
pub mod prover;
pub mod recursive_verifier;

View File

@ -125,7 +125,7 @@ pub(crate) fn reduce_with_powers_multi<
cumul
}
pub(crate) fn reduce_with_powers<'a, P: PackedField, T: IntoIterator<Item = &'a P>>(
pub fn reduce_with_powers<'a, P: PackedField, T: IntoIterator<Item = &'a P>>(
terms: T,
alpha: P::Scalar,
) -> P

View File

@ -239,6 +239,10 @@ pub(crate) struct ProofChallenges<F: RichField + Extendable<D>, const D: usize>
// Point at which the PLONK polynomials are opened.
pub plonk_zeta: F::Extension,
pub fri_challenges: FriChallenges<F, D>,
}
pub struct FriChallenges<F: RichField + Extendable<D>, const D: usize> {
// Scaling factor to combine polynomials.
pub fri_alpha: F::Extension,

View File

@ -92,7 +92,7 @@ pub(crate) fn verify_with_challenges<
verify_fri_proof::<F, C, D>(
&common_data.get_fri_instance(challenges.plonk_zeta),
&proof.openings,
&challenges,
&challenges.fri_challenges,
merkle_caps,
&proof.opening_proof,
&common_data.fri_params,

View File

@ -77,14 +77,19 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
StarkProofWithPublicInputs<F, C, D>
{
pub(crate) fn fri_query_indices(&self, config: &StarkConfig) -> anyhow::Result<Vec<usize>> {
Ok(self.get_challenges(config)?.fri_query_indices)
pub(crate) fn fri_query_indices(
&self,
config: &StarkConfig,
degree_bits: usize,
) -> anyhow::Result<Vec<usize>> {
Ok(self.get_challenges(config, degree_bits)?.fri_query_indices)
}
/// Computes all Fiat-Shamir challenges used in the Plonk proof.
pub(crate) fn get_challenges(
&self,
config: &StarkConfig,
degree_bits: usize,
) -> Result<StarkProofChallenges<F, D>> {
let StarkProof {
trace_cap,
@ -99,7 +104,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
},
} = &self.proof;
get_challenges(
get_challenges::<F, C, D>(
trace_cap,
quotient_polys_cap,
openings,
@ -107,6 +112,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
final_poly,
*pow_witness,
config,
degree_bits,
)
}
}

View File

@ -108,7 +108,7 @@ impl<F: RichField + Extendable<D>, const D: usize> StarkOpeningSet<F, D> {
quotient_polys,
} = self;
for v in &[local_values, next_values, permutation_zs, quotient_polys] {
self.observe_extension_elements(v);
challenger.observe_extension_elements(v);
}
}
}

View File

@ -102,7 +102,7 @@ where
)
);
let quotient_polys_cap = quotient_commitment.merkle_tree.cap;
challenger.observe_cap(quotient_polys_cap);
challenger.observe_cap(&quotient_polys_cap);
let zeta = challenger.get_extension_challenge::<D>();
// To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and

View File

@ -1,9 +1,11 @@
use anyhow::{ensure, Result};
use plonky2::field::extension_field::Extendable;
use plonky2::field::extension_field::{Extendable, FieldExtension};
use plonky2::field::field_types::Field;
use plonky2::fri::verifier::verify_fri_proof;
use plonky2::hash::hash_types::RichField;
use plonky2::plonk::circuit_data::CommonCircuitData;
use plonky2::plonk::config::GenericConfig;
use plonky2::plonk::plonk_common::reduce_with_powers;
use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_util::log2_strict;
@ -19,11 +21,17 @@ pub(crate) fn verify<
S: Stark<F, D>,
const D: usize,
>(
stark: S,
proof_with_pis: StarkProofWithPublicInputs<F, C, D>,
config: &StarkConfig,
) -> Result<()> {
let challenges = proof_with_pis.get_challenges(config)?;
verify_with_challenges(proof_with_pis, challenges, verifier_data, common_data)
degree_bits: usize,
) -> Result<()>
where
[(); S::COLUMNS]:,
[(); S::PUBLIC_INPUTS]:,
{
let challenges = proof_with_pis.get_challenges(config, degree_bits)?;
verify_with_challenges(stark, proof_with_pis, challenges, config)
}
pub(crate) fn verify_with_challenges<
@ -36,15 +44,18 @@ pub(crate) fn verify_with_challenges<
proof_with_pis: StarkProofWithPublicInputs<F, C, D>,
challenges: StarkProofChallenges<F, D>,
config: &StarkConfig,
) -> Result<()> {
) -> Result<()>
where
[(); S::COLUMNS]:,
[(); S::PUBLIC_INPUTS]:,
{
let StarkProofWithPublicInputs {
proof,
public_inputs,
} = proof_with_pis;
let degree = recover_degree(&proof, config);
let degree_log = log2_strict(degree);
let degree_bits = log2_strict(degree);
let local_constants = &proof.openings.constants;
let local_values = &proof.openings.local_values;
let next_values = &proof.openings.local_values;
let StarkOpeningSet {
@ -54,24 +65,32 @@ pub(crate) fn verify_with_challenges<
quotient_polys,
} = &proof.openings;
let vars = StarkEvaluationVars {
local_values,
next_values,
public_inputs: &public_inputs,
local_values: &local_values.to_vec().try_into().unwrap(),
next_values: &next_values.to_vec().try_into().unwrap(),
public_inputs: &public_inputs
.into_iter()
.map(F::Extension::from_basefield)
.collect::<Vec<_>>()
.try_into()
.unwrap(),
};
let (l_1, l_last) = eval_l_1_and_l_last(degree_bits, challenges.stark_zeta);
let mut consumer = ConstraintConsumer::<F::Extension>::new(
challenges.stark_alphas,
lagrange_first.values[i],
lagrange_last.values[i],
challenges
.stark_alphas
.iter()
.map(|&alpha| F::Extension::from_basefield(alpha))
.collect::<Vec<_>>(),
l_1.into(),
l_last.into(),
);
let (l_1, l_n) = eval_l_1_and_l_last(degree_log, challenges.stark_zeta);
stark.eval_ext()
stark.eval_ext(vars, &mut consumer);
let acc = consumer.accumulators();
// Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta.
let quotient_polys_zeta = &proof.openings.quotient_polys;
let zeta_pow_deg = challenges
.plonk_zeta
.exp_power_of_2(common_data.degree_bits);
let zeta_pow_deg = challenges.stark_zeta.exp_power_of_2(degree_bits);
let z_h_zeta = zeta_pow_deg - F::Extension::ONE;
// `quotient_polys_zeta` holds `num_challenges * quotient_degree_factor` evaluations.
// Each chunk of `quotient_degree_factor` holds the evaluations of `t_0(zeta),...,t_{quotient_degree_factor-1}(zeta)`
@ -79,41 +98,52 @@ pub(crate) fn verify_with_challenges<
// So to reconstruct `t(zeta)` we can compute `reduce_with_powers(chunk, zeta^n)` for each
// `quotient_degree_factor`-sized chunk of the original evaluations.
for (i, chunk) in quotient_polys_zeta
.chunks(common_data.quotient_degree_factor)
.chunks(config.fri_config.rate_bits)
.enumerate()
{
ensure!(vanishing_polys_zeta[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg));
ensure!(acc[i] == z_h_zeta * reduce_with_powers(chunk, zeta_pow_deg));
}
let merkle_caps = &[
verifier_data.constants_sigmas_cap.clone(),
proof.wires_cap,
proof.plonk_zs_partial_products_cap,
proof.quotient_polys_cap,
];
let merkle_caps = &[proof.trace_cap, proof.quotient_polys_cap];
verify_fri_proof::<F, C, D>(
&common_data.get_fri_instance(challenges.plonk_zeta),
&S::fri_instance(
challenges.stark_zeta,
F::primitive_root_of_unity(degree_bits).into(),
config.fri_config.rate_bits,
),
&proof.openings,
&challenges,
merkle_caps,
&proof.opening_proof,
&common_data.fri_params,
&config.fri_params(degree_bits),
)?;
Ok(())
}
/// Evaluate the Lagrange basis `L_1` and `L_n` at a point `x`.
fn eval_l_1_and_l_last<F: Field>(log_n: usize, x: F) -> (F,F) {
fn eval_l_1_and_l_last<F: Field>(log_n: usize, x: F) -> (F, F) {
let n = 1 << log_n;
let g = F::primitive_root_of_unity(log_n);
let z_x = x.exp_power_of_2(log_n);
let invs = F::batch_multiplicative_inverse(&[F::from_canonical_usize(n) * (x - F::ONE), F::from_canonical_usize(n) * (g*x - F::ONE)]);
let invs = F::batch_multiplicative_inverse(&[
F::from_canonical_usize(n) * (x - F::ONE),
F::from_canonical_usize(n) * (g * x - F::ONE),
]);
(z_x * invs[0], z_x * invs[1])
}
fn recover_degree<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(proof: &StarkProof<F, C,D>, config: &StarkConfig) -> usize {
1<<(proof.opening_proof.query_round_proofs[0].initial_trees_proof.evals_proofs[0].1.siblings.len() + config.fri_config.cap_height)
}
fn recover_degree<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
proof: &StarkProof<F, C, D>,
config: &StarkConfig,
) -> usize {
1 << (proof.opening_proof.query_round_proofs[0]
.initial_trees_proof
.evals_proofs[0]
.1
.siblings
.len()
+ config.fri_config.cap_height)
}