Merge pull request #827 from mir-protocol/cyclic_recursion_tweaks

Cyclic recursion tweaks
This commit is contained in:
Daniel Lubarov 2022-11-29 13:19:18 -08:00 committed by GitHub
commit 73de231e34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 233 additions and 146 deletions

View File

@ -112,7 +112,7 @@ pub struct FriProof<F: RichField + Extendable<D>, H: Hasher<F>, const D: usize>
pub pow_witness: F,
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct FriProofTarget<const D: usize> {
pub commit_phase_merkle_caps: Vec<MerkleCapTarget>,
pub query_round_proofs: Vec<FriQueryRoundTarget<D>>,

View File

@ -7,7 +7,7 @@ use crate::iop::target::Target;
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::util::reducing::ReducingFactorTarget;
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct PolynomialCoeffsExtTarget<const D: usize>(pub Vec<ExtensionTarget<D>>);
impl<const D: usize> PolynomialCoeffsExtTarget<D> {

View File

@ -1,5 +1,6 @@
use alloc::vec::Vec;
use anyhow::ensure;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::field::goldilocks_field::GoldilocksField;
@ -25,6 +26,7 @@ impl<F: Field> HashOut<F> {
elements: [F::ZERO; 4],
};
// TODO: Switch to a TryFrom impl.
pub fn from_vec(elements: Vec<F>) -> Self {
debug_assert!(elements.len() == 4);
Self {
@ -39,6 +41,23 @@ impl<F: Field> HashOut<F> {
}
}
impl<F: Field> From<[F; 4]> for HashOut<F> {
fn from(elements: [F; 4]) -> Self {
Self { elements }
}
}
impl<F: Field> TryFrom<&[F]> for HashOut<F> {
type Error = anyhow::Error;
fn try_from(elements: &[F]) -> Result<Self, Self::Error> {
ensure!(elements.len() == 4);
Ok(Self {
elements: elements.try_into().unwrap(),
})
}
}
impl<F> Sample for HashOut<F>
where
F: Field,
@ -97,6 +116,7 @@ pub struct HashOutTarget {
}
impl HashOutTarget {
// TODO: Switch to a TryFrom impl.
pub fn from_vec(elements: Vec<Target>) -> Self {
debug_assert!(elements.len() == 4);
Self {
@ -111,6 +131,23 @@ impl HashOutTarget {
}
}
impl From<[Target; 4]> for HashOutTarget {
fn from(elements: [Target; 4]) -> Self {
Self { elements }
}
}
impl TryFrom<&[Target]> for HashOutTarget {
type Error = anyhow::Error;
fn try_from(elements: &[Target]) -> Result<Self, Self::Error> {
ensure!(elements.len() == 4);
Ok(Self {
elements: elements.try_into().unwrap(),
})
}
}
#[derive(Clone, Debug)]
pub struct MerkleCapTarget(pub Vec<HashOutTarget>);

View File

@ -244,9 +244,15 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.register_public_input(t);
t
}
/// Add a virtual verifier data, register it as a public input and set it to `self.verifier_data_public_input`.
/// WARNING: Do not register any public input after calling this! TODO: relax this
pub(crate) fn add_verifier_data_public_input(&mut self) {
pub fn add_verifier_data_public_inputs(&mut self) {
assert!(
self.verifier_data_public_input.is_none(),
"add_verifier_data_public_inputs only needs to be called once"
);
let verifier_data = VerifierCircuitTarget {
constants_sigmas_cap: self.add_virtual_cap(self.config.fri_config.cap_height),
circuit_digest: self.add_virtual_hash(),
@ -886,7 +892,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
num_partial_products,
};
if let Some(goal_data) = self.goal_common_data {
assert_eq!(goal_data, common);
assert_eq!(goal_data, common, "The expected circuit data passed to cyclic recursion method did not match the actual circuit");
}
let prover_only = ProverOnlyCircuitData {

View File

@ -40,7 +40,7 @@ pub struct Proof<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const
pub opening_proof: FriProof<F, C::Hasher, D>,
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ProofTarget<const D: usize> {
pub wires_cap: MerkleCapTarget,
pub plonk_zs_partial_products_cap: MerkleCapTarget,
@ -283,7 +283,7 @@ pub(crate) struct FriInferredElements<F: RichField + Extendable<D>, const D: usi
pub Vec<F::Extension>,
);
#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct ProofWithPublicInputsTarget<const D: usize> {
pub proof: ProofTarget<D>,
pub public_inputs: Vec<Target>,

View File

@ -1,7 +1,5 @@
use alloc::vec;
use alloc::vec::Vec;
use anyhow::{ensure, Result};
use itertools::Itertools;
use crate::field::extension::Extendable;
@ -9,66 +7,16 @@ 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, VerifierOnlyCircuitData,
};
use crate::plonk::circuit_data::{CommonCircuitData, VerifierCircuitTarget};
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{
OpeningSetTarget, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget,
};
use crate::util::ceil_div_usize;
use crate::plonk::proof::{OpeningSetTarget, ProofTarget, ProofWithPublicInputsTarget};
use crate::with_context;
/// Generate a proof having a given `CommonCircuitData`.
pub(crate) fn dummy_proof<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
common_data: &CommonCircuitData<F, D>,
) -> Result<(
ProofWithPublicInputs<F, C, D>,
VerifierOnlyCircuitData<C, D>,
)> {
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`.
@ -369,6 +317,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
#[cfg(test)]
mod tests {
use anyhow::Result;
use hashbrown::HashMap;
use super::*;
use crate::field::types::Sample;
@ -376,6 +325,7 @@ mod tests {
use crate::iop::witness::{PartialWitness, Witness};
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
#[test]
fn test_conditional_recursive_verifier() -> Result<()> {
@ -400,7 +350,8 @@ mod tests {
data.verify(proof.clone())?;
// Generate dummy proof with the same `CommonCircuitData`.
let (dummy_proof, dummy_data) = dummy_proof(&data.common)?;
let dummy_data = dummy_circuit(&data.common);
let dummy_proof = dummy_proof(&dummy_data, HashMap::new())?;
// Conditionally verify the two proofs.
let mut builder = CircuitBuilder::<F, D>::new(config);
@ -418,7 +369,7 @@ mod tests {
constants_sigmas_cap: builder.add_virtual_cap(data.common.config.fri_config.cap_height),
circuit_digest: builder.add_virtual_hash(),
};
pw.set_verifier_data_target(&dummy_inner_data, &dummy_data);
pw.set_verifier_data_target(&dummy_inner_data, &dummy_data.verifier_only);
let b = builder.constant_bool(F::rand().0 % 2 == 0);
builder.conditionally_verify_proof::<C>(
b,

View File

@ -3,6 +3,7 @@
use alloc::vec;
use anyhow::{ensure, Result};
use hashbrown::HashMap;
use itertools::Itertools;
use crate::field::extension::Extendable;
@ -13,11 +14,11 @@ 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, VerifierOnlyCircuitData,
CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
};
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use crate::recursion::conditional_recursive_verifier::dummy_proof;
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
pub struct CyclicRecursionData<
'a,
@ -30,12 +31,17 @@ pub struct CyclicRecursionData<
common_data: &'a CommonCircuitData<F, D>,
}
pub struct CyclicRecursionTarget<const D: usize> {
pub proof: ProofWithPublicInputsTarget<D>,
pub verifier_data: VerifierCircuitTarget,
pub dummy_proof: ProofWithPublicInputsTarget<D>,
pub dummy_verifier_data: VerifierCircuitTarget,
pub condition: BoolTarget,
pub struct CyclicRecursionTarget<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
pub(crate) proof: ProofWithPublicInputsTarget<D>,
pub(crate) verifier_data: VerifierCircuitTarget,
pub(crate) dummy_proof: ProofWithPublicInputsTarget<D>,
pub(crate) dummy_verifier_data: VerifierCircuitTarget,
pub(crate) condition: BoolTarget,
pub(crate) dummy_circuit: CircuitData<F, C, D>,
}
impl<C: GenericConfig<D>, const D: usize> VerifierOnlyCircuitData<C, D> {
@ -107,17 +113,16 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub fn cyclic_recursion<C: GenericConfig<D, F = F>>(
&mut self,
condition: BoolTarget,
previous_virtual_public_inputs: &[Target],
common_data: &mut CommonCircuitData<F, D>,
) -> Result<CyclicRecursionTarget<D>>
proof_with_pis: &ProofWithPublicInputsTarget<D>,
common_data: &CommonCircuitData<F, D>,
) -> Result<CyclicRecursionTarget<F, C, D>>
where
C::Hasher: AlgebraicHasher<F>,
{
if self.verifier_data_public_input.is_none() {
self.add_verifier_data_public_input();
}
let verifier_data = self.verifier_data_public_input.clone().unwrap();
common_data.num_public_inputs = self.num_public_inputs();
let verifier_data = self
.verifier_data_public_input
.clone()
.expect("Must call add_verifier_data_public_inputs before cyclic recursion");
self.goal_common_data = Some(common_data.clone());
let dummy_verifier_data = VerifierCircuitTarget {
@ -125,10 +130,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
circuit_digest: self.add_virtual_hash(),
};
let proof = self.add_virtual_proof_with_pis::<C>(common_data);
let dummy_proof = self.add_virtual_proof_with_pis::<C>(common_data);
let pis = VerifierCircuitTarget::from_slice::<F, C, D>(&proof.public_inputs, common_data)?;
let pis = VerifierCircuitTarget::from_slice::<F, C, D>(
&proof_with_pis.public_inputs,
common_data,
)?;
// Connect previous verifier data to current one. This guarantees that every proof in the cycle uses the same verifier data.
self.connect_hashes(pis.circuit_digest, verifier_data.circuit_digest);
for (h0, h1) in pis
@ -140,17 +147,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.connect_hashes(*h0, *h1);
}
for (x, y) in previous_virtual_public_inputs
.iter()
.zip(&proof.public_inputs)
{
self.connect(*x, *y);
}
// Verify the real proof if `condition` is set to true, otherwise verify the dummy proof.
self.conditionally_verify_proof::<C>(
condition,
&proof,
proof_with_pis,
&verifier_data,
&dummy_proof,
&dummy_verifier_data,
@ -167,26 +167,29 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
Ok(CyclicRecursionTarget {
proof,
verifier_data: verifier_data.clone(),
proof: proof_with_pis.clone(),
verifier_data,
dummy_proof,
dummy_verifier_data,
condition,
dummy_circuit: dummy_circuit(common_data),
})
}
}
/// Set the targets in a `CyclicRecursionTarget` to their corresponding values in a `CyclicRecursionData`.
/// The `public_inputs` parameter let the caller specify certain public inputs (identified by their
/// indices) which should be given specific values. The rest will default to zero.
pub fn set_cyclic_recursion_data_target<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
pw: &mut PartialWitness<F>,
cyclic_recursion_data_target: &CyclicRecursionTarget<D>,
cyclic_recursion_data_target: &CyclicRecursionTarget<F, C, D>,
cyclic_recursion_data: &CyclicRecursionData<F, C, D>,
// Public inputs to set in the base case to seed some initial data.
public_inputs: &[F],
mut public_inputs: HashMap<usize, F>,
) -> Result<()>
where
C::Hasher: AlgebraicHasher<F>,
@ -204,36 +207,41 @@ where
cyclic_recursion_data.verifier_data,
);
} else {
let (dummy_proof, dummy_data) = dummy_proof::<F, C, D>(cyclic_recursion_data.common_data)?;
pw.set_bool_target(cyclic_recursion_data_target.condition, false);
let mut proof = dummy_proof.clone();
proof.public_inputs[0..public_inputs.len()].copy_from_slice(public_inputs);
let pis_len = proof.public_inputs.len();
// The circuit checks that the verifier data is the same throughout the cycle, so
// we set the verifier data to the "real" verifier data even though it's unused in the base case.
let num_cap = cyclic_recursion_data
let pis_len = cyclic_recursion_data_target
.dummy_circuit
.common
.num_public_inputs;
let cap_elements = cyclic_recursion_data
.common_data
.config
.fri_config
.num_cap_elements();
let s = pis_len - 4 - 4 * num_cap;
proof.public_inputs[s..s + 4]
.copy_from_slice(&cyclic_recursion_data.verifier_data.circuit_digest.elements);
for i in 0..num_cap {
proof.public_inputs[s + 4 * (1 + i)..s + 4 * (2 + i)].copy_from_slice(
&cyclic_recursion_data.verifier_data.constants_sigmas_cap.0[i].elements,
);
let start_vk_pis = pis_len - 4 - 4 * cap_elements;
// The circuit checks that the verifier data is the same throughout the cycle, so
// we set the verifier data to the "real" verifier data even though it's unused in the base case.
let verifier_data = &cyclic_recursion_data.verifier_data;
public_inputs.extend((start_vk_pis..).zip(verifier_data.circuit_digest.elements));
for i in 0..cap_elements {
let start = start_vk_pis + 4 + 4 * i;
public_inputs.extend((start..).zip(verifier_data.constants_sigmas_cap.0[i].elements));
}
let proof = dummy_proof(&cyclic_recursion_data_target.dummy_circuit, public_inputs)?;
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.proof, &proof);
pw.set_verifier_data_target(
&cyclic_recursion_data_target.verifier_data,
cyclic_recursion_data.verifier_data,
);
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.dummy_proof, &dummy_proof);
let dummy_p = dummy_proof(&cyclic_recursion_data_target.dummy_circuit, HashMap::new())?;
pw.set_proof_with_pis_target(&cyclic_recursion_data_target.dummy_proof, &dummy_p);
pw.set_verifier_data_target(
&cyclic_recursion_data_target.dummy_verifier_data,
&dummy_data,
&cyclic_recursion_data_target.dummy_circuit.verifier_only,
);
}
@ -264,11 +272,12 @@ where
#[cfg(test)]
mod tests {
use anyhow::Result;
use hashbrown::HashMap;
use crate::field::extension::Extendable;
use crate::field::types::{Field, PrimeField64};
use crate::gates::noop::NoopGate;
use crate::hash::hash_types::RichField;
use crate::hash::hash_types::{HashOutTarget, RichField};
use crate::hash::hashing::hash_n_to_hash_no_pad;
use crate::hash::poseidon::{PoseidonHash, PoseidonPermutation};
use crate::iop::witness::PartialWitness;
@ -315,6 +324,12 @@ mod tests {
builder.build::<C>().common
}
/// Uses cyclic recursion to build a hash chain.
/// The circuit has the following public input structure:
/// - Initial hash (4)
/// - Output for the tip of the hash chain (4)
/// - Chain length, i.e. the number of times the hash has been applied (1)
/// - VK for cyclic recursion (?)
#[test]
fn test_cyclic_recursion() -> Result<()> {
const D: usize = 2;
@ -322,55 +337,62 @@ mod tests {
type F = <C as GenericConfig<D>>::F;
let config = CircuitConfig::standard_recursion_config();
let mut pw = PartialWitness::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
let one = builder.one();
// Circuit that computes a repeated hash.
let initial_hash = builder.add_virtual_hash();
builder.register_public_inputs(&initial_hash.elements);
// Hash from the previous proof.
let old_hash = builder.add_virtual_hash();
// The input hash is either the previous hash or the initial hash depending on whether
// the last proof was a base case.
let input_hash = builder.add_virtual_hash();
let h = builder.hash_n_to_hash_no_pad::<PoseidonHash>(input_hash.elements.to_vec());
builder.register_public_inputs(&h.elements);
// Previous counter.
let old_counter = builder.add_virtual_target();
let new_counter = builder.add_virtual_public_input();
let old_pis = [
initial_hash.elements.as_slice(),
old_hash.elements.as_slice(),
[old_counter].as_slice(),
]
.concat();
let current_hash_in = builder.add_virtual_hash();
let current_hash_out =
builder.hash_n_to_hash_no_pad::<PoseidonHash>(current_hash_in.elements.to_vec());
builder.register_public_inputs(&current_hash_out.elements);
let counter = builder.add_virtual_public_input();
let mut common_data = common_data_for_recursion::<F, C, D>();
builder.add_verifier_data_public_inputs();
common_data.num_public_inputs = builder.num_public_inputs();
let condition = builder.add_virtual_bool_target_safe();
// Add cyclic recursion gadget.
// Unpack inner proof's public inputs.
let inner_proof_with_pis = builder.add_virtual_proof_with_pis::<C>(&common_data);
let inner_pis = &inner_proof_with_pis.public_inputs;
let inner_initial_hash = HashOutTarget::try_from(&inner_pis[0..4]).unwrap();
let inner_latest_hash = HashOutTarget::try_from(&inner_pis[4..8]).unwrap();
let inner_counter = inner_pis[8];
// Connect our initial hash to that of our inner proof. (If there is no inner proof, the
// initial hash will be unconstrained, which is intentional.)
builder.connect_hashes(initial_hash, inner_initial_hash);
// The input hash is the previous hash output if we have an inner proof, or the initial hash
// if this is the base case.
let actual_hash_in = builder.select_hash(condition, inner_latest_hash, initial_hash);
builder.connect_hashes(current_hash_in, actual_hash_in);
// Our chain length will be inner_counter + 1 if we have an inner proof, or 1 if not.
let new_counter = builder.mul_add(condition.target, inner_counter, one);
builder.connect(counter, new_counter);
let cyclic_data_target =
builder.cyclic_recursion::<C>(condition, &old_pis, &mut common_data)?;
let input_hash_bis =
builder.select_hash(cyclic_data_target.condition, old_hash, initial_hash);
builder.connect_hashes(input_hash, input_hash_bis);
// New counter is the previous counter +1 if the previous proof wasn't a base case.
let new_counter_bis = builder.add(old_counter, condition.target);
builder.connect(new_counter, new_counter_bis);
builder.cyclic_recursion::<C>(condition, &inner_proof_with_pis, &common_data)?;
let cyclic_circuit_data = builder.build::<C>();
let mut pw = PartialWitness::new();
let cyclic_recursion_data = CyclicRecursionData {
proof: &None, // Base case: We don't have a proof to put here yet.
verifier_data: &cyclic_circuit_data.verifier_only,
common_data: &cyclic_circuit_data.common,
};
let initial_hash = [F::ZERO, F::ONE, F::TWO, F::from_canonical_usize(3)];
let initial_hash_pis = initial_hash.into_iter().enumerate().collect();
set_cyclic_recursion_data_target(
&mut pw,
&cyclic_data_target,
&cyclic_recursion_data,
&initial_hash,
initial_hash_pis,
)?;
let proof = cyclic_circuit_data.prove(pw)?;
check_cyclic_proof_verifier_data(
@ -391,7 +413,7 @@ mod tests {
&mut pw,
&cyclic_data_target,
&cyclic_recursion_data,
&[],
HashMap::new(),
)?;
let proof = cyclic_circuit_data.prove(pw)?;
check_cyclic_proof_verifier_data(
@ -412,7 +434,7 @@ mod tests {
&mut pw,
&cyclic_data_target,
&cyclic_recursion_data,
&[],
HashMap::new(),
)?;
let proof = cyclic_circuit_data.prove(pw)?;
check_cyclic_proof_verifier_data(
@ -425,17 +447,20 @@ mod tests {
let initial_hash = &proof.public_inputs[..4];
let hash = &proof.public_inputs[4..8];
let counter = proof.public_inputs[8];
let mut h: [F; 4] = initial_hash.try_into().unwrap();
assert_eq!(
hash,
core::iter::repeat_with(|| {
h = hash_n_to_hash_no_pad::<F, PoseidonPermutation>(&h).elements;
h
})
.nth(counter.to_canonical_u64() as usize)
.unwrap()
let expected_hash: [F; 4] = iterate_poseidon(
initial_hash.try_into().unwrap(),
counter.to_canonical_u64() as usize,
);
assert_eq!(hash, expected_hash);
cyclic_circuit_data.verify(proof)
}
fn iterate_poseidon<F: RichField>(initial_state: [F; 4], n: usize) -> [F; 4] {
let mut current = initial_state;
for _ in 0..n {
current = hash_n_to_hash_no_pad::<F, PoseidonPermutation>(&current).elements;
}
current
}
}

View File

@ -0,0 +1,67 @@
use alloc::vec;
use hashbrown::HashMap;
use plonky2_field::extension::Extendable;
use plonky2_util::ceil_div_usize;
use crate::gates::noop::NoopGate;
use crate::hash::hash_types::RichField;
use crate::iop::witness::{PartialWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::{CircuitData, CommonCircuitData};
use crate::plonk::config::GenericConfig;
use crate::plonk::proof::ProofWithPublicInputs;
/// Generate a proof for a dummy circuit. The `public_inputs` parameter let the caller specify
/// certain public inputs (identified by their indices) which should be given specific values.
/// The rest will default to zero.
pub(crate) fn dummy_proof<F, C, const D: usize>(
circuit: &CircuitData<F, C, D>,
nonzero_public_inputs: HashMap<usize, F>,
) -> anyhow::Result<ProofWithPublicInputs<F, C, D>>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
let mut pw = PartialWitness::new();
for i in 0..circuit.common.num_public_inputs {
let pi = nonzero_public_inputs.get(&i).copied().unwrap_or_default();
pw.set_target(circuit.prover_only.public_inputs[i], pi);
}
circuit.prove(pw)
}
/// Generate a circuit matching a given `CommonCircuitData`.
pub(crate) fn dummy_circuit<
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
>(
common_data: &CommonCircuitData<F, D>,
) -> CircuitData<F, C, D> {
let config = common_data.config.clone();
assert!(
!common_data.config.zero_knowledge,
"Degree calculation can be off if zero-knowledge is on."
);
// 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 degree = common_data.degree();
let num_noop_gate = degree - ceil_div_usize(common_data.num_public_inputs, 8) - 2;
let mut builder = CircuitBuilder::<F, D>::new(config);
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 {
builder.add_virtual_public_input();
}
let circuit = builder.build::<C>();
assert_eq!(&circuit.common, common_data);
circuit
}

View File

@ -1,3 +1,4 @@
pub mod conditional_recursive_verifier;
pub mod cyclic_recursion;
pub(crate) mod dummy_circuit;
pub mod recursive_verifier;