mirror of
https://github.com/logos-storage/proof-aggregation.git
synced 2026-01-02 13:53:13 +00:00
use the circuit helper, clean up, and small refactoring.
This commit is contained in:
parent
c4eefceba8
commit
76374f084b
@ -33,6 +33,7 @@ pub trait Plonky2Circuit<
|
||||
) -> Result<(Self::Targets,CircuitData<F, C, D>)>{
|
||||
let mut builder = CircuitBuilder::<F, D>::new(circuit_config);
|
||||
|
||||
// NOTE: register public input is set to TRUE as default.
|
||||
let targets = self.add_targets(&mut builder, true)?;
|
||||
|
||||
Ok((targets,builder.build::<C>()))
|
||||
@ -80,7 +81,7 @@ pub trait Plonky2Circuit<
|
||||
&self,
|
||||
targets: &Self::Targets,
|
||||
input: &Self::Input,
|
||||
prover_circuit_data: ProverCircuitData<F, C, D>
|
||||
prover_circuit_data: &ProverCircuitData<F, C, D>
|
||||
)-> Result<ProofWithPublicInputs<F, C, D>>{
|
||||
let mut pw = PartialWitness::new();
|
||||
self.assign_targets(&mut pw, targets, input)?;
|
||||
|
||||
@ -2,14 +2,16 @@ use std::marker::PhantomData;
|
||||
use plonky2::hash::hash_types::{HashOutTarget, RichField};
|
||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use plonky2_field::extension::Extendable;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
||||
use crate::{error::CircuitError,Result};
|
||||
use crate::circuit_helper::Plonky2Circuit;
|
||||
|
||||
/// recursion compression circuit - verifies 1 inner proof
|
||||
/// recursion compression circuit
|
||||
/// verifies 1 inner proof and as result should shrink it
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CompressionCircuit<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
@ -31,6 +33,15 @@ pub struct CompressionTargets<
|
||||
pub inner_proof: ProofWithPublicInputsTarget<D>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CompressionInput<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
>{
|
||||
pub inner_proof: ProofWithPublicInputs<F, C, D>
|
||||
}
|
||||
|
||||
impl<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
@ -50,9 +61,20 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
/// build the compression circuit
|
||||
pub fn build(&self, builder: &mut CircuitBuilder<F, D>) -> Result<CompressionTargets<D>> {
|
||||
}
|
||||
|
||||
impl<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: AlgebraicHasher<F>,
|
||||
> Plonky2Circuit<F, C, D> for CompressionCircuit<F, D, C, H> where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
type Targets = CompressionTargets<D>;
|
||||
type Input = CompressionInput<F, D, C>;
|
||||
|
||||
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<Self::Targets> {
|
||||
let inner_common = self.inner_common_data.clone();
|
||||
|
||||
// the proof virtual targets
|
||||
@ -61,7 +83,9 @@ impl<
|
||||
|
||||
// take the public input from inner proof & make it public
|
||||
assert_eq!(inner_pub_input.len(), 8);
|
||||
builder.register_public_inputs(&inner_pub_input[0..4]);
|
||||
if register_pi {
|
||||
builder.register_public_inputs(&inner_pub_input[0..4]);
|
||||
}
|
||||
|
||||
// constant target for the verifier data
|
||||
let const_verifier_data = builder.constant_verifier_data(&self.inner_verifier_data);
|
||||
@ -78,7 +102,9 @@ impl<
|
||||
// make sure the VerifierData we use is the same as the tree root hash of the VerifierData
|
||||
builder.connect_hashes(hash_inner_vd_pub_input,HashOutTarget::from_vec(inner_pub_input[4..8].to_vec()));
|
||||
|
||||
builder.register_public_inputs(&hash_inner_vd_pub_input.elements);
|
||||
if register_pi {
|
||||
builder.register_public_inputs(&hash_inner_vd_pub_input.elements);
|
||||
}
|
||||
|
||||
// verify the proofs in-circuit
|
||||
builder.verify_proof::<C>(&vir_proof, &const_verifier_data, &inner_common);
|
||||
@ -88,39 +114,17 @@ impl<
|
||||
inner_proof: vir_proof,
|
||||
};
|
||||
Ok(t)
|
||||
|
||||
}
|
||||
|
||||
/// assign the compression targets with given input
|
||||
pub fn assign_targets(
|
||||
&self, pw: &mut PartialWitness<F>,
|
||||
targets: &CompressionTargets<D>,
|
||||
inner_proof: ProofWithPublicInputs<F, C, D>,
|
||||
) -> Result<()> {
|
||||
fn assign_targets(&self, pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> Result<()> {
|
||||
// assign the proof
|
||||
pw.set_proof_with_pis_target(&targets.inner_proof, &inner_proof)
|
||||
pw.set_proof_with_pis_target(&targets.inner_proof, &input.inner_proof)
|
||||
.map_err(|e| {
|
||||
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns the compression circuit data
|
||||
pub fn get_circuit_data (&self) -> Result<CircuitData<F, C, D>>
|
||||
where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||
|
||||
self.build(&mut builder)?;
|
||||
|
||||
let circ_data = builder.build::<C>();
|
||||
|
||||
Ok(circ_data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2,12 +2,13 @@ use std::marker::PhantomData;
|
||||
use plonky2::hash::hash_types::{HashOut, RichField};
|
||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use plonky2_field::extension::Extendable;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
||||
use crate::{error::CircuitError,Result};
|
||||
use crate::circuit_helper::Plonky2Circuit;
|
||||
|
||||
/// recursion leaf circuit - verifies N inner proof
|
||||
#[derive(Clone, Debug)]
|
||||
@ -32,6 +33,15 @@ pub struct LeafTargets <
|
||||
pub inner_proof: Vec<ProofWithPublicInputsTarget<D>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LeafInput<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
>{
|
||||
pub inner_proof: Vec<ProofWithPublicInputs<F, C, D>>
|
||||
}
|
||||
|
||||
impl<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
@ -42,18 +52,30 @@ impl<
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
pub fn new(
|
||||
inner_common_data: CommonCircuitData<F,D>,
|
||||
inner_common_data: CommonCircuitData<F, D>,
|
||||
inner_verifier_data: VerifierOnlyCircuitData<C, D>,
|
||||
) -> Self {
|
||||
Self{
|
||||
Self {
|
||||
inner_common_data,
|
||||
inner_verifier_data,
|
||||
phantom_data:PhantomData::default(),
|
||||
phantom_data: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// build the leaf circuit
|
||||
pub fn build(&self, builder: &mut CircuitBuilder<F, D>) -> Result<LeafTargets<D>> {
|
||||
impl<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: AlgebraicHasher<F>,
|
||||
const N: usize,
|
||||
> Plonky2Circuit<F, C, D> for LeafCircuit<F,D,C,H,N> where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
type Targets = LeafTargets<D>;
|
||||
type Input = LeafInput<F, D, C>;
|
||||
|
||||
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<LeafTargets<D>> {
|
||||
|
||||
let inner_common = self.inner_common_data.clone();
|
||||
|
||||
@ -69,11 +91,15 @@ impl<
|
||||
|
||||
// hash the public input & make it public
|
||||
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(pub_input);
|
||||
builder.register_public_inputs(&hash_inner_pub_input.elements);
|
||||
if register_pi {
|
||||
builder.register_public_inputs(&hash_inner_pub_input.elements);
|
||||
}
|
||||
|
||||
// pad the public input with constants so that it shares the same structure as the node
|
||||
let zero_hash = builder.constant_hash(HashOut::<F>::default());
|
||||
builder.register_public_inputs(&zero_hash.elements);
|
||||
if register_pi {
|
||||
builder.register_public_inputs(&zero_hash.elements);
|
||||
}
|
||||
|
||||
// virtual constant target for the verifier data
|
||||
let const_verifier_data = builder.constant_verifier_data(&self.inner_verifier_data);
|
||||
@ -91,16 +117,15 @@ impl<
|
||||
|
||||
}
|
||||
|
||||
/// assign the leaf targets with given input
|
||||
pub fn assign_targets(
|
||||
fn assign_targets(
|
||||
&self, pw: &mut PartialWitness<F>,
|
||||
targets: &LeafTargets<D>,
|
||||
inner_proof: &[ProofWithPublicInputs<F, C, D>],
|
||||
targets: &Self::Targets,
|
||||
input: &Self::Input,
|
||||
) -> Result<()> {
|
||||
assert_eq!(inner_proof.len(), N);
|
||||
assert_eq!(input.inner_proof.len(), N);
|
||||
// assign the proofs
|
||||
for i in 0..N {
|
||||
pw.set_proof_with_pis_target(&targets.inner_proof[i], &inner_proof[i])
|
||||
pw.set_proof_with_pis_target(&targets.inner_proof[i], &input.inner_proof[i])
|
||||
.map_err(|e| {
|
||||
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
|
||||
})?;
|
||||
@ -109,21 +134,6 @@ impl<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns the leaf circuit data
|
||||
pub fn get_circuit_data (&self) -> Result<CircuitData<F, C, D>>
|
||||
where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||
|
||||
self.build(&mut builder)?;
|
||||
|
||||
let circ_data = builder.build::<C>();
|
||||
|
||||
Ok(circ_data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -3,13 +3,13 @@ use plonky2::hash::hash_types::RichField;
|
||||
use plonky2::iop::target::BoolTarget;
|
||||
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use plonky2_field::extension::Extendable;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
||||
use crate::{error::CircuitError,Result};
|
||||
// use crate::circuits::utils::vec_to_array;
|
||||
use crate::circuit_helper::Plonky2Circuit;
|
||||
|
||||
/// recursion node circuit - verifies M leaf proofs
|
||||
#[derive(Clone, Debug)]
|
||||
@ -36,6 +36,17 @@ pub struct NodeTargets<
|
||||
pub condition: BoolTarget,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NodeInput<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
>{
|
||||
pub node_proofs: Vec<ProofWithPublicInputs<F, C, D>>,
|
||||
pub verifier_only_data: VerifierOnlyCircuitData<C, D>,
|
||||
pub condition: bool,
|
||||
}
|
||||
|
||||
impl<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
@ -57,9 +68,21 @@ impl<
|
||||
}
|
||||
}
|
||||
|
||||
/// build the leaf circuit
|
||||
pub fn build(&self, builder: &mut CircuitBuilder<F, D>) -> Result<NodeTargets<D>> {
|
||||
}
|
||||
|
||||
impl<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: AlgebraicHasher<F>,
|
||||
const M: usize,
|
||||
> Plonky2Circuit<F, C, D> for NodeCircuit<F, D, C, H, M> where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
type Targets = NodeTargets<D>;
|
||||
type Input = NodeInput<F, D, C>;
|
||||
|
||||
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<Self::Targets> {
|
||||
let inner_common = self.common_data.clone();
|
||||
|
||||
// assert public input is of size 8 - 2 hashout
|
||||
@ -77,7 +100,9 @@ impl<
|
||||
|
||||
// hash the public input & make it public
|
||||
let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::<H>(pub_input);
|
||||
builder.register_public_inputs(&hash_inner_pub_input.elements);
|
||||
if register_pi{
|
||||
builder.register_public_inputs(&hash_inner_pub_input.elements);
|
||||
}
|
||||
|
||||
// virtual target for the verifier data
|
||||
let node_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
|
||||
@ -92,7 +117,9 @@ impl<
|
||||
vd_pub_input.extend_from_slice(&node_verifier_data.constants_sigmas_cap.0[i].elements);
|
||||
}
|
||||
let vd_hash = builder.hash_n_to_hash_no_pad::<H>(vd_pub_input);
|
||||
builder.register_public_inputs(&vd_hash.elements);
|
||||
if register_pi {
|
||||
builder.register_public_inputs(&vd_hash.elements);
|
||||
}
|
||||
|
||||
// condition for switching between node and leaf
|
||||
let condition = builder.add_virtual_bool_target_safe();
|
||||
@ -102,7 +129,7 @@ impl<
|
||||
|
||||
// verify the proofs in-circuit - M proofs
|
||||
for i in 0..M {
|
||||
builder.verify_proof::<C>(&vir_proofs[i], &selected_vd, &inner_common);
|
||||
builder.verify_proof::<C>(&vir_proofs[i], &selected_vd, &inner_common);
|
||||
}
|
||||
|
||||
// Make sure we have every gate to match `common_data`.
|
||||
@ -110,8 +137,6 @@ impl<
|
||||
builder.add_gate_to_gate_set(g.clone());
|
||||
}
|
||||
|
||||
// let proofs = vec_to_array::<2, ProofWithPublicInputsTarget<D>>(vir_proofs)?;
|
||||
|
||||
// return targets
|
||||
let t = NodeTargets {
|
||||
leaf_proofs: vir_proofs,
|
||||
@ -119,56 +144,32 @@ impl<
|
||||
condition,
|
||||
};
|
||||
Ok(t)
|
||||
|
||||
}
|
||||
|
||||
/// assign the leaf targets with given input
|
||||
pub fn assign_targets(
|
||||
&self, pw: &mut PartialWitness<F>,
|
||||
targets: &NodeTargets<D>,
|
||||
node_proofs: &[ProofWithPublicInputs<F, C, D>],
|
||||
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
|
||||
condition: bool,
|
||||
) -> Result<()> {
|
||||
fn assign_targets(&self, pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> Result<()> {
|
||||
// assert size of proofs vec
|
||||
assert_eq!(node_proofs.len(), M);
|
||||
assert_eq!(input.node_proofs.len(), M);
|
||||
|
||||
// assign the proofs
|
||||
for i in 0..M {
|
||||
pw.set_proof_with_pis_target(&targets.leaf_proofs[i], &node_proofs[i])
|
||||
pw.set_proof_with_pis_target(&targets.leaf_proofs[i], &input.node_proofs[i])
|
||||
.map_err(|e| {
|
||||
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
|
||||
})?;
|
||||
}
|
||||
|
||||
// assign the verifier data
|
||||
pw.set_verifier_data_target(&targets.node_verifier_data, &verifier_only_data)
|
||||
pw.set_verifier_data_target(&targets.node_verifier_data, &input.verifier_only_data)
|
||||
.map_err(|e| {
|
||||
CircuitError::VerifierDataTargetAssignmentError(e.to_string())
|
||||
})?;
|
||||
|
||||
// assign the condition
|
||||
pw.set_bool_target(targets.condition, condition)
|
||||
pw.set_bool_target(targets.condition, input.condition)
|
||||
.map_err(|e| CircuitError::BoolTargetAssignmentError("condition".to_string(), e.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns the leaf circuit data
|
||||
pub fn get_circuit_data (&self) -> Result<CircuitData<F, C, D>>
|
||||
where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config.clone());
|
||||
|
||||
self.build(&mut builder)?;
|
||||
|
||||
let circ_data = builder.build::<C>();
|
||||
|
||||
Ok(circ_data)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
||||
use plonky2_field::extension::Extendable;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
||||
use crate::{error::CircuitError, Result};
|
||||
use crate::circuit_helper::Plonky2Circuit;
|
||||
|
||||
/// A circuit that verifies the aggregated public inputs from inner circuits.
|
||||
///
|
||||
@ -16,6 +17,7 @@ use crate::{error::CircuitError, Result};
|
||||
/// - `M`: Number of leaf proofs aggregated at the node level.
|
||||
/// - `T`: Total Number of inner-proofs.
|
||||
/// - `K`: Number of public input field elements per inner-proof (sampling proof).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PublicInputVerificationCircuit<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
@ -36,11 +38,25 @@ pub struct PublicInputVerificationCircuit<
|
||||
/// Holds the virtual targets for the circuit.
|
||||
/// - `inner_proof`: the proof to be verified and contains the public input to be verified.
|
||||
/// - `inner_pub_inputs`: A nested vector of targets with dimensions T×K.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PublicInputVerificationTargets<const D: usize> {
|
||||
pub inner_proof: ProofWithPublicInputsTarget<D>,
|
||||
pub inner_pub_inputs: Vec<Vec<Target>>,
|
||||
}
|
||||
|
||||
/// input to the circuit for public input verification
|
||||
/// - `inner_proof`: The tree root proof with 2 hash digests (8 Goldilocks field elements) public inputs [pi_hash, vd_hash].
|
||||
/// - `inner_pub_inputs_vals`: T×K public input values from inner proofs.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PublicInputVerificationInput<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
C: GenericConfig<D, F = F>,
|
||||
>{
|
||||
pub inner_proof: ProofWithPublicInputs<F, C, D>,
|
||||
pub inner_pub_inputs_vals: Vec<Vec<F>>,
|
||||
}
|
||||
|
||||
impl<F, const D: usize, C, H, const N: usize, const M: usize, const T: usize, const K: usize>
|
||||
PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
where
|
||||
@ -64,12 +80,23 @@ PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<F, const D: usize, C, H, const N: usize, const M: usize, const T: usize, const K: usize>
|
||||
Plonky2Circuit<F, C, D> for PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
where
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
C: GenericConfig<D, F = F>,
|
||||
H: AlgebraicHasher<F>,
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>,
|
||||
{
|
||||
type Targets = PublicInputVerificationTargets<D>;
|
||||
type Input = PublicInputVerificationInput<F, D, C>;
|
||||
|
||||
/// Builds the circuit by:
|
||||
/// 1. Verifies a proof target with 8 public inputs (the final [pi_hash, vd_hash]).
|
||||
/// 2. verifies correct tree hashing of all T×K targets to represent all inner public inputs.
|
||||
/// 3. verifies correct node_verifier_date used is the same as in public input (last 4 field elements).
|
||||
pub fn build(&self, builder: &mut CircuitBuilder<F, D>) -> Result<PublicInputVerificationTargets<D>> {
|
||||
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<PublicInputVerificationTargets<D>> {
|
||||
// Add a virtual proof with 8 public inputs. This is the final root proof whose
|
||||
// public inputs we want to check in-circuit.
|
||||
let inner_proof = builder.add_virtual_proof_with_pis(&self.node_common_data);
|
||||
@ -85,7 +112,11 @@ PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
for _ in 0..T {
|
||||
let mut row = Vec::with_capacity(K);
|
||||
for _ in 0..K {
|
||||
row.push(builder.add_virtual_public_input()); // public input
|
||||
if register_pi {
|
||||
row.push(builder.add_virtual_public_input()); // public input
|
||||
} else{
|
||||
row.push(builder.add_virtual_target());
|
||||
}
|
||||
}
|
||||
inner_pub_inputs.push(row);
|
||||
}
|
||||
@ -115,7 +146,9 @@ PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
let node_hash_t = builder.hash_n_to_hash_no_pad::<H>(node_vd_input_t);
|
||||
// make sure the VerifierData we use is the same as the tree root hash of the VerifierData
|
||||
builder.connect_hashes(node_hash_t,HashOutTarget::from_vec(node_vd_hash_t.to_vec()));
|
||||
builder.register_public_inputs(&node_hash_t.elements); // public input
|
||||
if register_pi {
|
||||
builder.register_public_inputs(&node_hash_t.elements); // public input
|
||||
}
|
||||
|
||||
let mut pub_in_hashes_t = Vec::new();
|
||||
|
||||
@ -174,31 +207,27 @@ PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
})
|
||||
}
|
||||
|
||||
/// Assigns witness values to the targets.
|
||||
/// - `inner_proof`: The tree root proof with 8 public inputs [pi_hash, vd_hash].
|
||||
/// - `inner_pub_inputs_vals`: T×K public input values from inner proofs.
|
||||
pub fn assign_targets(
|
||||
fn assign_targets(
|
||||
&self,
|
||||
pw: &mut PartialWitness<F>,
|
||||
targets: &PublicInputVerificationTargets<D>,
|
||||
inner_proof: ProofWithPublicInputs<F, C, D>,
|
||||
inner_pub_inputs_vals: Vec<Vec<F>>,
|
||||
targets: &Self::Targets,
|
||||
input: &Self::Input,
|
||||
) -> Result<()> {
|
||||
// Assign the final proof - it should have 8 public inputs
|
||||
pw.set_proof_with_pis_target(&targets.inner_proof, &inner_proof)
|
||||
pw.set_proof_with_pis_target(&targets.inner_proof, &input.inner_proof)
|
||||
.map_err(|e| {
|
||||
CircuitError::ProofTargetAssignmentError("final-proof".to_string(), e.to_string())
|
||||
})?;
|
||||
|
||||
// Assign T×K inner public inputs
|
||||
if inner_pub_inputs_vals.len() != T {
|
||||
if input.inner_pub_inputs_vals.len() != T {
|
||||
return Err(CircuitError::InvalidArgument(format!(
|
||||
"Expected T={} rows of inner_pub_inputs_vals, got {}",
|
||||
T,
|
||||
inner_pub_inputs_vals.len()
|
||||
input.inner_pub_inputs_vals.len()
|
||||
)));
|
||||
}
|
||||
for (i, row_vals) in inner_pub_inputs_vals.into_iter().enumerate() {
|
||||
for (i, row_vals) in input.inner_pub_inputs_vals.iter().enumerate() {
|
||||
if row_vals.len() != K {
|
||||
return Err(CircuitError::InvalidArgument(format!(
|
||||
"Expected K={} values in row {}, got {}",
|
||||
@ -207,7 +236,7 @@ PublicInputVerificationCircuit<F, D, C, H, N, M, T, K>
|
||||
row_vals.len()
|
||||
)));
|
||||
}
|
||||
for (j, val) in row_vals.into_iter().enumerate() {
|
||||
for (j, &val) in row_vals.into_iter().enumerate() {
|
||||
pw.set_target(targets.inner_pub_inputs[i][j], val).map_err(|e| {
|
||||
CircuitError::TargetAssignmentError(format!("inner public input index [{}][{}]", i,j), e.to_string())
|
||||
})?;
|
||||
|
||||
@ -1,17 +1,21 @@
|
||||
use std::marker::PhantomData;
|
||||
use plonky2::hash::hash_types::{HashOut, RichField};
|
||||
use plonky2::iop::witness::PartialWitness;
|
||||
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
||||
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierOnlyCircuitData};
|
||||
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
||||
use plonky2::plonk::proof::ProofWithPublicInputs;
|
||||
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
||||
use plonky2_field::extension::Extendable;
|
||||
use crate::{error::CircuitError, Result};
|
||||
use crate::circuit_helper::Plonky2Circuit;
|
||||
use crate::recursion::uniform::{leaf::{LeafTargets,LeafCircuit},node::{NodeTargets,NodeCircuit}};
|
||||
use crate::recursion::uniform::compress::{CompressionCircuit, CompressionTargets};
|
||||
use crate::recursion::uniform::compress::{CompressionCircuit, CompressionInput, CompressionTargets};
|
||||
use crate::recursion::uniform::leaf::LeafInput;
|
||||
use crate::recursion::uniform::node::NodeInput;
|
||||
|
||||
/// tree recursion
|
||||
/// - `N`: Number of inner-proofs aggregated at the leaf level.
|
||||
/// - `M`: Number of leaf proofs aggregated at the node level.
|
||||
pub struct TreeRecursion<
|
||||
F: RichField + Extendable<D> + Poseidon2,
|
||||
const D: usize,
|
||||
@ -31,7 +35,7 @@ pub struct TreeRecursion<
|
||||
leaf_targets: LeafTargets<D>,
|
||||
node_targets: NodeTargets<D>,
|
||||
compression_targets: CompressionTargets<D>,
|
||||
phantom_data: PhantomData<(H)>
|
||||
phantom_data: PhantomData<H>
|
||||
}
|
||||
|
||||
impl<
|
||||
@ -44,36 +48,38 @@ impl<
|
||||
> TreeRecursion<F, D, C, H, N, M> where
|
||||
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
|
||||
{
|
||||
|
||||
pub fn build(
|
||||
/// build with standard recursion config
|
||||
pub fn build_with_standard_config(
|
||||
inner_common_data: CommonCircuitData<F,D>,
|
||||
inner_verifier_data: VerifierOnlyCircuitData<C, D>,
|
||||
) -> Result<Self> {
|
||||
// build leaf with standard recursion config
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
Self::build(
|
||||
inner_common_data,
|
||||
inner_verifier_data,
|
||||
CircuitConfig::standard_recursion_config()
|
||||
)
|
||||
}
|
||||
|
||||
let leaf = LeafCircuit::<_,D,_,_,N>::new(inner_common_data.clone(), inner_verifier_data.clone());
|
||||
let leaf_targets = leaf.build(&mut builder)?;
|
||||
let leaf_circ_data = builder.build::<C>();
|
||||
/// build the tree with given config
|
||||
pub fn build(
|
||||
inner_common_data: CommonCircuitData<F,D>,
|
||||
inner_verifier_data: VerifierOnlyCircuitData<C, D>,
|
||||
config: CircuitConfig,
|
||||
) -> Result<Self> {
|
||||
// build leaf with standard recursion config
|
||||
let leaf = LeafCircuit::<_,D,_,_,N>::new(inner_common_data, inner_verifier_data);
|
||||
let (leaf_targets, leaf_circ_data) = leaf.build(config.clone())?;
|
||||
// println!("leaf circuit size = {:?}", leaf_circ_data.common.degree_bits());
|
||||
|
||||
// build node with standard recursion config
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let node = NodeCircuit::<_,D,_,_,M>::new(leaf_circ_data.common.clone(), leaf_circ_data.verifier_only.clone());
|
||||
let node_targets = node.build(&mut builder)?;
|
||||
let node_circ_data = builder.build::<C>();
|
||||
let (node_targets, node_circ_data) = node.build(config.clone())?;
|
||||
// println!("node circuit size = {:?}", node_circ_data.common.degree_bits());
|
||||
|
||||
// compression build
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
let node_common = node_circ_data.common.clone();
|
||||
let compression_circ = CompressionCircuit::new(node_common, node_circ_data.verifier_only.clone());
|
||||
let compression_targets = compression_circ.build(&mut builder)?;
|
||||
let compression_circ_data = builder.build::<C>();
|
||||
let (compression_targets, compression_circ_data) = compression_circ.build(config.clone())?;
|
||||
// println!("compress circuit size = {:?}", compression_circ_data.common.degree_bits());
|
||||
|
||||
Ok(Self{
|
||||
@ -109,12 +115,16 @@ impl<
|
||||
pub fn prove_tree_and_compress(
|
||||
&mut self,
|
||||
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
|
||||
) -> Result<(ProofWithPublicInputs<F, C, D>)>
|
||||
) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||
{
|
||||
let proof =
|
||||
self.prove_tree(proofs_with_pi)?;
|
||||
let mut pw = PartialWitness::<F>::new();
|
||||
self.compression.assign_targets(&mut pw, &self.compression_targets, proof)?;
|
||||
self.compression.assign_targets(
|
||||
&mut pw,
|
||||
&self.compression_targets,
|
||||
&CompressionInput{ inner_proof: proof},
|
||||
)?;
|
||||
|
||||
self.compression_circ_data.prove(pw).map_err(
|
||||
|e| CircuitError::InvalidProofError(e.to_string())
|
||||
@ -125,7 +135,7 @@ impl<
|
||||
(
|
||||
&mut self,
|
||||
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
|
||||
) -> Result<(ProofWithPublicInputs<F, C, D>)>
|
||||
) -> Result<ProofWithPublicInputs<F, C, D>>
|
||||
{
|
||||
if proofs_with_pi.len() % 2 != 0 {
|
||||
return
|
||||
@ -135,7 +145,7 @@ impl<
|
||||
}
|
||||
// process leaves
|
||||
let leaf_proofs = self.get_leaf_proofs(
|
||||
proofs_with_pi,
|
||||
&proofs_with_pi,
|
||||
)?;
|
||||
|
||||
// process nodes
|
||||
@ -149,14 +159,18 @@ impl<
|
||||
(
|
||||
&mut self,
|
||||
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
|
||||
) -> Result<(Vec<ProofWithPublicInputs<F, C, D>>)> {
|
||||
) -> Result<Vec<ProofWithPublicInputs<F, C, D>>> {
|
||||
|
||||
let mut leaf_proofs = vec![];
|
||||
|
||||
for proof in proofs_with_pi.chunks(N){
|
||||
for proofs in proofs_with_pi.chunks(N){
|
||||
let leaf_input = LeafInput{
|
||||
inner_proof: proofs.to_vec(),
|
||||
};
|
||||
|
||||
let mut pw = PartialWitness::<F>::new();
|
||||
|
||||
self.leaf.assign_targets(&mut pw,&self.leaf_targets,proof)?;
|
||||
self.leaf.assign_targets(&mut pw,&self.leaf_targets,&leaf_input)?;
|
||||
let proof = self.leaf_circ_data.prove(pw).unwrap();
|
||||
leaf_proofs.push(proof);
|
||||
}
|
||||
@ -186,12 +200,16 @@ impl<
|
||||
|
||||
let mut inner_pw = PartialWitness::new();
|
||||
|
||||
let node_input = NodeInput{
|
||||
node_proofs: chunk.to_vec().clone(),
|
||||
verifier_only_data: verifier_only_data.clone(),
|
||||
condition,
|
||||
};
|
||||
|
||||
self.node.assign_targets(
|
||||
&mut inner_pw,
|
||||
&self.node_targets,
|
||||
chunk,
|
||||
verifier_only_data,
|
||||
condition
|
||||
&node_input
|
||||
)?;
|
||||
|
||||
let proof = self.node_circ_data.prove(inner_pw)
|
||||
@ -256,7 +274,6 @@ impl<
|
||||
pub_in_hashes.push(hash);
|
||||
}
|
||||
|
||||
let mut level = 0;
|
||||
while pub_in_hashes.len() > 1 {
|
||||
let mut next_level_pi_hashes = Vec::new();
|
||||
for pi_chunk in pub_in_hashes.chunks(M) {
|
||||
@ -269,7 +286,6 @@ impl<
|
||||
next_level_pi_hashes.push(pi_hash);
|
||||
}
|
||||
pub_in_hashes = next_level_pi_hashes;
|
||||
level +=1;
|
||||
}
|
||||
|
||||
//check expected hash
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user