arnaucube
arnaucube/circom-compatPublic Self-hosted repository
circom-compat/src/circom/circuit.rs
src/circom/circuit.rsRaw
  1use ark_ff::PrimeField;
  2use ark_relations::r1cs::{
  3    ConstraintSynthesizer, ConstraintSystemRef, LinearCombination, SynthesisError, Variable,
  4};
  5
  6use super::R1CS;
  7
  8use color_eyre::Result;
  9
 10#[derive(Clone, Debug)]
 11pub struct CircomCircuit<F: PrimeField> {
 12    pub r1cs: R1CS<F>,
 13    pub witness: Option<Vec<F>>,
 14    pub inputs_already_allocated: bool,
 15}
 16
 17impl<F: PrimeField> CircomCircuit<F> {
 18    pub fn get_public_inputs(&self) -> Option<Vec<F>> {
 19        match &self.witness {
 20            None => None,
 21            Some(w) => match &self.r1cs.wire_mapping {
 22                None => Some(w[1..self.r1cs.num_inputs].to_vec()),
 23                Some(m) => Some(m[1..self.r1cs.num_inputs].iter().map(|i| w[*i]).collect()),
 24            },
 25        }
 26    }
 27}
 28
 29impl<F: PrimeField> ConstraintSynthesizer<F> for CircomCircuit<F> {
 30    fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
 31        let witness = &self.witness;
 32        let wire_mapping = &self.r1cs.wire_mapping;
 33
 34        // Start from 1 because Arkworks implicitly allocates One for the first input
 35        if !self.inputs_already_allocated {
 36            for i in 1..self.r1cs.num_inputs {
 37                cs.new_input_variable(|| {
 38                    Ok(match witness {
 39                        None => F::from(1u32),
 40                        Some(w) => match wire_mapping {
 41                            Some(m) => w[m[i]],
 42                            None => w[i],
 43                        },
 44                    })
 45                })?;
 46            }
 47        }
 48
 49        for i in 0..self.r1cs.num_aux {
 50            cs.new_witness_variable(|| {
 51                Ok(match witness {
 52                    None => F::from(1u32),
 53                    Some(w) => match wire_mapping {
 54                        Some(m) => w[m[i + self.r1cs.num_inputs]],
 55                        None => w[i + self.r1cs.num_inputs],
 56                    },
 57                })
 58            })?;
 59        }
 60
 61        let make_index = |index| {
 62            if index < self.r1cs.num_inputs {
 63                Variable::Instance(index)
 64            } else {
 65                Variable::Witness(index - self.r1cs.num_inputs)
 66            }
 67        };
 68        let make_lc = |lc_data: &[(usize, F)]| {
 69            lc_data.iter().fold(
 70                LinearCombination::<F>::zero(),
 71                |lc: LinearCombination<F>, (index, coeff)| lc + (*coeff, make_index(*index)),
 72            )
 73        };
 74
 75        let skip = if self.inputs_already_allocated {
 76            self.r1cs.num_inputs
 77        } else {
 78            0
 79        };
 80        for constraint in self.r1cs.constraints.iter().skip(skip) {
 81            cs.enforce_constraint(
 82                make_lc(&constraint.0),
 83                make_lc(&constraint.1),
 84                make_lc(&constraint.2),
 85            )?;
 86        }
 87
 88        Ok(())
 89    }
 90}
 91
 92#[cfg(test)]
 93mod tests {
 94    use super::*;
 95    use crate::{CircomBuilder, CircomConfig};
 96    use ark_bn254::Fr;
 97    use ark_relations::r1cs::ConstraintSystem;
 98
 99    #[test]
100    fn satisfied() {
101        let cfg = CircomConfig::<Fr>::new(
102            "./test-vectors/mycircuit.wasm",
103            "./test-vectors/mycircuit.r1cs",
104        )
105        .unwrap();
106        let mut builder = CircomBuilder::new(cfg);
107        builder.push_input("a", 3);
108        builder.push_input("b", 11);
109
110        let circom = builder.build().unwrap();
111        let cs = ConstraintSystem::<Fr>::new_ref();
112        circom.generate_constraints(cs.clone()).unwrap();
113        assert!(cs.is_satisfied().unwrap());
114    }
115}