plonky2/src/recursive_verifier.rs

368 lines
13 KiB
Rust
Raw Normal View History

2021-03-25 15:20:14 -07:00
use crate::circuit_builder::CircuitBuilder;
2021-07-12 14:25:28 +02:00
use crate::circuit_data::{CircuitConfig, CommonCircuitData, VerifierCircuitTarget};
use crate::field::extension_field::Extendable;
2021-07-12 14:25:28 +02:00
use crate::plonk_challenger::RecursiveChallenger;
use crate::proof::{HashTarget, ProofTarget};
use crate::util::scaling::ReducingFactorTarget;
use crate::vanishing_poly::eval_vanishing_poly_recursively;
use crate::vars::EvaluationTargets;
2021-02-26 13:18:41 -08:00
const MIN_WIRES: usize = 120; // TODO: Double check.
const MIN_ROUTED_WIRES: usize = 28; // TODO: Double check.
2021-07-08 17:16:26 +02:00
impl<F: Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Recursively verifies an inner proof.
pub fn add_recursive_verifier(
&mut self,
2021-07-12 14:25:28 +02:00
proof: ProofTarget<D>,
2021-07-09 10:01:58 +02:00
inner_config: &CircuitConfig,
2021-07-12 14:25:28 +02:00
inner_verifier_data: &VerifierCircuitTarget,
inner_common_data: &CommonCircuitData<F, D>,
2021-07-08 17:16:26 +02:00
) {
assert!(self.config.num_wires >= MIN_WIRES);
assert!(self.config.num_wires >= MIN_ROUTED_WIRES);
2021-07-12 14:25:28 +02:00
let one = self.one_extension();
let num_challenges = inner_config.num_challenges;
let mut challenger = RecursiveChallenger::new(self);
2021-07-14 20:54:30 +02:00
self.set_context("Challenger observes proof and generates challenges.");
2021-07-12 14:25:28 +02:00
let digest =
HashTarget::from_vec(self.constants(&inner_common_data.circuit_digest.elements));
challenger.observe_hash(&digest);
challenger.observe_hash(&proof.wires_root);
let betas = challenger.get_n_challenges(self, num_challenges);
let gammas = challenger.get_n_challenges(self, num_challenges);
challenger.observe_hash(&proof.plonk_zs_root);
let alphas = challenger.get_n_challenges(self, num_challenges);
2021-07-08 17:16:26 +02:00
2021-07-12 14:25:28 +02:00
challenger.observe_hash(&proof.quotient_polys_root);
let zeta = challenger.get_extension_challenge(self);
let local_constants = &proof.openings.constants;
let local_wires = &proof.openings.wires;
let vars = EvaluationTargets {
local_constants,
local_wires,
};
let local_zs = &proof.openings.plonk_zs;
let next_zs = &proof.openings.plonk_zs_right;
let s_sigmas = &proof.openings.plonk_sigmas;
let partial_products = &proof.openings.partial_products;
let zeta_pow_deg = self.exp_u64_extension(zeta, inner_common_data.degree() as u64);
2021-07-14 20:54:30 +02:00
self.set_context("Evaluate the vanishing polynomial at our challenge point, zeta.");
2021-07-12 14:25:28 +02:00
let vanishing_polys_zeta = eval_vanishing_poly_recursively(
self,
inner_common_data,
zeta,
zeta_pow_deg,
vars,
local_zs,
next_zs,
partial_products,
s_sigmas,
&betas,
&gammas,
&alphas,
);
2021-07-14 20:54:30 +02:00
self.set_context("Check vanishing and quotient polynomials.");
2021-07-13 09:44:35 +02:00
let quotient_polys_zeta = &proof.openings.quotient_polys;
let zeta_pow_deg = self.exp_u64_extension(zeta, 1 << inner_common_data.degree_bits as u64);
2021-07-15 10:55:18 +02:00
let mut scale = ReducingFactorTarget::new(zeta_pow_deg);
2021-07-13 09:44:35 +02:00
let z_h_zeta = self.sub_extension(zeta_pow_deg, one);
for (i, chunk) in quotient_polys_zeta
.chunks(inner_common_data.quotient_degree_factor)
.enumerate()
{
let mut rhs = scale.reduce(chunk, self);
rhs = self.mul_extension(z_h_zeta, rhs);
2021-07-14 20:54:30 +02:00
self.named_route_extension(
vanishing_polys_zeta[i],
rhs,
format!("Vanishing polynomial == Z_H * quotient, challenge {}", i),
);
2021-07-13 09:44:35 +02:00
}
let evaluations = proof.openings.clone();
let merkle_roots = &[
inner_verifier_data.constants_sigmas_root,
proof.wires_root,
proof.plonk_zs_root,
proof.quotient_polys_root,
];
proof.opening_proof.verify(
zeta,
&evaluations,
merkle_roots,
&mut challenger,
inner_common_data,
self,
);
2021-07-08 17:16:26 +02:00
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::crandall_field::CrandallField;
2021-07-14 21:52:34 +02:00
use crate::fri::FriConfig;
2021-07-08 17:16:26 +02:00
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
use crate::merkle_proofs::MerkleProofTarget;
use crate::polynomial::commitment::OpeningProofTarget;
use crate::proof::{
FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, FriQueryStepTarget,
2021-07-15 10:39:57 +02:00
OpeningSetTarget, Proof,
2021-07-08 17:16:26 +02:00
};
2021-07-09 10:01:58 +02:00
use crate::verifier::verify;
2021-07-08 17:16:26 +02:00
use crate::witness::PartialWitness;
2021-07-15 10:55:18 +02:00
// Construct a `FriQueryRoundTarget` with the same dimensions as the ones in `proof`.
2021-07-09 10:01:58 +02:00
fn get_fri_query_round<F: Extendable<D>, const D: usize>(
2021-07-08 17:16:26 +02:00
proof: &Proof<F, D>,
builder: &mut CircuitBuilder<F, D>,
2021-07-09 10:01:58 +02:00
) -> FriQueryRoundTarget<D> {
2021-07-08 17:16:26 +02:00
let mut query_round = FriQueryRoundTarget {
initial_trees_proof: FriInitialTreeProofTarget {
evals_proofs: vec![],
},
steps: vec![],
};
for (v, merkle_proof) in &proof.opening_proof.fri_proof.query_round_proofs[0]
.initial_trees_proof
.evals_proofs
{
query_round.initial_trees_proof.evals_proofs.push((
builder.add_virtual_targets(v.len()),
MerkleProofTarget {
siblings: builder.add_virtual_hashes(merkle_proof.siblings.len()),
},
));
}
for step in &proof.opening_proof.fri_proof.query_round_proofs[0].steps {
query_round.steps.push(FriQueryStepTarget {
evals: builder.add_virtual_extension_targets(step.evals.len()),
merkle_proof: MerkleProofTarget {
siblings: builder.add_virtual_hashes(step.merkle_proof.siblings.len()),
},
});
}
2021-07-09 10:01:58 +02:00
query_round
}
2021-07-08 17:16:26 +02:00
2021-07-15 10:55:18 +02:00
// Construct a `ProofTarget` with the same dimensions as `proof`.
2021-07-09 10:01:58 +02:00
fn proof_to_proof_target<F: Extendable<D>, const D: usize>(
proof: &Proof<F, D>,
builder: &mut CircuitBuilder<F, D>,
) -> ProofTarget<D> {
let wires_root = builder.add_virtual_hash();
let plonk_zs_root = builder.add_virtual_hash();
let quotient_polys_root = builder.add_virtual_hash();
let openings = OpeningSetTarget {
constants: builder.add_virtual_extension_targets(proof.openings.constants.len()),
2021-07-12 14:25:28 +02:00
plonk_sigmas: builder.add_virtual_extension_targets(proof.openings.plonk_sigmas.len()),
2021-07-09 10:01:58 +02:00
wires: builder.add_virtual_extension_targets(proof.openings.wires.len()),
plonk_zs: builder.add_virtual_extension_targets(proof.openings.plonk_zs.len()),
plonk_zs_right: builder
.add_virtual_extension_targets(proof.openings.plonk_zs_right.len()),
partial_products: builder
.add_virtual_extension_targets(proof.openings.partial_products.len()),
quotient_polys: builder
.add_virtual_extension_targets(proof.openings.quotient_polys.len()),
};
let query_round_proofs = (0..proof.opening_proof.fri_proof.query_round_proofs.len())
.map(|_| get_fri_query_round(proof, builder))
.collect();
let commit_phase_merkle_roots = (0..proof
.opening_proof
.fri_proof
.commit_phase_merkle_roots
.len())
.map(|_| builder.add_virtual_hash())
.collect();
2021-07-08 17:16:26 +02:00
let opening_proof =
OpeningProofTarget {
fri_proof: FriProofTarget {
2021-07-09 10:01:58 +02:00
commit_phase_merkle_roots,
query_round_proofs,
2021-07-08 17:16:26 +02:00
final_poly: PolynomialCoeffsExtTarget(builder.add_virtual_extension_targets(
proof.opening_proof.fri_proof.final_poly.len(),
)),
pow_witness: builder.add_virtual_target(),
},
};
ProofTarget {
wires_root,
plonk_zs_root,
quotient_polys_root,
openings,
opening_proof,
}
}
2021-07-15 10:55:18 +02:00
// Set the targets in a `ProofTarget` to their corresponding values in a `Proof`.
2021-07-08 17:16:26 +02:00
fn set_proof_target<F: Extendable<D>, const D: usize>(
proof: &Proof<F, D>,
pt: &ProofTarget<D>,
pw: &mut PartialWitness<F>,
) {
pw.set_hash_target(pt.wires_root, proof.wires_root);
pw.set_hash_target(pt.plonk_zs_root, proof.plonk_zs_root);
pw.set_hash_target(pt.quotient_polys_root, proof.quotient_polys_root);
for (&t, &x) in pt.openings.wires.iter().zip(&proof.openings.wires) {
pw.set_extension_target(t, x);
}
for (&t, &x) in pt.openings.constants.iter().zip(&proof.openings.constants) {
pw.set_extension_target(t, x);
}
for (&t, &x) in pt
.openings
.plonk_sigmas
.iter()
2021-07-12 14:25:28 +02:00
.zip(&proof.openings.plonk_sigmas)
2021-07-08 17:16:26 +02:00
{
pw.set_extension_target(t, x);
}
for (&t, &x) in pt.openings.plonk_zs.iter().zip(&proof.openings.plonk_zs) {
pw.set_extension_target(t, x);
}
for (&t, &x) in pt
.openings
.plonk_zs_right
.iter()
.zip(&proof.openings.plonk_zs_right)
{
pw.set_extension_target(t, x);
}
for (&t, &x) in pt
.openings
.partial_products
.iter()
.zip(&proof.openings.partial_products)
{
pw.set_extension_target(t, x);
}
for (&t, &x) in pt
.openings
.quotient_polys
.iter()
.zip(&proof.openings.quotient_polys)
{
pw.set_extension_target(t, x);
}
let fri_proof = &proof.opening_proof.fri_proof;
let fpt = &pt.opening_proof.fri_proof;
pw.set_target(fpt.pow_witness, fri_proof.pow_witness);
for (&t, &x) in fpt.final_poly.0.iter().zip(&fri_proof.final_poly.coeffs) {
pw.set_extension_target(t, x);
}
for (&t, &x) in fpt
.commit_phase_merkle_roots
.iter()
.zip(&fri_proof.commit_phase_merkle_roots)
{
pw.set_hash_target(t, x);
}
for (qt, q) in fpt
.query_round_proofs
.iter()
.zip(&fri_proof.query_round_proofs)
{
for (at, a) in qt
.initial_trees_proof
.evals_proofs
.iter()
.zip(&q.initial_trees_proof.evals_proofs)
{
for (&t, &x) in at.0.iter().zip(&a.0) {
pw.set_target(t, x);
}
for (&t, &x) in at.1.siblings.iter().zip(&a.1.siblings) {
pw.set_hash_target(t, x);
}
}
for (st, s) in qt.steps.iter().zip(&q.steps) {
for (&t, &x) in st.evals.iter().zip(&s.evals) {
pw.set_extension_target(t, x);
}
for (&t, &x) in st
.merkle_proof
.siblings
.iter()
.zip(&s.merkle_proof.siblings)
{
pw.set_hash_target(t, x);
}
}
}
}
#[test]
2021-07-15 09:11:54 +02:00
#[ignore]
2021-07-08 17:16:26 +02:00
fn test_recursive_verifier() {
2021-07-14 08:14:00 +02:00
env_logger::init();
2021-07-08 17:16:26 +02:00
type F = CrandallField;
2021-07-13 15:20:14 +02:00
const D: usize = 4;
2021-07-14 21:52:34 +02:00
let config = CircuitConfig {
num_wires: 134,
num_routed_wires: 28,
security_bits: 128,
rate_bits: 3,
num_challenges: 3,
fri_config: FriConfig {
proof_of_work_bits: 1,
rate_bits: 3,
reduction_arity_bits: vec![2, 2, 2, 2, 2, 2, 2],
num_query_rounds: 40,
},
};
2021-07-09 10:01:58 +02:00
let (proof, vd, cd) = {
2021-07-14 21:52:34 +02:00
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
2021-07-15 10:39:57 +02:00
let _two = builder.two();
let _two = builder.hash_n_to_hash(vec![_two], true).elements[0];
2021-07-15 10:55:18 +02:00
for _ in 0..5000 {
2021-07-15 10:39:57 +02:00
let _two = builder.mul(_two, _two);
2021-07-14 21:52:34 +02:00
}
2021-07-08 17:16:26 +02:00
let data = builder.build();
2021-07-09 10:01:58 +02:00
(
data.prove(PartialWitness::new()),
data.verifier_only,
data.common,
)
2021-07-08 17:16:26 +02:00
};
2021-07-12 14:25:28 +02:00
verify(proof.clone(), &vd, &cd).unwrap();
2021-07-08 17:16:26 +02:00
2021-07-13 15:20:14 +02:00
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
2021-07-08 17:16:26 +02:00
let mut pw = PartialWitness::new();
let pt = proof_to_proof_target(&proof, &mut builder);
set_proof_target(&proof, &pt, &mut pw);
2021-07-09 10:01:58 +02:00
let inner_data = VerifierCircuitTarget {
constants_sigmas_root: builder.add_virtual_hash(),
};
pw.set_hash_target(inner_data.constants_sigmas_root, vd.constants_sigmas_root);
2021-07-15 09:52:42 +02:00
builder.add_recursive_verifier(pt, &config, &inner_data, &cd);
2021-07-09 10:01:58 +02:00
let data = builder.build();
2021-07-14 20:54:30 +02:00
let recursive_proof = data.prove(pw);
2021-07-09 10:01:58 +02:00
verify(recursive_proof, &data.verifier_only, &data.common).unwrap();
2021-07-08 17:16:26 +02:00
}
2021-02-26 13:18:41 -08:00
}