Skip to main content

rings_node/extension/snark/
builder.rs

1#![warn(missing_docs)]
2//! [`SNARKTaskBuilder`] — loads an r1cs + witness calculator (local or remote), generates
3//! the recursive circuits, and assembles the per-curve [`SNARKProofTask`] (public params +
4//! initial fold).
5
6use rings_derive::wasm_export;
7use rings_snark::circuit;
8use rings_snark::prelude::nova::provider;
9use rings_snark::prelude::nova::provider::hyperkzg;
10use rings_snark::prelude::nova::provider::ipa_pc;
11use rings_snark::prelude::nova::spartan;
12use rings_snark::prelude::nova::traits::Engine;
13use rings_snark::r1cs;
14use rings_snark::snark::SNARK;
15
16use super::Circuit;
17use super::CircuitEnum;
18use super::CircuitGenerator;
19use super::FieldEnum;
20use super::Input;
21use super::SNARKGenerator;
22use super::SupportedPrimeField;
23use crate::error::Result;
24use crate::extension::types::snark::SNARKProofTask;
25
26/// Snark builder
27#[wasm_export]
28pub struct SNARKTaskBuilder {
29    circuit_generator: CircuitGenerator,
30}
31
32#[wasm_export]
33impl SNARKTaskBuilder {
34    /// Load r1cs sand witness from local path
35    pub async fn from_local(
36        r1cs_path: String,
37        witness_wasm_path: String,
38        field: SupportedPrimeField,
39    ) -> Result<SNARKTaskBuilder> {
40        match field {
41            SupportedPrimeField::Vesta => {
42                type F = <provider::VestaEngine as Engine>::Scalar;
43                let r1cs =
44                    r1cs::load_r1cs::<F>(r1cs::Path::Local(r1cs_path), r1cs::Format::Bin).await?;
45                let witness_calculator =
46                    r1cs::load_circom_witness_calculator(r1cs::Path::Local(witness_wasm_path))
47                        .await?;
48                let circuit_generator =
49                    circuit::WasmCircuitGenerator::<F>::new(r1cs, witness_calculator);
50                Ok(Self {
51                    circuit_generator: CircuitGenerator::Vesta(circuit_generator),
52                })
53            }
54            SupportedPrimeField::Pallas => {
55                type F = <provider::PallasEngine as Engine>::Scalar;
56                let r1cs =
57                    r1cs::load_r1cs::<F>(r1cs::Path::Local(r1cs_path), r1cs::Format::Bin).await?;
58                let witness_calculator =
59                    r1cs::load_circom_witness_calculator(r1cs::Path::Local(witness_wasm_path))
60                        .await?;
61                let circuit_generator =
62                    circuit::WasmCircuitGenerator::<F>::new(r1cs, witness_calculator);
63                Ok(Self {
64                    circuit_generator: CircuitGenerator::Pallas(circuit_generator),
65                })
66            }
67            SupportedPrimeField::Bn256KZG => {
68                type F = <provider::Bn256EngineKZG as Engine>::Scalar;
69                let r1cs =
70                    r1cs::load_r1cs::<F>(r1cs::Path::Local(r1cs_path), r1cs::Format::Bin).await?;
71                let witness_calculator =
72                    r1cs::load_circom_witness_calculator(r1cs::Path::Local(witness_wasm_path))
73                        .await?;
74                let circuit_generator =
75                    circuit::WasmCircuitGenerator::<F>::new(r1cs, witness_calculator);
76                Ok(Self {
77                    circuit_generator: CircuitGenerator::Bn256KZG(circuit_generator),
78                })
79            }
80        }
81    }
82
83    /// Load r1cs sand witness from remote url
84    pub async fn from_remote(
85        r1cs_path: String,
86        witness_wasm_path: String,
87        field: SupportedPrimeField,
88    ) -> Result<SNARKTaskBuilder> {
89        match field {
90            SupportedPrimeField::Vesta => {
91                type F = <provider::VestaEngine as Engine>::Scalar;
92                let r1cs =
93                    r1cs::load_r1cs::<F>(r1cs::Path::Remote(r1cs_path), r1cs::Format::Bin).await?;
94                let witness_calculator =
95                    r1cs::load_circom_witness_calculator(r1cs::Path::Remote(witness_wasm_path))
96                        .await?;
97                let circuit_generator =
98                    circuit::WasmCircuitGenerator::<F>::new(r1cs, witness_calculator);
99                Ok(Self {
100                    circuit_generator: CircuitGenerator::Vesta(circuit_generator),
101                })
102            }
103            SupportedPrimeField::Pallas => {
104                type F = <provider::PallasEngine as Engine>::Scalar;
105                let r1cs =
106                    r1cs::load_r1cs::<F>(r1cs::Path::Remote(r1cs_path), r1cs::Format::Bin).await?;
107                let witness_calculator =
108                    r1cs::load_circom_witness_calculator(r1cs::Path::Remote(witness_wasm_path))
109                        .await?;
110                let circuit_generator =
111                    circuit::WasmCircuitGenerator::<F>::new(r1cs, witness_calculator);
112                Ok(Self {
113                    circuit_generator: CircuitGenerator::Pallas(circuit_generator),
114                })
115            }
116            SupportedPrimeField::Bn256KZG => {
117                type F = <provider::Bn256EngineKZG as Engine>::Scalar;
118                let r1cs =
119                    r1cs::load_r1cs::<F>(r1cs::Path::Remote(r1cs_path), r1cs::Format::Bin).await?;
120                let witness_calculator =
121                    r1cs::load_circom_witness_calculator(r1cs::Path::Remote(witness_wasm_path))
122                        .await?;
123                let circuit_generator =
124                    circuit::WasmCircuitGenerator::<F>::new(r1cs, witness_calculator);
125                Ok(Self {
126                    circuit_generator: CircuitGenerator::Bn256KZG(circuit_generator),
127                })
128            }
129        }
130    }
131
132    /// generate recursive circuits
133    pub fn gen_circuits(
134        &self,
135        public_input: Input,
136        private_inputs: Vec<Input>,
137        round: usize,
138    ) -> Result<Vec<Circuit>> {
139        match &self.circuit_generator {
140            CircuitGenerator::Vesta(g) => {
141                type F = <provider::VestaEngine as Engine>::Scalar;
142
143                let input: circuit::Input<F> = public_input
144                    .into_iter()
145                    .map(|(s, v)| {
146                        (
147                            s,
148                            v.into_iter()
149                                .map(|inp| {
150                                    if let FieldEnum::Vesta(x) = inp.value {
151                                        x
152                                    } else {
153                                        panic!("Wrong curve, expect Vesta")
154                                    }
155                                })
156                                .collect(),
157                        )
158                    })
159                    .collect::<Vec<(String, Vec<F>)>>()
160                    .into();
161
162                let private_inputs: Vec<circuit::Input<F>> = private_inputs
163                    .into_iter()
164                    .map(|inp| {
165                        inp.into_iter()
166                            .map(|(s, v)| {
167                                let fields: Vec<F> = v
168                                    .into_iter()
169                                    .map(|inp| {
170                                        if let FieldEnum::Vesta(x) = inp.value {
171                                            x
172                                        } else {
173                                            panic!("Wrong curve, expect Vesta")
174                                        }
175                                    })
176                                    .collect();
177                                (s, fields)
178                            })
179                            .collect::<Vec<(String, Vec<F>)>>()
180                            .into()
181                    })
182                    .collect();
183
184                let circuits = g
185                    .gen_recursive_circuit(input, private_inputs, round, true)?
186                    .iter()
187                    .map(|c| Circuit {
188                        inner: CircuitEnum::Vesta(c.clone()),
189                    })
190                    .collect::<Vec<Circuit>>();
191                Ok(circuits)
192            }
193            CircuitGenerator::Pallas(g) => {
194                type F = <provider::PallasEngine as Engine>::Scalar;
195
196                let input: circuit::Input<F> = public_input
197                    .into_iter()
198                    .map(|(s, v)| {
199                        (
200                            s,
201                            v.into_iter()
202                                .map(|inp| {
203                                    if let FieldEnum::Pallas(x) = inp.value {
204                                        x
205                                    } else {
206                                        panic!("Wrong curve, expect pallas")
207                                    }
208                                })
209                                .collect(),
210                        )
211                    })
212                    .collect::<Vec<(String, Vec<F>)>>()
213                    .into();
214
215                let private_inputs: Vec<circuit::Input<F>> = private_inputs
216                    .into_iter()
217                    .map(|inp| {
218                        inp.into_iter()
219                            .map(|(s, v)| {
220                                let fields: Vec<F> = v
221                                    .into_iter()
222                                    .map(|inp| {
223                                        if let FieldEnum::Pallas(x) = inp.value {
224                                            x
225                                        } else {
226                                            panic!("Wrong curve, expect Vesta")
227                                        }
228                                    })
229                                    .collect();
230                                (s, fields)
231                            })
232                            .collect::<Vec<(String, Vec<F>)>>()
233                            .into()
234                    })
235                    .collect();
236
237                let circuits = g
238                    .gen_recursive_circuit(input, private_inputs, round, true)?
239                    .iter()
240                    .map(|c| Circuit {
241                        inner: CircuitEnum::Pallas(c.clone()),
242                    })
243                    .collect::<Vec<Circuit>>();
244                Ok(circuits)
245            }
246            CircuitGenerator::Bn256KZG(g) => {
247                type F = <provider::Bn256EngineKZG as Engine>::Scalar;
248
249                let input: circuit::Input<F> = public_input
250                    .into_iter()
251                    .map(|(s, v)| {
252                        (
253                            s,
254                            v.into_iter()
255                                .map(|inp| {
256                                    if let FieldEnum::Bn256KZG(x) = inp.value {
257                                        x
258                                    } else {
259                                        panic!("Wrong curve, expect bn256")
260                                    }
261                                })
262                                .collect(),
263                        )
264                    })
265                    .collect::<Vec<(String, Vec<F>)>>()
266                    .into();
267
268                let private_inputs: Vec<circuit::Input<F>> = private_inputs
269                    .into_iter()
270                    .map(|inp| {
271                        inp.into_iter()
272                            .map(|(s, v)| {
273                                let fields: Vec<F> = v
274                                    .into_iter()
275                                    .map(|inp| {
276                                        if let FieldEnum::Bn256KZG(x) = inp.value {
277                                            x
278                                        } else {
279                                            panic!("Wrong curve, expect bn256")
280                                        }
281                                    })
282                                    .collect();
283                                (s, fields)
284                            })
285                            .collect::<Vec<(String, Vec<F>)>>()
286                            .into()
287                    })
288                    .collect();
289
290                let circuits = g
291                    .gen_recursive_circuit(input, private_inputs, round, true)?
292                    .iter()
293                    .map(|c| Circuit {
294                        inner: CircuitEnum::Bn256KZG(c.clone()),
295                    })
296                    .collect::<Vec<Circuit>>();
297                Ok(circuits)
298            }
299        }
300    }
301}
302
303impl SNARKTaskBuilder {
304    /// Generate proof task
305    pub fn gen_proof_task(circuits: Vec<Circuit>) -> Result<SNARKProofTask> {
306        let task = match &circuits[0].inner {
307            CircuitEnum::Vesta(_) => {
308                type E1 = provider::VestaEngine;
309                type E2 = provider::PallasEngine;
310                type EE1 = ipa_pc::EvaluationEngine<E1>;
311                type EE2 = ipa_pc::EvaluationEngine<E2>;
312                type S1 = spartan::snark::RelaxedR1CSSNARK<E1, EE1>;
313                type S2 = spartan::snark::RelaxedR1CSSNARK<E2, EE2>;
314                let circuits: Vec<circuit::Circuit<<E1 as Engine>::Scalar>> = circuits
315                    .into_iter()
316                    .map(|circ| {
317                        if let CircuitEnum::Vesta(c) = circ.inner {
318                            c
319                        } else {
320                            panic!("Wrong curve, expect vesta")
321                        }
322                    })
323                    .collect();
324                let inputs = circuits[0].get_public_inputs();
325                let pp = SNARK::<E1, E2>::gen_pp::<S1, S2>(circuits[0].clone())?;
326                let snark = SNARK::<E1, E2>::new(&circuits[0], &pp, &inputs, &vec![
327                    <E2 as Engine>::Scalar::from(0),
328                ])?;
329
330                SNARKProofTask::VastaPallas(SNARKGenerator {
331                    pp: pp.into(),
332                    snark,
333                    circuits,
334                })
335            }
336            CircuitEnum::Pallas(_) => {
337                type E1 = provider::PallasEngine;
338                type E2 = provider::VestaEngine;
339                type EE1 = ipa_pc::EvaluationEngine<E1>;
340                type EE2 = ipa_pc::EvaluationEngine<E2>;
341                type S1 = spartan::snark::RelaxedR1CSSNARK<E1, EE1>;
342                type S2 = spartan::snark::RelaxedR1CSSNARK<E2, EE2>;
343                let circuits: Vec<circuit::Circuit<<E1 as Engine>::Scalar>> = circuits
344                    .into_iter()
345                    .map(|circ| {
346                        if let CircuitEnum::Pallas(c) = circ.inner {
347                            c
348                        } else {
349                            panic!("Wrong curve, expect vesta")
350                        }
351                    })
352                    .collect();
353                let inputs = circuits[0].get_public_inputs();
354                let pp = SNARK::<E1, E2>::gen_pp::<S1, S2>(circuits[0].clone())?;
355                let snark = SNARK::<E1, E2>::new(&circuits[0], &pp, &inputs, &vec![
356                    <E2 as Engine>::Scalar::from(0),
357                ])?;
358                SNARKProofTask::PallasVasta(SNARKGenerator {
359                    pp: pp.into(),
360                    snark,
361                    circuits,
362                })
363            }
364            CircuitEnum::Bn256KZG(_) => {
365                type E1 = provider::Bn256EngineKZG;
366                type E2 = provider::GrumpkinEngine;
367                type EE1 = hyperkzg::EvaluationEngine<E1>;
368                type EE2 = ipa_pc::EvaluationEngine<E2>;
369                type S1 = spartan::snark::RelaxedR1CSSNARK<E1, EE1>; // non-preprocessing SNARK
370                type S2 = spartan::snark::RelaxedR1CSSNARK<E2, EE2>; // non-preprocessing SNARK
371                let circuits: Vec<circuit::Circuit<<E1 as Engine>::Scalar>> = circuits
372                    .into_iter()
373                    .map(|circ| {
374                        if let CircuitEnum::Bn256KZG(c) = circ.inner {
375                            c
376                        } else {
377                            panic!("Wrong curve, expect vesta")
378                        }
379                    })
380                    .collect();
381                let inputs = circuits[0].get_public_inputs();
382                let pp = SNARK::<E1, E2>::gen_pp::<S1, S2>(circuits[0].clone())?;
383                let snark = SNARK::<E1, E2>::new(&circuits[0], &pp, &inputs, &vec![
384                    <E2 as Engine>::Scalar::from(0),
385                ])?;
386                SNARKProofTask::Bn256KZGGrumpkin(SNARKGenerator {
387                    pp: pp.into(),
388                    snark,
389                    circuits,
390                })
391            }
392        };
393        Ok(task)
394    }
395}