// circuit for regular merkle tree implementation (non-safe version) // the circuit uses caps in similar way as in Plonky2 Merkle tree implementation // NOTE: this might be deleted at later time, since we don't use it for codex use anyhow::Result; use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::RichField; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::{PartialWitness, WitnessWrite, Witness}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, VerifierCircuitData}; use plonky2::plonk::config::{AlgebraicHasher, GenericConfig, Hasher, GenericHashOut}; use plonky2::plonk::proof::ProofWithPublicInputs; use std::marker::PhantomData; use itertools::Itertools; use crate::merkle_tree::capped_tree::MerkleTree; use plonky2::hash::poseidon::PoseidonHash; use plonky2::hash::hash_types::{HashOutTarget, MerkleCapTarget, NUM_HASH_OUT_ELTS}; use crate::merkle_tree::capped_tree::MerkleProofTarget; use plonky2_poseidon2::poseidon2_hash::poseidon2::Poseidon2; use plonky2::plonk::config::PoseidonGoldilocksConfig; use plonky2::plonk::proof::Proof; use plonky2::hash::hashing::PlonkyPermutation; use plonky2::plonk::circuit_data::VerifierCircuitTarget; // size of leaf data (in number of field elements) pub const LEAF_LEN: usize = 4; #[derive(Clone, Debug, Eq, PartialEq)] pub struct MerkleTreeTargets< F: RichField + Extendable + Poseidon2, C: GenericConfig, const D: usize, H: Hasher + AlgebraicHasher, > { pub proof_target: MerkleProofTarget, pub cap_target: MerkleCapTarget, pub leaf: Vec, pub leaf_index_target: Target, _phantom: PhantomData<(C,H)>, } #[derive(Clone, Debug, Eq, PartialEq)] pub struct MerkleTreeCircuit< F: RichField + Extendable + Poseidon2, C: GenericConfig, const D: usize, H: Hasher + AlgebraicHasher, > { pub tree: MerkleTree, pub _phantom: PhantomData, } impl< F: RichField + Extendable + Poseidon2, C: GenericConfig, const D: usize, H: Hasher + AlgebraicHasher, > MerkleTreeCircuit{ pub fn tree_height(&self) -> usize { self.tree.leaves.len().trailing_zeros() as usize } // build the circuit and returns the circuit data pub fn build_circuit(&mut self, builder: &mut CircuitBuilder::) -> MerkleTreeTargets{ let proof_t = MerkleProofTarget { siblings: builder.add_virtual_hashes(self.tree_height()-self.tree.cap.height()), }; let cap_t = builder.add_virtual_cap(self.tree.cap.height()); let leaf_index_t = builder.add_virtual_target(); let leaf_index_bits = builder.split_le(leaf_index_t, self.tree_height()); // NOTE: takes the length from const LEAF_LEN and assume all lengths are the same let leaf_t: [Target; LEAF_LEN] = builder.add_virtual_targets(LEAF_LEN).try_into().unwrap(); let zero = builder.zero(); // let mut mt = MT(self.tree.clone()); self.verify_merkle_proof_to_cap_circuit( builder, leaf_t.to_vec(), &leaf_index_bits, &cap_t, &proof_t, ); MerkleTreeTargets{ proof_target: proof_t, cap_target: cap_t, leaf: leaf_t.to_vec(), leaf_index_target: leaf_index_t, _phantom: Default::default(), } } pub fn fill_targets( &self, pw: &mut PartialWitness, // leaf_data: Vec, leaf_index: usize, targets: MerkleTreeTargets, ) { let proof = self.tree.prove(leaf_index); for i in 0..proof.siblings.len() { pw.set_hash_target(targets.proof_target.siblings[i], proof.siblings[i]); } // set cap target manually // pw.set_cap_target(&cap_t, &tree.cap); for (ht, h) in targets.cap_target.0.iter().zip(&self.tree.cap.0) { pw.set_hash_target(*ht, *h); } pw.set_target( targets.leaf_index_target, F::from_canonical_usize(leaf_index), ); for j in 0..targets.leaf.len() { pw.set_target(targets.leaf[j], self.tree.leaves[leaf_index][j]); } } pub fn prove( &self, data: CircuitData, pw: PartialWitness ) -> Result> { let proof = data.prove(pw); return proof } // function to automate build and prove, useful for quick testing pub fn build_and_prove( &mut self, // builder: &mut CircuitBuilder::, config: CircuitConfig, // pw: &mut PartialWitness, leaf_index: usize, // data: CircuitData, ) -> Result<(CircuitData,ProofWithPublicInputs)> { let mut builder = CircuitBuilder::::new(config); let mut pw = PartialWitness::new(); // merkle proof let merkle_proof = self.tree.prove(leaf_index); let proof_t = MerkleProofTarget { siblings: builder.add_virtual_hashes(merkle_proof.siblings.len()), }; for i in 0..merkle_proof.siblings.len() { pw.set_hash_target(proof_t.siblings[i], merkle_proof.siblings[i]); } // merkle cap target let cap_t = builder.add_virtual_cap(self.tree.cap.height()); // set cap target manually // pw.set_cap_target(&cap_t, &tree.cap); for (ht, h) in cap_t.0.iter().zip(&self.tree.cap.0) { pw.set_hash_target(*ht, *h); } // leaf index target let leaf_index_t = builder.constant(F::from_canonical_usize(leaf_index)); let leaf_index_bits = builder.split_le(leaf_index_t, self.tree_height()); // leaf targets // NOTE: takes the length from const LEAF_LEN and assume all lengths are the same // let leaf_t = builder.add_virtual_targets(LEAF_LEN); let leaf_t = builder.add_virtual_targets(self.tree.leaves[leaf_index].len()); for j in 0..leaf_t.len() { pw.set_target(leaf_t[j], self.tree.leaves[leaf_index][j]); } // let mut mt = MT(self.tree.clone()); self.verify_merkle_proof_to_cap_circuit( &mut builder, leaf_t.to_vec(), &leaf_index_bits, &cap_t, &proof_t, ); let data = builder.build::(); let proof = data.prove(pw).unwrap(); Ok((data, proof)) } pub fn verify( &self, verifier_data: &VerifierCircuitData, public_inputs: Vec, proof: Proof ) -> Result<()> { verifier_data.verify(ProofWithPublicInputs { proof, public_inputs, }) } } impl + Poseidon2, const D: usize, C: GenericConfig, H: Hasher + AlgebraicHasher,> MerkleTreeCircuit { pub fn verify_merkle_proof_circuit( &mut self, builder: &mut CircuitBuilder, leaf_data: Vec, leaf_index_bits: &[BoolTarget], merkle_root: HashOutTarget, proof: &MerkleProofTarget, ) { let merkle_cap = MerkleCapTarget(vec![merkle_root]); self.verify_merkle_proof_to_cap_circuit(builder, leaf_data, leaf_index_bits, &merkle_cap, proof); } pub fn verify_merkle_proof_to_cap_circuit( &mut self, builder: &mut CircuitBuilder, leaf_data: Vec, leaf_index_bits: &[BoolTarget], merkle_cap: &MerkleCapTarget, proof: &MerkleProofTarget, ) { let cap_index = builder.le_sum(leaf_index_bits[proof.siblings.len()..].iter().copied()); self.verify_merkle_proof_to_cap_with_cap_index_circuit( builder, leaf_data, leaf_index_bits, cap_index, merkle_cap, proof, ); } pub fn verify_merkle_proof_to_cap_with_cap_index_circuit( &mut self, builder: &mut CircuitBuilder, leaf_data: Vec, leaf_index_bits: &[BoolTarget], cap_index: Target, merkle_cap: &MerkleCapTarget, proof: &MerkleProofTarget, ) { debug_assert!(H::AlgebraicPermutation::RATE >= NUM_HASH_OUT_ELTS); let zero = builder.zero(); let mut state: HashOutTarget = builder.hash_or_noop::(leaf_data); debug_assert_eq!(state.elements.len(), NUM_HASH_OUT_ELTS); for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) { debug_assert_eq!(sibling.elements.len(), NUM_HASH_OUT_ELTS); let mut perm_inputs = H::AlgebraicPermutation::default(); perm_inputs.set_from_slice(&state.elements, 0); perm_inputs.set_from_slice(&sibling.elements, NUM_HASH_OUT_ELTS); // Ensure the rest of the state, if any, is zero: perm_inputs.set_from_iter(core::iter::repeat(zero), 2 * NUM_HASH_OUT_ELTS); // let perm_outs = builder.permute_swapped::(perm_inputs, bit); let perm_outs = H::permute_swapped(perm_inputs, bit, builder); let hash_outs = perm_outs.squeeze()[0..NUM_HASH_OUT_ELTS] .try_into() .unwrap(); state = HashOutTarget { elements: hash_outs, }; } for i in 0..NUM_HASH_OUT_ELTS { let result = builder.random_access( cap_index, merkle_cap.0.iter().map(|h| h.elements[i]).collect(), ); builder.connect(result, state.elements[i]); } } pub fn verify_batch_merkle_proof_to_cap_with_cap_index_circuit( &mut self, builder: &mut CircuitBuilder, leaf_data: &[Vec], leaf_heights: &[usize], leaf_index_bits: &[BoolTarget], cap_index: Target, merkle_cap: &MerkleCapTarget, proof: &MerkleProofTarget, ) { debug_assert!(H::AlgebraicPermutation::RATE >= NUM_HASH_OUT_ELTS); let zero = builder.zero(); let mut state: HashOutTarget = builder.hash_or_noop::(leaf_data[0].clone()); debug_assert_eq!(state.elements.len(), NUM_HASH_OUT_ELTS); let mut current_height = leaf_heights[0]; let mut leaf_data_index = 1; for (&bit, &sibling) in leaf_index_bits.iter().zip(&proof.siblings) { debug_assert_eq!(sibling.elements.len(), NUM_HASH_OUT_ELTS); let mut perm_inputs = H::AlgebraicPermutation::default(); perm_inputs.set_from_slice(&state.elements, 0); perm_inputs.set_from_slice(&sibling.elements, NUM_HASH_OUT_ELTS); // Ensure the rest of the state, if any, is zero: perm_inputs.set_from_iter(core::iter::repeat(zero), 2 * NUM_HASH_OUT_ELTS); // let perm_outs = builder.permute_swapped::(perm_inputs, bit); let perm_outs = H::permute_swapped(perm_inputs, bit, builder); let hash_outs = perm_outs.squeeze()[0..NUM_HASH_OUT_ELTS] .try_into() .unwrap(); state = HashOutTarget { elements: hash_outs, }; current_height -= 1; if leaf_data_index < leaf_heights.len() && current_height == leaf_heights[leaf_data_index] { let mut new_leaves = state.elements.to_vec(); new_leaves.extend_from_slice(&leaf_data[leaf_data_index]); state = builder.hash_or_noop::(new_leaves); leaf_data_index += 1; } } for i in 0..NUM_HASH_OUT_ELTS { let result = builder.random_access( cap_index, merkle_cap.0.iter().map(|h| h.elements[i]).collect(), ); builder.connect(result, state.elements[i]); } } pub fn connect_hashes(&mut self, builder: &mut CircuitBuilder, x: HashOutTarget, y: HashOutTarget) { for i in 0..NUM_HASH_OUT_ELTS { builder.connect(x.elements[i], y.elements[i]); } } pub fn connect_merkle_caps(&mut self, builder: &mut CircuitBuilder, x: &MerkleCapTarget, y: &MerkleCapTarget) { for (h0, h1) in x.0.iter().zip_eq(&y.0) { self.connect_hashes(builder, *h0, *h1); } } pub fn connect_verifier_data(&mut self, builder: &mut CircuitBuilder, x: &VerifierCircuitTarget, y: &VerifierCircuitTarget) { self.connect_merkle_caps(builder, &x.constants_sigmas_cap, &y.constants_sigmas_cap); self.connect_hashes(builder, x.circuit_digest, y.circuit_digest); } } #[cfg(test)] pub mod tests { use std::time::Instant; use rand::rngs::OsRng; use rand::Rng; use super::*; use plonky2::field::types::Field; use crate::merkle_tree::capped_tree::MerkleTree; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_data::CircuitConfig; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; pub fn random_data(n: usize, k: usize) -> Vec> { (0..n).map(|_| F::rand_vec(k)).collect() } #[test] fn test_merkle_circuit() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; type H = PoseidonHash; // create Merkle tree let log_n = 8; let n = 1 << log_n; let cap_height = 1; let leaves = random_data::(n, LEAF_LEN); let tree = MerkleTree::>::Hasher>::new(leaves, cap_height); // ---- prover zone ---- // Build and prove let start_build = Instant::now(); let mut mt_circuit = MerkleTreeCircuit::{ tree: tree.clone(), _phantom: Default::default() }; let leaf_index: usize = OsRng.gen_range(0..n); let config = CircuitConfig::standard_recursion_config(); let (data, proof_with_pub_input) = mt_circuit.build_and_prove(config,leaf_index).unwrap(); println!("build and prove time is: {:?}", start_build.elapsed()); let vd = data.verifier_data(); let pub_input = proof_with_pub_input.public_inputs; let proof = proof_with_pub_input.proof; // ---- verifier zone ---- let start_verifier = Instant::now(); assert!(mt_circuit.verify(&vd,pub_input,proof).is_ok()); println!("verify time is: {:?}", start_verifier.elapsed()); Ok(()) } #[test] fn mod_test_merkle_circuit() -> Result<()> { const D: usize = 2; type C = PoseidonGoldilocksConfig; type F = >::F; // create Merkle tree let log_n = 8; let n = 1 << log_n; let cap_height = 0; let leaves = random_data::(n, LEAF_LEN); let tree = MerkleTree::>::Hasher>::new(leaves, cap_height); // Build circuit let start_build = Instant::now(); let mut mt_circuit = MerkleTreeCircuit{ tree: tree.clone(), _phantom: Default::default() }; let config = CircuitConfig::standard_recursion_config(); let mut builder = CircuitBuilder::::new(config); let targets = mt_circuit.build_circuit(&mut builder); let data = builder.build::(); let vd = data.verifier_data(); println!("build time is: {:?}", start_build.elapsed()); // Prover Zone let start_prover = Instant::now(); let mut pw = PartialWitness::new(); let leaf_index: usize = OsRng.gen_range(0..n); let proof = tree.prove(leaf_index); mt_circuit.fill_targets(&mut pw, leaf_index, targets); let proof_with_pub_input = mt_circuit.prove(data,pw).unwrap(); let pub_input = proof_with_pub_input.public_inputs; let proof = proof_with_pub_input.proof; println!("prove time is: {:?}", start_prover.elapsed()); // Verifier zone let start_verifier = Instant::now(); assert!(mt_circuit.verify(&vd,pub_input,proof).is_ok()); println!("verify time is: {:?}", start_verifier.elapsed()); Ok(()) } }