From aa13ff3253e1cc14eea2f8d2354d10fa37a65050 Mon Sep 17 00:00:00 2001 From: M Alghazwi Date: Thu, 20 Mar 2025 10:45:03 +0100 Subject: [PATCH] change hashing of VerifierData to constants. --- .../src/recursion/uniform/compress.rs | 43 +++--- .../src/recursion/uniform/leaf.rs | 39 +++--- .../src/recursion/uniform/node.rs | 61 ++++++--- .../src/recursion/uniform/pi_verifier.rs | 122 ++++-------------- .../src/recursion/uniform/tree.rs | 104 ++++++--------- proof-input/src/recursion/uniform.rs | 30 +++-- 6 files changed, 163 insertions(+), 236 deletions(-) diff --git a/codex-plonky2-circuits/src/recursion/uniform/compress.rs b/codex-plonky2-circuits/src/recursion/uniform/compress.rs index 1bdc8b6..727062d 100644 --- a/codex-plonky2-circuits/src/recursion/uniform/compress.rs +++ b/codex-plonky2-circuits/src/recursion/uniform/compress.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; -use plonky2::hash::hash_types::RichField; +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, VerifierCircuitTarget, VerifierOnlyCircuitData}; +use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierOnlyCircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2_field::extension::Extendable; @@ -20,7 +20,8 @@ pub struct CompressionCircuit< >::Hasher: AlgebraicHasher { inner_common_data: CommonCircuitData, - phantom_data: PhantomData<(C,H)> + inner_verifier_data: VerifierOnlyCircuitData, + phantom_data: PhantomData } #[derive(Clone, Debug)] @@ -28,7 +29,6 @@ pub struct CompressionTargets< const D: usize, >{ pub inner_proof: ProofWithPublicInputsTarget, - pub verifier_data: VerifierCircuitTarget, } impl< @@ -39,9 +39,13 @@ impl< > CompressionCircuit where >::Hasher: AlgebraicHasher { - pub fn new(inner_common_data: CommonCircuitData) -> Self { + pub fn new( + inner_common_data: CommonCircuitData, + inner_verifier_data: VerifierOnlyCircuitData, + ) -> Self { Self{ inner_common_data, + inner_verifier_data, phantom_data:PhantomData::default(), } } @@ -59,29 +63,29 @@ impl< assert_eq!(inner_pub_input.len(), 8); builder.register_public_inputs(&inner_pub_input[0..4]); - // virtual target for the verifier data - let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height); + // constant target for the verifier data + let const_verifier_data = builder.constant_verifier_data(&self.inner_verifier_data); // 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); + vd_pub_input.extend_from_slice(&const_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); + vd_pub_input.extend_from_slice(&const_verifier_data.constants_sigmas_cap.0[i].elements); } + let hash_inner_vd_pub_input = builder.hash_n_to_hash_no_pad::(vd_pub_input); - let mut vd_to_hash = vec![]; - vd_to_hash.extend_from_slice(&inner_pub_input[4..8]); - vd_to_hash.extend_from_slice(&hash_inner_vd_pub_input.elements); - let vd_hash = builder.hash_n_to_hash_no_pad::(vd_to_hash); - builder.register_public_inputs(&vd_hash.elements); + + // 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); // verify the proofs in-circuit - builder.verify_proof::(&vir_proof, &inner_verifier_data, &inner_common); + builder.verify_proof::(&vir_proof, &const_verifier_data, &inner_common); // return targets let t = CompressionTargets { inner_proof: vir_proof, - verifier_data: inner_verifier_data, }; Ok(t) @@ -92,7 +96,6 @@ impl< &self, pw: &mut PartialWitness, targets: &CompressionTargets, inner_proof: ProofWithPublicInputs, - verifier_only_data: &VerifierOnlyCircuitData, ) -> Result<()> { // assign the proof pw.set_proof_with_pis_target(&targets.inner_proof, &inner_proof) @@ -100,12 +103,6 @@ impl< CircuitError::ProofTargetAssignmentError("inner-proof".to_string(), e.to_string()) })?; - // assign the verifier data - pw.set_verifier_data_target(&targets.verifier_data, verifier_only_data) - .map_err(|e| { - CircuitError::VerifierDataTargetAssignmentError(e.to_string()) - })?; - Ok(()) } diff --git a/codex-plonky2-circuits/src/recursion/uniform/leaf.rs b/codex-plonky2-circuits/src/recursion/uniform/leaf.rs index 6269fff..b7f940f 100644 --- a/codex-plonky2-circuits/src/recursion/uniform/leaf.rs +++ b/codex-plonky2-circuits/src/recursion/uniform/leaf.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; -use plonky2::hash::hash_types::RichField; +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, VerifierCircuitTarget, VerifierOnlyCircuitData}; +use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierOnlyCircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2_field::extension::Extendable; @@ -21,7 +21,8 @@ pub struct LeafCircuit< >::Hasher: AlgebraicHasher { inner_common_data: CommonCircuitData, - phantom_data: PhantomData<(C,H)> + inner_verifier_data: VerifierOnlyCircuitData, + phantom_data: PhantomData } #[derive(Clone, Debug)] @@ -29,7 +30,6 @@ pub struct LeafTargets < const D: usize, >{ pub inner_proof: Vec>, - pub verifier_data: VerifierCircuitTarget, } impl< @@ -41,9 +41,13 @@ impl< > LeafCircuit where >::Hasher: AlgebraicHasher { - pub fn new(inner_common_data: CommonCircuitData) -> Self { + pub fn new( + inner_common_data: CommonCircuitData, + inner_verifier_data: VerifierOnlyCircuitData, + ) -> Self { Self{ inner_common_data, + inner_verifier_data, phantom_data:PhantomData::default(), } } @@ -67,27 +71,21 @@ impl< let hash_inner_pub_input = builder.hash_n_to_hash_no_pad::(pub_input); builder.register_public_inputs(&hash_inner_pub_input.elements); - // virtual target for the verifier data - let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height); + // pad the public input with constants so that it shares the same structure as the node + let zero_hash = builder.constant_hash(HashOut::::default()); + builder.register_public_inputs(&zero_hash.elements); - // 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::(vd_pub_input); - builder.register_public_inputs(&hash_inner_vd_pub_input.elements); + // virtual constant target for the verifier data + let const_verifier_data = builder.constant_verifier_data(&self.inner_verifier_data); // verify the proofs in-circuit for i in 0..N { - builder.verify_proof::(&vir_proofs[i], &inner_verifier_data, &inner_common); + builder.verify_proof::(&vir_proofs[i], &const_verifier_data, &inner_common); } // return targets let t = LeafTargets { inner_proof: vir_proofs, - verifier_data: inner_verifier_data, }; Ok(t) @@ -98,7 +96,6 @@ impl< &self, pw: &mut PartialWitness, targets: &LeafTargets, inner_proof: &[ProofWithPublicInputs], - verifier_only_data: &VerifierOnlyCircuitData, ) -> Result<()> { assert_eq!(inner_proof.len(), N); // assign the proofs @@ -109,12 +106,6 @@ impl< })?; } - // assign the verifier data - pw.set_verifier_data_target(&targets.verifier_data, verifier_only_data) - .map_err(|e| { - CircuitError::VerifierDataTargetAssignmentError(e.to_string()) - })?; - Ok(()) } diff --git a/codex-plonky2-circuits/src/recursion/uniform/node.rs b/codex-plonky2-circuits/src/recursion/uniform/node.rs index 468bb3f..b3d4518 100644 --- a/codex-plonky2-circuits/src/recursion/uniform/node.rs +++ b/codex-plonky2-circuits/src/recursion/uniform/node.rs @@ -1,5 +1,6 @@ use std::marker::PhantomData; 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}; @@ -8,7 +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::circuits::utils::vec_to_array; +// use crate::circuits::utils::vec_to_array; /// recursion node circuit - verifies M leaf proofs #[derive(Clone, Debug)] @@ -21,8 +22,9 @@ pub struct NodeCircuit< > where >::Hasher: AlgebraicHasher { - leaf_common_data: CommonCircuitData, - phantom_data: PhantomData<(C,H)> + common_data: CommonCircuitData, + leaf_verifier_data: VerifierOnlyCircuitData, + phantom_data: PhantomData } #[derive(Clone, Debug)] @@ -30,7 +32,8 @@ pub struct NodeTargets< const D: usize, >{ pub leaf_proofs: Vec>, - pub verifier_data: VerifierCircuitTarget, + pub node_verifier_data: VerifierCircuitTarget, + pub condition: BoolTarget, } impl< @@ -43,9 +46,13 @@ impl< >::Hasher: AlgebraicHasher { - pub fn new(inner_common_data: CommonCircuitData) -> Self { + pub fn new( + common_data: CommonCircuitData, + leaf_verifier_data: VerifierOnlyCircuitData, + ) -> Self { Self{ - leaf_common_data: inner_common_data, + common_data, + leaf_verifier_data, phantom_data:PhantomData::default(), } } @@ -53,7 +60,7 @@ impl< /// build the leaf circuit pub fn build(&self, builder: &mut CircuitBuilder) -> Result> { - let inner_common = self.leaf_common_data.clone(); + let inner_common = self.common_data.clone(); // assert public input is of size 8 - 2 hashout assert_eq!(inner_common.num_public_inputs, 8); @@ -61,13 +68,11 @@ impl< // the proof virtual targets - M proofs let mut vir_proofs = vec![]; let mut pub_input = vec![]; - let mut inner_vd_hashes = vec![]; for _i in 0..M { 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[0..4]); - inner_vd_hashes.extend_from_slice(&inner_pub_input[4..8]); } // hash the public input & make it public @@ -75,22 +80,34 @@ impl< builder.register_public_inputs(&hash_inner_pub_input.elements); // virtual target for the verifier data - let inner_verifier_data = builder.add_virtual_verifier_data(inner_common.config.fri_config.cap_height); + let node_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 + // virtual target for the verifier data + let const_leaf_verifier_data = builder.constant_verifier_data(&self.leaf_verifier_data); + + // register only the node verifier data hash as public input. let mut vd_pub_input = vec![]; - vd_pub_input.extend_from_slice(&inner_verifier_data.circuit_digest.elements); + vd_pub_input.extend_from_slice(&node_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); + 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::(vd_pub_input); - inner_vd_hashes.extend_from_slice(&vd_hash.elements); - let vd_hash_all = builder.hash_n_to_hash_no_pad::(inner_vd_hashes); - builder.register_public_inputs(&vd_hash_all.elements); + builder.register_public_inputs(&vd_hash.elements); + + // condition for switching between node and leaf + let condition = builder.add_virtual_bool_target_safe(); + + // true -> node, false -> leaf + let selected_vd = builder.select_verifier_data(condition.clone(), &node_verifier_data, &const_leaf_verifier_data); // verify the proofs in-circuit - M proofs for i in 0..M { - builder.verify_proof::(&vir_proofs[i], &inner_verifier_data, &inner_common); + builder.verify_proof::(&vir_proofs[i], &selected_vd, &inner_common); + } + + // Make sure we have every gate to match `common_data`. + for g in &inner_common.gates { + builder.add_gate_to_gate_set(g.clone()); } // let proofs = vec_to_array::<2, ProofWithPublicInputsTarget>(vir_proofs)?; @@ -98,7 +115,8 @@ impl< // return targets let t = NodeTargets { leaf_proofs: vir_proofs, - verifier_data: inner_verifier_data, + node_verifier_data, + condition, }; Ok(t) @@ -110,6 +128,7 @@ impl< targets: &NodeTargets, node_proofs: &[ProofWithPublicInputs], verifier_only_data: &VerifierOnlyCircuitData, + condition: bool, ) -> Result<()> { // assert size of proofs vec assert_eq!(node_proofs.len(), M); @@ -123,11 +142,15 @@ impl< } // assign the verifier data - pw.set_verifier_data_target(&targets.verifier_data, &verifier_only_data) + pw.set_verifier_data_target(&targets.node_verifier_data, &verifier_only_data) .map_err(|e| { CircuitError::VerifierDataTargetAssignmentError(e.to_string()) })?; + // assign the condition + pw.set_bool_target(targets.condition, condition) + .map_err(|e| CircuitError::BoolTargetAssignmentError("condition".to_string(), e.to_string()))?; + Ok(()) } diff --git a/codex-plonky2-circuits/src/recursion/uniform/pi_verifier.rs b/codex-plonky2-circuits/src/recursion/uniform/pi_verifier.rs index 2da9319..bea8126 100644 --- a/codex-plonky2-circuits/src/recursion/uniform/pi_verifier.rs +++ b/codex-plonky2-circuits/src/recursion/uniform/pi_verifier.rs @@ -1,9 +1,9 @@ use std::marker::PhantomData; -use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField}; +use plonky2::hash::hash_types::{ HashOutTarget, RichField}; use plonky2::iop::target::Target; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::{CircuitBuilder}; -use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierCircuitData, VerifierCircuitTarget}; +use plonky2::plonk::circuit_data::{CommonCircuitData, VerifierOnlyCircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig}; use plonky2::plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget}; use plonky2_field::extension::Extendable; @@ -29,20 +29,15 @@ pub struct PublicInputVerificationCircuit< >::Hasher: AlgebraicHasher, { pub node_common_data: CommonCircuitData, - phantom: PhantomData<(C, H)>, + pub node_verifier_data: VerifierOnlyCircuitData, + phantom: PhantomData, } /// 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. -/// - `node_verifier_data`: Verifier data for the node circuit. -/// - `leaf_verifier_data`: Verifier data for the leaf circuit. -/// - `inner_verifier_data`: Verifier data for the inner circuit. pub struct PublicInputVerificationTargets { pub inner_proof: ProofWithPublicInputsTarget, - pub node_verifier_data: VerifierCircuitTarget, - pub leaf_verifier_data: HashOutTarget, - pub inner_verifier_data: HashOutTarget, pub inner_pub_inputs: Vec>, } @@ -55,13 +50,17 @@ PublicInputVerificationCircuit >::Hasher: AlgebraicHasher, { /// Create a new instance of the circuit. - pub fn new(node_common_data: CommonCircuitData) -> Self { + pub fn new( + node_common_data: CommonCircuitData, + node_verifier_data: VerifierOnlyCircuitData, + ) -> Self { // we expect exactly 8 public inputs from the tree root proof - // 4 for the final aggregated public-input hash, 4 for the final aggregated verifier-data hash + // 4 for the final aggregated public-input hash, 4 for the node verifier-data hash assert_eq!(node_common_data.num_public_inputs, 8); Self { node_common_data, + node_verifier_data, phantom: PhantomData, } } @@ -69,19 +68,17 @@ PublicInputVerificationCircuit /// 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 tree hashing of node_verifier_date leaf_verifier_data and inner_verifier_data (each 4 field elements). + /// 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) -> Result> { // Add a virtual proof with 8 public inputs. This is the final root proof whose - // public inputs we want to check in-circuit. + // public inputs we want to check in-circuit. let inner_proof = builder.add_virtual_proof_with_pis(&self.node_common_data); - // Create a VerifierCircuitTarget for the node's verifier data (unhashed). - let node_verifier_data = builder.add_virtual_verifier_data( - self.node_common_data.config.fri_config.cap_height - ); + // Create a constant VerifierCircuitTarget for the node's verifier data. + let const_node_vd = builder.constant_verifier_data(&self.node_verifier_data); // verify the proof - builder.verify_proof::(&inner_proof, &node_verifier_data, &self.node_common_data); + builder.verify_proof::(&inner_proof, &const_node_vd, &self.node_common_data); // create T×K targets for all inner public inputs from the base level. let mut inner_pub_inputs = Vec::with_capacity(T); @@ -97,34 +94,30 @@ PublicInputVerificationCircuit // Summary of the logic: // // let final_pi = proof.public_inputs[0..4]; - // let final_vd = proof.public_inputs[4..8]; + // let node_vd = proof.public_inputs[4..8]; // ... - // leaf-level pub inputs tree hashing: chunks of N -> hash -> combine with inner_verifier_data - // node-level pub inputs tree hashing: chunks of M -> hash -> combine with either leaf_hash (only level 0) or node_hash + // leaf-level pub inputs tree hashing: chunks of N -> hash + // node-level pub inputs tree hashing: chunks of M -> hash // ... - // check final result matches final_pi, final_vd + // check final result matches final_pi // ------------------------------------------------------------------ // Extract the final 4 field elements for the public-input hash & next 4 for the verifier-data hash. let final_pi_hash_t = &inner_proof.public_inputs[0..4]; - let final_vd_hash_t = &inner_proof.public_inputs[4..8]; + let node_vd_hash_t = &inner_proof.public_inputs[4..8]; // Compute node_hash in-circuit let mut node_vd_input_t = Vec::new(); - node_vd_input_t.extend_from_slice(&node_verifier_data.circuit_digest.elements); - for cap_elem in node_verifier_data.constants_sigmas_cap.0.iter() { + node_vd_input_t.extend_from_slice(&const_node_vd.circuit_digest.elements); + for cap_elem in const_node_vd.constants_sigmas_cap.0.iter() { node_vd_input_t.extend_from_slice(&cap_elem.elements); } let node_hash_t = builder.hash_n_to_hash_no_pad::(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 - let mut pub_in_hashes_t = Vec::new(); - let mut vd_hashes_t = Vec::new(); - - // hash targets for the leaf and inner circuit's verifier data. - let leaf_hash_t = builder.add_virtual_hash_public_input(); // public input - let inner_hash_t = builder.add_virtual_hash_public_input(); // public input // Leaf level hashing: chunks of N let base_chunks = T / N; // T is assumed to be multiple of N @@ -139,20 +132,15 @@ PublicInputVerificationCircuit // track these in vectors pub_in_hashes_t.push(pi_hash_chunk); - vd_hashes_t.push(inner_hash_t); } // Now at the node level: - - let mut level = 0; let mut current_len = base_chunks; - while current_len > 1 { let next_len = (current_len + (M - 1)) / M; let mut next_pub_in_hashes_t = Vec::with_capacity(next_len); - let mut next_vd_hashes_t = Vec::with_capacity(next_len); for i in 0..next_len { let start_idx = i * M; @@ -164,44 +152,24 @@ PublicInputVerificationCircuit pi_flat.extend_from_slice(&pub_in_hashes_t[j].elements); } let pi_hash = builder.hash_n_to_hash_no_pad::(pi_flat); - - // flatten all vd_hashes in [start_idx..end_idx] - let mut vd_flat = Vec::with_capacity((end_idx - start_idx) * 4); - for j in start_idx..end_idx { - vd_flat.extend_from_slice(&vd_hashes_t[j].elements); - } - // use leaf_hash if level == 0, else node_hash - let hash_n_t = if level == 0 { leaf_hash_t } else { node_hash_t }; - vd_flat.extend_from_slice(&hash_n_t.elements); - - let vd_hash = builder.hash_n_to_hash_no_pad::(vd_flat); - next_pub_in_hashes_t.push(pi_hash); - next_vd_hashes_t.push(vd_hash); } pub_in_hashes_t = next_pub_in_hashes_t; - vd_hashes_t = next_vd_hashes_t; current_len = next_len; - level += 1; } - // now have exactly one pub_in_hash and one vd_hash + // now have exactly one pub_in_hash let final_computed_pi_t = &pub_in_hashes_t[0]; - let final_computed_vd_t = &vd_hashes_t[0]; - // connect them to the final 8 public inputs of `inner_proof`. + // connect them to the final 4 public inputs of `inner_proof`. for i in 0..4 { builder.connect(final_pi_hash_t[i], final_computed_pi_t.elements[i]); - builder.connect(final_vd_hash_t[i], final_computed_vd_t.elements[i]); } // return all the targets Ok(PublicInputVerificationTargets { inner_proof, - node_verifier_data, - leaf_verifier_data: leaf_hash_t, - inner_verifier_data: inner_hash_t, inner_pub_inputs, }) } @@ -209,18 +177,12 @@ PublicInputVerificationCircuit /// 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. - /// - `node_verifier_data`: node verifier data - /// - `leaf_verifier_data`: leaf circuit’s verifier data. - /// - `inner_verifier_data`:inner-circuit’s verifier data. pub fn assign_targets( &self, pw: &mut PartialWitness, targets: &PublicInputVerificationTargets, inner_proof: ProofWithPublicInputs, inner_pub_inputs_vals: Vec>, - node_verifier_data: &VerifierCircuitData, - leaf_verifier_data: &VerifierCircuitData, - inner_verifier_data: &VerifierCircuitData, ) -> Result<()> { // Assign the final proof - it should have 8 public inputs pw.set_proof_with_pis_target(&targets.inner_proof, &inner_proof) @@ -252,38 +214,6 @@ PublicInputVerificationCircuit } } - // Assign the node verifier data - pw.set_verifier_data_target(&targets.node_verifier_data, &node_verifier_data.verifier_only) - .map_err(|e| { - CircuitError::VerifierDataTargetAssignmentError(e.to_string()) - })?; - - // Assign the leaf circuit’s verifier data - let leaf_hash = Self::get_hash_of_verifier_data(leaf_verifier_data); - pw.set_hash_target(targets.leaf_verifier_data, leaf_hash).map_err(|e| { - CircuitError::HashTargetAssignmentError("leaf verifier data hash".to_string(), e.to_string()) - })?; - - // Assign the inner circuit’s verifier data - let inner_hash = Self::get_hash_of_verifier_data(inner_verifier_data); - pw.set_hash_target(targets.inner_verifier_data, inner_hash).map_err(|e| { - CircuitError::HashTargetAssignmentError("inner verifier data hash".to_string(), e.to_string()) - })?; - Ok(()) } - - /// helper fn to generate hash of verifier data - fn get_hash_of_verifier_data(verifier_data: &VerifierCircuitData) -> HashOut{ - let mut vd = vec![]; - let digest: &HashOut = &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; - vd.extend_from_slice(&cap_hash.elements); - } - - H::hash_no_pad(&vd) - } } diff --git a/codex-plonky2-circuits/src/recursion/uniform/tree.rs b/codex-plonky2-circuits/src/recursion/uniform/tree.rs index f05576a..0997cb7 100644 --- a/codex-plonky2-circuits/src/recursion/uniform/tree.rs +++ b/codex-plonky2-circuits/src/recursion/uniform/tree.rs @@ -46,13 +46,14 @@ impl< { pub fn build( - inner_common_data: CommonCircuitData + inner_common_data: CommonCircuitData, + inner_verifier_data: VerifierOnlyCircuitData, ) -> Result { // build leaf with standard recursion config let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - let leaf = LeafCircuit::<_,D,_,_,N>::new(inner_common_data.clone()); + 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::(); // println!("leaf circuit size = {:?}", leaf_circ_data.common.degree_bits()); @@ -61,7 +62,7 @@ impl< let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); - let node = NodeCircuit::<_,D,_,_,M>::new(leaf_circ_data.common.clone()); + 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::(); // println!("node circuit size = {:?}", node_circ_data.common.degree_bits()); @@ -70,7 +71,7 @@ impl< let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); let node_common = node_circ_data.common.clone(); - let compression_circ = CompressionCircuit::new(node_common); + 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::(); // println!("compress circuit size = {:?}", compression_circ_data.common.degree_bits()); @@ -108,13 +109,12 @@ impl< pub fn prove_tree_and_compress( &mut self, proofs_with_pi: &[ProofWithPublicInputs], - inner_verifier_only_data: &VerifierOnlyCircuitData, ) -> Result<(ProofWithPublicInputs)> { let proof = - self.prove_tree(proofs_with_pi, inner_verifier_only_data)?; + self.prove_tree(proofs_with_pi)?; let mut pw = PartialWitness::::new(); - self.compression.assign_targets(&mut pw, &self.compression_targets, proof, &self.node_circ_data.verifier_only)?; + self.compression.assign_targets(&mut pw, &self.compression_targets, proof)?; self.compression_circ_data.prove(pw).map_err( |e| CircuitError::InvalidProofError(e.to_string()) @@ -125,7 +125,6 @@ impl< ( &mut self, proofs_with_pi: &[ProofWithPublicInputs], - inner_verifier_only_data: &VerifierOnlyCircuitData, ) -> Result<(ProofWithPublicInputs)> { if proofs_with_pi.len() % 2 != 0 { @@ -137,12 +136,11 @@ impl< // process leaves let leaf_proofs = self.get_leaf_proofs( proofs_with_pi, - inner_verifier_only_data, )?; // process nodes - let (root_proof, vd) = - self.prove(&leaf_proofs,&self.leaf_circ_data.verifier_only)?; + let (root_proof, _vd) = + self.prove(&leaf_proofs,&self.leaf_circ_data.verifier_only, 0)?; Ok(root_proof) } @@ -151,7 +149,6 @@ impl< ( &mut self, proofs_with_pi: &[ProofWithPublicInputs], - inner_verifier_only_data: &VerifierOnlyCircuitData, ) -> Result<(Vec>)> { let mut leaf_proofs = vec![]; @@ -159,7 +156,7 @@ impl< for proof in proofs_with_pi.chunks(N){ let mut pw = PartialWitness::::new(); - self.leaf.assign_targets(&mut pw,&self.leaf_targets,proof,inner_verifier_only_data)?; + self.leaf.assign_targets(&mut pw,&self.leaf_targets,proof)?; let proof = self.leaf_circ_data.prove(pw).unwrap(); leaf_proofs.push(proof); } @@ -172,6 +169,7 @@ impl< &self, proofs_with_pi: &[ProofWithPublicInputs], verifier_only_data: &VerifierOnlyCircuitData, + level: usize, ) -> Result<(ProofWithPublicInputs, VerifierOnlyCircuitData)> where >::Hasher: AlgebraicHasher { @@ -182,6 +180,8 @@ impl< let mut new_proofs = vec![]; + let condition = if level == 0 {false} else {true}; + for chunk in proofs_with_pi.chunks(M) { let mut inner_pw = PartialWitness::new(); @@ -191,6 +191,7 @@ impl< &self.node_targets, chunk, verifier_only_data, + condition )?; let proof = self.node_circ_data.prove(inner_pw) @@ -198,7 +199,7 @@ impl< new_proofs.push(proof); } - self.prove(&new_proofs, &self.node_circ_data.verifier_only) + self.prove(&new_proofs, &self.node_circ_data.verifier_only, level+1) } pub fn verify_proof( @@ -219,18 +220,17 @@ impl< &self, proof: ProofWithPublicInputs, inner_public_input: Vec>, - inner_verifier_data: &VerifierCircuitData, is_compressed: bool, ) -> Result<()>{ let public_input = proof.public_inputs.clone(); if is_compressed{ self.compression_circ_data.verify(proof) .map_err(|e| CircuitError::InvalidProofError(e.to_string()))?; - self.verify_public_input(public_input, inner_public_input, inner_verifier_data, is_compressed) + self.verify_public_input(public_input, inner_public_input) }else { 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, is_compressed) + self.verify_public_input(public_input, inner_public_input) } } @@ -238,23 +238,15 @@ impl< &self, public_input: Vec, inner_public_input: Vec>, - inner_verifier_data: &VerifierCircuitData, - is_compressed: bool, ) -> 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 node_hash = 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.chunks(N){ let pub_in_flat: Vec = pub_in .iter() @@ -262,64 +254,50 @@ impl< .collect(); let hash = H::hash_no_pad(&pub_in_flat); 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(M).zip(inner_vd_hashes.chunks(M)) { + for pi_chunk in pub_in_hashes.chunks(M) { // collect field elements let pi_chunk_f: Vec = pi_chunk.iter() .flat_map(|h| h.elements.iter().cloned()) .collect(); - let mut vd_chunk_f: Vec = 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 + // Compute 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 mut expected_vd_hash = inner_vd_hashes[0]; - - if is_compressed { - let mut vd_to_hash = vec![]; - vd_to_hash.extend_from_slice(&expected_vd_hash.elements); - vd_to_hash.extend_from_slice(&node_hash.elements); - expected_vd_hash = H::hash_no_pad(&vd_to_hash); - } assert_eq!(given_input_hash, expected_pi_hash.elements); - assert_eq!(given_vd_hash, expected_vd_hash.elements); + assert_eq!(given_vd_hash, node_hash.elements); Ok(()) } - - /// helper fn to generate hash of verifier data - fn get_hash_of_verifier_data(verifier_data: &VerifierCircuitData) -> HashOut{ - let mut vd = vec![]; - let digest: &HashOut = &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; - vd.extend_from_slice(&cap_hash.elements); - } - - H::hash_no_pad(&vd) - } - } +/// helper fn to generate hash of verifier data +pub fn get_hash_of_verifier_data< + F: RichField + Extendable + Poseidon2, + const D: usize, + C: GenericConfig, + H: AlgebraicHasher, +>(verifier_data: &VerifierCircuitData) -> HashOut where + >::Hasher: AlgebraicHasher +{ + let mut vd = vec![]; + let digest: &HashOut = &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; + vd.extend_from_slice(&cap_hash.elements); + } + + H::hash_no_pad(&vd) +} diff --git a/proof-input/src/recursion/uniform.rs b/proof-input/src/recursion/uniform.rs index 63d8e13..de09490 100644 --- a/proof-input/src/recursion/uniform.rs +++ b/proof-input/src/recursion/uniform.rs @@ -13,6 +13,7 @@ mod tests { use crate::params::Params; use codex_plonky2_circuits::recursion::uniform::{tree::TreeRecursion}; use codex_plonky2_circuits::recursion::uniform::pi_verifier::PublicInputVerificationCircuit; + use codex_plonky2_circuits::recursion::uniform::tree::get_hash_of_verifier_data; #[test] fn test_uniform_recursion() -> anyhow::Result<()> { @@ -42,25 +43,25 @@ mod tests { const N: usize = 1; const M: usize = 2; - let mut tree = TreeRecursion::::build(inner_data.common.clone())?; + let mut tree = TreeRecursion::::build(inner_data.common.clone(), inner_data.verifier_only.clone())?; - let root = tree.prove_tree(&proofs, &inner_data.verifier_only)?; + let root = tree.prove_tree(&proofs)?; println!("pub input size = {}", root.public_inputs.len()); println!("proof size = {:?} bytes", root.to_bytes().len()); - let root_compressed = tree.prove_tree_and_compress(&proofs, &inner_data.verifier_only)?; + let root_compressed = tree.prove_tree_and_compress(&proofs)?; println!("pub input size (compressed) = {}", root_compressed.public_inputs.len()); println!("proof size compressed = {:?} bytes", root_compressed.to_bytes().len()); let inner_pi: Vec> = proofs.iter().map(|p| p.public_inputs.clone()).collect(); assert!( - tree.verify_proof_and_public_input(root,inner_pi.clone(),&inner_data.verifier_data(), false).is_ok(), + tree.verify_proof_and_public_input(root,inner_pi.clone(), false).is_ok(), "proof verification failed" ); assert!( - tree.verify_proof_and_public_input(root_compressed,inner_pi,&inner_data.verifier_data(), true).is_ok(), + tree.verify_proof_and_public_input(root_compressed,inner_pi, true).is_ok(), "compressed proof verification failed" ); @@ -99,22 +100,22 @@ mod tests { const N: usize = 1; const M: usize = 2; - let mut tree = TreeRecursion::::build(inner_data.common.clone())?; + let mut tree = TreeRecursion::::build(inner_data.common.clone(), inner_data.verifier_only.clone())?; - let root = tree.prove_tree(&proofs, &inner_data.verifier_only)?; + let root = tree.prove_tree(&proofs)?; println!("pub input size = {}", root.public_inputs.len()); println!("proof size = {:?} bytes", root.to_bytes().len()); let inner_pi: Vec> = proofs.iter().map(|p| p.public_inputs.clone()).collect(); assert!( - tree.verify_proof_and_public_input(root.clone(),inner_pi.clone(),&inner_data.verifier_data(), false).is_ok(), + tree.verify_proof_and_public_input(root.clone(),inner_pi.clone(), false).is_ok(), "proof verification failed" ); // ------------------- Public input verifier Circuit -------------------- - let pi_verifier_circ = PublicInputVerificationCircuit::::new(tree.get_node_common_data()); + let pi_verifier_circ = PublicInputVerificationCircuit::::new(tree.get_node_common_data(), tree.get_node_verifier_data().verifier_only); let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); @@ -125,7 +126,7 @@ mod tests { let mut pw = PartialWitness::::new(); - pi_verifier_circ.assign_targets(&mut pw, &pi_tarq, root, inner_pi.clone(), &tree.get_node_verifier_data(), &tree.get_leaf_verifier_data(), &inner_data.verifier_data())?; + pi_verifier_circ.assign_targets(&mut pw, &pi_tarq, root, inner_pi.clone())?; let proof = pi_circ_data.prove(pw)?; println!("pub input size = {}", proof.public_inputs.len()); @@ -134,13 +135,20 @@ mod tests { let pub_input_flat: Vec = inner_pi.iter().cloned().flatten().collect(); let num_pi = proof.public_inputs.len(); - // sanity check + // sanity check on public input for (i, e) in proof.public_inputs.iter().enumerate(){ if i < pub_input_flat.len() { assert_eq!(*e, pub_input_flat[i]) } } + // sanity check on the verifier data + let hashed_node_vd = get_hash_of_verifier_data::(&tree.get_node_verifier_data()); + for (i, &e) in proof.public_inputs[proof.public_inputs.len()-4 ..].iter().enumerate(){ + assert_eq!(e, hashed_node_vd.elements[i]) + } + + assert!( pi_circ_data.verify(proof).is_ok(), "pi-verifier proof verification failed"