frontend/hyrax_worldcoin/
v3.rs

1#![allow(warnings)]
2use std::collections::HashMap;
3use std::fmt::Display;
4use std::hash::Hash;
5
6use crate::{
7    layouter::builder::Circuit,
8    worldcoin_mpc::circuits::MPC_IRISCODE_INPUT_LAYER,
9    zk_iriscode_ss::{
10        circuits::{
11            V3_AUXILIARY_LAYER, V3_DIGITS_LAYER, V3_INPUT_IMAGE_LAYER, V3_RH_MATMULT_SHRED,
12            V3_SIGN_BITS_LAYER, V3_TO_SUB_MATMULT_SHRED,
13        },
14        data::IriscodeCircuitAuxData,
15        parameters::IRISCODE_COMMIT_LOG_NUM_COLS,
16        v3::{circuit_description_and_inputs, load_worldcoin_data},
17    },
18};
19use clap::error;
20use rand::rngs::ThreadRng;
21use rand::{CryptoRng, Rng, RngCore};
22use remainder::layer::LayerId;
23use remainder::mle::evals::MultilinearExtension;
24use remainder::prover::GKRCircuitDescription;
25use remainder::utils::mle::pad_with;
26use remainder::{
27    circuit_building_context::CircuitBuildingContext, verifiable_circuit::VerifiableCircuit,
28};
29use serde::{Deserialize, Serialize};
30use shared_types::curves::PrimeOrderCurve;
31use shared_types::halo2curves::{bn256::G1 as Bn256Point, group::Group};
32use shared_types::pedersen::PedersenCommitter;
33use shared_types::transcript::ec_transcript::{ECTranscript, ECTranscriptTrait};
34use shared_types::transcript::poseidon_sponge::PoseidonSponge;
35use shared_types::transcript::Transcript;
36use shared_types::{
37    config::{GKRCircuitProverConfig, GKRCircuitVerifierConfig, ProofConfig},
38    halo2curves::grumpkin::G1,
39};
40use shared_types::{
41    perform_function_under_prover_config, perform_function_under_verifier_config, Fq, Fr,
42};
43use shared_types::{Field, Zeroizable};
44use zeroize::Zeroize;
45
46use super::orb::SerializedImageCommitment;
47use crate::hyrax_worldcoin::orb::{IMAGE_COMMIT_LOG_NUM_COLS, PUBLIC_STRING};
48use hyrax::utils::vandermonde::VandermondeInverse;
49use hyrax::{
50    gkr::input_layer::{
51        commit_to_input_values, HyraxInputLayerDescription, HyraxProverInputCommitment,
52    },
53    verifiable_circuit::HyraxVerifiableCircuit,
54};
55use hyrax::{
56    gkr::{self, verify_hyrax_proof, HyraxProof},
57    provable_circuit::HyraxProvableCircuit,
58};
59use sha256::digest as sha256_digest;
60use thiserror::Error;
61
62use crate::zk_iriscode_ss::circuits::iriscode_ss_attach_input_data;
63use anyhow::{anyhow, Result};
64
65type Scalar = Fr;
66type Base = Fq;
67
68#[derive(Debug, Error)]
69pub enum IriscodeError {
70    #[error("Non-zero padding bits in iris/mask code")]
71    NonZeroPaddingBits,
72    #[error("Non-binary iris/mask code")]
73    NonBinaryIrisMaskCode,
74    #[error("Incorrect kernel values or thresholds")]
75    IncorrectKernelValuesOrThresholds,
76    #[error("Image commitment does not match expected hash")]
77    WrongHash,
78}
79
80/// Verify that the iris/mask code in the supplied proof is correct, and return the unpadded iris/mask code, along with the
81/// commitment to the image.
82/// Checks, in particular:
83/// * That the [HyraxProof] verifies.
84/// * That the correct kernel values and thresholds are being used in the supplied proof.
85/// * That the MLE encoding the iris/mask code has only 0s in the padding region.
86/// * That the unpadded iris/mask code consists only of 0s and 1s.
87/// * That the image commitment hash matches the expected hash.
88/// This is a helper function for verifying the v3 masked iriscode is correct.
89pub(crate) fn verify_v3_iriscode_proof_and_hash(
90    proof: &HyraxProof<Bn256Point>,
91    verifiable_circuit: &HyraxVerifiableCircuit<Bn256Point>,
92    expected_commitment_hash: &str,
93    committer: &PedersenCommitter<Bn256Point>,
94    proof_config: &ProofConfig,
95) -> Result<Vec<Bn256Point>> {
96    // Create a fresh transcript.
97    let mut transcript: ECTranscript<Bn256Point, PoseidonSponge<Base>> =
98        ECTranscript::new("V3 Iriscode Circuit Pipeline");
99
100    // Verify the relationship between iris/mask code and image.
101    verify_hyrax_proof(
102        &proof,
103        &verifiable_circuit,
104        &committer,
105        &mut transcript,
106        proof_config,
107    );
108
109    let image_layer_id = verifiable_circuit
110        .get_private_input_layer_id(V3_INPUT_IMAGE_LAYER)
111        .unwrap();
112    let code_layer_id = verifiable_circuit
113        .get_private_input_layer_id(V3_SIGN_BITS_LAYER)
114        .unwrap();
115
116    let image_commitment = proof.get_commitment_ref(image_layer_id).cloned().unwrap();
117    let code_commitment = proof.get_commitment_ref(code_layer_id).cloned().unwrap();
118
119    // Check that the image commitment matches the expected hash.
120    let commitment_hash = sha256_digest(
121        &image_commitment
122            .iter()
123            .flat_map(|p| p.to_bytes_compressed())
124            .collect::<Vec<u8>>(),
125    );
126
127    if commitment_hash != *expected_commitment_hash {
128        return Err(anyhow!(IriscodeError::WrongHash));
129    }
130
131    // Return the commitments to the code and the image.
132    Ok(code_commitment)
133}
134
135/// Prove a single instance of the iriscode circuit using the Hyrax proof system and the provided image precommit.
136/// This is a helper function for proving the v3 masked iriscode is correct.
137/// # Arguments:
138/// * `inputs`: _all_ inputs to the circuit, as MLEs.
139/// * `image_precommit`: The precommitment to the image (derived from the image using `committer`).
140/// ///
141/// This function is assumed to be called *with the prover config set*!
142pub fn prove_with_image_precommit(
143    mut provable_circuit: HyraxProvableCircuit<Bn256Point>,
144    image_layer_label: &str,
145    code_layer_label: &str,
146    image_precommit: HyraxProverInputCommitment<Bn256Point>,
147    committer: &PedersenCommitter<Bn256Point>,
148    blinding_rng: &mut (impl CryptoRng + RngCore),
149    converter: &mut VandermondeInverse<Scalar>,
150) -> ((
151    HyraxProof<Bn256Point>,
152    ProofConfig,
153    HyraxProverInputCommitment<Bn256Point>,
154)) {
155    provable_circuit
156        .set_pre_commitment(
157            image_layer_label,
158            image_precommit,
159            Some(IMAGE_COMMIT_LOG_NUM_COLS),
160        )
161        .unwrap();
162
163    // Create a fresh transcript.
164    let mut transcript: ECTranscript<Bn256Point, PoseidonSponge<Base>> =
165        ECTranscript::new("V3 Iriscode Circuit Pipeline");
166
167    // Prove the relationship between iris/mask code and image.
168    let (proof, proof_config) =
169        provable_circuit.prove(&committer, blinding_rng, converter, &mut transcript);
170
171    let code_layer_id = *provable_circuit
172        .layer_label_to_layer_id
173        .get(code_layer_label)
174        .unwrap();
175
176    let code_commit_in_proof = &proof
177        .hyrax_input_proofs
178        .iter()
179        .find(|hyrax_input_proof| hyrax_input_proof.layer_id == code_layer_id)
180        .unwrap()
181        .input_commitment;
182    let code_commit = provable_circuit
183        .get_commitment_ref_by_label(code_layer_label)
184        .unwrap()
185        .clone();
186    assert_eq!(*code_commit_in_proof, code_commit.commitment);
187
188    // Zeroize each of the commitments once we have grabbed the commitments that we need.
189    let private_input_layer_ids = provable_circuit.get_private_input_layer_ids();
190    private_input_layer_ids.iter().for_each(|layer_id| {
191        let commitment = provable_circuit.get_commitment_mut_ref(layer_id).unwrap();
192        commitment.zeroize();
193    });
194
195    (proof, proof_config, code_commit)
196}
197
198#[derive(Error, Debug)]
199pub enum V3ProofError {
200    #[error("Found empty proof field")]
201    EmptyProofField,
202
203    #[error("Verification produced an iriscode error")]
204    IriscodeError(#[from] IriscodeError),
205
206    #[error("Verifier returned an iriscode of the wrong length")]
207    IriscodeLengthMismatch,
208}
209
210/// A serializable struct containing the 4 Hyrax proofs needed for the
211/// V3 iris/mask circuit.
212/// To allow for incremental proving, this struct can be initialized empty
213/// and then proofs can be added as the become available.
214/// Serialization is possible at any stage, even if a proof is missing.
215/// Once all proofs are generated, the struct can be finalized into
216/// a `V3Proof` which can be then passed to the verifier.
217#[derive(Serialize, Deserialize)]
218pub struct V3Prover {
219    #[serde(skip)]
220    #[serde(default = "V3Prover::default_committer")]
221    committer: PedersenCommitter<Bn256Point>,
222
223    #[serde(skip)]
224    #[serde(default = "VandermondeInverse::new")]
225    converter: VandermondeInverse<Scalar>,
226
227    /// The Iriscode computation circuit description, along with auxiliary inputs (parameters which
228    /// are constant for both iris and mask computation), but no image/mask inputs.
229    v3_circuit: V3CircuitAndAuxData<Fr>,
230
231    prover_config: GKRCircuitProverConfig,
232
233    left_iris_proof: Option<HyraxProof<Bn256Point>>,
234    left_mask_proof: Option<HyraxProof<Bn256Point>>,
235    right_iris_proof: Option<HyraxProof<Bn256Point>>,
236    right_mask_proof: Option<HyraxProof<Bn256Point>>,
237}
238
239impl V3Prover {
240    pub fn default_committer() -> PedersenCommitter<Bn256Point> {
241        PedersenCommitter::new(1 << IMAGE_COMMIT_LOG_NUM_COLS, PUBLIC_STRING, None)
242    }
243
244    /// Generate an empty v3 prover with the given configuration.
245    pub fn new(prover_config: GKRCircuitProverConfig, circuit: V3CircuitAndAuxData<Fr>) -> Self {
246        Self {
247            committer: Self::default_committer(),
248            converter: VandermondeInverse::new(),
249            v3_circuit: circuit,
250            prover_config,
251            left_iris_proof: None,
252            left_mask_proof: None,
253            right_iris_proof: None,
254            right_mask_proof: None,
255        }
256    }
257
258    /// Generate a v3 prover with the given configuration, initialized
259    /// with optional proofs.
260    pub fn new_from_proofs(
261        prover_config: GKRCircuitProverConfig,
262        circuit: V3CircuitAndAuxData<Fr>,
263        left_image_proof: HyraxProof<Bn256Point>,
264        left_mask_proof: HyraxProof<Bn256Point>,
265        right_image_proof: HyraxProof<Bn256Point>,
266        right_mask_proof: HyraxProof<Bn256Point>,
267    ) -> Self {
268        Self {
269            committer: Self::default_committer(),
270            converter: VandermondeInverse::new(),
271            v3_circuit: circuit,
272            prover_config,
273            left_iris_proof: Some(left_image_proof),
274            left_mask_proof: Some(left_mask_proof),
275            right_iris_proof: Some(right_image_proof),
276            right_mask_proof: Some(right_mask_proof),
277        }
278    }
279
280    pub fn prove(
281        &mut self,
282        is_mask: bool,
283        is_left_eye: bool,
284        image_bytes: Vec<u8>,
285        image_precommit: HyraxProverInputCommitment<Bn256Point>,
286        rng: &mut (impl CryptoRng + RngCore),
287    ) -> HyraxProverInputCommitment<Bn256Point> {
288        // Load the inputs to the circuit (these are all MLEs, i.e. in the clear).
289        let input_data = load_worldcoin_data::<Fr>(image_bytes, is_mask);
290
291        // Clone the circuit to start attaching data.
292        let mut circuit = self.v3_circuit.get_circuit().clone();
293
294        let aux_data = if is_mask {
295            self.v3_circuit.get_mask_aux_data_ref().clone()
296        } else {
297            self.v3_circuit.get_iris_aux_data_ref().clone()
298        };
299
300        let circuit_with_inputs = iriscode_ss_attach_input_data::<
301            _,
302            { crate::zk_iriscode_ss::parameters::BASE },
303        >(circuit, input_data, aux_data)
304        .unwrap();
305
306        let provable_circuit = circuit_with_inputs.gen_hyrax_provable_circuit().unwrap();
307
308        // Prove the iriscode circuit with the image precommit.
309        let (proof, _, code_commit) = prove_with_image_precommit(
310            provable_circuit,
311            V3_INPUT_IMAGE_LAYER,
312            V3_SIGN_BITS_LAYER,
313            image_precommit,
314            &mut self.committer,
315            rng,
316            &mut self.converter,
317        );
318
319        self.set(is_mask, is_left_eye, proof);
320
321        code_commit
322    }
323
324    /// Set the field indicated by `is_mask` and `is_left_eye` to `proof`,
325    /// overwritting any existing value.
326    pub fn set(&mut self, is_mask: bool, is_left_eye: bool, proof: HyraxProof<Bn256Point>) {
327        match (is_mask, is_left_eye) {
328            (false, false) => self.set_right_iris_proof(proof),
329            (false, true) => self.set_left_iris_proof(proof),
330            (true, false) => self.set_right_mask_proof(proof),
331            (true, true) => self.set_left_mask_proof(proof),
332        }
333    }
334
335    /// Set the left image proof to `proof`, overwritting any existing value.
336    pub fn set_left_iris_proof(&mut self, proof: HyraxProof<Bn256Point>) {
337        self.left_iris_proof = Some(proof)
338    }
339
340    /// Set the left mask proof to `proof`, overwritting any existing value.
341    pub fn set_left_mask_proof(&mut self, proof: HyraxProof<Bn256Point>) {
342        self.left_mask_proof = Some(proof)
343    }
344
345    /// Set the right image proof to `proof`, overwritting any existing value.
346    pub fn set_right_iris_proof(&mut self, proof: HyraxProof<Bn256Point>) {
347        self.right_iris_proof = Some(proof)
348    }
349
350    /// Set the right mask proof to `proof`, overwritting any existing value.
351    pub fn set_right_mask_proof(&mut self, proof: HyraxProof<Bn256Point>) {
352        self.right_mask_proof = Some(proof)
353    }
354
355    /// Returns whether the proof corresponding to `is_mask` and `is_left_eye`
356    /// is present. If `true`, then `self.get()` is guaranteed to return `Some`
357    /// value.
358    pub fn is_set(&self, is_mask: bool, is_left_eye: bool) -> bool {
359        match (is_mask, is_left_eye) {
360            (false, false) => self.right_iris_proof.is_some(),
361            (false, true) => self.left_iris_proof.is_some(),
362            (true, false) => self.right_mask_proof.is_some(),
363            (true, true) => self.left_mask_proof.is_some(),
364        }
365    }
366
367    /// Get the proof indicated by `is_mask` and `is_left_eye`, if any,
368    /// otherwise return `None`.
369    pub fn get(&self, is_mask: bool, is_left_eye: bool) -> Option<&HyraxProof<Bn256Point>> {
370        match (is_mask, is_left_eye) {
371            (false, false) => self.get_right_iris_proof(),
372            (false, true) => self.get_left_iris_proof(),
373            (true, false) => self.get_right_mask_proof(),
374            (true, true) => self.get_left_mask_proof(),
375        }
376    }
377
378    /// Return a reference to the left image proof, if any, otherwise return
379    /// `None`.
380    pub fn get_left_iris_proof(&self) -> Option<&HyraxProof<Bn256Point>> {
381        self.left_iris_proof.as_ref()
382    }
383
384    /// Return a reference to the left mask proof, if any, otherwise return
385    /// `None`.
386    pub fn get_left_mask_proof(&self) -> Option<&HyraxProof<Bn256Point>> {
387        self.left_mask_proof.as_ref()
388    }
389
390    /// Return a reference to the right image proof, if any, otherwise return
391    /// `None`.
392    pub fn get_right_iris_proof(&self) -> Option<&HyraxProof<Bn256Point>> {
393        self.right_iris_proof.as_ref()
394    }
395
396    /// Return a reference to the right mask proof, if any, otherwise return
397    /// `None`.
398    pub fn get_right_mask_proof(&self) -> Option<&HyraxProof<Bn256Point>> {
399        self.right_mask_proof.as_ref()
400    }
401
402    /// Serializes `self` into a binary representation.
403    pub fn serialize(&self) -> Vec<u8> {
404        let serialized_proof = bincode::serialize(self).unwrap();
405
406        serialized_proof
407    }
408
409    /// Checks whether `self` is ready to be finalized, i.e. whether all 4
410    /// proofs are present.`
411    fn is_ready_to_finalize(&self) -> bool {
412        self.is_set(false, false)
413            && self.is_set(false, true)
414            && self.is_set(true, false)
415            && self.is_set(true, true)
416    }
417    /// If `self` is ready to be finalized, it generates a `V3Proof` containing
418    /// all 4 proofs in `self` along with the `ProofConfig` used to generate
419    /// them.
420    /// Returns `None` if not all proofs are present.
421    pub fn finalize(&self) -> Result<V3Proof, V3ProofError> {
422        if self.is_ready_to_finalize() {
423            let proof_config = ProofConfig::new_from_prover_config(&self.prover_config);
424
425            Ok(V3Proof::new(
426                proof_config,
427                self.left_iris_proof.as_ref().unwrap().clone(),
428                self.left_mask_proof.as_ref().unwrap().clone(),
429                self.right_iris_proof.as_ref().unwrap().clone(),
430                self.right_mask_proof.as_ref().unwrap().clone(),
431            ))
432        } else {
433            Err(V3ProofError::EmptyProofField)
434        }
435    }
436
437    /// Deserializes `serialized_proof` and returns it.
438    pub fn deserialize(serialized_prover: &[u8]) -> Self {
439        bincode::deserialize(serialized_prover).unwrap()
440    }
441
442    /// Get a mutable reference to the proof indicated by `is_mask` and `is_left_eye`.
443    pub fn get_as_mut(&mut self, is_mask: bool, is_left_eye: bool) -> &mut HyraxProof<Bn256Point> {
444        match (is_mask, is_left_eye) {
445            (false, false) => self.right_iris_proof.as_mut().unwrap(),
446            (false, true) => self.left_iris_proof.as_mut().unwrap(),
447            (true, false) => self.right_mask_proof.as_mut().unwrap(),
448            (true, true) => self.left_mask_proof.as_mut().unwrap(),
449        }
450    }
451}
452
453#[derive(Clone, Debug, Serialize, Deserialize)]
454pub struct V3Proof {
455    #[serde(skip)]
456    #[serde(default = "V3Prover::default_committer")]
457    committer: PedersenCommitter<Bn256Point>,
458
459    proof_config: ProofConfig,
460
461    left_iris_proof: HyraxProof<Bn256Point>,
462    left_mask_proof: HyraxProof<Bn256Point>,
463    right_iris_proof: HyraxProof<Bn256Point>,
464    right_mask_proof: HyraxProof<Bn256Point>,
465}
466
467impl V3Proof {
468    pub fn new(
469        proof_config: ProofConfig,
470        left_iris_proof: HyraxProof<Bn256Point>,
471        left_mask_proof: HyraxProof<Bn256Point>,
472        right_iris_proof: HyraxProof<Bn256Point>,
473        right_mask_proof: HyraxProof<Bn256Point>,
474    ) -> Self {
475        Self {
476            committer: V3Prover::default_committer(),
477            proof_config,
478            left_iris_proof,
479            left_mask_proof,
480            right_iris_proof,
481            right_mask_proof,
482        }
483    }
484
485    pub fn insert_aux_public_data(
486        &mut self,
487        aux_mle: &MultilinearExtension<Fr>,
488        is_mask: bool,
489        is_left_eye: bool,
490        aux_layer_id: LayerId,
491    ) {
492        let proof = self.get_as_mut(is_mask, is_left_eye);
493        proof.insert_aux_public_data_by_id(aux_mle, aux_layer_id);
494    }
495
496    /// Get the proof indicated by `is_mask` and `is_left_eye`.
497    pub fn get_as_mut(&mut self, is_mask: bool, is_left_eye: bool) -> &mut HyraxProof<Bn256Point> {
498        match (is_mask, is_left_eye) {
499            (false, false) => &mut self.right_iris_proof,
500            (false, true) => &mut self.left_iris_proof,
501            (true, false) => &mut self.right_mask_proof,
502            (true, true) => &mut self.left_mask_proof,
503        }
504    }
505
506    /// Get the proof indicated by `is_mask` and `is_left_eye`.
507    pub fn get(&self, is_mask: bool, is_left_eye: bool) -> &HyraxProof<Bn256Point> {
508        match (is_mask, is_left_eye) {
509            (false, false) => self.get_right_iris_proof(),
510            (false, true) => self.get_left_iris_proof(),
511            (true, false) => self.get_right_mask_proof(),
512            (true, true) => self.get_left_mask_proof(),
513        }
514    }
515
516    /// Return a reference to the left image proof.
517    pub fn get_left_iris_proof(&self) -> &HyraxProof<Bn256Point> {
518        &self.left_iris_proof
519    }
520
521    /// Return a reference to the left mask proof.
522    pub fn get_left_mask_proof(&self) -> &HyraxProof<Bn256Point> {
523        &self.left_mask_proof
524    }
525
526    /// Return a reference to the right image proof.
527    pub fn get_right_iris_proof(&self) -> &HyraxProof<Bn256Point> {
528        &self.right_iris_proof
529    }
530
531    /// Return a reference to the right mask proof.
532    pub fn get_right_mask_proof(&self) -> &HyraxProof<Bn256Point> {
533        &self.right_mask_proof
534    }
535
536    /// Serializes `self` into a binary representation.
537    pub fn serialize(&self) -> Vec<u8> {
538        let serialized_proof = bincode::serialize(self).unwrap();
539
540        serialized_proof
541    }
542
543    /// Deserializes `serialized_proof` and returns it.
544    pub fn deserialize(serialized_proof: &[u8]) -> Self {
545        bincode::deserialize(serialized_proof).unwrap()
546    }
547
548    /// NOTE: This function calls other internal functions which will
549    /// enforce the verifier config, but itself does not do so. It therefore
550    /// should be wrapped in a `perform_function_under_verifier_config!` macro.
551    pub fn verify(
552        &self,
553        is_mask: bool,
554        is_left_eye: bool,
555        verifiable_circuit: &HyraxVerifiableCircuit<Bn256Point>,
556        commitment_hash: &str,
557    ) -> Result<()> {
558        let proof = self.get(is_mask, is_left_eye);
559
560        verify_v3_iriscode_proof_and_hash(
561            proof,
562            verifiable_circuit,
563            commitment_hash,
564            &self.committer,
565            &self.proof_config,
566        )?;
567
568        Ok(())
569    }
570}
571
572/// A circuit computing the V3 iriscode from an iris image and a mask.
573/// The computation when the input is an iris image is essentially the same as the when the input is
574/// a mask, the only difference being the values of some parameters which are passed as inputs to
575/// the circuit, called auxiliary MLEs.
576/// This struct holds a [Circuit] which has _already_ been initialized with input parameters which
577/// are common between the cases of an iris image and a mask, and separatelly contains entries for
578/// the auxiliary MLEs encoding the parameters specific to the iris image computation and the mask
579/// computation.
580#[derive(Clone, Debug, Serialize, Deserialize)]
581#[serde(bound = "F: Field")]
582pub struct V3CircuitAndAuxData<F: Field> {
583    circuit: Circuit<F>,
584    iris_aux_data: IriscodeCircuitAuxData<F>,
585    mask_aux_data: IriscodeCircuitAuxData<F>,
586}
587
588impl<F: Field> V3CircuitAndAuxData<F> {
589    pub fn new(
590        circuit: Circuit<F>,
591        iris_aux_data: IriscodeCircuitAuxData<F>,
592        mask_aux_data: IriscodeCircuitAuxData<F>,
593    ) -> Self {
594        // Verify `circuit` contains the necessary input layers, which do _not_ contain any data
595        // yet.
596        // TODO: Provide methods in the `Circuit` struct that can check whether
597        // 1. All input MLEs are empty, and
598        // 2. The circuit contains input layers with labels corresponding exactly to a given list of
599        // labels.
600        // Having those we can replace the below tests with two such method calls.
601        assert!(!circuit
602            .input_layer_contains_data(V3_INPUT_IMAGE_LAYER)
603            .unwrap());
604        assert!(!circuit.input_layer_contains_data(V3_DIGITS_LAYER).unwrap());
605        assert!(!circuit
606            .input_layer_contains_data(V3_SIGN_BITS_LAYER)
607            .unwrap());
608        assert!(!circuit
609            .input_layer_contains_data(V3_AUXILIARY_LAYER)
610            .unwrap());
611
612        Self {
613            circuit,
614            iris_aux_data,
615            mask_aux_data,
616        }
617    }
618
619    pub fn get_circuit(&self) -> &Circuit<F> {
620        &self.circuit
621    }
622
623    pub fn get_iris_aux_data_ref(&self) -> &IriscodeCircuitAuxData<F> {
624        &self.iris_aux_data
625    }
626
627    pub fn get_mask_aux_data_ref(&self) -> &IriscodeCircuitAuxData<F> {
628        &self.mask_aux_data
629    }
630
631    pub fn serialize(&self) -> Vec<u8> {
632        bincode::serialize(&self).expect("Failed to serialize V3CircuitAndAuxData")
633    }
634
635    pub fn deserialize(bytes: &[u8]) -> Self {
636        bincode::deserialize(bytes).expect("Failed to deserialize V3CircuitAndAuxData")
637    }
638}