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