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 noir_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}