arnaucube
arnaucube/plonky2-semaphorePublic Self-hosted repository
plonky2-semaphore/src/recursion.rs
src/recursion.rsRaw
  1use anyhow::Result;
  2use plonky2::iop::witness::{PartialWitness, WitnessWrite};
  3use plonky2::plonk::circuit_builder::CircuitBuilder;
  4use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData};
  5use plonky2::plonk::proof::ProofWithPublicInputs;
  6
  7use crate::access_set::AccessSet;
  8use crate::signal::{Digest, PlonkyProof, Signal, C, F};
  9
 10impl AccessSet {
 11    pub fn aggregate_signals(
 12        &self,
 13        topic0: Digest,
 14        signal0: Signal,
 15        topic1: Digest,
 16        signal1: Signal,
 17        verifier_data: &VerifierCircuitData<F, C, 2>,
 18    ) -> Result<(Digest, Digest, PlonkyProof, VerifierCircuitData<F, C, 2>)> {
 19        let config = CircuitConfig::standard_recursion_zk_config();
 20        let mut builder = CircuitBuilder::new(config);
 21        let mut pw = PartialWitness::new();
 22
 23        let public_inputs0: Vec<F> = self
 24            .0
 25            .cap
 26            .0
 27            .iter()
 28            .flat_map(|h| h.elements)
 29            .chain(signal0.nullifier)
 30            .chain(topic0)
 31            .collect();
 32        let public_inputs1: Vec<F> = self
 33            .0
 34            .cap
 35            .0
 36            .iter()
 37            .flat_map(|h| h.elements)
 38            .chain(signal1.nullifier)
 39            .chain(topic1)
 40            .collect();
 41
 42        // `add_virtual_proof_with_pis` is an extended version of the `add_virtual_target`, but
 43        // that takes care of adding all the values of the proof and the public inputs (pis).
 44        let proof_target0 = builder.add_virtual_proof_with_pis(&verifier_data.common);
 45        // set the public inputs
 46        builder.register_public_inputs(&proof_target0.public_inputs);
 47        // `set_proof_with_pis_target` is an extended version of the `set_target`, but that takes
 48        // care of adding all the values of the proof and the public inputs.
 49        pw.set_proof_with_pis_target(
 50            &proof_target0,
 51            &ProofWithPublicInputs {
 52                proof: signal0.proof,
 53                public_inputs: public_inputs0,
 54            },
 55        )?;
 56        // add & set the verifier data
 57        let vd_target =
 58            builder.add_virtual_verifier_data(verifier_data.common.config.fri_config.cap_height);
 59        pw.set_verifier_data_target(&vd_target, &verifier_data.verifier_only)?;
 60
 61        // now, the same as we did with the proof0, with the proof1 related values:
 62        let proof_target1 = builder.add_virtual_proof_with_pis(&verifier_data.common);
 63        builder.register_public_inputs(&proof_target1.public_inputs);
 64        pw.set_proof_with_pis_target(
 65            &proof_target1,
 66            &ProofWithPublicInputs {
 67                proof: signal1.proof,
 68                public_inputs: public_inputs1,
 69            },
 70        )?;
 71        let vd_target =
 72            builder.add_virtual_verifier_data(verifier_data.common.config.fri_config.cap_height);
 73        pw.set_verifier_data_target(&vd_target, &verifier_data.verifier_only)?;
 74
 75        pw.set_cap_target(
 76            &vd_target.constants_sigmas_cap,
 77            &verifier_data.verifier_only.constants_sigmas_cap,
 78        )?;
 79
 80        builder.verify_proof::<C>(&proof_target0, &vd_target, &verifier_data.common);
 81        builder.verify_proof::<C>(&proof_target1, &vd_target, &verifier_data.common);
 82
 83        let data = builder.build();
 84        let recursive_proof = data.prove(pw)?;
 85
 86        data.verify(recursive_proof.clone())?;
 87
 88        Ok((
 89            signal0.nullifier,
 90            signal1.nullifier,
 91            recursive_proof.proof,
 92            data.verifier_data(),
 93        ))
 94    }
 95}
 96
 97#[cfg(test)]
 98mod tests {
 99    use anyhow::Result;
100    use plonky2::field::types::{Field, Sample};
101    use plonky2::hash::merkle_tree::MerkleTree;
102    use plonky2::hash::poseidon::PoseidonHash;
103    use plonky2::plonk::config::Hasher;
104    use plonky2::plonk::proof::ProofWithPublicInputs;
105
106    use crate::access_set::AccessSet;
107    use crate::signal::{Digest, F};
108
109    #[test]
110    fn test_recursion() -> Result<()> {
111        let n = 1 << 20;
112        let private_keys: Vec<Digest> = (0..n).map(|_| F::rand_array()).collect();
113        let public_keys: Vec<Vec<F>> = private_keys
114            .iter()
115            .map(|&sk| {
116                PoseidonHash::hash_no_pad(&[sk, [F::ZERO; 4]].concat())
117                    .elements
118                    .to_vec()
119            })
120            .collect();
121        let access_set = AccessSet(MerkleTree::new(public_keys, 0));
122
123        // first proof
124        let i0 = 12;
125        let topic0 = F::rand_array();
126        let (signal0, vd0) = access_set.make_signal(private_keys[i0], topic0, i0)?;
127        access_set.verify_signal(topic0, signal0.clone(), &vd0)?;
128
129        // second proof
130        let i1 = 42;
131        let topic1 = F::rand_array();
132        let (signal1, vd1) = access_set.make_signal(private_keys[i1], topic1, i1)?;
133        access_set.verify_signal(topic1, signal1.clone(), &vd1)?;
134
135        // generate recursive proof
136        let (nullifier0, nullifier1, recursive_proof, vd2) =
137            access_set.aggregate_signals(topic0, signal0, topic1, signal1, &vd0)?;
138
139        // verify recursive proof
140        let public_inputs: Vec<F> = access_set
141            .0
142            .cap
143            .0
144            .iter()
145            .flat_map(|h| h.elements)
146            .chain(nullifier0)
147            .chain(topic0)
148            .chain(access_set.0.cap.0.iter().flat_map(|h| h.elements))
149            .chain(nullifier1)
150            .chain(topic1)
151            .collect();
152
153        vd2.verify(ProofWithPublicInputs {
154            proof: recursive_proof,
155            public_inputs,
156        })?;
157        Ok(())
158    }
159}