Skip to main content

ark_circom/circom/
builder.rs

1use super::{CircomCircuit, R1CS};
2use crate::{
3    circom::R1CSFile,
4    witness::{Wasm, WitnessCalculator},
5};
6use ark_ff::PrimeField;
7use color_eyre::Result;
8use num_bigint::BigInt;
9use serde::Deserialize;
10use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
11use wasmer::Store;
12
13#[derive(Debug)]
14pub struct CircomBuilder<F: PrimeField> {
15    pub cfg: CircomConfig<F>,
16    pub inputs: HashMap<String, Vec<BigInt>>,
17}
18
19// Add utils for creating this from files / directly from bytes
20#[derive(Debug)]
21pub struct CircomConfig<F: PrimeField> {
22    pub r1cs: R1CS<F>,
23    pub wtns: WitnessCalculator,
24    pub store: Store,
25    pub sanity_check: bool,
26}
27
28impl<F: PrimeField> CircomConfig<F> {
29    pub fn new(wtns: impl AsRef<Path>, r1cs: impl AsRef<Path>) -> Result<Self> {
30        let mut store = Store::default();
31        let wtns = WitnessCalculator::new(&mut store, wtns).unwrap();
32        let reader = BufReader::new(File::open(r1cs)?);
33        let r1cs = R1CSFile::new(reader)?.into();
34        Ok(Self {
35            wtns,
36            r1cs,
37            store,
38            sanity_check: false,
39        })
40    }
41
42    pub fn new_from_wasm(wasm: Wasm, r1cs: impl AsRef<Path>) -> Result<Self> {
43        let mut store = Store::default();
44        let wtns = WitnessCalculator::new_from_wasm(&mut store, wasm).unwrap();
45        let reader = File::open(r1cs)?;
46        let r1cs = R1CSFile::new(reader)?.into();
47        Ok(Self {
48            wtns,
49            r1cs,
50            store,
51            sanity_check: false,
52        })
53    }
54}
55
56impl<F: PrimeField> CircomBuilder<F> {
57    /// Instantiates a new builder using the provided WitnessGenerator and R1CS files
58    /// for your circuit
59    pub fn new(cfg: CircomConfig<F>) -> Self {
60        Self {
61            cfg,
62            inputs: HashMap::new(),
63        }
64    }
65
66    /// Pushes a Circom input at the specified name.
67    pub fn push_input<T: Into<BigInt>>(&mut self, name: impl ToString, val: T) {
68        let values = self.inputs.entry(name.to_string()).or_default();
69        values.push(val.into());
70    }
71    // Loads the input.json file from the specified path
72    pub fn load_input_json<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
73        let file = File::open(path)?;
74        let reader = BufReader::new(file);
75
76        let raw: HashMap<String, BigIntOrVec> = serde_json::from_reader(reader)?;
77
78        let parsed = raw
79            .into_iter()
80            .map(|(k, v)| {
81                let vec = match v {
82                    BigIntOrVec::Single(s) => vec![s.parse::<BigInt>()?],
83                    BigIntOrVec::Vec(vs) => vs
84                        .into_iter()
85                        .map(|s| s.parse::<BigInt>())
86                        .collect::<Result<_, _>>()?,
87                };
88                Ok((k, vec))
89            })
90            .collect::<Result<HashMap<_, _>, color_eyre::eyre::Error>>()?;
91
92        self.inputs = parsed;
93        Ok(())
94    }
95
96    /// Generates an empty circom circuit with no witness set, to be used for
97    /// generation of the trusted setup parameters
98    pub fn setup(&self) -> CircomCircuit<F> {
99        let mut circom = CircomCircuit {
100            r1cs: self.cfg.r1cs.clone(),
101            witness: None,
102        };
103
104        // Disable the wire mapping
105        circom.r1cs.wire_mapping = None;
106
107        circom
108    }
109
110    /// Creates the circuit populated with the witness corresponding to the previously
111    /// provided inputs
112    pub fn build(mut self) -> Result<CircomCircuit<F>> {
113        let mut circom = self.setup();
114
115        // calculate the witness
116        let witness = self.cfg.wtns.calculate_witness_element::<F, _>(
117            &mut self.cfg.store,
118            self.inputs,
119            self.cfg.sanity_check,
120        )?;
121        circom.witness = Some(witness);
122
123        // sanity check
124        debug_assert!({
125            use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem};
126            let cs = ConstraintSystem::<F>::new_ref();
127            circom.clone().generate_constraints(cs.clone()).unwrap();
128            let is_satisfied = cs.is_satisfied().unwrap();
129            if !is_satisfied {
130                println!(
131                    "Unsatisfied constraint: {:?}",
132                    cs.which_is_unsatisfied().unwrap()
133                );
134            }
135
136            is_satisfied
137        });
138
139        Ok(circom)
140    }
141}
142
143#[derive(Debug, Deserialize)]
144#[serde(untagged)]
145enum BigIntOrVec {
146    Single(String),
147    Vec(Vec<String>),
148}