use super::{CircomCircuit, R1CS};
use crate::{
circom::R1CSFile,
witness::{Wasm, WitnessCalculator},
};
use ark_ff::PrimeField;
use color_eyre::Result;
use num_bigint::BigInt;
use serde::Deserialize;
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
use wasmer::Store;
#[derive(Debug)]
pub struct CircomBuilder<F: PrimeField> {
pub cfg: CircomConfig<F>,
pub inputs: HashMap<String, Vec<BigInt>>,
}
#[derive(Debug)]
pub struct CircomConfig<F: PrimeField> {
pub r1cs: R1CS<F>,
pub wtns: WitnessCalculator,
pub store: Store,
pub sanity_check: bool,
}
impl<F: PrimeField> CircomConfig<F> {
pub fn new(wtns: impl AsRef<Path>, r1cs: impl AsRef<Path>) -> Result<Self> {
let mut store = Store::default();
let wtns = WitnessCalculator::new(&mut store, wtns).unwrap();
let reader = BufReader::new(File::open(r1cs)?);
let r1cs = R1CSFile::new(reader)?.into();
Ok(Self {
wtns,
r1cs,
store,
sanity_check: false,
})
}
pub fn new_from_wasm(wasm: Wasm, r1cs: impl AsRef<Path>) -> Result<Self> {
let mut store = Store::default();
let wtns = WitnessCalculator::new_from_wasm(&mut store, wasm).unwrap();
let reader = File::open(r1cs)?;
let r1cs = R1CSFile::new(reader)?.into();
Ok(Self {
wtns,
r1cs,
store,
sanity_check: false,
})
}
}
impl<F: PrimeField> CircomBuilder<F> {
pub fn new(cfg: CircomConfig<F>) -> Self {
Self {
cfg,
inputs: HashMap::new(),
}
}
pub fn push_input<T: Into<BigInt>>(&mut self, name: impl ToString, val: T) {
let values = self.inputs.entry(name.to_string()).or_default();
values.push(val.into());
}
pub fn load_input_json<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let raw: HashMap<String, BigIntOrVec> = serde_json::from_reader(reader)?;
let parsed = raw
.into_iter()
.map(|(k, v)| {
let vec = match v {
BigIntOrVec::Single(s) => vec![s.parse::<BigInt>()?],
BigIntOrVec::Vec(vs) => vs
.into_iter()
.map(|s| s.parse::<BigInt>())
.collect::<Result<_, _>>()?,
};
Ok((k, vec))
})
.collect::<Result<HashMap<_, _>, color_eyre::eyre::Error>>()?;
self.inputs = parsed;
Ok(())
}
pub fn setup(&self) -> CircomCircuit<F> {
let mut circom = CircomCircuit {
r1cs: self.cfg.r1cs.clone(),
witness: None,
};
circom.r1cs.wire_mapping = None;
circom
}
pub fn build(mut self) -> Result<CircomCircuit<F>> {
let mut circom = self.setup();
let witness = self.cfg.wtns.calculate_witness_element::<F, _>(
&mut self.cfg.store,
self.inputs,
self.cfg.sanity_check,
)?;
circom.witness = Some(witness);
debug_assert!({
use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem};
let cs = ConstraintSystem::<F>::new_ref();
circom.clone().generate_constraints(cs.clone()).unwrap();
let is_satisfied = cs.is_satisfied().unwrap();
if !is_satisfied {
println!(
"Unsatisfied constraint: {:?}",
cs.which_is_unsatisfied().unwrap()
);
}
is_satisfied
});
Ok(circom)
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum BigIntOrVec {
Single(String),
Vec(Vec<String>),
}