add generic circuit helper and refactor.

This commit is contained in:
M Alghazwi 2025-04-03 14:27:54 +02:00
parent 65508f3da9
commit c4eefceba8
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
5 changed files with 139 additions and 13 deletions

View File

@ -0,0 +1,105 @@
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, ProverCircuitData, VerifierCircuitData};
use plonky2::plonk::config::GenericConfig;
use plonky2::plonk::proof::ProofWithPublicInputs;
use plonky2_field::extension::Extendable;
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
use crate::error::CircuitError;
use crate::Result;
/// Plonky2Circuit is the trait used to define the logic of the circuit and assign witnesses
/// to that circuit instance.
pub trait Plonky2Circuit<
F: RichField + Extendable<D> + Poseidon2,
C: GenericConfig<D, F = F>,
const D: usize,
> {
type Targets;
type Input:Clone;
/// build the circuit with standard config
fn build_with_standard_config(
&self,
) -> Result<(Self::Targets,CircuitData<F, C, D>)>{
self.build(CircuitConfig::standard_recursion_config())
}
/// build the circuit
fn build(
&self,
circuit_config: CircuitConfig,
) -> Result<(Self::Targets,CircuitData<F, C, D>)>{
let mut builder = CircuitBuilder::<F, D>::new(circuit_config);
let targets = self.add_targets(&mut builder, true)?;
Ok((targets,builder.build::<C>()))
}
/// build the circuit logic and return targets to be assigned later
/// based on register_pi, registers the public input or not.
fn add_targets(
&self,
builder: &mut CircuitBuilder<F, D>,
register_pi: bool
) -> Result<Self::Targets>;
/// assign the actual witness values for the current instance of the circuit.
fn assign_targets(
&self,
pw: &mut PartialWitness<F>,
targets: &Self::Targets,
input: &Self::Input,
) -> Result<()>;
/// get the common data for the circuit with standard config
fn get_common_data_standard_config(
&self
) -> Result<CircuitData<F, C, D>>{
self.get_common_data(CircuitConfig::standard_recursion_config())
}
/// get the common data for the circuit
fn get_common_data(
&self,
circuit_config: CircuitConfig,
) -> Result<CircuitData<F, C, D>>{
let mut builder = CircuitBuilder::<F, D>::new(circuit_config);
self.add_targets(&mut builder, true)?;
let circ_data = builder.build::<C>();
Ok(circ_data)
}
/// generates a proof for the circuit using the given targets, input and prover circuit data
fn prove(
&self,
targets: &Self::Targets,
input: &Self::Input,
prover_circuit_data: ProverCircuitData<F, C, D>
)-> Result<ProofWithPublicInputs<F, C, D>>{
let mut pw = PartialWitness::new();
self.assign_targets(&mut pw, targets, input)?;
let proof = prover_circuit_data.prove(pw).map_err(
|e| CircuitError::ProofGenerationError(e.to_string())
)?;
Ok(proof)
}
/// verify the given proof with the verifier circuit data
fn verify(
proof_with_pi: ProofWithPublicInputs<F, C, D>,
verifier_circuit_data: VerifierCircuitData<F, C, D>
) -> Result<()>{
verifier_circuit_data.verify(proof_with_pi).map_err( |e|
CircuitError::InvalidProofError(e.to_string())
)
}
}

View File

@ -3,7 +3,7 @@
// https://github.com/codex-storage/codex-storage-proofs-circuits/blob/master/circuit/codex/merkle.circom
use plonky2::{
field::{extension::Extendable, types::Field},
field::extension::Extendable,
hash::hash_types::{HashOutTarget, RichField, NUM_HASH_OUT_ELTS},
iop::target::BoolTarget,
plonk::{

View File

@ -11,7 +11,6 @@ use plonky2::{
field::extension::Extendable,
hash::{
hash_types::{HashOut, HashOutTarget, NUM_HASH_OUT_ELTS, RichField},
hashing::PlonkyPermutation,
},
iop::{
target::{BoolTarget, Target},
@ -19,7 +18,6 @@ use plonky2::{
},
plonk::circuit_builder::CircuitBuilder,
};
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2;
@ -33,6 +31,7 @@ use crate::{
Result,
error::CircuitError,
};
use crate::circuit_helper::Plonky2Circuit;
/// circuit for sampling a slot in a dataset merkle tree
#[derive(Clone, Debug)]
@ -168,7 +167,6 @@ impl<
// Create virtual target for slot root and index
let slot_root = builder.add_virtual_hash();
let slot_index = builder.add_virtual_target();// public input
// let slot_index = builder.add_virtual_public_input();// public input
// dataset path bits (binary decomposition of leaf_index)
let d_path_bits = builder.split_le(slot_index,max_log2_n_slots);
@ -200,7 +198,6 @@ impl<
// expected Merkle root
let d_expected_root = builder.add_virtual_hash(); // public input
// let d_expected_root = builder.add_virtual_hash_public_input(); // public input
// check equality with expected root
for i in 0..NUM_HASH_OUT_ELTS {
@ -212,7 +209,6 @@ impl<
let mut data_targets =vec![];
let mut slot_sample_proofs = vec![];
let entropy_target = builder.add_virtual_hash(); // public input
// let entropy_target = builder.add_virtual_hash_public_input(); // public input
// virtual target for n_cells_per_slot
let n_cells_per_slot = builder.add_virtual_target();
@ -229,7 +225,7 @@ impl<
let mut b_mask_bits = builder.split_le(slot_last_index,max_depth);
// last and mask bits for the slot tree
let mut s_last_bits = b_last_bits.split_off(block_tree_depth);
let s_last_bits = b_last_bits.split_off(block_tree_depth);
let mut s_mask_bits = b_mask_bits.split_off(block_tree_depth);
// pad mask bits with 0
@ -238,7 +234,7 @@ impl<
for i in 0..n_samples{
// cell data targets
let mut data_i = (0..n_field_elems_per_cell).map(|_| builder.add_virtual_target()).collect::<Vec<_>>();
let data_i = (0..n_field_elems_per_cell).map(|_| builder.add_virtual_target()).collect::<Vec<_>>();
// hash the cell data
let mut hash_inputs:Vec<Target>= Vec::new();
hash_inputs.extend_from_slice(&data_i);
@ -256,13 +252,13 @@ impl<
}
// paths for block and slot
let mut b_path_bits = self.calculate_cell_index_bits(builder, &entropy_target, &d_targets.leaf, &ctr, mask_bits.clone())?;
let mut s_path_bits = b_path_bits.split_off(block_tree_depth);
let s_path_bits = b_path_bits.split_off(block_tree_depth);
let mut b_merkle_path = MerkleProofTarget {
let b_merkle_path = MerkleProofTarget {
path: (0..block_tree_depth).map(|_| builder.add_virtual_hash()).collect(),
};
let mut s_merkle_path = MerkleProofTarget {
let s_merkle_path = MerkleProofTarget {
path: (0..(max_depth - block_tree_depth)).map(|_| builder.add_virtual_hash()).collect(),
};
@ -323,7 +319,7 @@ impl<
}
/// calculate the cell index = H( entropy | slotRoot | counter ) `mod` nCells
pub fn calculate_cell_index_bits(&self, builder: &mut CircuitBuilder<F, D>, entropy: &HashOutTarget, slot_root: &HashOutTarget, ctr: &HashOutTarget, mask_bits: Vec<BoolTarget>) -> Result<Vec<BoolTarget>> {
fn calculate_cell_index_bits(&self, builder: &mut CircuitBuilder<F, D>, entropy: &HashOutTarget, slot_root: &HashOutTarget, ctr: &HashOutTarget, mask_bits: Vec<BoolTarget>) -> Result<Vec<BoolTarget>> {
let mut hash_inputs:Vec<Target>= Vec::new();
hash_inputs.extend_from_slice(&entropy.elements);
hash_inputs.extend_from_slice(&slot_root.elements);
@ -421,3 +417,27 @@ impl<
}
}
/// Implements the Plonky2Circuit trait
impl<
F: RichField + Extendable<D> + Poseidon2,
C: GenericConfig<D, F = F>,
const D: usize,
H: AlgebraicHasher<F>,
> Plonky2Circuit<F,C,D> for SampleCircuit<F,D,H> {
type Targets = SampleTargets;
type Input = SampleCircuitInput<F, D>;
fn add_targets(&self, builder: &mut CircuitBuilder<F, D>, register_pi: bool) -> Result<Self::Targets> {
let targets = if register_pi {
self.sample_slot_circuit_with_public_input(builder)
} else { self.sample_slot_circuit(builder) };
targets
}
fn assign_targets(&self, pw: &mut PartialWitness<F>, targets: &Self::Targets, input: &Self::Input) -> Result<()> {
self.sample_slot_assign_witness(pw,targets,input)
}
}

View File

@ -1,5 +1,6 @@
pub mod circuits;
pub mod recursion;
pub mod error;
pub mod circuit_helper;
pub type Result<T> = core::result::Result<T, error::CircuitError>;

View File

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