arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/frontend/circom/mod.rs
folding-schemes/src/frontend/circom/mod.rsRaw
  1use ark_circom::circom::CircomCircuit;
  2use ark_ff::PrimeField;
  3use ark_r1cs_std::alloc::AllocVar;
  4use ark_r1cs_std::fields::fp::FpVar;
  5use ark_r1cs_std::R1CSVar;
  6use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, SynthesisError};
  7use ark_std::fmt::Debug;
  8use num_bigint::BigInt;
  9use std::path::PathBuf;
 10
 11use crate::frontend::FCircuit;
 12use crate::Error;
 13
 14pub mod utils;
 15use utils::CircomWrapper;
 16
 17/// Define CircomFCircuit
 18#[derive(Clone, Debug)]
 19pub struct CircomFCircuit<F: PrimeField> {
 20    circom_wrapper: CircomWrapper<F>,
 21}
 22
 23impl<F: PrimeField> FCircuit<F> for CircomFCircuit<F> {
 24    type Params = (PathBuf, PathBuf);
 25
 26    fn new(params: Self::Params) -> Self {
 27        let (r1cs_path, wasm_path) = params;
 28        let circom_wrapper = CircomWrapper::new(r1cs_path, wasm_path);
 29        Self { circom_wrapper }
 30    }
 31
 32    fn state_len(&self) -> usize {
 33        1
 34    }
 35
 36    fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
 37        // Converts PrimeField values to BigInt for computing witness.
 38        let input_num_bigint = z_i
 39            .iter()
 40            .map(|val| self.circom_wrapper.ark_primefield_to_num_bigint(*val))
 41            .collect::<Vec<BigInt>>();
 42
 43        // Computes witness
 44        let witness = self
 45            .circom_wrapper
 46            .extract_witness(&[("ivc_input".to_string(), input_num_bigint)])
 47            .map_err(|e| {
 48                Error::WitnessCalculationError(format!("Failed to calculate witness: {}", e))
 49            })?;
 50
 51        // Extracts the z_i1(next state) from the witness vector.
 52        let z_i1 = witness[1..1 + self.state_len()].to_vec();
 53        Ok(z_i1)
 54    }
 55
 56    fn generate_step_constraints(
 57        &self,
 58        cs: ConstraintSystemRef<F>,
 59        _i: usize,
 60        z_i: Vec<FpVar<F>>,
 61    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 62        let mut input_values = Vec::new();
 63        // Converts each FpVar to PrimeField value, then to num_bigint::BigInt.
 64        for fp_var in z_i.iter() {
 65            // Extracts the PrimeField value from FpVar.
 66            let primefield_value = fp_var.value()?;
 67            // Converts the PrimeField value to num_bigint::BigInt.
 68            let num_bigint_value = self
 69                .circom_wrapper
 70                .ark_primefield_to_num_bigint(primefield_value);
 71            input_values.push(num_bigint_value);
 72        }
 73
 74        let num_bigint_inputs = vec![("ivc_input".to_string(), input_values)];
 75
 76        // Extracts R1CS and witness.
 77        let (r1cs, witness) = self
 78            .circom_wrapper
 79            .extract_r1cs_and_witness(&num_bigint_inputs)
 80            .map_err(|_| SynthesisError::AssignmentMissing)?;
 81
 82        // Initializes the CircomCircuit.
 83        let circom_circuit = CircomCircuit {
 84            r1cs,
 85            witness: witness.clone(),
 86            inputs_already_computed: true,
 87        };
 88
 89        // Generates the constraints for the circom_circuit.
 90        circom_circuit
 91            .generate_constraints(cs.clone())
 92            .map_err(|_| SynthesisError::AssignmentMissing)?;
 93
 94        // Checks for constraint satisfaction.
 95        if !cs.is_satisfied().unwrap() {
 96            return Err(SynthesisError::Unsatisfiable);
 97        }
 98
 99        let w = witness.ok_or(SynthesisError::Unsatisfiable)?;
100
101        // Extracts the z_i1(next state) from the witness vector.
102        let z_i1: Vec<FpVar<F>> =
103            Vec::<FpVar<F>>::new_witness(cs.clone(), || Ok(w[1..1 + self.state_len()].to_vec()))?;
104
105        Ok(z_i1)
106    }
107}
108
109#[cfg(test)]
110pub mod tests {
111    use super::*;
112    use ark_pallas::Fr;
113    use ark_r1cs_std::alloc::AllocVar;
114    use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
115
116    // Tests the step_native function of CircomFCircuit.
117    #[test]
118    fn test_circom_step_native() {
119        let r1cs_path = PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit.r1cs");
120        let wasm_path =
121            PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
122
123        let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path));
124
125        let z_i = vec![Fr::from(3u32)];
126        let z_i1 = circom_fcircuit.step_native(1, z_i).unwrap();
127        assert_eq!(z_i1, vec![Fr::from(35u32)]);
128    }
129
130    // Tests the generate_step_constraints function of CircomFCircuit.
131    #[test]
132    fn test_circom_step_constraints() {
133        let r1cs_path = PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit.r1cs");
134        let wasm_path =
135            PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
136
137        let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path));
138
139        let cs = ConstraintSystem::<Fr>::new_ref();
140
141        let z_i = vec![Fr::from(3u32)];
142
143        let z_i_var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i)).unwrap();
144
145        let cs = ConstraintSystem::<Fr>::new_ref();
146        let z_i1_var = circom_fcircuit
147            .generate_step_constraints(cs.clone(), 1, z_i_var)
148            .unwrap();
149        assert_eq!(z_i1_var.value().unwrap(), vec![Fr::from(35u32)]);
150    }
151
152    // Tests the WrapperCircuit with CircomFCircuit.
153    #[test]
154    fn test_wrapper_circomtofcircuit() {
155        let r1cs_path = PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit.r1cs");
156        let wasm_path =
157            PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
158
159        let circom_fcircuit = CircomFCircuit::<Fr>::new((r1cs_path, wasm_path));
160
161        // Allocates z_i1 by using step_native function.
162        let z_i = vec![Fr::from(3_u32)];
163        let wrapper_circuit = crate::frontend::tests::WrapperCircuit {
164            FC: circom_fcircuit.clone(),
165            z_i: Some(z_i.clone()),
166            z_i1: Some(circom_fcircuit.step_native(0, z_i.clone()).unwrap()),
167        };
168
169        let cs = ConstraintSystem::<Fr>::new_ref();
170
171        wrapper_circuit.generate_constraints(cs.clone()).unwrap();
172        assert!(
173            cs.is_satisfied().unwrap(),
174            "Constraint system is not satisfied"
175        );
176    }
177}