plonky2-semaphore/src/circuit.rs
src/circuit.rsRaw
1use anyhow::Result;
2use plonky2::field::types::Field;
3use plonky2::hash::hash_types::HashOutTarget;
4use plonky2::hash::merkle_proofs::MerkleProofTarget;
5use plonky2::hash::poseidon::PoseidonHash;
6use plonky2::iop::target::Target;
7use plonky2::iop::witness::{PartialWitness, WitnessWrite};
8use plonky2::plonk::circuit_builder::CircuitBuilder;
9
10use crate::access_set::AccessSet;
11use crate::signal::{Digest, F};
12
13pub struct SemaphoreTargets {
14 merkle_root: HashOutTarget,
15 topic: [Target; 4],
16 merkle_proof: MerkleProofTarget,
17 private_key: [Target; 4],
18 public_key_index: Target,
19}
20
21impl AccessSet {
22 pub fn tree_height(&self) -> usize {
23 self.0.leaves.len().trailing_zeros() as usize
24 }
25
26 pub fn semaphore_circuit(&self, builder: &mut CircuitBuilder<F, 2>) -> SemaphoreTargets {
27 // Register public inputs.
28 let merkle_root = builder.add_virtual_hash();
29 builder.register_public_inputs(&merkle_root.elements);
30 let nullifier = builder.add_virtual_hash();
31 builder.register_public_inputs(&nullifier.elements);
32 let topic: [Target; 4] = builder.add_virtual_targets(4).try_into().unwrap();
33 builder.register_public_inputs(&topic);
34
35 // Merkle proof
36 let merkle_proof = MerkleProofTarget {
37 siblings: builder.add_virtual_hashes(self.tree_height()),
38 };
39
40 // Verify public key Merkle proof.
41 let private_key: [Target; 4] = builder.add_virtual_targets(4).try_into().unwrap();
42 let public_key_index = builder.add_virtual_target();
43 let public_key_index_bits = builder.split_le(public_key_index, self.tree_height());
44 let zero = builder.zero();
45 builder.verify_merkle_proof::<PoseidonHash>(
46 [private_key, [zero; 4]].concat(),
47 &public_key_index_bits,
48 merkle_root,
49 &merkle_proof,
50 );
51
52 // Check nullifier.
53 let should_be_nullifier =
54 builder.hash_n_to_hash_no_pad::<PoseidonHash>([private_key, topic].concat());
55 for i in 0..4 {
56 builder.connect(nullifier.elements[i], should_be_nullifier.elements[i]);
57 }
58
59 SemaphoreTargets {
60 merkle_root,
61 topic,
62 merkle_proof,
63 private_key,
64 public_key_index,
65 }
66 }
67
68 pub fn fill_semaphore_targets(
69 &self,
70 pw: &mut PartialWitness<F>,
71 private_key: Digest,
72 topic: Digest,
73 public_key_index: usize,
74 targets: SemaphoreTargets,
75 ) -> Result<()> {
76 let SemaphoreTargets {
77 merkle_root,
78 topic: topic_target,
79 merkle_proof: merkle_proof_target,
80 private_key: private_key_target,
81 public_key_index: public_key_index_target,
82 } = targets;
83
84 pw.set_hash_target(merkle_root, self.0.cap.0[0])?;
85 pw.set_target_arr(&private_key_target, &private_key)?;
86 pw.set_target_arr(&topic_target, &topic)?;
87 pw.set_target(
88 public_key_index_target,
89 F::from_canonical_usize(public_key_index),
90 )?;
91
92 let merkle_proof = self.0.prove(public_key_index);
93 for (ht, h) in merkle_proof_target
94 .siblings
95 .into_iter()
96 .zip(merkle_proof.siblings)
97 {
98 pw.set_hash_target(ht, h)?;
99 }
100 Ok(())
101 }
102}