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