ark_circom/circom/
builder.rs

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