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::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder; 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::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
@ -46,15 +46,6 @@ pub struct LeafTargets <
pub inner_proof: ProofWithPublicInputsTarget<D>, pub inner_proof: ProofWithPublicInputsTarget<D>,
pub verifier_data: VerifierCircuitTarget, 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< impl<
F: RichField + Extendable<D> + Poseidon2, F: RichField + Extendable<D> + Poseidon2,
@ -83,6 +74,15 @@ impl<
// virtual target for the verifier data // virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height); 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 ) // verify the proofs in-circuit (only one )
builder.verify_proof::<C>(&vir_proof, &inner_verifier_data, &inner_common); builder.verify_proof::<C>(&vir_proof, &inner_verifier_data, &inner_common);
@ -99,16 +99,17 @@ impl<
pub fn assign_targets( pub fn assign_targets(
&self, pw: &mut PartialWitness<F>, &self, pw: &mut PartialWitness<F>,
targets: &LeafTargets<D>, targets: &LeafTargets<D>,
input: &LeafInput<F, D, C> inner_proof: &ProofWithPublicInputs<F, C, D>,
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<()> { ) -> Result<()> {
// assign the proofs // 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| { .map_err(|e| {
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string()) CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
})?; })?;
// assign the verifier data // 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| { .map_err(|e| {
CircuitError::VerifierDataTargetAssignmentError(e.to_string()) CircuitError::VerifierDataTargetAssignmentError(e.to_string())
})?; })?;

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField; use plonky2::hash::hash_types::RichField;
use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::iop::witness::{PartialWitness, WitnessWrite};
use plonky2::plonk::circuit_builder::CircuitBuilder; 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::config::{AlgebraicHasher, GenericConfig};
use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget};
use plonky2_field::extension::Extendable; use plonky2_field::extension::Extendable;
@ -47,15 +47,6 @@ pub struct NodeTargets<
pub leaf_proofs: [ProofWithPublicInputsTarget<D>; 2], pub leaf_proofs: [ProofWithPublicInputsTarget<D>; 2],
pub verifier_data: VerifierCircuitTarget, 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< impl<
F: RichField + Extendable<D> + Poseidon2, F: RichField + Extendable<D> + Poseidon2,
@ -71,14 +62,19 @@ impl<
let inner_common = self.leaf_common_data.clone(); 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 // the proof virtual targets - 2 proofs
let mut vir_proofs = vec![]; let mut vir_proofs = vec![];
let mut pub_input = vec![]; let mut pub_input = vec![];
let mut inner_vd_hashes = vec![];
for _i in 0..2 { for _i in 0..2 {
let vir_proof = builder.add_virtual_proof_with_pis(&inner_common); let vir_proof = builder.add_virtual_proof_with_pis(&inner_common);
let inner_pub_input = vir_proof.public_inputs.clone(); let inner_pub_input = vir_proof.public_inputs.clone();
vir_proofs.push(vir_proof); 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 // hash the public input & make it public
@ -88,6 +84,17 @@ impl<
// virtual target for the verifier data // virtual target for the verifier data
let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height); 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 // verify the proofs in-circuit - 2 proofs
for i in 0..2 { for i in 0..2 {
builder.verify_proof::<C>(&vir_proofs[i], &inner_verifier_data, &inner_common); builder.verify_proof::<C>(&vir_proofs[i], &inner_verifier_data, &inner_common);
@ -108,18 +115,22 @@ impl<
pub fn assign_targets( pub fn assign_targets(
&self, pw: &mut PartialWitness<F>, &self, pw: &mut PartialWitness<F>,
targets: &NodeTargets<D>, targets: &NodeTargets<D>,
input: &NodeInput<F, D, C> node_proofs: &[ProofWithPublicInputs<F, C, D>],
verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<()> { ) -> Result<()> {
// assert size of proofs vec
assert_eq!(node_proofs.len(), 2);
// assign the proofs // assign the proofs
for i in 0..2 { 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| { .map_err(|e| {
CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string()) CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string())
})?; })?;
} }
// assign the verifier data // 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| { .map_err(|e| {
CircuitError::VerifierDataTargetAssignmentError(e.to_string()) CircuitError::VerifierDataTargetAssignmentError(e.to_string())
})?; })?;

View File

@ -1,15 +1,15 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use plonky2::hash::hash_types::RichField; use plonky2::hash::hash_types::{HashOut, RichField};
use plonky2::iop::witness::PartialWitness; use plonky2::iop::witness::PartialWitness;
use plonky2::plonk::circuit_builder::CircuitBuilder; 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::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_field::extension::Extendable; use plonky2_field::extension::Extendable;
use crate::{error::CircuitError, Result}; 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 /// tree recursion
pub struct TreeRecursion< 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 pub fn prove_tree
( (
&mut self, &mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>], 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>)> ) -> Result<(ProofWithPublicInputs<F, C, D>)>
{ {
if proofs_with_pi.len() % 2 != 0 { if proofs_with_pi.len() % 2 != 0 {
@ -86,33 +94,29 @@ impl<
// process leaves // process leaves
let leaf_proofs = self.get_leaf_proofs( let leaf_proofs = self.get_leaf_proofs(
proofs_with_pi, proofs_with_pi,
inner_verifier_data, inner_verifier_only_data,
)?; )?;
// process nodes // process nodes
let (root_proof, vd) = 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) Ok(root_proof)
} }
fn get_leaf_proofs fn get_leaf_proofs
( (
&mut self, &mut self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>], 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>>)> { ) -> Result<(Vec<ProofWithPublicInputs<F, C, D>>)> {
let mut leaf_proofs = vec![]; let mut leaf_proofs = vec![];
for proof in proofs_with_pi{ for proof in proofs_with_pi{
let mut pw = PartialWitness::<F>::new(); let mut pw = PartialWitness::<F>::new();
let leaf_in = LeafInput{
inner_proof: proof.clone(), self.leaf.assign_targets(&mut pw,&self.leaf_targets,proof,inner_verifier_only_data)?;
verifier_data: inner_verifier_data.clone(),
};
self.leaf.assign_targets(&mut pw,&self.leaf_targets,&leaf_in)?;
let proof = self.leaf_circ_data.prove(pw).unwrap(); let proof = self.leaf_circ_data.prove(pw).unwrap();
leaf_proofs.push(proof); leaf_proofs.push(proof);
} }
@ -122,15 +126,15 @@ impl<
/// generates a proof - only one node /// generates a proof - only one node
fn prove( fn prove(
&mut self, &self,
proofs_with_pi: &[ProofWithPublicInputs<F, C, D>], proofs_with_pi: &[ProofWithPublicInputs<F, C, D>],
verifier_data: VerifierCircuitData<F, C, D>, verifier_only_data: &VerifierOnlyCircuitData<C, D>,
) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierCircuitData<F, C, D>)> where ) -> Result<(ProofWithPublicInputs<F, C, D>, VerifierOnlyCircuitData<C, D>)> where
<C as GenericConfig<D>>::Hasher: AlgebraicHasher<F> <C as GenericConfig<D>>::Hasher: AlgebraicHasher<F>
{ {
if proofs_with_pi.len() == 1 { 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![]; let mut new_proofs = vec![];
@ -138,18 +142,102 @@ impl<
for chunk in proofs_with_pi.chunks(2) { for chunk in proofs_with_pi.chunks(2) {
let mut inner_pw = PartialWitness::new(); let mut inner_pw = PartialWitness::new();
let node_in = NodeInput{
node_proofs: [chunk[0].clone(), chunk[1].clone()], self.node.assign_targets(
verifier_data: verifier_data.clone() , &mut inner_pw,
}; &self.node_targets,
self.node.assign_targets(&mut inner_pw,&self.node_targets,&node_in)?; chunk,
verifier_only_data,
)?;
let proof = self.node_circ_data.prove(inner_pw) let proof = self.node_circ_data.prove(inner_pw)
.map_err(|e| CircuitError::ProofGenerationError(e.to_string()))?; .map_err(|e| CircuitError::ProofGenerationError(e.to_string()))?;
new_proofs.push(proof); 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)] #[cfg(test)]
mod tests { mod tests {
use std::fs;
use plonky2::iop::witness::{PartialWitness}; use plonky2::iop::witness::{PartialWitness};
use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_builder::CircuitBuilder;
use plonky2::plonk::circuit_data::{CircuitConfig}; 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 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(()) Ok(())
} }
} }

View File

@ -51,7 +51,7 @@ fn bench_uniform_recursion<const N: usize,>(c: &mut Criterion) -> anyhow::Result
// Proving Phase // Proving Phase
group.bench_function("prove tree", |b| { group.bench_function("prove tree", |b| {
b.iter(|| { 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){ fn bench_uniform_tree_recursion(c: &mut Criterion){
const N: usize = 2; // number of child nodes - binary here 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::<4>(c).expect("bench failed");
bench_uniform_recursion::<8>(c).expect("bench failed"); bench_uniform_recursion::<8>(c).expect("bench failed");
bench_uniform_recursion::<16>(c).expect("bench failed"); bench_uniform_recursion::<16>(c).expect("bench failed");