A few more cyclic recursion changes

In preparation for adding the zkEVM aggregation circuit. Mainly,

- Adds a `WitnessWrite` trait, a sub-trait of `Witness`, and move the write methods to it. `GeneratedValues` impls `WitnessWrite`, which lets generators like `DummyProofGenerator` access all our write methods like `set_proof_with_pis_target`. Also removes some duplication.

- Remove `set_cyclic_recursion_data_target` - now that dummy proof data is automatically populated, all that remains is populating `condition` and the cyclic proof + VK. I think it's easy enough for callers to do this; the steps are the same as with `conditionally_verify_proof`. This way there's no cyclic-recursion-specific API to learn about.

- Split `cyclic_recursion` into two variants, one which checks the current circuit or a dummy, and a more general one which checks the current circuit or some other circuit. We can use the latter to build a more efficient aggregation circuit, where we check another aggregation proof or an EVM proof, with no dummy proofs involved.
This commit is contained in:
Daniel Lubarov 2022-11-22 07:37:43 -08:00
parent f58556c28d
commit 92974aa105
51 changed files with 425 additions and 445 deletions

View File

@ -8,7 +8,7 @@ use plonky2::field::types::{Field, PrimeField};
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::PartitionWitness;
use plonky2::iop::witness::{PartitionWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::curve::glv::{decompose_secp256k1_scalar, GLV_BETA, GLV_S};

View File

@ -8,7 +8,7 @@ use plonky2::field::types::{Field, PrimeField};
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::PartitionWitness;
use plonky2::iop::witness::{PartitionWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::util::ceil_div_usize;
use plonky2_u32::gadgets::arithmetic_u32::{CircuitBuilderU32, U32Target};

View File

@ -18,7 +18,7 @@ use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2::util::reducing::ReducingFactorTarget;
use plonky2::with_context;
use crate::all_stark::{AllStark, Table, NUM_TABLES};
use crate::all_stark::{all_cross_table_lookups, AllStark, Table, NUM_TABLES};
use crate::config::StarkConfig;
use crate::constraint_consumer::RecursiveConstraintConsumer;
use crate::cpu::cpu_stark::CpuStark;
@ -162,7 +162,6 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
builder: &mut CircuitBuilder<F, D>,
recursive_all_proof_target: RecursiveAllProofTargetWithData<D>,
verifier_data: &[VerifierCircuitData<F, C, D>; NUM_TABLES],
cross_table_lookups: Vec<CrossTableLookup<F>>,
inner_config: &StarkConfig,
) where
[(); C::Hasher::HASH_SIZE]:,
@ -219,7 +218,7 @@ impl<F: RichField + Extendable<D>, C: GenericConfig<D, F = F>, const D: usize>
let degrees_bits = std::array::from_fn(|i| verifier_data[i].common.degree_bits());
verify_cross_table_lookups_circuit::<F, C, D>(
builder,
cross_table_lookups,
all_cross_table_lookups(),
pis.map(|p| p.ctl_zs_last),
degrees_bits,
ctl_challenges,
@ -842,7 +841,7 @@ pub(crate) mod tests {
use plonky2::hash::hash_types::RichField;
use plonky2::hash::hashing::SPONGE_WIDTH;
use plonky2::iop::challenger::RecursiveChallenger;
use plonky2::iop::witness::{PartialWitness, Witness};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher};

View File

@ -3,7 +3,7 @@ use plonky2::field::extension::{Extendable, FieldExtension};
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
use plonky2::field::types::{Field, Sample};
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, Witness};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, Hasher};

View File

@ -14,7 +14,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -12,7 +12,7 @@ use log::{info, Level, LevelFilter};
use maybe_rayon::rayon;
use plonky2::gates::noop::NoopGate;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, Witness};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{
CircuitConfig, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use plonky2::field::types::Field;
use plonky2::iop::witness::{PartialWitness, Witness};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};

View File

@ -1,6 +1,6 @@
use anyhow::Result;
use plonky2::field::types::Field;
use plonky2::iop::witness::{PartialWitness, Witness};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};

View File

@ -5,7 +5,7 @@ use plonky2::field::types::{PrimeField, Sample};
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness};
use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};

View File

@ -3,7 +3,7 @@ use itertools::Itertools;
use crate::field::extension::Extendable;
use crate::fri::proof::{FriProof, FriProofTarget};
use crate::hash::hash_types::RichField;
use crate::iop::witness::Witness;
use crate::iop::witness::WitnessWrite;
use crate::plonk::config::AlgebraicHasher;
/// Set the targets in a `FriProofTarget` to their corresponding values in a `FriProof`.
@ -13,7 +13,7 @@ pub fn set_fri_proof_target<F, W, H, const D: usize>(
fri_proof: &FriProof<F, H, D>,
) where
F: RichField + Extendable<D>,
W: Witness<F> + ?Sized,
W: WitnessWrite<F> + ?Sized,
H: AlgebraicHasher<F>,
{
witness.set_target(fri_proof_target.pow_witness, fri_proof.pow_witness);

View File

@ -9,7 +9,7 @@ use crate::gates::exponentiation::ExponentiationGate;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {

View File

@ -10,7 +10,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::util::bits_u64;
@ -573,7 +573,7 @@ mod tests {
use crate::field::extension::algebra::ExtensionAlgebra;
use crate::field::types::Sample;
use crate::iop::ext_target::ExtensionAlgebraTarget;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
@ -588,7 +588,7 @@ mod tests {
let config = CircuitConfig::standard_recursion_config();
let mut pw = PartialWitness::new();
let mut pw = PartialWitness::<F>::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
let vs = FF::rand_vec(3);

View File

@ -5,7 +5,7 @@ use crate::field::extension::Extendable;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {

View File

@ -41,7 +41,7 @@ mod tests {
use anyhow::Result;
use crate::field::types::Sample;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
@ -54,7 +54,7 @@ mod tests {
type F = <C as GenericConfig<D>>::F;
type FF = <C as GenericConfig<D>>::FE;
let config = CircuitConfig::standard_recursion_config();
let mut pw = PartialWitness::new();
let mut pw = PartialWitness::<F>::new();
let mut builder = CircuitBuilder::<F, D>::new(config);
let (x, y) = (FF::rand(), FF::rand());

View File

@ -10,7 +10,7 @@ use crate::gates::base_sum::BaseSumGate;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::util::log_floor;

View File

@ -6,7 +6,7 @@ use crate::gates::base_sum::BaseSumGate;
use crate::hash::hash_types::RichField;
use crate::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::util::ceil_div_usize;

View File

@ -12,7 +12,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::vars::{

View File

@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -14,7 +14,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};

View File

@ -16,7 +16,7 @@ use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::vars::{

View File

@ -8,7 +8,7 @@ use crate::field::polynomial::{PolynomialCoeffs, PolynomialValues};
use crate::field::types::{Field, Sample};
use crate::gates::gate::Gate;
use crate::hash::hash_types::{HashOut, RichField};
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::GenericConfig;

View File

@ -18,7 +18,7 @@ use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -19,7 +19,7 @@ use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -17,7 +17,7 @@ use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};
@ -514,7 +514,7 @@ mod tests {
use crate::hash::poseidon::Poseidon;
use crate::iop::generator::generate_partial_witness;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};

View File

@ -16,7 +16,7 @@ use crate::hash::poseidon::Poseidon;
use crate::iop::ext_target::{ExtensionAlgebraTarget, ExtensionTarget};
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -17,7 +17,7 @@ use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::vars::{

View File

@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -11,7 +11,7 @@ use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartitionWitness, Witness};
use crate::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -2,6 +2,7 @@ use alloc::vec;
use alloc::vec::Vec;
use anyhow::{ensure, Result};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use crate::field::extension::Extendable;
@ -145,6 +146,12 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.connect(x.elements[i], y.elements[i]);
}
}
pub fn connect_merkle_caps(&mut self, x: &MerkleCapTarget, y: &MerkleCapTarget) {
for (h0, h1) in x.0.iter().zip_eq(&y.0) {
self.connect_hashes(*h0, *h1);
}
}
}
#[cfg(test)]
@ -156,7 +163,7 @@ mod tests {
use super::*;
use crate::field::types::Field;
use crate::hash::merkle_tree::MerkleTree;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};

View File

@ -3,13 +3,13 @@ use alloc::vec::Vec;
use core::fmt::Debug;
use core::marker::PhantomData;
use crate::field::extension::{Extendable, FieldExtension};
use crate::field::extension::Extendable;
use crate::field::types::Field;
use crate::hash::hash_types::{HashOut, HashOutTarget, RichField};
use crate::hash::hash_types::RichField;
use crate::iop::ext_target::ExtensionTarget;
use crate::iop::target::{BoolTarget, Target};
use crate::iop::target::Target;
use crate::iop::wire::Wire;
use crate::iop::witness::{PartialWitness, PartitionWitness, Witness};
use crate::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
use crate::plonk::circuit_data::{CommonCircuitData, ProverOnlyCircuitData};
use crate::plonk::config::GenericConfig;
@ -120,6 +120,12 @@ impl<F: Field> From<Vec<(Target, F)>> for GeneratedValues<F> {
}
}
impl<F: Field> WitnessWrite<F> for GeneratedValues<F> {
fn set_target(&mut self, target: Target, value: F) {
self.target_values.push((target, value));
}
}
impl<F: Field> GeneratedValues<F> {
pub fn with_capacity(capacity: usize) -> Self {
Vec::with_capacity(capacity).into()
@ -137,10 +143,6 @@ impl<F: Field> GeneratedValues<F> {
vec![(target, value)].into()
}
pub fn clear(&mut self) {
self.target_values.clear();
}
pub fn singleton_extension_target<const D: usize>(
et: ExtensionTarget<D>,
value: F::Extension,
@ -152,56 +154,6 @@ impl<F: Field> GeneratedValues<F> {
witness.set_extension_target(et, value);
witness
}
pub fn set_target(&mut self, target: Target, value: F) {
self.target_values.push((target, value))
}
pub fn set_bool_target(&mut self, target: BoolTarget, value: bool) {
self.set_target(target.target, F::from_bool(value))
}
pub fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut<F>) {
ht.elements
.iter()
.zip(value.elements)
.for_each(|(&t, x)| self.set_target(t, x));
}
pub fn set_extension_target<const D: usize>(
&mut self,
et: ExtensionTarget<D>,
value: F::Extension,
) where
F: RichField + Extendable<D>,
{
let limbs = value.to_basefield_array();
(0..D).for_each(|i| {
self.set_target(et.0[i], limbs[i]);
});
}
pub fn set_wire(&mut self, wire: Wire, value: F) {
self.set_target(Target::Wire(wire), value)
}
pub fn set_wires<W>(&mut self, wires: W, values: &[F])
where
W: IntoIterator<Item = Wire>,
{
// If we used itertools, we could use zip_eq for extra safety.
for (wire, &value) in wires.into_iter().zip(values) {
self.set_wire(wire, value);
}
}
pub fn set_ext_wires<W, const D: usize>(&mut self, wires: W, value: F::Extension)
where
F: RichField + Extendable<D>,
W: IntoIterator<Item = Wire>,
{
self.set_wires(wires, &value.to_basefield_array());
}
}
/// A generator which runs once after a list of dependencies is present in the witness.

View File

@ -17,71 +17,9 @@ use crate::plonk::circuit_data::{VerifierCircuitTarget, VerifierOnlyCircuitData}
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{Proof, ProofTarget, ProofWithPublicInputs, ProofWithPublicInputsTarget};
/// A witness holds information on the values of targets in a circuit.
pub trait Witness<F: Field> {
fn try_get_target(&self, target: Target) -> Option<F>;
pub trait WitnessWrite<F: Field> {
fn set_target(&mut self, target: Target, value: F);
fn get_target(&self, target: Target) -> F {
self.try_get_target(target).unwrap()
}
fn get_targets(&self, targets: &[Target]) -> Vec<F> {
targets.iter().map(|&t| self.get_target(t)).collect()
}
fn get_extension_target<const D: usize>(&self, et: ExtensionTarget<D>) -> F::Extension
where
F: RichField + Extendable<D>,
{
F::Extension::from_basefield_array(
self.get_targets(&et.to_target_array()).try_into().unwrap(),
)
}
fn get_extension_targets<const D: usize>(&self, ets: &[ExtensionTarget<D>]) -> Vec<F::Extension>
where
F: RichField + Extendable<D>,
{
ets.iter()
.map(|&et| self.get_extension_target(et))
.collect()
}
fn get_bool_target(&self, target: BoolTarget) -> bool {
let value = self.get_target(target.target);
if value.is_zero() {
return false;
}
if value.is_one() {
return true;
}
panic!("not a bool")
}
fn get_hash_target(&self, ht: HashOutTarget) -> HashOut<F> {
HashOut {
elements: self.get_targets(&ht.elements).try_into().unwrap(),
}
}
fn get_wire(&self, wire: Wire) -> F {
self.get_target(Target::Wire(wire))
}
fn try_get_wire(&self, wire: Wire) -> Option<F> {
self.try_get_target(Target::Wire(wire))
}
fn contains(&self, target: Target) -> bool {
self.try_get_target(target).is_some()
}
fn contains_all(&self, targets: &[Target]) -> bool {
targets.iter().all(|&t| self.contains(t))
}
fn set_hash_target(&mut self, ht: HashOutTarget, value: HashOut<F>) {
ht.elements
.iter()
@ -239,6 +177,70 @@ pub trait Witness<F: Field> {
}
}
/// A witness holds information on the values of targets in a circuit.
pub trait Witness<F: Field>: WitnessWrite<F> {
fn try_get_target(&self, target: Target) -> Option<F>;
fn get_target(&self, target: Target) -> F {
self.try_get_target(target).unwrap()
}
fn get_targets(&self, targets: &[Target]) -> Vec<F> {
targets.iter().map(|&t| self.get_target(t)).collect()
}
fn get_extension_target<const D: usize>(&self, et: ExtensionTarget<D>) -> F::Extension
where
F: RichField + Extendable<D>,
{
F::Extension::from_basefield_array(
self.get_targets(&et.to_target_array()).try_into().unwrap(),
)
}
fn get_extension_targets<const D: usize>(&self, ets: &[ExtensionTarget<D>]) -> Vec<F::Extension>
where
F: RichField + Extendable<D>,
{
ets.iter()
.map(|&et| self.get_extension_target(et))
.collect()
}
fn get_bool_target(&self, target: BoolTarget) -> bool {
let value = self.get_target(target.target);
if value.is_zero() {
return false;
}
if value.is_one() {
return true;
}
panic!("not a bool")
}
fn get_hash_target(&self, ht: HashOutTarget) -> HashOut<F> {
HashOut {
elements: self.get_targets(&ht.elements).try_into().unwrap(),
}
}
fn get_wire(&self, wire: Wire) -> F {
self.get_target(Target::Wire(wire))
}
fn try_get_wire(&self, wire: Wire) -> Option<F> {
self.try_get_target(Target::Wire(wire))
}
fn contains(&self, target: Target) -> bool {
self.try_get_target(target).is_some()
}
fn contains_all(&self, targets: &[Target]) -> bool {
targets.iter().all(|&t| self.contains(t))
}
}
#[derive(Clone, Debug)]
pub struct MatrixWitness<F: Field> {
pub(crate) wire_values: Vec<Vec<F>>,
@ -263,23 +265,25 @@ impl<F: Field> PartialWitness<F> {
}
}
impl<F: Field> Witness<F> for PartialWitness<F> {
fn try_get_target(&self, target: Target) -> Option<F> {
self.target_values.get(&target).copied()
}
impl<F: Field> WitnessWrite<F> for PartialWitness<F> {
fn set_target(&mut self, target: Target, value: F) {
let opt_old_value = self.target_values.insert(target, value);
if let Some(old_value) = opt_old_value {
assert_eq!(
old_value, value,
"Target {:?} was set twice with different values",
target
value, old_value,
"Target {:?} was set twice with different values: {} != {}",
target, old_value, value
);
}
}
}
impl<F: Field> Witness<F> for PartialWitness<F> {
fn try_get_target(&self, target: Target) -> Option<F> {
self.target_values.get(&target).copied()
}
}
/// `PartitionWitness` holds a disjoint-set forest of the targets respecting a circuit's copy constraints.
/// The value of a target is defined to be the value of its root in the forest.
#[derive(Clone)]
@ -308,8 +312,8 @@ impl<'a, F: Field> PartitionWitness<'a, F> {
if let Some(old_value) = *rep_value {
assert_eq!(
value, old_value,
"Partition containing {:?} was set twice with different values",
target
"Partition containing {:?} was set twice with different values: {} != {}",
target, old_value, value
);
None
} else {
@ -337,13 +341,15 @@ impl<'a, F: Field> PartitionWitness<'a, F> {
}
}
impl<'a, F: Field> WitnessWrite<F> for PartitionWitness<'a, F> {
fn set_target(&mut self, target: Target, value: F) {
self.set_target_returning_rep(target, value);
}
}
impl<'a, F: Field> Witness<F> for PartitionWitness<'a, F> {
fn try_get_target(&self, target: Target) -> Option<F> {
let rep_index = self.representative_map[self.target_index(target)];
self.values[rep_index]
}
fn set_target(&mut self, target: Target, value: F) {
self.set_target_returning_rep(target, value);
}
}

View File

@ -40,7 +40,7 @@ use crate::plonk::circuit_data::{
CircuitConfig, CircuitData, CommonCircuitData, ProverCircuitData, ProverOnlyCircuitData,
VerifierCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
};
use crate::plonk::config::{GenericConfig, GenericHashOut, Hasher};
use crate::plonk::config::{AlgebraicHasher, GenericConfig, GenericHashOut, Hasher};
use crate::plonk::copy_constraint::CopyConstraint;
use crate::plonk::permutation_argument::Forest;
use crate::plonk::plonk_common::PlonkOracle;
@ -247,7 +247,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// 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 fn add_verifier_data_public_inputs(&mut self) {
pub fn add_verifier_data_public_inputs(&mut self) -> VerifierCircuitTarget {
assert!(
self.verifier_data_public_input.is_none(),
"add_verifier_data_public_inputs only needs to be called once"
@ -263,7 +263,8 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
self.register_public_inputs(&verifier_data.constants_sigmas_cap.0[i].elements);
}
self.verifier_data_public_input = Some(verifier_data);
self.verifier_data_public_input = Some(verifier_data.clone());
verifier_data
}
/// Adds a gate to the circuit, and returns its index.
@ -436,6 +437,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
MerkleCapTarget(cap.0.iter().map(|h| self.constant_hash(*h)).collect())
}
pub fn constant_verifier_data<C: GenericConfig<D, F = F>>(
&mut self,
verifier_data: &VerifierOnlyCircuitData<C, D>,
) -> VerifierCircuitTarget
where
C::Hasher: AlgebraicHasher<F>,
{
VerifierCircuitTarget {
constants_sigmas_cap: self.constant_merkle_cap(&verifier_data.constants_sigmas_cap),
circuit_digest: self.constant_hash(verifier_data.circuit_digest),
}
}
/// If the given target is a constant (i.e. it was created by the `constant(F)` method), returns
/// its constant value. Otherwise, returns `None`.
pub fn target_as_constant(&self, target: Target) -> Option<F> {

View File

@ -470,7 +470,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CommonCircuitData<F, D> {
/// is intentionally missing certain fields, such as `CircuitConfig`, because we support only a
/// limited form of dynamic inner circuits. We can't practically make things like the wire count
/// dynamic, at least not without setting a maximum wire count and paying for the worst case.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct VerifierCircuitTarget {
/// A commitment to each constant polynomial and each permutation polynomial.
pub constants_sigmas_cap: MerkleCapTarget,

View File

@ -31,6 +31,55 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
) where
C::Hasher: AlgebraicHasher<F>,
{
let selected_proof =
self.select_proof_with_pis(condition, proof_with_pis0, proof_with_pis1);
let selected_verifier_data = VerifierCircuitTarget {
constants_sigmas_cap: self.select_cap(
condition,
&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::<C>(&selected_proof, &selected_verifier_data, inner_common_data);
}
/// Conditionally verify a proof with a new generated dummy proof.
pub fn conditionally_verify_proof_or_dummy<C: GenericConfig<D, F = F> + 'static>(
&mut self,
condition: BoolTarget,
proof_with_pis: &ProofWithPublicInputsTarget<D>,
inner_verifier_data: &VerifierCircuitTarget,
inner_common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
self.dummy_proof_and_vk::<C>(inner_common_data)?;
self.conditionally_verify_proof::<C>(
condition,
proof_with_pis,
inner_verifier_data,
&dummy_proof_with_pis_target,
&dummy_verifier_data_target,
inner_common_data,
);
Ok(())
}
/// Computes `if b { proof_with_pis0 } else { proof_with_pis1 }`.
fn select_proof_with_pis(
&mut self,
b: BoolTarget,
proof_with_pis0: &ProofWithPublicInputsTarget<D>,
proof_with_pis1: &ProofWithPublicInputsTarget<D>,
) -> ProofWithPublicInputsTarget<D> {
let ProofWithPublicInputsTarget {
proof:
ProofTarget {
@ -53,20 +102,19 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
},
public_inputs: public_inputs1,
} = proof_with_pis1;
let selected_proof = with_context!(self, "select proof", {
let selected_wires_cap = self.select_cap(condition, wires_cap0, wires_cap1);
with_context!(self, "select proof", {
let selected_wires_cap = self.select_cap(b, wires_cap0, wires_cap1);
let selected_plonk_zs_partial_products_cap = self.select_cap(
condition,
b,
plonk_zs_partial_products_cap0,
plonk_zs_partial_products_cap1,
);
let selected_quotient_polys_cap =
self.select_cap(condition, quotient_polys_cap0, quotient_polys_cap1);
let selected_openings = self.select_opening_set(condition, openings0, openings1);
self.select_cap(b, quotient_polys_cap0, quotient_polys_cap1);
let selected_openings = self.select_opening_set(b, openings0, openings1);
let selected_opening_proof =
self.select_opening_proof(condition, opening_proof0, opening_proof1);
let selected_public_inputs = self.select_vec(condition, public_inputs0, public_inputs1);
self.select_opening_proof(b, opening_proof0, opening_proof1);
let selected_public_inputs = self.select_vec(b, public_inputs0, public_inputs1);
ProofWithPublicInputsTarget {
proof: ProofTarget {
wires_cap: selected_wires_cap,
@ -77,52 +125,10 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
},
public_inputs: selected_public_inputs,
}
});
let selected_verifier_data = VerifierCircuitTarget {
constants_sigmas_cap: self.select_cap(
condition,
&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::<C>(&selected_proof, &selected_verifier_data, inner_common_data);
}
/// 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, D>,
) -> (ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)
where
C::Hasher: AlgebraicHasher<F>,
{
let dummy_proof = self.add_virtual_proof_with_pis::<C>(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::<C>(
condition,
proof_with_pis,
inner_verifier_data,
&dummy_proof,
&dummy_verifier_data,
inner_common_data,
);
(dummy_proof, dummy_verifier_data)
})
}
/// Computes `if b { v0 } else { v1 }`.
fn select_vec(&mut self, b: BoolTarget, v0: &[Target], v1: &[Target]) -> Vec<Target> {
v0.iter()
.zip_eq(v1)
@ -130,6 +136,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.collect()
}
/// Computes `if b { h0 } else { h1 }`.
pub(crate) fn select_hash(
&mut self,
b: BoolTarget,
@ -141,6 +148,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { cap0 } else { cap1 }`.
fn select_cap(
&mut self,
b: BoolTarget,
@ -157,6 +165,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
)
}
/// Computes `if b { v0 } else { v1 }`.
fn select_vec_cap(
&mut self,
b: BoolTarget,
@ -169,6 +178,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.collect()
}
/// Computes `if b { os0 } else { os1 }`.
fn select_opening_set(
&mut self,
b: BoolTarget,
@ -186,6 +196,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { v0 } else { v1 }`.
fn select_vec_ext(
&mut self,
b: BoolTarget,
@ -198,6 +209,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.collect()
}
/// Computes `if b { proof0 } else { proof1 }`.
fn select_opening_proof(
&mut self,
b: BoolTarget,
@ -224,6 +236,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { qr0 } else { qr1 }`.
fn select_query_round(
&mut self,
b: BoolTarget,
@ -240,6 +253,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { v0 } else { v1 }`.
fn select_vec_query_round(
&mut self,
b: BoolTarget,
@ -252,6 +266,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.collect()
}
/// Computes `if b { proof0 } else { proof1 }`.
fn select_initial_tree_proof(
&mut self,
b: BoolTarget,
@ -273,6 +288,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { proof0 } else { proof1 }`.
fn select_merkle_proof(
&mut self,
b: BoolTarget,
@ -289,6 +305,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { qs0 } else { qs01 }`.
fn select_query_step(
&mut self,
b: BoolTarget,
@ -301,6 +318,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
}
}
/// Computes `if b { v0 } else { v1 }`.
fn select_vec_query_step(
&mut self,
b: BoolTarget,
@ -322,7 +340,7 @@ mod tests {
use super::*;
use crate::field::types::Sample;
use crate::gates::noop::NoopGate;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};

View File

@ -1,48 +1,17 @@
#![allow(clippy::int_plus_one)] // Makes more sense for some inequalities below.
use alloc::vec;
use anyhow::{ensure, Result};
use hashbrown::HashMap;
use itertools::Itertools;
use crate::field::extension::Extendable;
use crate::gates::noop::NoopGate;
use crate::hash::hash_types::{HashOut, HashOutTarget, MerkleCapTarget, RichField};
use crate::hash::merkle_tree::MerkleCap;
use crate::iop::target::{BoolTarget, Target};
use crate::iop::witness::{PartialWitness, Witness};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::{
CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
};
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use crate::recursion::dummy_circuit::{dummy_circuit, dummy_proof};
pub struct CyclicRecursionData<
'a,
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
const D: usize,
> {
proof: &'a Option<ProofWithPublicInputs<F, C, D>>,
verifier_data: &'a VerifierOnlyCircuitData<C, D>,
common_data: &'a CommonCircuitData<F, D>,
}
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> {
fn from_slice(slice: &[C::F], common_data: &CommonCircuitData<C::F, D>) -> Result<Self>
@ -98,7 +67,7 @@ impl VerifierCircuitTarget {
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// If `condition` is true, recursively verify a proof for the same circuit as the one we're
/// currently building.
/// currently building. Otherwise, verify `other_proof_with_pis`.
///
/// For a typical IVC use case, `condition` will be false for the very first proof in a chain,
/// i.e. the base case.
@ -110,12 +79,14 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
/// that the verification key matches.
///
/// WARNING: Do not register any public input after calling this! TODO: relax this
pub fn cyclic_recursion<C: GenericConfig<D, F = F>>(
pub fn conditionally_verify_cyclic_proof<C: GenericConfig<D, F = F>>(
&mut self,
condition: BoolTarget,
proof_with_pis: &ProofWithPublicInputsTarget<D>,
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
other_proof_with_pis: &ProofWithPublicInputsTarget<D>,
other_verifier_data: &VerifierCircuitTarget,
common_data: &CommonCircuitData<F, D>,
) -> Result<CyclicRecursionTarget<F, C, D>>
) -> Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
@ -123,131 +94,67 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
.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 {
constants_sigmas_cap: self.add_virtual_cap(self.config.fri_config.cap_height),
circuit_digest: self.add_virtual_hash(),
};
if let Some(existing_common_data) = self.goal_common_data.as_ref() {
assert_eq!(existing_common_data, common_data);
} else {
self.goal_common_data = Some(common_data.clone());
}
let dummy_proof = self.add_virtual_proof_with_pis::<C>(common_data);
let pis = VerifierCircuitTarget::from_slice::<F, C, D>(
&proof_with_pis.public_inputs,
let inner_cyclic_pis = VerifierCircuitTarget::from_slice::<F, C, D>(
&cyclic_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
.constants_sigmas_cap
.0
.iter()
.zip_eq(&verifier_data.constants_sigmas_cap.0)
{
self.connect_hashes(*h0, *h1);
}
self.connect_hashes(
inner_cyclic_pis.circuit_digest,
verifier_data.circuit_digest,
);
self.connect_merkle_caps(
&inner_cyclic_pis.constants_sigmas_cap,
&verifier_data.constants_sigmas_cap,
);
// Verify the real proof if `condition` is set to true, otherwise verify the dummy proof.
// Verify the cyclic proof if `condition` is set to true, otherwise verify the other proof.
self.conditionally_verify_proof::<C>(
condition,
proof_with_pis,
cyclic_proof_with_pis,
&verifier_data,
&dummy_proof,
&dummy_verifier_data,
other_proof_with_pis,
other_verifier_data,
common_data,
);
// Make sure we have enough gates to match `common_data`.
while self.num_gates() < (common_data.degree() / 2) {
self.add_gate(NoopGate, vec![]);
}
// Make sure we have every gate to match `common_data`.
for g in &common_data.gates {
self.add_gate_to_gate_set(g.clone());
}
Ok(CyclicRecursionTarget {
proof: proof_with_pis.clone(),
verifier_data,
dummy_proof,
dummy_verifier_data,
Ok(())
}
pub fn conditionally_verify_cyclic_proof_or_dummy<C: GenericConfig<D, F = F> + 'static>(
&mut self,
condition: BoolTarget,
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
common_data: &CommonCircuitData<F, D>,
) -> Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
self.dummy_proof_and_vk::<C>(common_data)?;
self.conditionally_verify_cyclic_proof::<C>(
condition,
dummy_circuit: dummy_circuit(common_data),
})
cyclic_proof_with_pis,
&dummy_proof_with_pis_target,
&dummy_verifier_data_target,
common_data,
)?;
Ok(())
}
}
/// 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<F, C, D>,
cyclic_recursion_data: &CyclicRecursionData<F, C, D>,
// Public inputs to set in the base case to seed some initial data.
mut public_inputs: HashMap<usize, F>,
) -> Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
if let Some(proof) = cyclic_recursion_data.proof {
pw.set_bool_target(cyclic_recursion_data_target.condition, true);
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, proof);
pw.set_verifier_data_target(
&cyclic_recursion_data_target.dummy_verifier_data,
cyclic_recursion_data.verifier_data,
);
} else {
pw.set_bool_target(cyclic_recursion_data_target.condition, false);
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 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,
);
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,
&cyclic_recursion_data_target.dummy_circuit.verifier_only,
);
}
Ok(())
}
/// Additional checks to be performed on a cyclic recursive proof in addition to verifying the proof.
/// Checks that the purported verifier data in the public inputs match the real verifier data.
pub fn check_cyclic_proof_verifier_data<
@ -272,7 +179,6 @@ where
#[cfg(test)]
mod tests {
use anyhow::Result;
use hashbrown::HashMap;
use crate::field::extension::Extendable;
use crate::field::types::{Field, PrimeField64};
@ -280,13 +186,12 @@ mod tests {
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;
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::{CircuitConfig, CommonCircuitData, VerifierCircuitTarget};
use crate::plonk::config::{AlgebraicHasher, GenericConfig, PoseidonGoldilocksConfig};
use crate::recursion::cyclic_recursion::{
check_cyclic_proof_verifier_data, set_cyclic_recursion_data_target, CyclicRecursionData,
};
use crate::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
use crate::recursion::dummy_circuit::cyclic_base_proof;
// Generates `CommonCircuitData` usable for recursion.
fn common_data_for_recursion<
@ -341,8 +246,8 @@ mod tests {
let one = builder.one();
// Circuit that computes a repeated hash.
let initial_hash = builder.add_virtual_hash();
builder.register_public_inputs(&initial_hash.elements);
let initial_hash_target = builder.add_virtual_hash();
builder.register_public_inputs(&initial_hash_target.elements);
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());
@ -350,97 +255,84 @@ mod tests {
let counter = builder.add_virtual_public_input();
let mut common_data = common_data_for_recursion::<F, C, D>();
builder.add_verifier_data_public_inputs();
let verifier_data_target = builder.add_verifier_data_public_inputs();
common_data.num_public_inputs = builder.num_public_inputs();
let condition = builder.add_virtual_bool_target_safe();
// 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];
let inner_cyclic_proof_with_pis = builder.add_virtual_proof_with_pis::<C>(&common_data);
let inner_cyclic_pis = &inner_cyclic_proof_with_pis.public_inputs;
let inner_cyclic_initial_hash = HashOutTarget::try_from(&inner_cyclic_pis[0..4]).unwrap();
let inner_cyclic_latest_hash = HashOutTarget::try_from(&inner_cyclic_pis[4..8]).unwrap();
let inner_cyclic_counter = inner_cyclic_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);
builder.connect_hashes(initial_hash_target, inner_cyclic_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);
let actual_hash_in =
builder.select_hash(condition, inner_cyclic_latest_hash, initial_hash_target);
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);
let new_counter = builder.mul_add(condition.target, inner_cyclic_counter, one);
builder.connect(counter, new_counter);
let cyclic_data_target =
builder.cyclic_recursion::<C>(condition, &inner_proof_with_pis, &common_data)?;
builder.conditionally_verify_cyclic_proof_or_dummy::<C>(
condition,
&inner_cyclic_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_pis,
)?;
pw.set_bool_target(condition, false);
pw.set_proof_with_pis_target::<C, D>(
&inner_cyclic_proof_with_pis,
&cyclic_base_proof(
&common_data,
&cyclic_circuit_data.verifier_only,
initial_hash_pis,
),
);
pw.set_verifier_data_target(&verifier_data_target, &cyclic_circuit_data.verifier_only);
let proof = cyclic_circuit_data.prove(pw)?;
check_cyclic_proof_verifier_data(
&proof,
cyclic_recursion_data.verifier_data,
cyclic_recursion_data.common_data,
&cyclic_circuit_data.verifier_only,
&cyclic_circuit_data.common,
)?;
cyclic_circuit_data.verify(proof.clone())?;
// 1st recursive layer.
let mut pw = PartialWitness::new();
let cyclic_recursion_data = CyclicRecursionData {
proof: &Some(proof), // Input previous proof.
verifier_data: &cyclic_circuit_data.verifier_only,
common_data: &cyclic_circuit_data.common,
};
set_cyclic_recursion_data_target(
&mut pw,
&cyclic_data_target,
&cyclic_recursion_data,
HashMap::new(),
)?;
pw.set_bool_target(condition, true);
pw.set_proof_with_pis_target(&inner_cyclic_proof_with_pis, &proof);
pw.set_verifier_data_target(&verifier_data_target, &cyclic_circuit_data.verifier_only);
let proof = cyclic_circuit_data.prove(pw)?;
check_cyclic_proof_verifier_data(
&proof,
cyclic_recursion_data.verifier_data,
cyclic_recursion_data.common_data,
&cyclic_circuit_data.verifier_only,
&cyclic_circuit_data.common,
)?;
cyclic_circuit_data.verify(proof.clone())?;
// 2nd recursive layer.
let mut pw = PartialWitness::new();
let cyclic_recursion_data = CyclicRecursionData {
proof: &Some(proof), // Input previous proof.
verifier_data: &cyclic_circuit_data.verifier_only,
common_data: &cyclic_circuit_data.common,
};
set_cyclic_recursion_data_target(
&mut pw,
&cyclic_data_target,
&cyclic_recursion_data,
HashMap::new(),
)?;
pw.set_bool_target(condition, true);
pw.set_proof_with_pis_target(&inner_cyclic_proof_with_pis, &proof);
pw.set_verifier_data_target(&verifier_data_target, &cyclic_circuit_data.verifier_only);
let proof = cyclic_circuit_data.prove(pw)?;
check_cyclic_proof_verifier_data(
&proof,
cyclic_recursion_data.verifier_data,
cyclic_recursion_data.common_data,
&cyclic_circuit_data.verifier_only,
&cyclic_circuit_data.common,
)?;
// Verify that the proof correctly computes a repeated hash.

View File

@ -6,11 +6,47 @@ 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::iop::generator::{GeneratedValues, SimpleGenerator};
use crate::iop::target::Target;
use crate::iop::witness::{PartialWitness, PartitionWitness, WitnessWrite};
use crate::plonk::circuit_builder::CircuitBuilder;
use crate::plonk::circuit_data::{CircuitData, CommonCircuitData};
use crate::plonk::config::GenericConfig;
use crate::plonk::proof::ProofWithPublicInputs;
use crate::plonk::circuit_data::{
CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData,
};
use crate::plonk::config::{AlgebraicHasher, GenericConfig};
use crate::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
/// Creates a dummy proof which is suitable for use as a base proof in a cyclic recursion tree.
/// Such a base proof will not actually be verified, so most of its data is arbitrary. However, its
/// public inputs which encode the cyclic verification key must be set properly, and this method
/// takes care of that. It also allows the user to specify any other public inputs which should be
/// set in this base proof.
pub fn cyclic_base_proof<F, C, const D: usize>(
common_data: &CommonCircuitData<F, D>,
verifier_data: &VerifierOnlyCircuitData<C, D>,
mut nonzero_public_inputs: HashMap<usize, F>,
) -> ProofWithPublicInputs<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
C::Hasher: AlgebraicHasher<C::F>,
{
let pis_len = common_data.num_public_inputs;
let cap_elements = common_data.config.fri_config.num_cap_elements();
let start_vk_pis = pis_len - 4 - 4 * cap_elements;
// Add the cyclic verifier data public inputs.
nonzero_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;
nonzero_public_inputs
.extend((start..).zip(verifier_data.constants_sigmas_cap.0[i].elements));
}
// TODO: A bit wasteful to build a dummy circuit here. We could potentially use a proof that
// just consists of zeros, apart from public inputs.
dummy_proof(&dummy_circuit(common_data), nonzero_public_inputs).unwrap()
}
/// 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.
@ -65,3 +101,59 @@ pub(crate) fn dummy_circuit<
assert_eq!(&circuit.common, common_data);
circuit
}
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
pub(crate) fn dummy_proof_and_vk<C: GenericConfig<D, F = F> + 'static>(
&mut self,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<(ProofWithPublicInputsTarget<D>, VerifierCircuitTarget)>
where
C::Hasher: AlgebraicHasher<F>,
{
let dummy_circuit = dummy_circuit::<F, C, D>(common_data);
let dummy_proof_with_pis = dummy_proof(&dummy_circuit, HashMap::new())?;
let dummy_proof_with_pis_target = self.add_virtual_proof_with_pis::<C>(common_data);
let dummy_verifier_data_target = VerifierCircuitTarget {
constants_sigmas_cap: self.add_virtual_cap(self.config.fri_config.cap_height),
circuit_digest: self.add_virtual_hash(),
};
self.add_simple_generator(DummyProofGenerator {
proof_with_pis_target: dummy_proof_with_pis_target.clone(),
proof_with_pis: dummy_proof_with_pis,
verifier_data_target: dummy_verifier_data_target.clone(),
verifier_data: dummy_circuit.verifier_only,
});
Ok((dummy_proof_with_pis_target, dummy_verifier_data_target))
}
}
#[derive(Debug)]
pub(crate) struct DummyProofGenerator<F, C, const D: usize>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F>,
{
pub(crate) proof_with_pis_target: ProofWithPublicInputsTarget<D>,
pub(crate) proof_with_pis: ProofWithPublicInputs<F, C, D>,
pub(crate) verifier_data_target: VerifierCircuitTarget,
pub(crate) verifier_data: VerifierOnlyCircuitData<C, D>,
}
impl<F, C, const D: usize> SimpleGenerator<F> for DummyProofGenerator<F, C, D>
where
F: RichField + Extendable<D>,
C: GenericConfig<D, F = F> + 'static,
C::Hasher: AlgebraicHasher<F>,
{
fn dependencies(&self) -> Vec<Target> {
vec![]
}
fn run_once(&self, _witness: &PartitionWitness<F>, out_buffer: &mut GeneratedValues<F>) {
out_buffer.set_proof_with_pis_target(&self.proof_with_pis_target, &self.proof_with_pis);
out_buffer.set_verifier_data_target(&self.verifier_data_target, &self.verifier_data);
}
}

View File

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

View File

@ -191,7 +191,7 @@ mod tests {
use crate::fri::reduction_strategies::FriReductionStrategy;
use crate::fri::FriConfig;
use crate::gates::noop::NoopGate;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_data::{CircuitConfig, VerifierOnlyCircuitData};
use crate::plonk::config::{GenericConfig, KeccakGoldilocksConfig, PoseidonGoldilocksConfig};
use crate::plonk::proof::{CompressedProofWithPublicInputs, ProofWithPublicInputs};

View File

@ -278,7 +278,7 @@ mod tests {
use super::*;
use crate::field::types::Sample;
use crate::iop::witness::{PartialWitness, Witness};
use crate::iop::witness::{PartialWitness, WitnessWrite};
use crate::plonk::circuit_data::CircuitConfig;
use crate::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
use crate::plonk::verifier::verify;

View File

@ -6,7 +6,7 @@ use plonky2::field::extension::{Extendable, FieldExtension};
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
use plonky2::field::types::{Field, Sample};
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, Witness};
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::config::{GenericConfig, Hasher};

View File

@ -14,7 +14,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -16,7 +16,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::vars::{

View File

@ -15,7 +15,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
use plonky2::plonk::vars::{

View File

@ -12,7 +12,7 @@ use plonky2::hash::hash_types::RichField;
use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
use plonky2::plonk::vars::{EvaluationTargets, EvaluationVars, EvaluationVarsBase};

View File

@ -15,7 +15,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::vars::{

View File

@ -1,6 +1,6 @@
use plonky2::field::types::{Field, PrimeField64};
use plonky2::iop::generator::GeneratedValues;
use plonky2::iop::witness::Witness;
use plonky2::iop::witness::{Witness, WitnessWrite};
use crate::gadgets::arithmetic_u32::U32Target;

View File

@ -8,7 +8,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::plonk_common::{reduce_with_powers, reduce_with_powers_ext_circuit};
use plonky2::plonk::vars::{

View File

@ -9,7 +9,7 @@ use plonky2::iop::ext_target::ExtensionTarget;
use plonky2::iop::generator::{GeneratedValues, WitnessGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::wire::Wire;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::CircuitConfig;
use plonky2::plonk::vars::{

View File

@ -6,7 +6,7 @@ use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::Target;
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use crate::bimap::bimap_from_lists;

View File

@ -6,7 +6,7 @@ use plonky2::field::types::Field;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
use plonky2::iop::target::{BoolTarget, Target};
use plonky2::iop::witness::{PartitionWitness, Witness};
use plonky2::iop::witness::{PartitionWitness, Witness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2_util::ceil_div_usize;