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 // To create the circuit 'values', we call the `add_virtual_target` and it's variants (ie.
28 // `add_virtual_target_arr`, or `add_virtual_hash` which internally calls it for the 4
29 // field values that conform the hash.
30 // The method `add_virtual_target` keeps an incremental index for each new virtual target
31 // created, and returns a new `VirtualTarget`, used for intermediate values in witness
32 // generation (which when needed can be copied to specific witness location (Wire)).
33 //
34 // The `register_public_input` and it's variants, append to the CircuitBuilder's
35 // `public_inputs` vector the given Target (either VirtualTarget or Wire).
36 //
37 // Notice that when created, the targets are not given any value, and they will be set later
38 // at the method `fill_semaphore_targets`.
39
40 // Register public inputs.
41 let merkle_root = builder.add_virtual_hash();
42 builder.register_public_inputs(&merkle_root.elements);
43 let nullifier = builder.add_virtual_hash();
44 builder.register_public_inputs(&nullifier.elements);
45 let topic: [Target; 4] = builder.add_virtual_targets(4).try_into().unwrap();
46 builder.register_public_inputs(&topic);
47
48 // Merkle proof
49 let merkle_proof = MerkleProofTarget {
50 siblings: builder.add_virtual_hashes(self.tree_height()),
51 };
52
53 // Verify public key Merkle proof.
54 let private_key: [Target; 4] = builder.add_virtual_targets(4).try_into().unwrap();
55 let public_key_index = builder.add_virtual_target();
56 let public_key_index_bits = builder.split_le(public_key_index, self.tree_height());
57 let zero = builder.zero();
58 builder.verify_merkle_proof::<PoseidonHash>(
59 [private_key, [zero; 4]].concat(),
60 &public_key_index_bits,
61 merkle_root,
62 &merkle_proof,
63 );
64
65 // Check nullifier.
66 let should_be_nullifier =
67 builder.hash_n_to_hash_no_pad::<PoseidonHash>([private_key, topic].concat());
68 for i in 0..4 {
69 builder.connect(nullifier.elements[i], should_be_nullifier.elements[i]);
70 }
71
72 SemaphoreTargets {
73 merkle_root,
74 topic,
75 merkle_proof,
76 private_key,
77 public_key_index,
78 }
79 }
80
81 // Fill the semaphore targets that we defined at the method `semaphore_circuit` with the given
82 // values.
83 pub fn fill_semaphore_targets(
84 &self,
85 pw: &mut PartialWitness<F>,
86 private_key: Digest,
87 topic: Digest,
88 public_key_index: usize,
89 targets: SemaphoreTargets,
90 ) -> Result<()> {
91 let SemaphoreTargets {
92 merkle_root,
93 topic: topic_target,
94 merkle_proof: merkle_proof_target,
95 private_key: private_key_target,
96 public_key_index: public_key_index_target,
97 } = targets;
98
99 pw.set_hash_target(merkle_root, self.0.cap.0[0])?;
100 pw.set_target_arr(&private_key_target, &private_key)?;
101 pw.set_target_arr(&topic_target, &topic)?;
102 pw.set_target(
103 public_key_index_target,
104 F::from_canonical_usize(public_key_index),
105 )?;
106
107 let merkle_proof = self.0.prove(public_key_index);
108 for (ht, h) in merkle_proof_target
109 .siblings
110 .into_iter()
111 .zip(merkle_proof.siblings)
112 {
113 pw.set_hash_target(ht, h)?;
114 }
115 Ok(())
116 }
117}