add support for any generic hash function

This commit is contained in:
M Alghazwi 2025-01-30 10:28:33 +01:00
parent 909dba1b43
commit 46c947828f
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
12 changed files with 214 additions and 110 deletions

View File

@ -141,7 +141,7 @@ impl<
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F> <C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{ {
let config = CircuitConfig::standard_recursion_config(); let config = CircuitConfig::standard_recursion_config();
let mut builder = CircuitBuilder::<F, D>::new(config); let mut builder = CircuitBuilder::<F, D>::new(config.clone());
self.build::<C,H>(&mut builder)?; self.build::<C,H>(&mut builder)?;

View File

@ -2,7 +2,6 @@
// and run the inner circuit -> resulting in one proof that again can be fed // and run the inner circuit -> resulting in one proof that again can be fed
// into another cyclic circle. // into another cyclic circle.
use hashbrown::HashMap;
use plonky2::hash::hash_types::{HashOutTarget, RichField}; use plonky2::hash::hash_types::{HashOutTarget, RichField};
use plonky2::iop::target::{BoolTarget}; use plonky2::iop::target::{BoolTarget};
use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::iop::witness::{PartialWitness, WitnessWrite};
@ -10,14 +9,14 @@ use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget}; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2::recursion::dummy_circuit::cyclic_base_proof;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit; use crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2::gates::noop::NoopGate; use plonky2::gates::noop::NoopGate;
use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
use crate::circuits::utils::select_hash; use crate::circuits::utils::select_hash;
use crate::error::CircuitError; use crate::error::CircuitError;
use crate::recursion::utils::conditional_verifier::{dummy_circuit};
use crate::recursion::utils::dummy_gen::DummyProofGen;
use crate::Result; use crate::Result;
/// cyclic circuit struct /// cyclic circuit struct
@ -80,7 +79,9 @@ impl<
// the hash of the public input // the hash of the public input
let pub_input_hash = builder.add_virtual_hash_public_input(); let pub_input_hash = builder.add_virtual_hash_public_input();
// verifier data for inner proofs // verifier data for inner proofs
let verifier_data_target = builder.add_verifier_data_public_inputs(); // TODO: make verifier data public
// let verifier_data_target = builder.add_verifier_data_public_inputs();
let verifier_data_target = builder.add_virtual_verifier_data(builder.config.fri_config.cap_height);
// common data should have same num of public input as inner proofs // common data should have same num of public input as inner proofs
common_data.num_public_inputs = builder.num_public_inputs(); common_data.num_public_inputs = builder.num_public_inputs();
@ -107,11 +108,7 @@ impl<
builder.connect_hashes(pub_input_hash,outer_pi_hash); builder.connect_hashes(pub_input_hash,outer_pi_hash);
// verify proof in-circuit // verify proof in-circuit
builder.conditionally_verify_cyclic_proof_or_dummy::<C>( builder.verify_proof::<C>(&inner_cyclic_proof_with_pis, &verifier_data_target, &common_data);
condition,
&inner_cyclic_proof_with_pis,
&common_data,
).map_err(|e| CircuitError::ConditionalVerificationError(e.to_string()))?;
// build the cyclic circuit // build the cyclic circuit
let cyclic_circuit_data = builder.build::<C>(); let cyclic_circuit_data = builder.build::<C>();
@ -159,14 +156,17 @@ impl<
)?; )?;
pw.set_proof_with_pis_target::<C, D>( pw.set_proof_with_pis_target::<C, D>(
&cyc_targets.inner_cyclic_proof_with_pis, &cyc_targets.inner_cyclic_proof_with_pis,
&cyclic_base_proof( &DummyProofGen::<F, D, C>::get_dummy_node_proof(
common_data, common_data,
&circ_data.verifier_only, &circ_data.verifier_only,
HashMap::new(),
), ),
).map_err(|e| ).map_err(|e|
CircuitError::ProofTargetAssignmentError("cyclic proof".to_string(),e.to_string()), CircuitError::ProofTargetAssignmentError("cyclic proof".to_string(),e.to_string()),
)?; )?;
// assign verifier data
let dummy_ver = dummy_circuit::<F, C, D>(common_data).verifier_only;
pw.set_verifier_data_target(&cyc_targets.verifier_data, &dummy_ver)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
}else{ // else add last proof }else{ // else add last proof
pw.set_bool_target(cyc_targets.condition, true) pw.set_bool_target(cyc_targets.condition, true)
.map_err(|e| .map_err(|e|
@ -182,23 +182,15 @@ impl<
.map_err(|e| .map_err(|e|
CircuitError::ProofTargetAssignmentError("cyclic proof".to_string(),e.to_string()), CircuitError::ProofTargetAssignmentError("cyclic proof".to_string(),e.to_string()),
)?; )?;
// assign verifier data
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
} }
// assign verifier data
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
// prove // prove
let proof = circ_data.prove(pw).map_err( let proof = circ_data.prove(pw).map_err(
|e| CircuitError::InvalidProofError(e.to_string()) |e| CircuitError::InvalidProofError(e.to_string())
)?; )?;
// check that the correct verifier data is consistent
check_cyclic_proof_verifier_data(
&proof,
&circ_data.verifier_only,
&circ_data.common,
).map_err(
|e| CircuitError::RecursiveProofVerifierDataCheckError(e.to_string())
)?;
self.latest_proof = Some(proof.clone()); self.latest_proof = Some(proof.clone());
self.layer = self.layer + 1; self.layer = self.layer + 1;
@ -232,16 +224,7 @@ impl<
.ok_or_else(|| CircuitError::OptionError("cyclic proof".to_string()))? .ok_or_else(|| CircuitError::OptionError("cyclic proof".to_string()))?
.clone(); .clone();
// check that the correct verifier data is consistent // TODO: check that the correct verifier data is consistent
//TODO: test if it works with only one layer proof
check_cyclic_proof_verifier_data(
&proof,
&self.cyclic_circuit_data.verifier_only,
&self.cyclic_circuit_data.common,
).map_err(
|e| CircuitError::RecursiveProofVerifierDataCheckError(e.to_string())
)?;
self.cyclic_circuit_data.verify(proof).map_err( self.cyclic_circuit_data.verify(proof).map_err(
|e| CircuitError::InvalidProofError(e.to_string()) |e| CircuitError::InvalidProofError(e.to_string())

View File

@ -10,7 +10,7 @@ use plonky2_field::extension::Extendable;
use crate::{error::CircuitError, Result}; use crate::{error::CircuitError, Result};
use crate::circuits::utils::vec_to_array; use crate::circuits::utils::vec_to_array;
use crate::recursion::circuits::leaf_circuit::{LeafCircuit, LeafInput}; use crate::recursion::circuits::leaf_circuit::{LeafCircuit, LeafInput};
use crate::recursion::hybrid::node_circuit::NodeCircuit; use crate::recursion::hybrid::node_circuit::{NodeCircuit, NodeCircuitTargets};
/// Hybrid tree recursion - combines simple and tree recursion /// Hybrid tree recursion - combines simple and tree recursion
/// - N: number of leaf proofs to verify in the node circuit /// - N: number of leaf proofs to verify in the node circuit
@ -55,11 +55,12 @@ impl<
// process leaves // process leaves
let (leaf_proofs, leaf_data) = self.get_leaf_proofs::<C,H>( let (leaf_proofs, leaf_data) = self.get_leaf_proofs::<C,H>(
proofs_with_pi, proofs_with_pi,
inner_verifier_data inner_verifier_data,
)?; )?;
// process nodes // process nodes
let (root_proof, last_verifier_data) = self.prove::<C,H>(&leaf_proofs,leaf_data.verifier_data())?; let (root_proof, last_verifier_data) =
self.prove::<C,H>(&leaf_proofs,leaf_data.verifier_data(), None, None, 0)?;
Ok((root_proof, last_verifier_data)) Ok((root_proof, last_verifier_data))
} }
@ -80,6 +81,7 @@ impl<
let leaf_targets = self.leaf.build::<C,H>(&mut builder)?; let leaf_targets = self.leaf.build::<C,H>(&mut builder)?;
let leaf_data = builder.build::<C>(); let leaf_data = builder.build::<C>();
println!("leaf circuit size = {:?}", leaf_data.common.degree_bits());
let mut leaf_proofs = vec![]; let mut leaf_proofs = vec![];
@ -107,6 +109,9 @@ impl<
&mut self, &mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>], proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
verifier_data: VerifierCircuitData<F, C, D>, verifier_data: VerifierCircuitData<F, C, D>,
node_target_options: Option<NodeCircuitTargets<D, N>>,
node_data_option: Option<CircuitData<F, C, D>>,
layer: usize,
) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierCircuitData<F, C, D>)> where ) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierCircuitData<F, C, D>)> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F> <C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{ {
@ -117,10 +122,15 @@ impl<
let mut new_proofs = vec![]; let mut new_proofs = vec![];
let node_config = CircuitConfig::standard_recursion_config(); let (node_data, node_targets) = if layer<2 {
let mut node_builder = CircuitBuilder::<F, D>::new(node_config); let node_config = CircuitConfig::standard_recursion_config();
let node_targets = NodeCircuit::<F,D,C,N>::build_circuit::<H>(&mut node_builder, &verifier_data.common)?; let mut node_builder = CircuitBuilder::<F, D>::new(node_config);
let node_data = node_builder.build::<C>(); let targets = NodeCircuit::<F, D, C, N>::build_circuit::<H>(&mut node_builder, &verifier_data.common)?;
let data = node_builder.build::<C>();
(data, targets)
}else{
(node_data_option.unwrap(), node_target_options.unwrap())
};
for chunk in proofs_with_pi.chunks(N) { for chunk in proofs_with_pi.chunks(N) {
@ -135,7 +145,7 @@ impl<
new_proofs.push(proof); new_proofs.push(proof);
} }
self.prove::<C,H>(&new_proofs, node_data.verifier_data()) self.prove::<C,H>(&new_proofs, node_data.verifier_data(), Some(node_targets),Some(node_data), layer+1)
} }
} }

View File

@ -4,3 +4,4 @@ pub mod simple;
pub mod tree1; pub mod tree1;
pub mod tree2; pub mod tree2;
pub mod hybrid; pub mod hybrid;
pub mod utils;

View File

@ -5,14 +5,14 @@ use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData
use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::recursion::dummy_circuit::cyclic_base_proof;
use hashbrown::HashMap;
use plonky2::gates::noop::NoopGate; use plonky2::gates::noop::NoopGate;
use plonky2::iop::target::BoolTarget; use plonky2::iop::target::BoolTarget;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::circuits::utils::{select_hash, vec_to_array}; use crate::circuits::utils::{select_hash, vec_to_array};
use crate::{error::CircuitError, Result}; use crate::{error::CircuitError, Result};
use crate::recursion::circuits::inner_circuit::InnerCircuit; use crate::recursion::circuits::inner_circuit::InnerCircuit;
use crate::recursion::utils::conditional_verifier::dummy_circuit;
use crate::recursion::utils::dummy_gen::DummyProofGen;
/// Node circuit struct /// Node circuit struct
/// contains necessary data /// contains necessary data
@ -84,7 +84,9 @@ impl<
let mut common_data = Self::common_data_for_node()?; let mut common_data = Self::common_data_for_node()?;
let pub_input_hash = builder.add_virtual_hash_public_input(); let pub_input_hash = builder.add_virtual_hash_public_input();
let verifier_data_target = builder.add_verifier_data_public_inputs(); // TODO: make verifier data public
// let verifier_data_target = builder.add_verifier_data_public_inputs();
let verifier_data_target = builder.add_virtual_verifier_data(builder.config.fri_config.cap_height);
common_data.num_public_inputs = builder.num_public_inputs(); common_data.num_public_inputs = builder.num_public_inputs();
// condition // condition
@ -113,8 +115,6 @@ impl<
outer_pis.push( I::get_pub_input_targets(&inner_t[i])); outer_pis.push( I::get_pub_input_targets(&inner_t[i]));
} }
// hash all the public input -> generate one HashOut at the end // hash all the public input -> generate one HashOut at the end
// this is not an optimal way to do it, verification might be ugly if M > 1
// TODO: optimize this
let mut outer_pi_hashes = vec![]; let mut outer_pi_hashes = vec![];
for i in 0..M { for i in 0..M {
let hash_res = builder.hash_n_to_hash_no_pad::<H>(outer_pis[i].clone()); let hash_res = builder.hash_n_to_hash_no_pad::<H>(outer_pis[i].clone());
@ -137,11 +137,8 @@ impl<
// verify all N proofs in-circuit // verify all N proofs in-circuit
for i in 0..N { for i in 0..N {
builder.conditionally_verify_cyclic_proof_or_dummy::<C>( builder.verify_proof::<C>(&inner_cyclic_proof_with_pis[i], &verifier_data_target, &common_data);
condition,
&inner_cyclic_proof_with_pis[i],
&common_data,
).map_err(|e| CircuitError::ConditionalVerificationError(e.to_string()))?;
} }
// build the cyclic circuit // build the cyclic circuit
@ -188,13 +185,16 @@ impl<
for i in 0..N { for i in 0..N {
pw.set_proof_with_pis_target::<C, D>( pw.set_proof_with_pis_target::<C, D>(
&cyc_targets.inner_proofs_with_pis[i], &cyc_targets.inner_proofs_with_pis[i],
&cyclic_base_proof( &DummyProofGen::<F, D, C>::get_dummy_node_proof(
common_data, common_data,
&circ_data.verifier_only, &circ_data.verifier_only,
HashMap::new(),
), ),
).map_err(|e| CircuitError::ProofTargetAssignmentError("inner proofs".to_string(),e.to_string()))?; ).map_err(|e| CircuitError::ProofTargetAssignmentError("inner proofs".to_string(),e.to_string()))?;
} }
// assign verifier data
let dummy_ver = dummy_circuit::<F, C, D>(common_data).verifier_only;
pw.set_verifier_data_target(&cyc_targets.verifier_data, &dummy_ver)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
}else{ }else{
pw.set_bool_target(cyc_targets.condition, true) pw.set_bool_target(cyc_targets.condition, true)
.map_err(|e| CircuitError::BoolTargetAssignmentError("condition".to_string(),e.to_string()))?; .map_err(|e| CircuitError::BoolTargetAssignmentError("condition".to_string(),e.to_string()))?;
@ -204,11 +204,11 @@ impl<
pw.set_proof_with_pis_target(&cyc_targets.inner_proofs_with_pis[i], &proofs[i]) pw.set_proof_with_pis_target(&cyc_targets.inner_proofs_with_pis[i], &proofs[i])
.map_err(|e| CircuitError::ProofTargetAssignmentError("inner proofs".to_string(),e.to_string()))?; .map_err(|e| CircuitError::ProofTargetAssignmentError("inner proofs".to_string(),e.to_string()))?;
} }
// assign verifier data
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
} }
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
Ok(()) Ok(())
} }
@ -249,7 +249,7 @@ impl<
let proof = builder.add_virtual_proof_with_pis(&data.common); let proof = builder.add_virtual_proof_with_pis(&data.common);
builder.verify_proof::<C>(&proof, &verifier_data, &data.common); builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
} }
// pad. TODO: optimize this padding to only needed number of gates // pad - padding depends on the inner circuit size
while builder.num_gates() < 1 << 13 { while builder.num_gates() < 1 << 13 {
builder.add_gate(NoopGate, vec![]); builder.add_gate(NoopGate, vec![]);
} }

View File

@ -1,12 +1,11 @@
use std::array::from_fn; use std::array::from_fn;
use plonky2::hash::hash_types::RichField; use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::iop::witness::{PartialWitness};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit; use crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
use crate::{error::CircuitError, Result}; use crate::{error::CircuitError, Result};
use crate::recursion::tree1::node_circuit::NodeCircuit; use crate::recursion::tree1::node_circuit::NodeCircuit;
@ -70,10 +69,6 @@ impl<
)?; )?;
let circ_data = &self.node_circ.cyclic_circuit_data; let circ_data = &self.node_circ.cyclic_circuit_data;
let cyc_targets = &self.node_circ.cyclic_target;
pw.set_verifier_data_target(&cyc_targets.verifier_data, &circ_data.verifier_only)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
let proof = circ_data.prove(pw) let proof = circ_data.prove(pw)
.map_err(|e| CircuitError::InvalidProofError(e.to_string()))?; .map_err(|e| CircuitError::InvalidProofError(e.to_string()))?;
@ -150,11 +145,7 @@ impl<
let circ_data = &self.node_circ.cyclic_circuit_data; let circ_data = &self.node_circ.cyclic_circuit_data;
check_cyclic_proof_verifier_data( // TODO: check verifier_data
&proof,
&circ_data.verifier_only,
&circ_data.common,
).map_err(|e| CircuitError::RecursiveProofVerifierDataCheckError(e.to_string()))?;
circ_data.verify(proof).map_err(|e|CircuitError::InvalidProofError(e.to_string()))?; circ_data.verify(proof).map_err(|e|CircuitError::InvalidProofError(e.to_string()))?;

View File

@ -1,3 +1,2 @@
pub mod dummy_gen;
pub mod node_circuit; pub mod node_circuit;
pub mod tree_circuit; pub mod tree_circuit;

View File

@ -10,7 +10,7 @@ use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit; use crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
use crate::circuits::utils::{select_hash, select_vec, vec_to_array}; use crate::circuits::utils::{select_vec, vec_to_array};
use crate::{error::CircuitError, Result}; use crate::{error::CircuitError, Result};
use crate::recursion::circuits::leaf_circuit::LeafCircuit; use crate::recursion::circuits::leaf_circuit::LeafCircuit;
@ -38,6 +38,7 @@ pub struct NodeCircuitTargets<
pub condition: BoolTarget, pub condition: BoolTarget,
pub node_proofs: [ProofWithPublicInputsTarget<D>; N], pub node_proofs: [ProofWithPublicInputsTarget<D>; N],
pub leaf_verifier_data: VerifierCircuitTarget, pub leaf_verifier_data: VerifierCircuitTarget,
pub node_verifier_data: VerifierCircuitTarget,
} }
/// Node common data and verifier data /// Node common data and verifier data
@ -98,7 +99,7 @@ impl<
} }
// leaf verifier data // leaf verifier data
// TODO: double check that it is ok for this verifier data to be private/witness // TODO: make verifier data public
let leaf_verifier_data = builder.add_virtual_verifier_data(leaf_common.config.fri_config.cap_height); let leaf_verifier_data = builder.add_virtual_verifier_data(leaf_common.config.fri_config.cap_height);
// condition // condition
@ -107,26 +108,19 @@ impl<
// verify leaf proofs in-circuit if it is a leaf node, // verify leaf proofs in-circuit if it is a leaf node,
// meaning that we are on bottom layer of the tree // meaning that we are on bottom layer of the tree
for i in 0..N{ for i in 0..N{
builder.conditionally_verify_proof_or_dummy::<C>( builder.verify_proof::<C>(&leaf_proofs[i], &leaf_verifier_data, &leaf_common);
condition,
&leaf_proofs[i],
&leaf_verifier_data,
&leaf_common
).map_err(|e| CircuitError::ConditionalVerificationError(e.to_string()))?;
} }
// common data for recursion // common data for recursion
let mut common_data = Self::get_common_data_for_node()?; let mut common_data = Self::get_common_data_for_node()?;
// public input hash. defined here so that is public_input[0..4] // public input hash. defined here so that is public_input[0..4]
let pub_input_hash = builder.add_virtual_hash_public_input(); let pub_input_hash = builder.add_virtual_hash_public_input();
// verifier data for the recursion. // TODO: make verifier data public
let _verifier_data_target = builder.add_verifier_data_public_inputs(); // let _verifier_data_target = builder.add_verifier_data_public_inputs();
let verifier_data_target = builder.add_virtual_verifier_data(builder.config.fri_config.cap_height);
common_data.num_public_inputs = builder.num_public_inputs(); common_data.num_public_inputs = builder.num_public_inputs();
// flipped condition. used to conditionally verify the node proofs (recursive proofs)
let one = builder.one();
let flipped_condition = BoolTarget::new_unsafe(builder.sub(one,condition.target));
let inner_cyclic_proof_with_pis: [ProofWithPublicInputsTarget<D>; N] = let inner_cyclic_proof_with_pis: [ProofWithPublicInputsTarget<D>; N] =
vec_to_array::<N, ProofWithPublicInputsTarget<D>>( vec_to_array::<N, ProofWithPublicInputsTarget<D>>(
(0..N) (0..N)
@ -150,11 +144,8 @@ impl<
// verify all N proofs in-circuit // verify all N proofs in-circuit
for i in 0..N { for i in 0..N {
builder.conditionally_verify_cyclic_proof_or_dummy::<C>( builder.verify_proof::<C>(&inner_cyclic_proof_with_pis[i], &verifier_data_target, &common_data);
flipped_condition,
&inner_cyclic_proof_with_pis[i],
&common_data,
).map_err(|e| CircuitError::ConditionalVerificationError(e.to_string()))?;
} }
// build the node circuit // build the node circuit
@ -173,7 +164,8 @@ impl<
leaf_proofs, leaf_proofs,
condition, condition,
node_proofs: inner_cyclic_proof_with_pis, node_proofs: inner_cyclic_proof_with_pis,
leaf_verifier_data leaf_verifier_data,
node_verifier_data: verifier_data_target
}; };
let node_data = NodeData{ let node_data = NodeData{
@ -200,6 +192,7 @@ impl<
leaf_proofs: [ProofWithPublicInputs<F, C, D>; N], leaf_proofs: [ProofWithPublicInputs<F, C, D>; N],
node_proofs: [ProofWithPublicInputs<F, C, D>; N], node_proofs: [ProofWithPublicInputs<F, C, D>; N],
leaf_verifier_only_data: &VerifierOnlyCircuitData<C, D>, leaf_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
node_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
pw: &mut PartialWitness<F>, pw: &mut PartialWitness<F>,
is_leaf: bool, is_leaf: bool,
) -> Result<()>{ ) -> Result<()>{
@ -240,9 +233,11 @@ impl<
).map_err(|e| CircuitError::ProofTargetAssignmentError("dummy leaf proofs".to_string(),e.to_string()))?; ).map_err(|e| CircuitError::ProofTargetAssignmentError("dummy leaf proofs".to_string(),e.to_string()))?;
} }
} }
// assign the verifier data (only for the leaf proofs) // assign the verifier data
pw.set_verifier_data_target(&node_targets.leaf_verifier_data, leaf_verifier_only_data) pw.set_verifier_data_target(&node_targets.leaf_verifier_data, leaf_verifier_only_data)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?; .map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
pw.set_verifier_data_target(&node_targets.node_verifier_data, node_verifier_only_data)
.map_err(|e| CircuitError::VerifierDataTargetAssignmentError(e.to_string()))?;
Ok(()) Ok(())
} }
@ -284,8 +279,8 @@ impl<
let proof = builder.add_virtual_proof_with_pis(&data.common); let proof = builder.add_virtual_proof_with_pis(&data.common);
builder.verify_proof::<C>(&proof, &verifier_data, &data.common); builder.verify_proof::<C>(&proof, &verifier_data, &data.common);
} }
// pad. TODO: optimize this padding to only needed number of gates // pad.
while builder.num_gates() < 1 << 14 { while builder.num_gates() < 1 << 12 {
builder.add_gate(NoopGate, vec![]); builder.add_gate(NoopGate, vec![]);
} }
Ok(builder.build::<C>().common) Ok(builder.build::<C>().common)

View File

@ -1,12 +1,11 @@
use plonky2::hash::hash_types::RichField; use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::PartialWitness; use plonky2::iop::witness::{PartialWitness};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::recursion::circuits::inner_circuit::InnerCircuit; use crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2::recursion::cyclic_recursion::check_cyclic_proof_verifier_data;
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
use crate::recursion::tree2::dummy_gen::DummyProofGen; use crate::recursion::utils::dummy_gen::DummyProofGen;
use crate::{error::CircuitError, Result}; use crate::{error::CircuitError, Result};
use crate::circuits::utils::vec_to_array; use crate::circuits::utils::vec_to_array;
use crate::recursion::circuits::leaf_circuit::LeafCircuit; use crate::recursion::circuits::leaf_circuit::LeafCircuit;
@ -67,13 +66,13 @@ impl<
leaf_proofs, leaf_proofs,
node_proofs, node_proofs,
&self.node.node_data.leaf_circuit_data.verifier_only, &self.node.node_data.leaf_circuit_data.verifier_only,
&self.node.node_data.node_circuit_data.verifier_only,
&mut pw, &mut pw,
is_leaf, is_leaf,
)?; )?;
let proof = self.node.node_data.node_circuit_data.prove(pw) let proof = self.node.node_data.node_circuit_data.prove(pw)
.map_err(|e| CircuitError::ProofGenerationError(e.to_string()))?; .map_err(|e| CircuitError::ProofGenerationError(e.to_string()))?;
Ok(proof) Ok(proof)
} }
@ -158,13 +157,7 @@ impl<
is_leaf: bool, is_leaf: bool,
) -> Result<()>{ ) -> Result<()>{
if !is_leaf { // TODO: if !is_leaf check verifier data
check_cyclic_proof_verifier_data(
&proof,
&self.node.node_data.node_circuit_data.verifier_only,
&self.node.node_data.node_circuit_data.common,
).map_err(|e| CircuitError::InvalidProofError(e.to_string()))?;
}
self.node.node_data.node_circuit_data.verify(proof) self.node.node_data.node_circuit_data.verify(proof)
.map_err(|e| CircuitError::InvalidProofError(e.to_string()))?; .map_err(|e| CircuitError::InvalidProofError(e.to_string()))?;

View File

@ -0,0 +1,121 @@
use plonky2::gates::noop::NoopGate;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::target::BoolTarget;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitData, CommonCircuitData, VerifierCircuitTarget};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::ProofWithPublicInputsTarget;
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
/// this takes verifier data (not public) and doesn't check the verifier data for consistency
pub fn conditionally_verify_recursion_proof_or_dummy<F: RichField + Extendable<D> + Poseidon2, const D: usize ,C: GenericConfig<D, F = F> + 'static>(
builder: &mut CircuitBuilder<F, D>,
condition: BoolTarget,
cyclic_proof_with_pis: &ProofWithPublicInputsTarget<D>,
verifier_data: &VerifierCircuitTarget,
common_data: &CommonCircuitData<F, D>,
) -> anyhow::Result<()>
where
C::Hasher: AlgebraicHasher<F>,
{
let (dummy_proof_with_pis_target, dummy_verifier_data_target) =
dummy_proof_and_vk_no_generator::<F, D, C>(builder, common_data)?;
// TODO: make verifier data public
// // Connect previous verifier data to current one. This guarantees that every proof in the cycle uses the same verifier data.
// 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 cyclic proof if `condition` is set to true, otherwise verify the other proof.
builder.conditionally_verify_proof::<C>(
condition,
cyclic_proof_with_pis,
verifier_data,
&dummy_proof_with_pis_target,
&dummy_verifier_data_target,
common_data,
);
// Make sure we have every gate to match `common_data`.
for g in &common_data.gates {
builder.add_gate_to_gate_set(g.clone());
}
Ok(())
}
/// Conditionally verify a proof with a new generated dummy proof.
pub fn conditionally_verify_proof_or_dummy<F: RichField + Extendable<D> + Poseidon2, const D: usize ,C: GenericConfig<D, F = F> + 'static>(
builder: &mut CircuitBuilder<F, D>,
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) =
dummy_proof_and_vk_no_generator::<F, D, C>(builder, inner_common_data)?;
builder.conditionally_verify_proof::<C>(
condition,
proof_with_pis,
inner_verifier_data,
&dummy_proof_with_pis_target,
&dummy_verifier_data_target,
inner_common_data,
);
Ok(())
}
/// Generate a circuit matching a given `CommonCircuitData`.
pub(crate) fn dummy_circuit<F: RichField + Extendable<D> + Poseidon2, 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 - common_data.num_public_inputs.div_ceil(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
}
pub(crate) fn dummy_proof_and_vk_no_generator<F: RichField + Extendable<D> + Poseidon2, const D: usize ,C: GenericConfig<D, F = F> + 'static> (
builder: &mut CircuitBuilder<F, D>,
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_target = builder.add_virtual_proof_with_pis(common_data);
let dummy_verifier_data_target = builder.constant_verifier_data(&dummy_circuit.verifier_only);
Ok((dummy_proof_with_pis_target, dummy_verifier_data_target))
}

View File

@ -1,9 +1,10 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
use plonky2::plonk::proof::ProofWithPublicInputs; use plonky2::plonk::proof::{ProofWithPublicInputs};
use plonky2::recursion::dummy_circuit::{cyclic_base_proof, dummy_circuit, dummy_proof}; use plonky2::recursion::dummy_circuit::{dummy_proof};
use crate::recursion::utils::conditional_verifier::dummy_circuit;
use hashbrown::HashMap; use hashbrown::HashMap;
use plonky2::hash::hash_types::RichField; use plonky2::hash::hash_types::{ RichField};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
@ -43,7 +44,18 @@ impl<
node_common: &CommonCircuitData<F, D>, node_common: &CommonCircuitData<F, D>,
node_verifier_only_data: &VerifierOnlyCircuitData<C, D>, node_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> ProofWithPublicInputs<F, C, D> { ) -> ProofWithPublicInputs<F, C, D> {
cyclic_base_proof(node_common, node_verifier_only_data, HashMap::new()) Self::recursion_base_proof(node_common, HashMap::new())
}
fn recursion_base_proof(
common_data: &CommonCircuitData<F, D>,
mut nonzero_public_inputs: HashMap<usize, F>
) -> ProofWithPublicInputs<F, C, D>{
dummy_proof::<F, C, D>(
&dummy_circuit::<F, C, D>(common_data),
nonzero_public_inputs,
)
.unwrap()
} }
/// Generates an array of `N` dummy leaf proofs. /// Generates an array of `N` dummy leaf proofs.
@ -65,6 +77,3 @@ impl<
vec_to_array::<N, ProofWithPublicInputs<F, C, D>>(n_dummy_vec) vec_to_array::<N, ProofWithPublicInputs<F, C, D>>(n_dummy_vec)
} }
} }

View File

@ -0,0 +1,2 @@
pub mod dummy_gen;
pub mod conditional_verifier;