Merge pull request #775 from mir-protocol/generate_dummy_proof

Generate dummy proof with a given shape
This commit is contained in:
wborgeaud 2022-10-17 10:23:31 +02:00 committed by GitHub
commit 598b91c3d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 293 additions and 134 deletions

View File

@ -586,6 +586,7 @@ where
VerifierCircuitTarget {
constants_sigmas_cap: builder
.constant_merkle_cap(&verifier_data.verifier_only.constants_sigmas_cap),
circuit_digest: builder.add_virtual_hash(),
}
});
RecursiveAllProofTargetWithData {

View File

@ -117,11 +117,9 @@ where
let inner_data = VerifierCircuitTarget {
constants_sigmas_cap: builder.add_virtual_cap(inner_cd.config.fri_config.cap_height),
circuit_digest: builder.add_virtual_hash(),
};
pw.set_cap_target(
&inner_data.constants_sigmas_cap,
&inner_vd.constants_sigmas_cap,
);
pw.set_verifier_data_target(&inner_data, inner_vd);
builder.verify_proof(pt, &inner_data, inner_cd);
builder.print_gate_counts(0);
@ -151,6 +149,7 @@ where
/// Test serialization and print some size info.
fn test_serialization<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>(
proof: &ProofWithPublicInputs<F, C, D>,
vd: &VerifierOnlyCircuitData<C, D>,
cd: &CommonCircuitData<F, C, D>,
) -> Result<()>
where
@ -162,8 +161,10 @@ where
assert_eq!(proof, &proof_from_bytes);
let now = std::time::Instant::now();
let compressed_proof = proof.clone().compress(cd)?;
let decompressed_compressed_proof = compressed_proof.clone().decompress(cd)?;
let compressed_proof = proof.clone().compress(&vd.circuit_digest, cd)?;
let decompressed_compressed_proof = compressed_proof
.clone()
.decompress(&vd.circuit_digest, cd)?;
info!("{:.4}s to compress proof", now.elapsed().as_secs_f64());
assert_eq!(proof, &decompressed_compressed_proof);
@ -204,14 +205,14 @@ fn benchmark(config: &CircuitConfig, log2_inner_size: usize) -> Result<()> {
// Add a second layer of recursion to shrink the proof size further
let outer = recursive_proof::<F, C, C, D>(&middle, config, None)?;
let (proof, _, cd) = &outer;
let (proof, vd, cd) = &outer;
info!(
"Double recursion proof degree {} = 2^{}",
cd.degree(),
cd.degree_bits()
);
test_serialization(proof, cd)?;
test_serialization(proof, vd, cd)?;
Ok(())
}

View File

@ -54,7 +54,7 @@ impl FriConfig {
/// FRI parameters, including generated parameters which are specific to an instance size, in
/// contrast to `FriConfig` which is user-specified and independent of instance size.
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub struct FriParams {
/// User-specified FRI configuration.
pub config: FriConfig,

View File

@ -9,7 +9,7 @@ use crate::hash::hash_types::RichField;
/// Placeholder value to indicate that a gate doesn't use a selector polynomial.
pub(crate) const UNUSED_SELECTOR: usize = u32::MAX as usize;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct SelectorsInfo {
pub(crate) selector_indices: Vec<usize>,
pub(crate) groups: Vec<Range<usize>>,

View File

@ -13,6 +13,7 @@ use crate::hash::merkle_tree::MerkleCap;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::target::{BoolTarget, Target};
use crate::iop::wire::Wire;
use crate::plonk::circuit_data::{VerifierCircuitTarget, VerifierOnlyCircuitData};
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{Proof, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget};
@ -197,6 +198,18 @@ pub trait Witness<F: Field> {
}
}
fn set_verifier_data_target<C: GenericConfig<D, F = F>, const D: usize>(
&mut self,
vdt: &VerifierCircuitTarget,
vd: &VerifierOnlyCircuitData<C, D>,
) where
F: RichField + Extendable<D>,
C::Hasher: AlgebraicHasher<F>,
{
self.set_cap_target(&vdt.constants_sigmas_cap, &vd.constants_sigmas_cap);
self.set_hash_target(vdt.circuit_digest, vd.circuit_digest);
}
fn set_wire(&mut self, wire: Wire, value: F) {
self.set_target(Target::Wire(wire), value)
}

View File

@ -271,6 +271,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
);
}
pub fn add_gate_to_gate_set(&mut self, gate: GateRef<F, D>) {
self.gates.insert(gate);
}
pub fn connect_extension(&mut self, src: ExtensionTarget<D>, dst: ExtensionTarget<D>) {
for i in 0..D {
self.connect(src.0[i], dst.0[i]);
@ -751,11 +755,6 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
Some(&fft_root_table),
);
let constants_sigmas_cap = constants_sigmas_commitment.merkle_tree.cap.clone();
let verifier_only = VerifierOnlyCircuitData {
constants_sigmas_cap: constants_sigmas_cap.clone(),
};
// Map between gates where not all generators are used and the gate's number of used generators.
let incomplete_gates = self
.current_slots
@ -796,17 +795,6 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
indices.shrink_to_fit();
}
let prover_only = ProverOnlyCircuitData {
generators: self.generators,
generator_indices_by_watches,
constants_sigmas_commitment,
sigmas: transpose_poly_values(sigma_vecs),
subgroup,
public_inputs: self.public_inputs,
representative_map: forest.parents,
fft_root_table: Some(fft_root_table),
};
let num_gate_constraints = gates
.iter()
.map(|gate| gate.0.num_constraints())
@ -816,6 +804,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let num_partial_products =
num_partial_products(self.config.num_routed_wires, quotient_degree_factor);
let constants_sigmas_cap = constants_sigmas_commitment.merkle_tree.cap.clone();
// TODO: This should also include an encoding of gate constraints.
let circuit_digest_parts = [
constants_sigmas_cap.flatten(),
@ -837,6 +826,22 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
num_public_inputs,
k_is,
num_partial_products,
};
let prover_only = ProverOnlyCircuitData {
generators: self.generators,
generator_indices_by_watches,
constants_sigmas_commitment,
sigmas: transpose_poly_values(sigma_vecs),
subgroup,
public_inputs: self.public_inputs,
representative_map: forest.parents,
fft_root_table: Some(fft_root_table),
circuit_digest,
};
let verifier_only = VerifierOnlyCircuitData {
constants_sigmas_cap,
circuit_digest,
};

View File

@ -15,7 +15,7 @@ use crate::fri::structure::{
use crate::fri::{FriConfig, FriParams};
use crate::gates::gate::GateRef;
use crate::gates::selectors::SelectorsInfo;
use crate::hash::hash_types::{MerkleCapTarget, RichField};
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
use crate::hash::merkle_tree::MerkleCap;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::WitnessGenerator;
@ -29,7 +29,7 @@ use crate::plonk::prover::prove;
use crate::plonk::verifier::verify;
use crate::util::timing::TimingTree;
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CircuitConfig {
pub num_wires: usize,
pub num_routed_wires: usize,
@ -141,6 +141,23 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
compressed_proof_with_pis.verify(&self.verifier_only, &self.common)
}
pub fn compress(
&self,
proof: ProofWithPublicInputs<F, C, D>,
) -> Result<CompressedProofWithPublicInputs<F, C, D>> {
proof.compress(&self.verifier_only.circuit_digest, &self.common)
}
pub fn decompress(
&self,
proof: CompressedProofWithPublicInputs<F, C, D>,
) -> Result<ProofWithPublicInputs<F, C, D>>
where
[(); C::Hasher::HASH_SIZE]:,
{
proof.decompress(&self.verifier_only.circuit_digest, &self.common)
}
pub fn verifier_data(self) -> VerifierCircuitData<F, C, D> {
let CircuitData {
verifier_only,
@ -253,6 +270,9 @@ pub struct ProverOnlyCircuitData<
pub representative_map: Vec<usize>,
/// Pre-computed roots for faster FFT.
pub fft_root_table: Option<FftRootTable<F>>,
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
/// seed Fiat-Shamir.
pub circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<F>>::Hash,
}
/// Circuit data required by the verifier, but not the prover.
@ -260,10 +280,13 @@ pub struct ProverOnlyCircuitData<
pub struct VerifierOnlyCircuitData<C: GenericConfig<D>, const D: usize> {
/// A commitment to each constant polynomial and each permutation polynomial.
pub constants_sigmas_cap: MerkleCap<C::F, C::Hasher>,
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
/// seed Fiat-Shamir.
pub circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
}
/// Circuit data required by both the prover and the verifier.
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub struct CommonCircuitData<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
@ -274,7 +297,7 @@ pub struct CommonCircuitData<
pub(crate) fri_params: FriParams,
/// The types of gates used in this circuit, along with their prefixes.
pub(crate) gates: Vec<GateRef<F, D>>,
pub(crate) gates: Vec<GateRef<C::F, D>>,
/// Information on the circuit's selector polynomials.
pub(crate) selectors_info: SelectorsInfo,
@ -291,14 +314,10 @@ pub struct CommonCircuitData<
pub(crate) num_public_inputs: usize,
/// The `{k_i}` valued used in `S_ID_i` in Plonk's permutation argument.
pub(crate) k_is: Vec<F>,
pub(crate) k_is: Vec<C::F>,
/// The number of partial products needed to compute the `Z` polynomials.
pub(crate) num_partial_products: usize,
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
/// seed Fiat-Shamir.
pub(crate) circuit_digest: <<C as GenericConfig<D>>::Hasher as Hasher<F>>::Hash,
}
impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
@ -478,4 +497,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
pub struct VerifierCircuitTarget {
/// A commitment to each constant polynomial and each permutation polynomial.
pub constants_sigmas_cap: MerkleCapTarget,
/// A digest of the "circuit" (i.e. the instance, minus public inputs), which can be used to
/// seed Fiat-Shamir.
pub circuit_digest: HashOutTarget,
}

View File

@ -1,29 +1,84 @@
use anyhow::{ensure, Result};
use itertools::Itertools;
use plonky2_field::extension::Extendable;
use plonky2_util::ceil_div_usize;
use crate::fri::proof::{
FriInitialTreeProofTarget, FriProofTarget, FriQueryRoundTarget, FriQueryStepTarget,
};
use crate::gadgets::polynomial::PolynomialCoeffsExtTarget;
use crate::gates::noop::NoopGate;
use crate::hash::hash_types::{HashOutTarget, MerkleCapTarget, RichField};
use crate::hash::merkle_proofs::MerkleProofTarget;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartialWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::{CommonCircuitData, VerifierCircuitTarget};
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{OpeningSetTarget, ProofTarget, ProofWithPublicInputsTarget};
use crate::plonk::circuit_data::{
CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
};
use crate::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};
use crate::plonk::proof::{
OpeningSetTarget, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget,
};
use crate::with_context;
/// Generate a proof having a given `CommonCircuitData`.
#[allow(unused)] // TODO: should be used soon.
pub(crate) fn dummy_proof<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
common_data: &CommonCircuitData<F, C, D>,
) -> Result<(
ProofWithPublicInputs<F, C, D>,
VerifierOnlyCircuitData<C, D>,
)>
where
[(); C::Hasher::HASH_SIZE]:,
{
let config = common_data.config.clone();
let mut pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
ensure!(
!common_data.config.zero_knowledge,
"Degree calculation can be off if zero-knowledge is on."
);
let degree = common_data.degree();
// Number of `NoopGate`s to add to get a circuit of size `degree` in the end.
// Need to account for public input hashing, a `PublicInputGate` and a `ConstantGate`.
let num_noop_gate = degree - ceil_div_usize(common_data.num_public_inputs, 8) - 2;
for _ in 0..num_noop_gate {
builder.add_gate(NoopGate, vec![]);
}
for gate in &common_data.gates {
builder.add_gate_to_gate_set(gate.clone());
}
for _ in 0..common_data.num_public_inputs {
let t = builder.add_virtual_public_input();
pw.set_target(t, F::ZERO);
}
let data = builder.build::<C>();
assert_eq!(&data.common, common_data);
let proof = data.prove(pw)?;
Ok((proof, data.verifier_only))
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// Verify `proof0` if `condition` else verify `proof1`.
/// `proof0` and `proof1` are assumed to use the same `CommonCircuitData`.
pub fn conditionally_verify_proof<C: GenericConfig<D, F = F>>(
&mut self,
condition: BoolTarget,
proof_with_pis0: ProofWithPublicInputsTarget<D>,
proof_with_pis0: &ProofWithPublicInputsTarget<D>,
inner_verifier_data0: &VerifierCircuitTarget,
proof_with_pis1: ProofWithPublicInputsTarget<D>,
proof_with_pis1: &ProofWithPublicInputsTarget<D>,
inner_verifier_data1: &VerifierCircuitTarget,
inner_common_data: &CommonCircuitData<F, C, D>,
) where
@ -79,18 +134,52 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let selected_verifier_data = VerifierCircuitTarget {
constants_sigmas_cap: self.select_cap(
condition,
inner_verifier_data0.constants_sigmas_cap.clone(),
inner_verifier_data1.constants_sigmas_cap.clone(),
&inner_verifier_data0.constants_sigmas_cap,
&inner_verifier_data1.constants_sigmas_cap,
),
circuit_digest: self.select_hash(
condition,
inner_verifier_data0.circuit_digest,
inner_verifier_data1.circuit_digest,
),
};
self.verify_proof(selected_proof, &selected_verifier_data, inner_common_data);
}
fn select_vec(&mut self, b: BoolTarget, v0: Vec<Target>, v1: Vec<Target>) -> Vec<Target> {
v0.into_iter()
/// Conditionally verify a proof with a new generated dummy proof.
pub fn conditionally_verify_proof_or_dummy<C: GenericConfig<D, F = F>>(
&mut self,
condition: BoolTarget,
proof_with_pis: &ProofWithPublicInputsTarget<D>,
inner_verifier_data: &VerifierCircuitTarget,
inner_common_data: &CommonCircuitData<F, C, D>,
) -> (ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)
where
C::Hasher: AlgebraicHasher<F>,
{
let dummy_proof = self.add_virtual_proof_with_pis(inner_common_data);
let dummy_verifier_data = VerifierCircuitTarget {
constants_sigmas_cap: self
.add_virtual_cap(inner_common_data.config.fri_config.cap_height),
circuit_digest: self.add_virtual_hash(),
};
self.conditionally_verify_proof(
condition,
proof_with_pis,
inner_verifier_data,
&dummy_proof,
&dummy_verifier_data,
inner_common_data,
);
(dummy_proof, dummy_verifier_data)
}
fn select_vec(&mut self, b: BoolTarget, v0: &[Target], v1: &[Target]) -> Vec<Target> {
v0.iter()
.zip_eq(v1)
.map(|(t0, t1)| self.select(b, t0, t1))
.map(|(t0, t1)| self.select(b, *t0, *t1))
.collect()
}
@ -108,15 +197,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_cap(
&mut self,
b: BoolTarget,
cap0: MerkleCapTarget,
cap1: MerkleCapTarget,
cap0: &MerkleCapTarget,
cap1: &MerkleCapTarget,
) -> MerkleCapTarget {
assert_eq!(cap0.0.len(), cap1.0.len());
MerkleCapTarget(
cap0.0
.into_iter()
.zip_eq(cap1.0)
.map(|(h0, h1)| self.select_hash(b, h0, h1))
.iter()
.zip_eq(&cap1.0)
.map(|(h0, h1)| self.select_hash(b, *h0, *h1))
.collect(),
)
}
@ -124,10 +213,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_vec_cap(
&mut self,
b: BoolTarget,
v0: Vec<MerkleCapTarget>,
v1: Vec<MerkleCapTarget>,
v0: &[MerkleCapTarget],
v1: &[MerkleCapTarget],
) -> Vec<MerkleCapTarget> {
v0.into_iter()
v0.iter()
.zip_eq(v1)
.map(|(c0, c1)| self.select_cap(b, c0, c1))
.collect()
@ -136,53 +225,53 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_opening_set(
&mut self,
b: BoolTarget,
os0: OpeningSetTarget<D>,
os1: OpeningSetTarget<D>,
os0: &OpeningSetTarget<D>,
os1: &OpeningSetTarget<D>,
) -> OpeningSetTarget<D> {
OpeningSetTarget {
constants: self.select_vec_ext(b, os0.constants, os1.constants),
plonk_sigmas: self.select_vec_ext(b, os0.plonk_sigmas, os1.plonk_sigmas),
wires: self.select_vec_ext(b, os0.wires, os1.wires),
plonk_zs: self.select_vec_ext(b, os0.plonk_zs, os1.plonk_zs),
plonk_zs_next: self.select_vec_ext(b, os0.plonk_zs_next, os1.plonk_zs_next),
partial_products: self.select_vec_ext(b, os0.partial_products, os1.partial_products),
quotient_polys: self.select_vec_ext(b, os0.quotient_polys, os1.quotient_polys),
constants: self.select_vec_ext(b, &os0.constants, &os1.constants),
plonk_sigmas: self.select_vec_ext(b, &os0.plonk_sigmas, &os1.plonk_sigmas),
wires: self.select_vec_ext(b, &os0.wires, &os1.wires),
plonk_zs: self.select_vec_ext(b, &os0.plonk_zs, &os1.plonk_zs),
plonk_zs_next: self.select_vec_ext(b, &os0.plonk_zs_next, &os1.plonk_zs_next),
partial_products: self.select_vec_ext(b, &os0.partial_products, &os1.partial_products),
quotient_polys: self.select_vec_ext(b, &os0.quotient_polys, &os1.quotient_polys),
}
}
fn select_vec_ext(
&mut self,
b: BoolTarget,
v0: Vec<ExtensionTarget<D>>,
v1: Vec<ExtensionTarget<D>>,
v0: &[ExtensionTarget<D>],
v1: &[ExtensionTarget<D>],
) -> Vec<ExtensionTarget<D>> {
v0.into_iter()
v0.iter()
.zip_eq(v1)
.map(|(e0, e1)| self.select_ext(b, e0, e1))
.map(|(e0, e1)| self.select_ext(b, *e0, *e1))
.collect()
}
fn select_opening_proof(
&mut self,
b: BoolTarget,
proof0: FriProofTarget<D>,
proof1: FriProofTarget<D>,
proof0: &FriProofTarget<D>,
proof1: &FriProofTarget<D>,
) -> FriProofTarget<D> {
FriProofTarget {
commit_phase_merkle_caps: self.select_vec_cap(
b,
proof0.commit_phase_merkle_caps,
proof1.commit_phase_merkle_caps,
&proof0.commit_phase_merkle_caps,
&proof1.commit_phase_merkle_caps,
),
query_round_proofs: self.select_vec_query_round(
b,
proof0.query_round_proofs,
proof1.query_round_proofs,
&proof0.query_round_proofs,
&proof1.query_round_proofs,
),
final_poly: PolynomialCoeffsExtTarget(self.select_vec_ext(
b,
proof0.final_poly.0,
proof1.final_poly.0,
&proof0.final_poly.0,
&proof1.final_poly.0,
)),
pow_witness: self.select(b, proof0.pow_witness, proof1.pow_witness),
}
@ -191,26 +280,26 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_query_round(
&mut self,
b: BoolTarget,
qr0: FriQueryRoundTarget<D>,
qr1: FriQueryRoundTarget<D>,
qr0: &FriQueryRoundTarget<D>,
qr1: &FriQueryRoundTarget<D>,
) -> FriQueryRoundTarget<D> {
FriQueryRoundTarget {
initial_trees_proof: self.select_initial_tree_proof(
b,
qr0.initial_trees_proof,
qr1.initial_trees_proof,
&qr0.initial_trees_proof,
&qr1.initial_trees_proof,
),
steps: self.select_vec_query_step(b, qr0.steps, qr1.steps),
steps: self.select_vec_query_step(b, &qr0.steps, &qr1.steps),
}
}
fn select_vec_query_round(
&mut self,
b: BoolTarget,
v0: Vec<FriQueryRoundTarget<D>>,
v1: Vec<FriQueryRoundTarget<D>>,
v0: &[FriQueryRoundTarget<D>],
v1: &[FriQueryRoundTarget<D>],
) -> Vec<FriQueryRoundTarget<D>> {
v0.into_iter()
v0.iter()
.zip_eq(v1)
.map(|(qr0, qr1)| self.select_query_round(b, qr0, qr1))
.collect()
@ -219,14 +308,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_initial_tree_proof(
&mut self,
b: BoolTarget,
proof0: FriInitialTreeProofTarget,
proof1: FriInitialTreeProofTarget,
proof0: &FriInitialTreeProofTarget,
proof1: &FriInitialTreeProofTarget,
) -> FriInitialTreeProofTarget {
FriInitialTreeProofTarget {
evals_proofs: proof0
.evals_proofs
.into_iter()
.zip_eq(proof1.evals_proofs)
.iter()
.zip_eq(&proof1.evals_proofs)
.map(|((v0, p0), (v1, p1))| {
(
self.select_vec(b, v0, v1),
@ -240,15 +329,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_merkle_proof(
&mut self,
b: BoolTarget,
proof0: MerkleProofTarget,
proof1: MerkleProofTarget,
proof0: &MerkleProofTarget,
proof1: &MerkleProofTarget,
) -> MerkleProofTarget {
MerkleProofTarget {
siblings: proof0
.siblings
.into_iter()
.zip_eq(proof1.siblings)
.map(|(h0, h1)| self.select_hash(b, h0, h1))
.iter()
.zip_eq(&proof1.siblings)
.map(|(h0, h1)| self.select_hash(b, *h0, *h1))
.collect(),
}
}
@ -256,22 +345,22 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
fn select_query_step(
&mut self,
b: BoolTarget,
qs0: FriQueryStepTarget<D>,
qs1: FriQueryStepTarget<D>,
qs0: &FriQueryStepTarget<D>,
qs1: &FriQueryStepTarget<D>,
) -> FriQueryStepTarget<D> {
FriQueryStepTarget {
evals: self.select_vec_ext(b, qs0.evals, qs1.evals),
merkle_proof: self.select_merkle_proof(b, qs0.merkle_proof, qs1.merkle_proof),
evals: self.select_vec_ext(b, &qs0.evals, &qs1.evals),
merkle_proof: self.select_merkle_proof(b, &qs0.merkle_proof, &qs1.merkle_proof),
}
}
fn select_vec_query_step(
&mut self,
b: BoolTarget,
v0: Vec<FriQueryStepTarget<D>>,
v1: Vec<FriQueryStepTarget<D>>,
v0: &[FriQueryStepTarget<D>],
v1: &[FriQueryStepTarget<D>],
) -> Vec<FriQueryStepTarget<D>> {
v0.into_iter()
v0.iter()
.zip_eq(v1)
.map(|(qs0, qs1)| self.select_query_step(b, qs0, qs1))
.collect()
@ -297,6 +386,7 @@ mod tests {
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
// Generate proof.
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
let mut pw = PartialWitness::new();
let t = builder.add_virtual_target();
@ -310,33 +400,32 @@ mod tests {
let proof = data.prove(pw)?;
data.verify(proof.clone())?;
// Generate dummy proof with the same `CommonCircuitData`.
let (dummy_proof, dummy_data) = dummy_proof(&data.common)?;
// Conditionally verify the two proofs.
let mut builder = CircuitBuilder::<F, D>::new(config);
let mut pw = PartialWitness::new();
let pt = builder.add_virtual_proof_with_pis(&data.common);
pw.set_proof_with_pis_target(&pt, &proof);
let dummy_pt = builder.add_virtual_proof_with_pis(&data.common);
pw.set_proof_with_pis_target(&dummy_pt, &proof);
pw.set_proof_with_pis_target(&dummy_pt, &dummy_proof);
let inner_data = VerifierCircuitTarget {
constants_sigmas_cap: builder.add_virtual_cap(data.common.config.fri_config.cap_height),
circuit_digest: builder.add_virtual_hash(),
};
pw.set_cap_target(
&inner_data.constants_sigmas_cap,
&data.verifier_only.constants_sigmas_cap,
);
pw.set_verifier_data_target(&inner_data, &data.verifier_only);
let dummy_inner_data = VerifierCircuitTarget {
constants_sigmas_cap: builder.add_virtual_cap(data.common.config.fri_config.cap_height),
circuit_digest: builder.add_virtual_hash(),
};
pw.set_cap_target(
&dummy_inner_data.constants_sigmas_cap,
&data.verifier_only.constants_sigmas_cap,
);
pw.set_verifier_data_target(&dummy_inner_data, &dummy_data);
let b = builder.constant_bool(F::rand().0 % 2 == 0);
builder.conditionally_verify_proof(
b,
pt,
&pt,
&inner_data,
dummy_pt,
&dummy_pt,
&dummy_inner_data,
&data.common,
);

View File

@ -29,6 +29,7 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
commit_phase_merkle_caps: &[MerkleCap<F, C::Hasher>],
final_poly: &PolynomialCoeffs<F::Extension>,
pow_witness: F,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<ProofChallenges<F, D>> {
let config = &common_data.config;
@ -37,7 +38,7 @@ fn get_challenges<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, cons
let mut challenger = Challenger::<F, C::Hasher>::new();
// Observe the instance.
challenger.observe_hash::<C::Hasher>(common_data.circuit_digest);
challenger.observe_hash::<C::Hasher>(*circuit_digest);
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
challenger.observe_cap(wires_cap);
@ -72,10 +73,11 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
{
pub(crate) fn fri_query_indices(
&self,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<Vec<usize>> {
Ok(self
.get_challenges(self.get_public_inputs_hash(), common_data)?
.get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?
.fri_challenges
.fri_query_indices)
}
@ -84,6 +86,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
pub(crate) fn get_challenges(
&self,
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<ProofChallenges<F, D>> {
let Proof {
@ -109,6 +112,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
commit_phase_merkle_caps,
final_poly,
*pow_witness,
circuit_digest,
common_data,
)
}
@ -121,6 +125,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
pub(crate) fn get_challenges(
&self,
public_inputs_hash: <<C as GenericConfig<D>>::InnerHasher as Hasher<F>>::Hash,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<ProofChallenges<F, D>> {
let CompressedProof {
@ -146,6 +151,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
commit_phase_merkle_caps,
final_poly,
*pow_witness,
circuit_digest,
common_data,
)
}
@ -237,6 +243,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
commit_phase_merkle_caps: &[MerkleCapTarget],
final_poly: &PolynomialCoeffsExtTarget<D>,
pow_witness: Target,
inner_circuit_digest: HashOutTarget,
inner_common_data: &CommonCircuitData<F, C, D>,
) -> ProofChallengesTarget<D>
where
@ -248,9 +255,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
let mut challenger = RecursiveChallenger::<F, C::Hasher, D>::new(self);
// Observe the instance.
let digest =
HashOutTarget::from_vec(self.constants(&inner_common_data.circuit_digest.elements));
challenger.observe_hash(&digest);
challenger.observe_hash(&inner_circuit_digest);
challenger.observe_hash(&public_inputs_hash);
challenger.observe_cap(wires_cap);
@ -286,6 +291,7 @@ impl<const D: usize> ProofWithPublicInputsTarget<D> {
&self,
builder: &mut CircuitBuilder<F, D>,
public_inputs_hash: HashOutTarget,
inner_circuit_digest: HashOutTarget,
inner_common_data: &CommonCircuitData<F, C, D>,
) -> ProofChallengesTarget<D>
where
@ -314,6 +320,7 @@ impl<const D: usize> ProofWithPublicInputsTarget<D> {
commit_phase_merkle_caps,
final_poly,
*pow_witness,
inner_circuit_digest,
inner_common_data,
)
}

View File

@ -81,9 +81,10 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
{
pub fn compress(
self,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<CompressedProofWithPublicInputs<F, C, D>> {
let indices = self.fri_query_indices(common_data)?;
let indices = self.fri_query_indices(circuit_digest, common_data)?;
let compressed_proof = self.proof.compress(&indices, &common_data.fri_params);
Ok(CompressedProofWithPublicInputs {
public_inputs: self.public_inputs,
@ -176,12 +177,14 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
{
pub fn decompress(
self,
circuit_digest: &<<C as GenericConfig<D>>::Hasher as Hasher<C::F>>::Hash,
common_data: &CommonCircuitData<F, C, D>,
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>>
where
[(); C::Hasher::HASH_SIZE]:,
{
let challenges = self.get_challenges(self.get_public_inputs_hash(), common_data)?;
let challenges =
self.get_challenges(self.get_public_inputs_hash(), circuit_digest, common_data)?;
let fri_inferred_elements = self.get_inferred_elements(&challenges, common_data);
let decompressed_proof =
self.proof
@ -205,7 +208,11 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
"Number of public inputs doesn't match circuit data."
);
let public_inputs_hash = self.get_public_inputs_hash();
let challenges = self.get_challenges(public_inputs_hash, common_data)?;
let challenges = self.get_challenges(
public_inputs_hash,
&verifier_data.circuit_digest,
common_data,
)?;
let fri_inferred_elements = self.get_inferred_elements(&challenges, common_data);
let decompressed_proof =
self.proof
@ -418,8 +425,8 @@ mod tests {
verify(proof.clone(), &data.verifier_only, &data.common)?;
// Verify that `decompress ∘ compress = identity`.
let compressed_proof = proof.clone().compress(&data.common)?;
let decompressed_compressed_proof = compressed_proof.clone().decompress(&data.common)?;
let compressed_proof = data.compress(proof.clone())?;
let decompressed_compressed_proof = data.decompress(compressed_proof.clone())?;
assert_eq!(proof, decompressed_compressed_proof);
verify(proof, &data.verifier_only, &data.common)?;

View File

@ -81,7 +81,7 @@ where
let mut challenger = Challenger::<F, C::Hasher>::new();
// Observe the instance.
challenger.observe_hash::<C::Hasher>(common_data.circuit_digest);
challenger.observe_hash::<C::Hasher>(prover_data.circuit_digest);
challenger.observe_hash::<C::InnerHasher>(public_inputs_hash);
challenger.observe_cap(&wires_commitment.merkle_tree.cap);

View File

@ -29,7 +29,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
);
let public_inputs_hash =
self.hash_n_to_hash_no_pad::<C::InnerHasher>(proof_with_pis.public_inputs.clone());
let challenges = proof_with_pis.get_challenges(self, public_inputs_hash, inner_common_data);
let challenges = proof_with_pis.get_challenges(
self,
public_inputs_hash,
inner_verifier_data.circuit_digest,
inner_common_data,
);
self.verify_proof_with_challenges(
proof_with_pis.proof,
@ -205,9 +210,9 @@ mod tests {
let config = CircuitConfig::standard_recursion_zk_config();
let (proof, vd, cd) = dummy_proof::<F, C, D>(&config, 4_000)?;
let (proof, _vd, cd) =
let (proof, vd, cd) =
recursive_proof::<F, C, C, D>(proof, vd, cd, &config, None, true, true)?;
test_serialization(&proof, &cd)?;
test_serialization(&proof, &vd, &cd)?;
Ok(())
}
@ -231,11 +236,11 @@ mod tests {
assert_eq!(cd.degree_bits(), 13);
// Shrink it to 2^12.
let (proof, _vd, cd) =
let (proof, vd, cd) =
recursive_proof::<F, C, C, D>(proof, vd, cd, &config, None, true, true)?;
assert_eq!(cd.degree_bits(), 12);
test_serialization(&proof, &cd)?;
test_serialization(&proof, &vd, &cd)?;
Ok(())
}
@ -287,11 +292,11 @@ mod tests {
},
..high_rate_config
};
let (proof, _vd, cd) =
let (proof, vd, cd) =
recursive_proof::<F, KC, C, D>(proof, vd, cd, &final_config, None, true, true)?;
assert_eq!(cd.degree_bits(), 12, "final proof too large");
test_serialization(&proof, &cd)?;
test_serialization(&proof, &vd, &cd)?;
Ok(())
}
@ -309,11 +314,11 @@ mod tests {
let (proof, vd, cd) =
recursive_proof::<F, PC, PC, D>(proof, vd, cd, &config, None, false, false)?;
test_serialization(&proof, &cd)?;
test_serialization(&proof, &vd, &cd)?;
let (proof, _vd, cd) =
let (proof, vd, cd) =
recursive_proof::<F, KC, PC, D>(proof, vd, cd, &config, None, false, false)?;
test_serialization(&proof, &cd)?;
test_serialization(&proof, &vd, &cd)?;
Ok(())
}
@ -372,11 +377,13 @@ mod tests {
let inner_data = VerifierCircuitTarget {
constants_sigmas_cap: builder.add_virtual_cap(inner_cd.config.fri_config.cap_height),
circuit_digest: builder.add_virtual_hash(),
};
pw.set_cap_target(
&inner_data.constants_sigmas_cap,
&inner_vd.constants_sigmas_cap,
);
pw.set_hash_target(inner_data.circuit_digest, inner_vd.circuit_digest);
builder.verify_proof(pt, &inner_data, &inner_cd);
@ -414,6 +421,7 @@ mod tests {
const D: usize,
>(
proof: &ProofWithPublicInputs<F, C, D>,
vd: &VerifierOnlyCircuitData<C, D>,
cd: &CommonCircuitData<F, C, D>,
) -> Result<()>
where
@ -425,8 +433,10 @@ mod tests {
assert_eq!(proof, &proof_from_bytes);
let now = std::time::Instant::now();
let compressed_proof = proof.clone().compress(cd)?;
let decompressed_compressed_proof = compressed_proof.clone().decompress(cd)?;
let compressed_proof = proof.clone().compress(&vd.circuit_digest, cd)?;
let decompressed_compressed_proof = compressed_proof
.clone()
.decompress(&vd.circuit_digest, cd)?;
info!("{:.4}s to compress proof", now.elapsed().as_secs_f64());
assert_eq!(proof, &decompressed_compressed_proof);

View File

@ -23,7 +23,11 @@ where
validate_proof_with_pis_shape(&proof_with_pis, common_data)?;
let public_inputs_hash = proof_with_pis.get_public_inputs_hash();
let challenges = proof_with_pis.get_challenges(public_inputs_hash, common_data)?;
let challenges = proof_with_pis.get_challenges(
public_inputs_hash,
&verifier_data.circuit_digest,
common_data,
)?;
verify_with_challenges(
proof_with_pis.proof,