arnaucube
arnaucube/circom-compatPublic Self-hosted repository
circom-compat/src/circom/builder.rs
src/circom/builder.rsRaw
  1use ark_ec::PairingEngine;
  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
 12pub struct CircomBuilder<E: PairingEngine> {
 13    pub cfg: CircomConfig<E>,
 14    pub inputs: HashMap<String, Vec<BigInt>>,
 15}
 16
 17// Add utils for creating this from files / directly from bytes
 18pub struct CircomConfig<E: PairingEngine> {
 19    pub r1cs: R1CS<E>,
 20    pub wtns: WitnessCalculator,
 21    pub sanity_check: bool,
 22}
 23
 24impl<E: PairingEngine> CircomConfig<E> {
 25    pub fn new(wtns: impl AsRef<Path>, r1cs: impl AsRef<Path>) -> Result<Self> {
 26        let wtns = WitnessCalculator::new(wtns).unwrap();
 27        let reader = File::open(r1cs)?;
 28        let r1cs = R1CSFile::new(reader)?.into();
 29        Ok(Self {
 30            wtns,
 31            r1cs,
 32            sanity_check: false,
 33        })
 34    }
 35}
 36
 37impl<E: PairingEngine> CircomBuilder<E> {
 38    /// Instantiates a new builder using the provided WitnessGenerator and R1CS files
 39    /// for your circuit
 40    pub fn new(cfg: CircomConfig<E>) -> Self {
 41        Self {
 42            cfg,
 43            inputs: HashMap::new(),
 44        }
 45    }
 46
 47    /// Pushes a Circom input at the specified name.
 48    pub fn push_input<T: Into<BigInt>>(&mut self, name: impl ToString, val: T) {
 49        let values = self.inputs.entry(name.to_string()).or_insert_with(Vec::new);
 50        values.push(val.into());
 51    }
 52
 53    /// Generates an empty circom circuit with no witness set, to be used for
 54    /// generation of the trusted setup parameters
 55    pub fn setup(&self) -> CircomCircuit<E> {
 56        let mut circom = CircomCircuit {
 57            r1cs: self.cfg.r1cs.clone(),
 58            witness: None,
 59        };
 60
 61        // Disable the wire mapping
 62        circom.r1cs.wire_mapping = None;
 63
 64        circom
 65    }
 66
 67    /// Creates the circuit populated with the witness corresponding to the previously
 68    /// provided inputs
 69    pub fn build(mut self) -> Result<CircomCircuit<E>> {
 70        let mut circom = self.setup();
 71
 72        // calculate the witness
 73        let witness = self
 74            .cfg
 75            .wtns
 76            .calculate_witness(self.inputs, self.cfg.sanity_check)?;
 77
 78        use ark_ff::{PrimeField, FpParameters};
 79        let modulus = <<E::Fr as PrimeField>::Params as FpParameters>::MODULUS;
 80
 81        // convert it to field elements
 82        use num_traits::Signed;
 83        let witness = witness
 84            .into_iter()
 85            .map(|w| {
 86                let w = if w.sign() == num_bigint::Sign::Minus {
 87                    // Need to negate the witness element if negative
 88                    modulus.into() - w.abs().to_biguint().unwrap()
 89                } else {
 90                    w.to_biguint().unwrap()
 91                };
 92                E::Fr::from(w)
 93            }).collect::<Vec<_>>();
 94        circom.witness = Some(witness);
 95
 96        // sanity check
 97        debug_assert!({
 98            use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
 99            let cs = ConstraintSystem::<E::Fr>::new_ref();
100            circom.clone().generate_constraints(cs.clone()).unwrap();
101            cs.is_satisfied().unwrap()
102        });
103
104        Ok(circom)
105    }
106}