2025-01-09 10:32:14 +01:00
|
|
|
// the simple aggregation approach is verifying N proofs in-circuit and generating one final proof
|
2024-12-13 16:36:26 +03:00
|
|
|
|
2025-01-14 10:54:43 +01:00
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField};
|
2024-12-06 09:12:02 +01:00
|
|
|
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
|
|
|
|
|
use plonky2::plonk::circuit_builder::CircuitBuilder;
|
2025-01-09 10:32:14 +01:00
|
|
|
use plonky2::plonk::circuit_data::{VerifierCircuitData, VerifierCircuitTarget};
|
2025-01-14 10:54:43 +01:00
|
|
|
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
|
2024-12-06 09:12:02 +01:00
|
|
|
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
|
2025-01-14 10:54:43 +01:00
|
|
|
use plonky2_field::extension::Extendable;
|
2024-12-06 09:12:02 +01:00
|
|
|
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
|
2025-01-14 10:54:43 +01:00
|
|
|
use crate::error::CircuitError;
|
2025-01-10 11:29:03 +01:00
|
|
|
use crate::recursion::circuits::inner_circuit::InnerCircuit;
|
|
|
|
|
use crate::Result;
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-14 10:54:43 +01:00
|
|
|
// ---------------------- Simple recursion Approach 1 ---------------------------
|
2025-01-09 10:32:14 +01:00
|
|
|
// The simple approach here separates the build (setting the targets) and assigning the witness.
|
2025-01-14 10:54:43 +01:00
|
|
|
// the public input of the inner-proofs is the public input of the final proof except that
|
|
|
|
|
// the entropy is expected to be the same therefore only one entropy public input is in the final proof
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
pub struct SimpleRecursionCircuit<
|
2025-01-14 10:54:43 +01:00
|
|
|
F: RichField + Extendable<D> + Poseidon2,
|
|
|
|
|
const D: usize,
|
|
|
|
|
I: InnerCircuit<F, D>,
|
2025-01-09 10:32:14 +01:00
|
|
|
const N: usize,
|
2025-01-14 10:54:43 +01:00
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
> {
|
2025-01-09 10:32:14 +01:00
|
|
|
pub inner_circuit: I,
|
2025-01-14 10:54:43 +01:00
|
|
|
phantom_data: PhantomData<(F,C)>
|
2025-01-09 10:32:14 +01:00
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct SimpleRecursionTargets<
|
2025-01-14 10:54:43 +01:00
|
|
|
const D: usize,
|
2025-01-09 10:32:14 +01:00
|
|
|
> {
|
|
|
|
|
pub proofs_with_pi: Vec<ProofWithPublicInputsTarget<D>>,
|
|
|
|
|
pub verifier_data: VerifierCircuitTarget,
|
|
|
|
|
pub entropy: HashOutTarget,
|
|
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
pub struct SimpleRecursionInput<
|
2025-01-14 10:54:43 +01:00
|
|
|
F: RichField + Extendable<D> + Poseidon2,
|
|
|
|
|
const D: usize,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
2025-01-09 10:32:14 +01:00
|
|
|
>{
|
|
|
|
|
pub proofs: Vec<ProofWithPublicInputs<F, C, D>>,
|
|
|
|
|
pub verifier_data: VerifierCircuitData<F, C, D>,
|
|
|
|
|
pub entropy: HashOut<F>,
|
|
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
impl<
|
2025-01-14 10:54:43 +01:00
|
|
|
F: RichField + Extendable<D> + Poseidon2,
|
|
|
|
|
const D: usize,
|
|
|
|
|
I: InnerCircuit<F, D>,
|
2025-01-09 10:32:14 +01:00
|
|
|
const N: usize,
|
2025-01-14 10:54:43 +01:00
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
> SimpleRecursionCircuit<F, D, I, N, C> where
|
|
|
|
|
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>,
|
2025-01-09 10:32:14 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
pub fn new(
|
|
|
|
|
inner_circuit: I,
|
|
|
|
|
)->Self{
|
|
|
|
|
Self{
|
|
|
|
|
inner_circuit,
|
2025-01-14 10:54:43 +01:00
|
|
|
phantom_data: PhantomData::default(),
|
2025-01-09 10:32:14 +01:00
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
/// contains the circuit logic and returns the witness & public input targets
|
2025-01-14 10:54:43 +01:00
|
|
|
pub fn build_circuit<
|
|
|
|
|
>(
|
2025-01-09 10:32:14 +01:00
|
|
|
&self,
|
2025-01-14 10:54:43 +01:00
|
|
|
builder: &mut CircuitBuilder<F, D>,
|
|
|
|
|
) -> Result<SimpleRecursionTargets<D>>{
|
2025-01-09 10:32:14 +01:00
|
|
|
// the proof virtual targets
|
|
|
|
|
let mut proof_targets = vec![];
|
|
|
|
|
let mut inner_entropy_targets = vec![];
|
|
|
|
|
let inner_common = self.inner_circuit.get_common_data()?;
|
|
|
|
|
|
2025-01-14 10:54:43 +01:00
|
|
|
for _ in 0..N {
|
2025-01-09 10:32:14 +01:00
|
|
|
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
|
|
|
|
|
// register the inner public input as public input
|
|
|
|
|
// only register the slot index and dataset root, entropy later
|
|
|
|
|
// assuming public input are ordered:
|
|
|
|
|
// [slot_root (1 element), dataset_root (4 element), entropy (4 element)]
|
|
|
|
|
let num_pub_input = vir_proof.public_inputs.len();
|
|
|
|
|
for j in 0..(num_pub_input-4){
|
|
|
|
|
builder.register_public_input(vir_proof.public_inputs[j]);
|
|
|
|
|
}
|
|
|
|
|
// collect entropy targets
|
|
|
|
|
let mut entropy_i = vec![];
|
|
|
|
|
for k in (num_pub_input-4)..num_pub_input{
|
|
|
|
|
entropy_i.push(vir_proof.public_inputs[k])
|
|
|
|
|
}
|
|
|
|
|
inner_entropy_targets.push(entropy_i);
|
|
|
|
|
proof_targets.push(vir_proof);
|
|
|
|
|
}
|
|
|
|
|
// virtual target for the verifier data
|
|
|
|
|
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
// verify the proofs in-circuit
|
|
|
|
|
for i in 0..N {
|
|
|
|
|
builder.verify_proof::<C>(&proof_targets[i],&inner_verifier_data,&inner_common);
|
|
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
// register entropy as public input
|
|
|
|
|
let outer_entropy_target = builder.add_virtual_hash_public_input();
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
// connect the public input of the recursion circuit to the inner proofs
|
|
|
|
|
for i in 0..N {
|
|
|
|
|
for j in 0..4 {
|
|
|
|
|
builder.connect(inner_entropy_targets[i][j], outer_entropy_target.elements[j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// return targets
|
|
|
|
|
let srt = SimpleRecursionTargets {
|
|
|
|
|
proofs_with_pi: proof_targets,
|
|
|
|
|
verifier_data: inner_verifier_data,
|
|
|
|
|
entropy: outer_entropy_target,
|
|
|
|
|
};
|
|
|
|
|
Ok(srt)
|
|
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
/// assign the targets
|
2025-01-14 10:54:43 +01:00
|
|
|
pub fn assign_witness<
|
|
|
|
|
>(
|
2025-01-09 10:32:14 +01:00
|
|
|
&self,
|
|
|
|
|
pw: &mut PartialWitness<F>,
|
2025-01-14 10:54:43 +01:00
|
|
|
targets: &SimpleRecursionTargets<D>,
|
|
|
|
|
witnesses: SimpleRecursionInput<F, D, C>,
|
|
|
|
|
) -> Result<()>{
|
2025-01-09 10:32:14 +01:00
|
|
|
// assign the proofs with public input
|
|
|
|
|
for i in 0..N{
|
2025-01-14 10:54:43 +01:00
|
|
|
pw.set_proof_with_pis_target(&targets.proofs_with_pi[i],&witnesses.proofs[i])
|
|
|
|
|
.map_err(|e| {
|
|
|
|
|
CircuitError::ProofTargetAssignmentError(format!("proof {}", i), e.to_string())
|
|
|
|
|
})?;
|
2025-01-09 10:32:14 +01:00
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
// assign the verifier data
|
2025-01-14 10:54:43 +01:00
|
|
|
pw.set_verifier_data_target(&targets.verifier_data, &witnesses.verifier_data.verifier_only)
|
|
|
|
|
.map_err(|e| {
|
|
|
|
|
CircuitError::VerifierDataTargetAssignmentError(e.to_string())
|
|
|
|
|
})?;
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
// set the entropy hash target
|
2025-01-14 10:54:43 +01:00
|
|
|
pw.set_hash_target(targets.entropy, witnesses.entropy)
|
|
|
|
|
.map_err(|e| {
|
|
|
|
|
CircuitError::HashTargetAssignmentError("entropy".to_string(), e.to_string())
|
|
|
|
|
})?;
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
Ok(())
|
2024-12-06 09:12:02 +01:00
|
|
|
|
2025-01-09 10:32:14 +01:00
|
|
|
}
|
2024-12-06 09:12:02 +01:00
|
|
|
}
|