plonky2/evm/src/recursive_verifier.rs

410 lines
13 KiB
Rust
Raw Normal View History

2022-05-04 20:57:07 +02:00
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;
2022-05-24 16:24:52 +02:00
use plonky2::iop::target::Target;
2022-05-04 20:57:07 +02:00
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-24 16:24:52 +02:00
use crate::all_stark::{AllStark, Table};
2022-05-04 20:57:07 +02:00
use crate::config::StarkConfig;
use crate::constraint_consumer::RecursiveConstraintConsumer;
2022-05-24 16:24:52 +02:00
use crate::cpu::cpu_stark::CpuStark;
use crate::cross_table_lookup::{verify_cross_table_lookups_circuit, CtlCheckVarsTarget};
use crate::keccak::keccak_stark::KeccakStark;
2022-05-04 20:57:07 +02:00
use crate::permutation::PermutationCheckDataTarget;
use crate::proof::{
AllProof, AllProofChallengesTarget, AllProofTarget, StarkOpeningSetTarget, StarkProof,
2022-05-20 11:21:13 +02:00
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>,
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
2022-05-24 16:24:52 +02:00
[(); CpuStark::<F, D>::COLUMNS]:,
[(); CpuStark::<F, D>::PUBLIC_INPUTS]:,
[(); KeccakStark::<F, D>::COLUMNS]:,
[(); KeccakStark::<F, D>::PUBLIC_INPUTS]:,
2022-05-04 20:57:07 +02:00
C::Hasher: AlgebraicHasher<F>,
{
2022-05-20 11:21:13 +02:00
let AllProofChallengesTarget {
stark_challenges,
ctl_challenges,
2022-05-24 16:24:52 +02:00
} = all_proof.get_challenges::<F, C>(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;
2022-05-24 16:24:52 +02:00
let ctl_vars_per_table = CtlCheckVarsTarget::from_proofs(
2022-05-20 11:21:13 +02:00
&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
2022-05-24 16:24:52 +02:00
verify_stark_proof_with_challenges_circuit::<F, C, _, D>(
builder,
2022-05-20 11:21:13 +02:00
cpu_stark,
&all_proof.stark_proofs[Table::Cpu as usize],
&stark_challenges[Table::Cpu as usize],
&ctl_vars_per_table[Table::Cpu as usize],
2022-05-24 16:24:52 +02:00
inner_config,
);
verify_stark_proof_with_challenges_circuit::<F, C, _, D>(
builder,
2022-05-20 11:21:13 +02:00
keccak_stark,
&all_proof.stark_proofs[Table::Keccak as usize],
&stark_challenges[Table::Keccak as usize],
&ctl_vars_per_table[Table::Keccak as usize],
2022-05-24 16:24:52 +02:00
inner_config,
);
2022-05-20 11:21:13 +02:00
2022-05-24 16:24:52 +02:00
verify_cross_table_lookups_circuit::<F, C, D>(
builder,
2022-05-20 11:21:13 +02:00
cross_table_lookups,
&all_proof.stark_proofs,
ctl_challenges,
2022-05-24 16:24:52 +02:00
inner_config,
2022-05-20 11:21:13 +02:00
)
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,
2022-05-24 16:24:52 +02:00
proof_with_pis: &StarkProofWithPublicInputsTarget<D>,
challenges: &StarkProofChallengesTarget<D>,
ctl_vars: &[CtlCheckVarsTarget<D>],
2022-05-04 20:57:07 +02:00
inner_config: &StarkConfig,
) where
C::Hasher: AlgebraicHasher<F>,
[(); S::COLUMNS]:,
[(); S::PUBLIC_INPUTS]:,
{
2022-05-24 16:24:52 +02:00
let zero = builder.zero();
2022-05-04 20:57:07 +02:00
let one = builder.one_extension();
let StarkProofWithPublicInputsTarget {
proof,
public_inputs,
} = proof_with_pis;
2022-05-24 16:24:52 +02:00
assert_eq!(public_inputs.len(), S::PUBLIC_INPUTS);
2022-05-04 20:57:07 +02:00
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-24 16:24:52 +02:00
ctl_zs_last,
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
2022-05-24 16:24:52 +02:00
.iter()
.map(|&t| builder.convert_to_ext(t))
2022-05-04 20:57:07 +02:00
.collect::<Vec<_>>()
.try_into()
.unwrap(),
};
2022-05-24 16:24:52 +02:00
let degree_bits = proof.recover_degree_bits(inner_config);
2022-05-04 20:57:07 +02:00
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(),
2022-05-24 16:24:52 +02:00
challenges.stark_alphas.clone(),
2022-05-04 20:57:07 +02:00
z_last,
l_1,
l_last,
);
let permutation_data = stark
.uses_permutation_args()
.then(|| PermutationCheckDataTarget {
2022-05-24 16:24:52 +02:00
local_zs: permutation_zs.clone(),
next_zs: permutation_zs_right.clone(),
permutation_challenge_sets: challenges.permutation_challenge_sets.clone().unwrap(),
2022-05-04 20:57:07 +02:00
});
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,
2022-05-24 16:24:52 +02:00
ctl_vars,
2022-05-04 20:57:07 +02:00
&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);
}
2022-05-24 16:24:52 +02:00
let merkle_caps = vec![
proof.trace_cap.clone(),
proof.permutation_ctl_zs_cap.clone(),
proof.quotient_polys_cap.clone(),
];
2022-05-04 20:57:07 +02:00
let fri_instance = stark.fri_instance_target(
builder,
challenges.stark_zeta,
F::primitive_root_of_unity(degree_bits),
2022-05-24 16:24:52 +02:00
degree_bits,
ctl_zs_last.len(),
2022-05-04 20:57:07 +02:00
inner_config,
);
builder.verify_fri_proof::<C>(
&fri_instance,
2022-05-24 16:24:52 +02:00
&proof.openings.to_fri_openings(zero),
2022-05-04 20:57:07 +02:00
&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_all_proof<F: RichField + Extendable<D>, const D: usize>(
builder: &mut CircuitBuilder<F, D>,
all_stark: &AllStark<F, D>,
config: &StarkConfig,
degree_bits: &[usize],
2022-05-26 16:27:15 +02:00
nums_ctl_zs: &[usize],
) -> AllProofTarget<D> {
let stark_proofs = vec![
{
let proof = add_virtual_stark_proof(
builder,
all_stark.cpu_stark,
config,
degree_bits[Table::Cpu as usize],
2022-05-26 16:27:15 +02:00
nums_ctl_zs[Table::Cpu as usize],
);
let public_inputs = builder.add_virtual_targets(CpuStark::<F, D>::PUBLIC_INPUTS);
StarkProofWithPublicInputsTarget {
proof,
public_inputs,
}
},
{
let proof = add_virtual_stark_proof(
builder,
all_stark.keccak_stark,
config,
degree_bits[Table::Keccak as usize],
2022-05-26 16:27:15 +02:00
nums_ctl_zs[Table::Keccak as usize],
);
let public_inputs = builder.add_virtual_targets(KeccakStark::<F, D>::PUBLIC_INPUTS);
StarkProofWithPublicInputsTarget {
proof,
public_inputs,
}
},
];
assert_eq!(stark_proofs.len(), Table::num_tables());
AllProofTarget { stark_proofs }
}
2022-05-04 20:57:07 +02:00
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,
2022-05-25 08:00:41 +02:00
num_ctl_zs: usize,
2022-05-04 20:57:07 +02:00
) -> StarkProofWithPublicInputsTarget<D> {
2022-05-25 08:00:41 +02:00
let proof = add_virtual_stark_proof::<F, S, D>(builder, stark, config, degree_bits, num_ctl_zs);
2022-05-04 20:57:07 +02:00
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,
2022-05-25 08:00:41 +02:00
num_ctl_zs: usize,
2022-05-04 20:57:07 +02:00
) -> StarkProofTarget<D> {
let fri_params = config.fri_params(degree_bits);
let cap_height = fri_params.config.cap_height;
2022-05-26 16:27:15 +02:00
let num_leaves_per_oracle = vec![
S::COLUMNS,
stark.num_permutation_batches(config) + num_ctl_zs,
stark.quotient_degree_factor() * config.num_challenges,
];
2022-05-04 20:57:07 +02:00
2022-05-24 16:24:52 +02:00
let permutation_zs_cap = builder.add_virtual_cap(cap_height);
2022-05-04 20:57:07 +02:00
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),
2022-05-26 16:27:15 +02:00
openings: add_stark_opening_set::<F, S, D>(builder, stark, num_ctl_zs, config),
2022-05-04 20:57:07 +02:00
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,
2022-05-25 08:00:41 +02:00
num_ctl_zs: usize,
2022-05-04 20:57:07 +02:00
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-24 16:24:52 +02:00
permutation_ctl_zs: builder
2022-05-26 16:27:15 +02:00
.add_virtual_extension_targets(stark.num_permutation_batches(config) + num_ctl_zs),
2022-05-24 16:24:52 +02:00
permutation_ctl_zs_right: builder
2022-05-26 16:27:15 +02:00
.add_virtual_extension_targets(stark.num_permutation_batches(config) + num_ctl_zs),
ctl_zs_last: builder.add_virtual_targets(num_ctl_zs),
2022-05-04 20:57:07 +02:00
quotient_polys: builder
.add_virtual_extension_targets(stark.quotient_degree_factor() * num_challenges),
}
}
pub fn set_all_proof_target<F, C: GenericConfig<D, F = F>, W, const D: usize>(
witness: &mut W,
all_proof_target: &AllProofTarget<D>,
all_proof: &AllProof<F, C, D>,
zero: Target,
) where
F: RichField + Extendable<D>,
C::Hasher: AlgebraicHasher<F>,
W: Witness<F>,
{
for (pt, p) in all_proof_target
.stark_proofs
.iter()
.zip_eq(&all_proof.stark_proofs)
{
set_stark_proof_with_pis_target(witness, pt, p, zero);
}
}
2022-05-04 20:57:07 +02:00
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>,
2022-05-24 16:24:52 +02:00
zero: Target,
2022-05-04 20:57:07 +02:00
) 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);
}
2022-05-24 16:24:52 +02:00
set_stark_proof_target(witness, pt, proof, zero);
2022-05-04 20:57:07 +02:00
}
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>,
2022-05-24 16:24:52 +02:00
zero: Target,
2022-05-04 20:57:07 +02:00
) 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(
2022-05-24 16:24:52 +02:00
&proof_target.openings.to_fri_openings(zero),
2022-05-04 20:57:07 +02:00
&proof.openings.to_fri_openings(),
);
2022-05-24 16:24:52 +02:00
witness.set_cap_target(
&proof_target.permutation_ctl_zs_cap,
&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);
}