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