2022-05-04 20:57:07 +02:00
|
|
|
use std::iter::once;
|
|
|
|
|
|
|
|
|
|
use anyhow::{ensure, Result};
|
|
|
|
|
use itertools::Itertools;
|
|
|
|
|
use plonky2::field::extension_field::Extendable;
|
|
|
|
|
use plonky2::field::field_types::Field;
|
|
|
|
|
use plonky2::fri::witness_util::set_fri_proof_target;
|
|
|
|
|
use plonky2::hash::hash_types::RichField;
|
|
|
|
|
use plonky2::iop::ext_target::ExtensionTarget;
|
|
|
|
|
use plonky2::iop::witness::Witness;
|
|
|
|
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
|
|
|
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
|
|
|
|
use plonky2::util::reducing::ReducingFactorTarget;
|
|
|
|
|
use plonky2::with_context;
|
|
|
|
|
|
2022-05-20 11:21:13 +02:00
|
|
|
use crate::all_stark::AllStark;
|
2022-05-04 20:57:07 +02:00
|
|
|
use crate::config::StarkConfig;
|
|
|
|
|
use crate::constraint_consumer::RecursiveConstraintConsumer;
|
|
|
|
|
use crate::permutation::PermutationCheckDataTarget;
|
|
|
|
|
use crate::proof::{
|
2022-05-20 11:21:13 +02:00
|
|
|
AllProof, AllProofChallengesTarget, AllProofTarget, StarkOpeningSetTarget, StarkProof,
|
|
|
|
|
StarkProofChallengesTarget, StarkProofTarget, StarkProofWithPublicInputs,
|
|
|
|
|
StarkProofWithPublicInputsTarget,
|
2022-05-04 20:57:07 +02:00
|
|
|
};
|
|
|
|
|
use crate::stark::Stark;
|
2022-05-18 09:22:58 +02:00
|
|
|
use crate::vanishing_poly::eval_vanishing_poly_circuit;
|
2022-05-04 20:57:07 +02:00
|
|
|
use crate::vars::StarkEvaluationTargets;
|
|
|
|
|
|
2022-05-20 11:21:13 +02:00
|
|
|
pub fn verify_proof_circuit<
|
2022-05-04 20:57:07 +02:00
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
S: Stark<F, D>,
|
|
|
|
|
const D: usize,
|
|
|
|
|
>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
2022-05-20 11:21:13 +02:00
|
|
|
all_stark: AllStark<F, D>,
|
|
|
|
|
all_proof: AllProofTarget<D>,
|
2022-05-04 20:57:07 +02:00
|
|
|
inner_config: &StarkConfig,
|
|
|
|
|
) where
|
|
|
|
|
C::Hasher: AlgebraicHasher<F>,
|
|
|
|
|
[(); S::COLUMNS]:,
|
|
|
|
|
[(); S::PUBLIC_INPUTS]:,
|
|
|
|
|
{
|
2022-05-20 11:21:13 +02:00
|
|
|
let AllProofChallengesTarget {
|
|
|
|
|
stark_challenges,
|
|
|
|
|
ctl_challenges,
|
|
|
|
|
} = all_proof.get_challenges(builder, &all_stark, inner_config);
|
2022-05-04 20:57:07 +02:00
|
|
|
|
2022-05-20 11:21:13 +02:00
|
|
|
let nums_permutation_zs = all_stark.nums_permutation_zs(inner_config);
|
|
|
|
|
|
|
|
|
|
let AllStark {
|
|
|
|
|
cpu_stark,
|
|
|
|
|
keccak_stark,
|
|
|
|
|
cross_table_lookups,
|
|
|
|
|
} = all_stark;
|
|
|
|
|
|
|
|
|
|
let ctl_vars_per_table = CtlCheckVars::from_proofs(
|
|
|
|
|
&all_proof.stark_proofs,
|
|
|
|
|
&cross_table_lookups,
|
|
|
|
|
&ctl_challenges,
|
|
|
|
|
&nums_permutation_zs,
|
2022-05-04 20:57:07 +02:00
|
|
|
);
|
2022-05-20 11:21:13 +02:00
|
|
|
|
|
|
|
|
verify_stark_proof_with_challenges(
|
|
|
|
|
cpu_stark,
|
|
|
|
|
&all_proof.stark_proofs[Table::Cpu as usize],
|
|
|
|
|
&stark_challenges[Table::Cpu as usize],
|
|
|
|
|
&ctl_vars_per_table[Table::Cpu as usize],
|
|
|
|
|
config,
|
|
|
|
|
)?;
|
|
|
|
|
verify_stark_proof_with_challenges(
|
|
|
|
|
keccak_stark,
|
|
|
|
|
&all_proof.stark_proofs[Table::Keccak as usize],
|
|
|
|
|
&stark_challenges[Table::Keccak as usize],
|
|
|
|
|
&ctl_vars_per_table[Table::Keccak as usize],
|
|
|
|
|
config,
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
verify_cross_table_lookups(
|
|
|
|
|
cross_table_lookups,
|
|
|
|
|
&all_proof.stark_proofs,
|
|
|
|
|
ctl_challenges,
|
|
|
|
|
config,
|
|
|
|
|
)
|
2022-05-04 20:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Recursively verifies an inner proof.
|
2022-05-18 09:22:58 +02:00
|
|
|
fn verify_stark_proof_with_challenges_circuit<
|
2022-05-04 20:57:07 +02:00
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
S: Stark<F, D>,
|
|
|
|
|
const D: usize,
|
|
|
|
|
>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
stark: S,
|
|
|
|
|
proof_with_pis: StarkProofWithPublicInputsTarget<D>,
|
|
|
|
|
challenges: StarkProofChallengesTarget<D>,
|
|
|
|
|
inner_config: &StarkConfig,
|
|
|
|
|
degree_bits: usize,
|
|
|
|
|
) where
|
|
|
|
|
C::Hasher: AlgebraicHasher<F>,
|
|
|
|
|
[(); S::COLUMNS]:,
|
|
|
|
|
[(); S::PUBLIC_INPUTS]:,
|
|
|
|
|
{
|
|
|
|
|
check_permutation_options(&stark, &proof_with_pis, &challenges).unwrap();
|
|
|
|
|
let one = builder.one_extension();
|
|
|
|
|
|
|
|
|
|
let StarkProofWithPublicInputsTarget {
|
|
|
|
|
proof,
|
|
|
|
|
public_inputs,
|
|
|
|
|
} = proof_with_pis;
|
|
|
|
|
let StarkOpeningSetTarget {
|
|
|
|
|
local_values,
|
|
|
|
|
next_values,
|
2022-05-20 11:21:13 +02:00
|
|
|
permutation_ctl_zs: permutation_zs,
|
|
|
|
|
permutation_ctl_zs_right: permutation_zs_right,
|
2022-05-04 20:57:07 +02:00
|
|
|
quotient_polys,
|
|
|
|
|
} = &proof.openings;
|
|
|
|
|
let vars = StarkEvaluationTargets {
|
|
|
|
|
local_values: &local_values.to_vec().try_into().unwrap(),
|
|
|
|
|
next_values: &next_values.to_vec().try_into().unwrap(),
|
|
|
|
|
public_inputs: &public_inputs
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|t| builder.convert_to_ext(t))
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.try_into()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let zeta_pow_deg = builder.exp_power_of_2_extension(challenges.stark_zeta, degree_bits);
|
|
|
|
|
let z_h_zeta = builder.sub_extension(zeta_pow_deg, one);
|
|
|
|
|
let (l_1, l_last) =
|
2022-05-18 09:22:58 +02:00
|
|
|
eval_l_1_and_l_last_circuit(builder, degree_bits, challenges.stark_zeta, z_h_zeta);
|
2022-05-04 20:57:07 +02:00
|
|
|
let last =
|
|
|
|
|
builder.constant_extension(F::Extension::primitive_root_of_unity(degree_bits).inverse());
|
|
|
|
|
let z_last = builder.sub_extension(challenges.stark_zeta, last);
|
|
|
|
|
|
|
|
|
|
let mut consumer = RecursiveConstraintConsumer::<F, D>::new(
|
|
|
|
|
builder.zero_extension(),
|
|
|
|
|
challenges.stark_alphas,
|
|
|
|
|
z_last,
|
|
|
|
|
l_1,
|
|
|
|
|
l_last,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let permutation_data = stark
|
|
|
|
|
.uses_permutation_args()
|
|
|
|
|
.then(|| PermutationCheckDataTarget {
|
|
|
|
|
local_zs: permutation_zs.as_ref().unwrap().clone(),
|
|
|
|
|
next_zs: permutation_zs_right.as_ref().unwrap().clone(),
|
|
|
|
|
permutation_challenge_sets: challenges.permutation_challenge_sets.unwrap(),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
with_context!(
|
|
|
|
|
builder,
|
|
|
|
|
"evaluate vanishing polynomial",
|
2022-05-18 09:22:58 +02:00
|
|
|
eval_vanishing_poly_circuit::<F, C, S, D>(
|
2022-05-04 20:57:07 +02:00
|
|
|
builder,
|
|
|
|
|
&stark,
|
|
|
|
|
inner_config,
|
|
|
|
|
vars,
|
|
|
|
|
permutation_data,
|
|
|
|
|
&mut consumer,
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
let vanishing_polys_zeta = consumer.accumulators();
|
|
|
|
|
|
|
|
|
|
// Check each polynomial identity, of the form `vanishing(x) = Z_H(x) quotient(x)`, at zeta.
|
|
|
|
|
let mut scale = ReducingFactorTarget::new(zeta_pow_deg);
|
|
|
|
|
for (i, chunk) in quotient_polys
|
|
|
|
|
.chunks(stark.quotient_degree_factor())
|
|
|
|
|
.enumerate()
|
|
|
|
|
{
|
|
|
|
|
let recombined_quotient = scale.reduce(chunk, builder);
|
|
|
|
|
let computed_vanishing_poly = builder.mul_extension(z_h_zeta, recombined_quotient);
|
|
|
|
|
builder.connect_extension(vanishing_polys_zeta[i], computed_vanishing_poly);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let merkle_caps = once(proof.trace_cap)
|
2022-05-20 11:21:13 +02:00
|
|
|
.chain(proof.permutation_ctl_zs_cap)
|
2022-05-04 20:57:07 +02:00
|
|
|
.chain(once(proof.quotient_polys_cap))
|
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
|
|
let fri_instance = stark.fri_instance_target(
|
|
|
|
|
builder,
|
|
|
|
|
challenges.stark_zeta,
|
|
|
|
|
F::primitive_root_of_unity(degree_bits),
|
|
|
|
|
inner_config,
|
|
|
|
|
);
|
|
|
|
|
builder.verify_fri_proof::<C>(
|
|
|
|
|
&fri_instance,
|
|
|
|
|
&proof.openings.to_fri_openings(),
|
|
|
|
|
&challenges.fri_challenges,
|
|
|
|
|
&merkle_caps,
|
|
|
|
|
&proof.opening_proof,
|
|
|
|
|
&inner_config.fri_params(degree_bits),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-18 09:22:58 +02:00
|
|
|
fn eval_l_1_and_l_last_circuit<F: RichField + Extendable<D>, const D: usize>(
|
2022-05-04 20:57:07 +02:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
log_n: usize,
|
|
|
|
|
x: ExtensionTarget<D>,
|
|
|
|
|
z_x: ExtensionTarget<D>,
|
|
|
|
|
) -> (ExtensionTarget<D>, ExtensionTarget<D>) {
|
|
|
|
|
let n = builder.constant_extension(F::Extension::from_canonical_usize(1 << log_n));
|
|
|
|
|
let g = builder.constant_extension(F::Extension::primitive_root_of_unity(log_n));
|
|
|
|
|
let one = builder.one_extension();
|
|
|
|
|
let l_1_deno = builder.mul_sub_extension(n, x, n);
|
|
|
|
|
let l_last_deno = builder.mul_sub_extension(g, x, one);
|
|
|
|
|
let l_last_deno = builder.mul_extension(n, l_last_deno);
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
builder.div_extension(z_x, l_1_deno),
|
|
|
|
|
builder.div_extension(z_x, l_last_deno),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_virtual_stark_proof_with_pis<
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
S: Stark<F, D>,
|
|
|
|
|
const D: usize,
|
|
|
|
|
>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
stark: S,
|
|
|
|
|
config: &StarkConfig,
|
|
|
|
|
degree_bits: usize,
|
|
|
|
|
) -> StarkProofWithPublicInputsTarget<D> {
|
|
|
|
|
let proof = add_virtual_stark_proof::<F, S, D>(builder, stark, config, degree_bits);
|
|
|
|
|
let public_inputs = builder.add_virtual_targets(S::PUBLIC_INPUTS);
|
|
|
|
|
StarkProofWithPublicInputsTarget {
|
|
|
|
|
proof,
|
|
|
|
|
public_inputs,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add_virtual_stark_proof<F: RichField + Extendable<D>, S: Stark<F, D>, const D: usize>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
stark: S,
|
|
|
|
|
config: &StarkConfig,
|
|
|
|
|
degree_bits: usize,
|
|
|
|
|
) -> StarkProofTarget<D> {
|
|
|
|
|
let fri_params = config.fri_params(degree_bits);
|
|
|
|
|
let cap_height = fri_params.config.cap_height;
|
|
|
|
|
|
|
|
|
|
let num_leaves_per_oracle = once(S::COLUMNS)
|
|
|
|
|
.chain(
|
|
|
|
|
stark
|
|
|
|
|
.uses_permutation_args()
|
|
|
|
|
.then(|| stark.num_permutation_batches(config)),
|
|
|
|
|
)
|
|
|
|
|
.chain(once(stark.quotient_degree_factor() * config.num_challenges))
|
|
|
|
|
.collect_vec();
|
|
|
|
|
|
|
|
|
|
let permutation_zs_cap = stark
|
|
|
|
|
.uses_permutation_args()
|
|
|
|
|
.then(|| builder.add_virtual_cap(cap_height));
|
|
|
|
|
|
|
|
|
|
StarkProofTarget {
|
|
|
|
|
trace_cap: builder.add_virtual_cap(cap_height),
|
2022-05-20 11:21:13 +02:00
|
|
|
permutation_ctl_zs_cap: permutation_zs_cap,
|
2022-05-04 20:57:07 +02:00
|
|
|
quotient_polys_cap: builder.add_virtual_cap(cap_height),
|
|
|
|
|
openings: add_stark_opening_set::<F, S, D>(builder, stark, config),
|
|
|
|
|
opening_proof: builder.add_virtual_fri_proof(&num_leaves_per_oracle, &fri_params),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn add_stark_opening_set<F: RichField + Extendable<D>, S: Stark<F, D>, const D: usize>(
|
|
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
stark: S,
|
|
|
|
|
config: &StarkConfig,
|
|
|
|
|
) -> StarkOpeningSetTarget<D> {
|
|
|
|
|
let num_challenges = config.num_challenges;
|
|
|
|
|
StarkOpeningSetTarget {
|
|
|
|
|
local_values: builder.add_virtual_extension_targets(S::COLUMNS),
|
|
|
|
|
next_values: builder.add_virtual_extension_targets(S::COLUMNS),
|
2022-05-20 11:21:13 +02:00
|
|
|
permutation_ctl_zs: stark
|
2022-05-04 20:57:07 +02:00
|
|
|
.uses_permutation_args()
|
|
|
|
|
.then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))),
|
2022-05-20 11:21:13 +02:00
|
|
|
permutation_ctl_zs_right: stark
|
2022-05-04 20:57:07 +02:00
|
|
|
.uses_permutation_args()
|
|
|
|
|
.then(|| builder.add_virtual_extension_targets(stark.num_permutation_batches(config))),
|
|
|
|
|
quotient_polys: builder
|
|
|
|
|
.add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_stark_proof_with_pis_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
|
|
|
|
|
witness: &mut W,
|
|
|
|
|
stark_proof_with_pis_target: &StarkProofWithPublicInputsTarget<D>,
|
|
|
|
|
stark_proof_with_pis: &StarkProofWithPublicInputs<F, C, D>,
|
|
|
|
|
) where
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C::Hasher: AlgebraicHasher<F>,
|
|
|
|
|
W: Witness<F>,
|
|
|
|
|
{
|
|
|
|
|
let StarkProofWithPublicInputs {
|
|
|
|
|
proof,
|
|
|
|
|
public_inputs,
|
|
|
|
|
} = stark_proof_with_pis;
|
|
|
|
|
let StarkProofWithPublicInputsTarget {
|
|
|
|
|
proof: pt,
|
|
|
|
|
public_inputs: pi_targets,
|
|
|
|
|
} = stark_proof_with_pis_target;
|
|
|
|
|
|
|
|
|
|
// Set public inputs.
|
|
|
|
|
for (&pi_t, &pi) in pi_targets.iter().zip_eq(public_inputs) {
|
|
|
|
|
witness.set_target(pi_t, pi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_stark_proof_target(witness, pt, proof);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_stark_proof_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
|
|
|
|
|
witness: &mut W,
|
|
|
|
|
proof_target: &StarkProofTarget<D>,
|
|
|
|
|
proof: &StarkProof<F, C, D>,
|
|
|
|
|
) where
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C::Hasher: AlgebraicHasher<F>,
|
|
|
|
|
W: Witness<F>,
|
|
|
|
|
{
|
|
|
|
|
witness.set_cap_target(&proof_target.trace_cap, &proof.trace_cap);
|
|
|
|
|
witness.set_cap_target(&proof_target.quotient_polys_cap, &proof.quotient_polys_cap);
|
|
|
|
|
|
|
|
|
|
witness.set_fri_openings(
|
|
|
|
|
&proof_target.openings.to_fri_openings(),
|
|
|
|
|
&proof.openings.to_fri_openings(),
|
|
|
|
|
);
|
|
|
|
|
|
2022-05-20 11:21:13 +02:00
|
|
|
if let Some(permutation_zs_cap_target) = &proof_target.permutation_ctl_zs_cap {
|
2022-05-17 09:41:06 +02:00
|
|
|
witness.set_cap_target(permutation_zs_cap_target, &proof.permutation_ctl_zs_cap);
|
2022-05-04 20:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_fri_proof_target(witness, &proof_target.opening_proof, &proof.opening_proof);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Utility function to check that all permutation data wrapped in `Option`s are `Some` iff
|
|
|
|
|
/// the Stark uses a permutation argument.
|
|
|
|
|
fn check_permutation_options<F: RichField + Extendable<D>, S: Stark<F, D>, const D: usize>(
|
|
|
|
|
stark: &S,
|
|
|
|
|
proof_with_pis: &StarkProofWithPublicInputsTarget<D>,
|
|
|
|
|
challenges: &StarkProofChallengesTarget<D>,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
let options_is_some = [
|
2022-05-20 11:21:13 +02:00
|
|
|
proof_with_pis.proof.permutation_ctl_zs_cap.is_some(),
|
|
|
|
|
proof_with_pis.proof.openings.permutation_ctl_zs.is_some(),
|
|
|
|
|
proof_with_pis
|
|
|
|
|
.proof
|
|
|
|
|
.openings
|
|
|
|
|
.permutation_ctl_zs_right
|
|
|
|
|
.is_some(),
|
2022-05-04 20:57:07 +02:00
|
|
|
challenges.permutation_challenge_sets.is_some(),
|
|
|
|
|
];
|
|
|
|
|
ensure!(
|
|
|
|
|
options_is_some
|
|
|
|
|
.into_iter()
|
|
|
|
|
.all(|b| b == stark.uses_permutation_args()),
|
|
|
|
|
"Permutation data doesn't match with Stark configuration."
|
|
|
|
|
);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|