arnaucube
arnaucube/plonky2-semaphorePublic Self-hosted repository
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}