ark_circom/circom/
builder.rs1use 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#[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 pub fn new(cfg: CircomConfig<F>) -> Self {
60 Self {
61 cfg,
62 inputs: HashMap::new(),
63 }
64 }
65
66 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 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 pub fn setup(&self) -> CircomCircuit<F> {
99 let mut circom = CircomCircuit {
100 r1cs: self.cfg.r1cs.clone(),
101 witness: None,
102 };
103
104 circom.r1cs.wire_mapping = None;
106
107 circom
108 }
109
110 pub fn build(mut self) -> Result<CircomCircuit<F>> {
113 let mut circom = self.setup();
114
115 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 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}