arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/frontend/noir/mod.rs
folding-schemes/src/frontend/noir/mod.rsRaw
  1use std::collections::HashMap;
  2
  3use crate::Error;
  4
  5use super::FCircuit;
  6use acvm::{
  7    acir::{
  8        acir_field::GenericFieldElement,
  9        circuit::{Circuit, Program},
 10        native_types::{Witness as AcvmWitness, WitnessMap},
 11    },
 12    blackbox_solver::StubbedBlackBoxSolver,
 13    pwg::ACVM,
 14};
 15use ark_ff::PrimeField;
 16use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
 17use ark_relations::r1cs::ConstraintSynthesizer;
 18use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
 19use arkworks_backend::{read_program_from_file, sonobe_bridge::AcirCircuitSonobe};
 20
 21#[derive(Clone, Debug)]
 22pub struct NoirFCircuit<F: PrimeField> {
 23    pub circuit: Circuit<GenericFieldElement<F>>,
 24    pub state_len: usize,
 25    pub external_inputs_len: usize,
 26}
 27
 28impl<F: PrimeField> FCircuit<F> for NoirFCircuit<F> {
 29    type Params = (String, usize, usize);
 30
 31    fn new(params: Self::Params) -> Result<Self, crate::Error> {
 32        let (path, state_len, external_inputs_len) = params;
 33        let program =
 34            read_program_from_file(path).map_err(|ee| Error::Other(format!("{:?}", ee)))?;
 35        let circuit: Circuit<GenericFieldElement<F>> = program.functions[0].clone();
 36        let ivc_input_length = circuit.public_parameters.0.len();
 37        let ivc_return_length = circuit.return_values.0.len();
 38
 39        if ivc_input_length != ivc_return_length {
 40            return Err(Error::NotSameLength(
 41                "IVC input: ".to_string(),
 42                ivc_input_length,
 43                "IVC output: ".to_string(),
 44                ivc_return_length,
 45            ));
 46        }
 47
 48        Ok(NoirFCircuit {
 49            circuit,
 50            state_len,
 51            external_inputs_len,
 52        })
 53    }
 54
 55    fn state_len(&self) -> usize {
 56        self.state_len
 57    }
 58
 59    fn external_inputs_len(&self) -> usize {
 60        self.external_inputs_len
 61    }
 62
 63    fn step_native(
 64        &self,
 65        _i: usize,
 66        z_i: Vec<F>,
 67        external_inputs: Vec<F>, // inputs that are not part of the state
 68    ) -> Result<Vec<F>, crate::Error> {
 69        let mut acvm = ACVM::new(
 70            &StubbedBlackBoxSolver,
 71            &self.circuit.opcodes,
 72            WitnessMap::new(),
 73            &[],
 74            &[],
 75        );
 76
 77        self.circuit
 78            .public_parameters
 79            .0
 80            .iter()
 81            .map(|witness| {
 82                let idx: usize = witness.as_usize();
 83                let value = z_i[idx].to_string();
 84                let witness = AcvmWitness(witness.witness_index());
 85                let f = GenericFieldElement::<F>::try_from_str(&value)
 86                    .ok_or(SynthesisError::Unsatisfiable)?;
 87                acvm.overwrite_witness(witness, f);
 88                Ok(())
 89            })
 90            .collect::<Result<Vec<()>, SynthesisError>>()?;
 91
 92        // write witness values for external_inputs
 93        self.circuit
 94            .private_parameters
 95            .iter()
 96            .map(|witness| {
 97                let idx = witness.as_usize() - z_i.len();
 98                let value = external_inputs[idx].to_string();
 99                let f = GenericFieldElement::<F>::try_from_str(&value)
100                    .ok_or(SynthesisError::Unsatisfiable)?;
101                acvm.overwrite_witness(AcvmWitness(witness.witness_index()), f);
102                Ok(())
103            })
104            .collect::<Result<Vec<()>, SynthesisError>>()?;
105        let _ = acvm.solve();
106
107        let witness_map = acvm.finalize();
108
109        // get the z_{i+1} output state
110        let assigned_z_i1 = self
111            .circuit
112            .return_values
113            .0
114            .iter()
115            .map(|witness| {
116                let noir_field_element = witness_map
117                    .get(witness)
118                    .ok_or(SynthesisError::AssignmentMissing)?;
119                Ok(noir_field_element.into_repr())
120            })
121            .collect::<Result<Vec<F>, SynthesisError>>()?;
122
123        Ok(assigned_z_i1)
124    }
125
126    fn generate_step_constraints(
127        &self,
128        cs: ConstraintSystemRef<F>,
129        _i: usize,
130        z_i: Vec<FpVar<F>>,
131        external_inputs: Vec<FpVar<F>>, // inputs that are not part of the state
132    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
133        let mut acvm = ACVM::new(
134            &StubbedBlackBoxSolver,
135            &self.circuit.opcodes,
136            WitnessMap::new(),
137            &[],
138            &[],
139        );
140
141        let mut already_assigned_witness_values = HashMap::new();
142
143        self.circuit
144            .public_parameters
145            .0
146            .iter()
147            .map(|witness| {
148                let idx: usize = witness.as_usize();
149                let witness = AcvmWitness(witness.witness_index());
150                already_assigned_witness_values.insert(witness, &z_i[idx]);
151                let val = z_i[idx].value()?;
152                let value = if val == F::zero() {
153                    "0".to_string()
154                } else {
155                    val.to_string()
156                };
157
158                let f = GenericFieldElement::<F>::try_from_str(&value)
159                    .ok_or(SynthesisError::Unsatisfiable)?;
160                acvm.overwrite_witness(witness, f);
161                Ok(())
162            })
163            .collect::<Result<Vec<()>, SynthesisError>>()?;
164
165        // write witness values for external_inputs
166        self.circuit
167            .private_parameters
168            .iter()
169            .map(|witness| {
170                let idx = witness.as_usize() - z_i.len();
171                let witness = AcvmWitness(witness.witness_index());
172                already_assigned_witness_values.insert(witness, &external_inputs[idx]);
173
174                let val = external_inputs[idx].value()?;
175                let value = if val == F::zero() {
176                    "0".to_string()
177                } else {
178                    val.to_string()
179                };
180
181                let f = GenericFieldElement::<F>::try_from_str(&value)
182                    .ok_or(SynthesisError::Unsatisfiable)?;
183                acvm.overwrite_witness(witness, f);
184                Ok(())
185            })
186            .collect::<Result<Vec<()>, SynthesisError>>()?;
187
188        // computes the witness
189        let _ = acvm.solve();
190        let witness_map = acvm.finalize();
191
192        // get the z_{i+1} output state
193        let assigned_z_i1 = self
194            .circuit
195            .return_values
196            .0
197            .iter()
198            .map(|witness| {
199                let noir_field_element = witness_map
200                    .get(witness)
201                    .ok_or(SynthesisError::AssignmentMissing)?;
202                FpVar::<F>::new_witness(cs.clone(), || Ok(noir_field_element.into_repr()))
203            })
204            .collect::<Result<Vec<FpVar<F>>, SynthesisError>>()?;
205
206        // initialize circuit and set already assigned values
207        let mut acir_circuit = AcirCircuitSonobe::from((&self.circuit, witness_map));
208        acir_circuit.already_assigned_witnesses = already_assigned_witness_values;
209
210        acir_circuit.generate_constraints(cs.clone())?;
211
212        Ok(assigned_z_i1)
213    }
214}
215
216pub fn load_noir_circuit<F: PrimeField>(path: String) -> Circuit<GenericFieldElement<F>> {
217    let program: Program<GenericFieldElement<F>> = read_program_from_file(path).unwrap();
218    let circuit: Circuit<GenericFieldElement<F>> = program.functions[0].clone();
219    circuit
220}
221
222#[cfg(test)]
223mod tests {
224    use crate::frontend::{noir::load_noir_circuit, FCircuit};
225    use ark_bn254::Fr;
226    use ark_r1cs_std::R1CSVar;
227    use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar};
228    use ark_relations::r1cs::ConstraintSystem;
229    use std::env;
230
231    use crate::frontend::noir::NoirFCircuit;
232
233    #[test]
234    fn test_step_native() {
235        let cur_path = env::current_dir().unwrap();
236        let circuit_path = format!(
237            "{}/src/frontend/noir/test_folder/test_circuit/target/test_circuit.json",
238            cur_path.to_str().unwrap()
239        );
240        let circuit = load_noir_circuit(circuit_path);
241        let noirfcircuit = NoirFCircuit {
242            circuit,
243            state_len: 2,
244            external_inputs_len: 2,
245        };
246        let inputs = vec![Fr::from(2), Fr::from(5)];
247        let res = noirfcircuit.step_native(0, inputs.clone(), inputs);
248        assert!(res.is_ok());
249        assert_eq!(res.unwrap(), vec![Fr::from(4), Fr::from(25)]);
250    }
251
252    #[test]
253    fn test_step_constraints() {
254        let cs = ConstraintSystem::<Fr>::new_ref();
255        let cur_path = env::current_dir().unwrap();
256        let circuit_path = format!(
257            "{}/src/frontend/noir/test_folder/test_circuit/target/test_circuit.json",
258            cur_path.to_str().unwrap()
259        );
260        let circuit = load_noir_circuit(circuit_path);
261        let noirfcircuit = NoirFCircuit {
262            circuit,
263            state_len: 2,
264            external_inputs_len: 2,
265        };
266        let inputs = vec![Fr::from(2), Fr::from(5)];
267        let z_i = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(inputs.clone())).unwrap();
268        let external_inputs = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(inputs)).unwrap();
269        let output = noirfcircuit
270            .generate_step_constraints(cs.clone(), 0, z_i, external_inputs)
271            .unwrap();
272        assert_eq!(output[0].value().unwrap(), Fr::from(4));
273        assert_eq!(output[1].value().unwrap(), Fr::from(25));
274    }
275
276    #[test]
277    fn test_step_constraints_no_external_inputs() {
278        let cs = ConstraintSystem::<Fr>::new_ref();
279        let cur_path = env::current_dir().unwrap();
280        let circuit_path = format!(
281            "{}/src/frontend/noir/test_folder/test_no_external_inputs/target/test_no_external_inputs.json",
282            cur_path.to_str().unwrap()
283        );
284        let circuit = load_noir_circuit(circuit_path);
285        let noirfcircuit = NoirFCircuit {
286            circuit,
287            state_len: 2,
288            external_inputs_len: 0,
289        };
290        let inputs = vec![Fr::from(2), Fr::from(5)];
291        let z_i = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(inputs.clone())).unwrap();
292        let external_inputs = vec![];
293        let output = noirfcircuit
294            .generate_step_constraints(cs.clone(), 0, z_i, external_inputs)
295            .unwrap();
296        assert_eq!(output[0].value().unwrap(), Fr::from(4));
297        assert_eq!(output[1].value().unwrap(), Fr::from(25));
298    }
299}