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