add hashing for verifierData

This commit is contained in:
M Alghazwi 2025-01-31 12:18:14 +01:00
parent 69983cd2dd
commit 5acbcf1f06
No known key found for this signature in database
GPG Key ID: 646E567CAD7DB607
5 changed files with 162 additions and 53 deletions

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget};
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable;
@ -46,15 +46,6 @@ pub struct LeafTargets <
pub inner_proof: ProofWithPublicInputsTarget<D>,
pub verifier_data: VerifierCircuitTarget,
}
#[derive(Clone, Debug)]
pub struct LeafInput<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
>{
pub inner_proof: ProofWithPublicInputs<F, C, D>,
pub verifier_data: VerifierCircuitData<F, C, D>
}
impl<
F: RichField + Extendable<D> + Poseidon2,
@ -83,6 +74,15 @@ impl<
// virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
// register verifier data hash as public input.
let mut vd_pub_input = vec![];
vd_pub_input.extend_from_slice(&inner_verifier_data.circuit_digest.elements);
for i in 0..builder.config.fri_config.num_cap_elements() {
vd_pub_input.extend_from_slice(&inner_verifier_data.constants_sigmas_cap.0[i].elements);
}
let hash_inner_vd_pub_input = builder.hash_n_to_hash_no_pad::<H>(vd_pub_input);
builder.register_public_inputs(&hash_inner_vd_pub_input.elements);
// verify the proofs in-circuit (only one )
builder.verify_proof::<C>(&vir_proof, &inner_verifier_data, &inner_common);
@ -99,16 +99,17 @@ impl<
pub fn assign_targets(
&self, pw: &mut PartialWitness<F>,
targets: &LeafTargets<D>,
input: &LeafInput<F, D, C>
inner_proof: &ProofWithPublicInputs<F, C, D>,
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<()> {
// assign the proofs
pw.set_proof_with_pis_target(&targets.inner_proof, &input.inner_proof)
pw.set_proof_with_pis_target(&targets.inner_proof, inner_proof)
.map_err(|e| {
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
})?;
// assign the verifier data
pw.set_verifier_data_target(&targets.verifier_data, &input.verifier_data.verifier_only)
pw.set_verifier_data_target(&targets.verifier_data, verifier_only_data)
.map_err(|e| {
CircuitError::VerifierDataTargetAssignmentError(e.to_string())
})?;

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget};
use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitTarget, VerifierOnlyCircuitData};
use plonky2::plonk::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable;
@ -47,15 +47,6 @@ pub struct NodeTargets<
pub leaf_proofs: [ProofWithPublicInputsTarget<D>; 2],
pub verifier_data: VerifierCircuitTarget,
}
#[derive(Clone, Debug)]
pub struct NodeInput<
F: RichField + Extendable<D> + Poseidon2,
const D: usize,
C: GenericConfig<D, F = F>,
>{
pub node_proofs: [ProofWithPublicInputs<F, C, D>;2],
pub verifier_data: VerifierCircuitData<F, C, D>
}
impl<
F: RichField + Extendable<D> + Poseidon2,
@ -71,14 +62,19 @@ impl<
let inner_common = self.leaf_common_data.clone();
// assert public input is of size 8 - 2 hashout
assert_eq!(inner_common.num_public_inputs, 8);
// the proof virtual targets - 2 proofs
let mut vir_proofs = vec![];
let mut pub_input = vec![];
let mut inner_vd_hashes = vec![];
for _i in 0..2 {
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
let inner_pub_input = vir_proof.public_inputs.clone();
vir_proofs.push(vir_proof);
pub_input.extend_from_slice(&inner_pub_input);
pub_input.extend_from_slice(&inner_pub_input[0..4]);
inner_vd_hashes.extend_from_slice(&inner_pub_input[4..8]);
}
// hash the public input & make it public
@ -88,6 +84,17 @@ impl<
// virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height);
// register verifier data hash as public input. H(H_l, H_l, H_n) -> public input
let mut vd_pub_input = vec![];
vd_pub_input.extend_from_slice(&inner_verifier_data.circuit_digest.elements);
for i in 0..builder.config.fri_config.num_cap_elements() {
vd_pub_input.extend_from_slice(&inner_verifier_data.constants_sigmas_cap.0[i].elements);
}
let vd_hash = builder.hash_n_to_hash_no_pad::<H>(vd_pub_input);
inner_vd_hashes.extend_from_slice(&vd_hash.elements);
let vd_hash_all = builder.hash_n_to_hash_no_pad::<H>(inner_vd_hashes);
builder.register_public_inputs(&vd_hash_all.elements);
// verify the proofs in-circuit - 2 proofs
for i in 0..2 {
builder.verify_proof::<C>(&vir_proofs[i], &inner_verifier_data, &inner_common);
@ -108,18 +115,22 @@ impl<
pub fn assign_targets(
&self, pw: &mut PartialWitness<F>,
targets: &NodeTargets<D>,
input: &NodeInput<F, D, C>
node_proofs: &[ProofWithPublicInputs<F, C, D>],
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<()> {
// assert size of proofs vec
assert_eq!(node_proofs.len(), 2);
// assign the proofs
for i in 0..2 {
pw.set_proof_with_pis_target(&targets.leaf_proofs[i], &input.node_proofs[i])
pw.set_proof_with_pis_target(&targets.leaf_proofs[i], &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.verifier_data, &input.verifier_data.verifier_only)
pw.set_verifier_data_target(&targets.verifier_data, &verifier_only_data)
.map_err(|e| {
CircuitError::VerifierDataTargetAssignmentError(e.to_string())
})?;

View File

@ -1,15 +1,15 @@
use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField;
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};
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 crate::recursion::circuits::inner_circuit::InnerCircuit;
use plonky2_field::extension::Extendable;
use crate::{error::CircuitError, Result};
use crate::recursion::uniform::{leaf::{LeafTargets,LeafInput,LeafCircuit},node::{NodeTargets,NodeInput,NodeCircuit}};
use crate::recursion::uniform::{leaf::{LeafTargets,LeafCircuit},node::{NodeTargets,NodeCircuit}};
/// tree recursion
pub struct TreeRecursion<
@ -70,11 +70,19 @@ impl<
})
}
pub fn get_leaf_verifier_data(&self) -> VerifierCircuitData<F, C, D>{
self.leaf_circ_data.verifier_data()
}
pub fn get_node_verifier_data(&self) -> VerifierCircuitData<F, C, D>{
self.node_circ_data.verifier_data()
}
pub fn prove_tree
(
&mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
inner_verifier_data: VerifierCircuitData<F, C, D>,
inner_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(ProofWithPublicInputs<F, C, D>)>
{
if proofs_with_pi.len() % 2 != 0 {
@ -86,33 +94,29 @@ impl<
// process leaves
let leaf_proofs = self.get_leaf_proofs(
proofs_with_pi,
inner_verifier_data,
inner_verifier_only_data,
)?;
// process nodes
let (root_proof, vd) =
self.prove(&leaf_proofs,self.leaf_circ_data.verifier_data())?;
self.prove(&leaf_proofs,&self.leaf_circ_data.verifier_only)?;
Ok(root_proof)
}
fn get_leaf_proofs
(
&mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
inner_verifier_data: VerifierCircuitData<F, C, D>,
inner_verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(Vec<ProofWithPublicInputs<F, C, D>>)> {
let mut leaf_proofs = vec![];
for proof in proofs_with_pi{
let mut pw = PartialWitness::<F>::new();
let leaf_in = LeafInput{
inner_proof: proof.clone(),
verifier_data: inner_verifier_data.clone(),
};
self.leaf.assign_targets(&mut pw,&self.leaf_targets,&leaf_in)?;
self.leaf.assign_targets(&mut pw,&self.leaf_targets,proof,inner_verifier_only_data)?;
let proof = self.leaf_circ_data.prove(pw).unwrap();
leaf_proofs.push(proof);
}
@ -122,15 +126,15 @@ impl<
/// generates a proof - only one node
fn prove(
&mut self,
&self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
verifier_data: VerifierCircuitData<F, C, D>,
) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierCircuitData<F, C, D>)> where
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierOnlyCircuitData<C, D>)> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{
if proofs_with_pi.len() == 1 {
return Ok((proofs_with_pi[0].clone(), verifier_data));
return Ok((proofs_with_pi[0].clone(), verifier_only_data.clone()));
}
let mut new_proofs = vec![];
@ -138,18 +142,102 @@ impl<
for chunk in proofs_with_pi.chunks(2) {
let mut inner_pw = PartialWitness::new();
let node_in = NodeInput{
node_proofs: [chunk[0].clone(), chunk[1].clone()],
verifier_data: verifier_data.clone() ,
};
self.node.assign_targets(&mut inner_pw,&self.node_targets,&node_in)?;
self.node.assign_targets(
&mut inner_pw,
&self.node_targets,
chunk,
verifier_only_data,
)?;
let proof = self.node_circ_data.prove(inner_pw)
.map_err(|e| CircuitError::ProofGenerationError(e.to_string()))?;
new_proofs.push(proof);
}
self.prove(&new_proofs, self.node_circ_data.verifier_data())
self.prove(&new_proofs, &self.node_circ_data.verifier_only)
}
pub fn verify_proof_and_public_input(
&self,
proof: ProofWithPublicInputs<F, C, D>,
inner_public_input: Vec<Vec<F>>,
inner_verifier_data: &VerifierCircuitData<F, C, D>) -> Result<()>
{
let public_input = proof.public_inputs.clone();
self.node_circ_data.verify(proof)
.map_err(|e| CircuitError::InvalidProofError(e.to_string()))?;
self.verify_public_input(public_input, inner_public_input, inner_verifier_data)
}
pub fn verify_public_input(&self, public_input: Vec<F>, inner_public_input: Vec<Vec<F>>, inner_verifier_data: &VerifierCircuitData<F, C, D>) -> Result<()>{
assert_eq!(public_input.len(), 8);
let given_input_hash = &public_input[0..4];
let given_vd_hash = &public_input[4..8];
let inner_hash = Self::get_hash_of_verifier_data(&inner_verifier_data);
let leaf_hash = Self::get_hash_of_verifier_data(&self.leaf_circ_data.verifier_data());
let node_hash = Self::get_hash_of_verifier_data(&self.node_circ_data.verifier_data());
let mut pub_in_hashes = vec![];
let mut inner_vd_hashes = vec![];
for pub_in in inner_public_input{
let hash = H::hash_no_pad(&pub_in);
pub_in_hashes.push(hash);
inner_vd_hashes.push(inner_hash.clone());
}
let mut level = 0;
while pub_in_hashes.len() > 1 {
let mut next_level_pi_hashes = Vec::new();
let mut next_level_vd_hashes = Vec::new();
for (pi_chunk, vd_chunk) in pub_in_hashes.chunks(2).zip(inner_vd_hashes.chunks(2)) {
// collect field elements
let pi_chunk_f: Vec<F> = pi_chunk.iter()
.flat_map(|h| h.elements.iter().cloned())
.collect();
let mut vd_chunk_f: Vec<F> = vd_chunk.iter()
.flat_map(|h| h.elements.iter().cloned())
.collect();
let hash_n = if level == 0 {leaf_hash} else{node_hash};
vd_chunk_f.extend_from_slice(&hash_n.elements);
// Compute Poseidon2 hash of the concatenated chunk
let pi_hash = H::hash_no_pad(&pi_chunk_f);
let vd_hash = H::hash_no_pad(&vd_chunk_f);
next_level_pi_hashes.push(pi_hash);
next_level_vd_hashes.push(vd_hash);
}
pub_in_hashes = next_level_pi_hashes;
inner_vd_hashes = next_level_vd_hashes;
level +=1;
}
//check expected hash
let expected_pi_hash = pub_in_hashes[0];
let expected_vd_hash = inner_vd_hashes[0];
assert_eq!(given_input_hash, expected_pi_hash.elements);
assert_eq!(given_vd_hash, expected_vd_hash.elements);
Ok(())
}
/// helper fn to generate hash of verifier data
fn get_hash_of_verifier_data(verifier_data: &VerifierCircuitData<F, C, D>) -> HashOut<F>{
let mut vd = vec![];
let digest: &HashOut<F> = &verifier_data.verifier_only.circuit_digest;
let caps = &verifier_data.verifier_only.constants_sigmas_cap;
vd.extend_from_slice(&digest.elements);
for i in 0..verifier_data.common.config.fri_config.num_cap_elements() {
let cap_hash = caps.0[i] as HashOut<F>;
vd.extend_from_slice(&cap_hash.elements);
}
H::hash_no_pad(&vd)
}
}

View File

@ -2,6 +2,7 @@
#[cfg(test)]
mod tests {
use std::fs;
use plonky2::iop::witness::{PartialWitness};
use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig};
@ -40,9 +41,16 @@ mod tests {
let mut tree = TreeRecursion::<F,D,C,HF>::build(inner_data.common.clone())?;
let root = tree.prove_tree(&proofs, inner_data.verifier_data())?;
let root = tree.prove_tree(&proofs, &inner_data.verifier_only)?;
println!("pub input size = {}", root.public_inputs.len());
let inner_pi: Vec<Vec<F>> = proofs.iter().map(|p| p.public_inputs.clone()).collect();
assert!(
tree.verify_proof_and_public_input(root,inner_pi,&inner_data.verifier_data()).is_ok(),
"proof verification failed"
);
Ok(())
}
}

View File

@ -51,7 +51,7 @@ fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result
// Proving Phase
group.bench_function("prove tree", |b| {
b.iter(|| {
proof = Some(tree.prove_tree(&proofs, inner_data.verifier_data()).unwrap());
proof = Some(tree.prove_tree(&proofs, &inner_data.verifier_only).unwrap());
})
});
@ -70,6 +70,7 @@ fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result
fn bench_uniform_tree_recursion(c: &mut Criterion){
const N: usize = 2; // number of child nodes - binary here
bench_uniform_recursion::<2>(c).expect("bench failed");
bench_uniform_recursion::<4>(c).expect("bench failed");
bench_uniform_recursion::<8>(c).expect("bench failed");
bench_uniform_recursion::<16>(c).expect("bench failed");