arnaucube
arnaucube/circom-compatPublic Self-hosted repository
circom-compat/src/circuit/builder.rs
src/circuit/builder.rsRaw
 1use ark_ec::PairingEngine;
 2use std::{fs::File, path::Path};
 3
 4use super::{CircomCircuit, R1CS};
 5
 6use num_bigint::BigInt;
 7use std::collections::HashMap;
 8
 9use crate::{circuit::R1CSFile, WitnessCalculator};
10use color_eyre::Result;
11
12pub struct CircomBuilder<E: PairingEngine> {
13    pub cfg: CircuitConfig<E>,
14    pub inputs: HashMap<String, Vec<BigInt>>,
15}
16
17// Add utils for creating this from files / directly from bytes
18pub struct CircuitConfig<E: PairingEngine> {
19    pub r1cs: R1CS<E>,
20    pub wtns: WitnessCalculator,
21    pub sanity_check: bool,
22}
23
24impl<E: PairingEngine> CircuitConfig<E> {
25    pub fn new(wtns: impl AsRef<Path>, r1cs: impl AsRef<Path>) -> Result<Self> {
26        let wtns = WitnessCalculator::new(wtns).unwrap();
27        let reader = File::open(r1cs)?;
28        let r1cs = R1CSFile::new(reader)?.into();
29        Ok(Self {
30            wtns,
31            r1cs,
32            sanity_check: false,
33        })
34    }
35}
36
37impl<E: PairingEngine> CircomBuilder<E> {
38    /// Instantiates a new builder using the provided WitnessGenerator and R1CS files
39    /// for your circuit
40    pub fn new(cfg: CircuitConfig<E>) -> Self {
41        Self {
42            cfg,
43            inputs: HashMap::new(),
44        }
45    }
46
47    /// Pushes a Circom input at the specified name.
48    pub fn push_input<T: Into<BigInt>>(&mut self, name: impl ToString, val: T) {
49        let values = self.inputs.entry(name.to_string()).or_insert_with(Vec::new);
50        values.push(val.into());
51    }
52
53    /// Generates an empty circom circuit with no witness set, to be used for
54    /// generation of the trusted setup parameters
55    pub fn setup(&self) -> CircomCircuit<E> {
56        let mut circom = CircomCircuit {
57            r1cs: self.cfg.r1cs.clone(),
58            witness: None,
59        };
60
61        // Disable the wire mapping
62        circom.r1cs.wire_mapping = None;
63
64        circom
65    }
66
67    /// Creates the circuit populated with the witness corresponding to the previously
68    /// provided inputs
69    pub fn build(mut self) -> Result<CircomCircuit<E>> {
70        let mut circom = self.setup();
71
72        // calculate the witness
73        let witness = self
74            .cfg
75            .wtns
76            .calculate_witness(self.inputs, self.cfg.sanity_check)?;
77
78        // convert it to field elements
79        let witness = witness
80            .into_iter()
81            .map(|w| E::Fr::from(w.to_biguint().unwrap()))
82            .collect::<Vec<_>>();
83        circom.witness = Some(witness);
84
85        // sanity check
86        debug_assert!({
87            use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
88            let cs = ConstraintSystem::<E::Fr>::new_ref();
89            circom.clone().generate_constraints(cs.clone()).unwrap();
90            cs.is_satisfied().unwrap()
91        });
92
93        Ok(circom)
94    }
95}