circom_prover/prover/
circom.rs

1// This file is copied from https://github.dev/zkmopro/circom-compat/tree/wasm-delete
2
3//! Helpers for converting Arkworks types to BigUint-tuples as expected by the
4//! Solidity Groth16 Verifier smart contracts
5use ark_bls12_381::{
6    Bls12_381, Fq as bls12_381_fq, Fq2 as bls12_381_Fq2, Fr as bls12_381_Fr,
7    G1Affine as bls12_381_G1Affine, G2Affine as bls12_381_G2Affine,
8};
9use ark_bn254::{
10    Bn254, Fq as bn254_Fq, Fq2 as bn254_Fq2, Fr as bn254_Fr, G1Affine as bn254_G1Affine,
11    G2Affine as bn254_G2Affine,
12};
13use ark_ec::AffineRepr;
14use ark_ff::{BigInteger, PrimeField};
15use ark_serialize::CanonicalDeserialize;
16use num::BigUint;
17use num_traits::Zero;
18use serde::{Deserialize, Serialize};
19
20pub const PROTOCOL_GROTH16: &str = "groth16";
21pub const CURVE_BN254: &str = "bn128";
22pub const CURVE_BLS12_381: &str = "bls12381";
23
24pub struct Inputs(pub Vec<BigUint>);
25
26impl From<&[bn254_Fr]> for Inputs {
27    fn from(src: &[bn254_Fr]) -> Self {
28        let els = src.iter().map(|point| point_to_biguint(*point)).collect();
29        Self(els)
30    }
31}
32
33impl From<&[bls12_381_Fr]> for Inputs {
34    fn from(src: &[bls12_381_Fr]) -> Self {
35        let els = src.iter().map(|point| point_to_biguint(*point)).collect();
36        Self(els)
37    }
38}
39
40impl From<Inputs> for Vec<bn254_Fr> {
41    fn from(inputs: Inputs) -> Self {
42        inputs
43            .0
44            .iter()
45            .map(|biguint| biguint_to_point(biguint.clone()))
46            .collect()
47    }
48}
49
50impl From<Inputs> for Vec<bls12_381_Fr> {
51    fn from(inputs: Inputs) -> Self {
52        inputs
53            .0
54            .iter()
55            .map(|biguint| biguint_to_point(biguint.clone()))
56            .collect()
57    }
58}
59
60// Follow the interface: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/snarkjs/index.d.cts
61#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
62pub struct G1 {
63    pub x: BigUint,
64    pub y: BigUint,
65    pub z: BigUint,
66}
67
68type G1Tup = (BigUint, BigUint, BigUint);
69
70impl G1 {
71    pub fn as_tuple(&self) -> (BigUint, BigUint, BigUint) {
72        (self.x.clone(), self.y.clone(), self.z.clone())
73    }
74
75    // BN254
76    pub fn to_bn254(self) -> bn254_G1Affine {
77        let x: bn254_Fq = biguint_to_point(self.x);
78        let y: bn254_Fq = biguint_to_point(self.y);
79        if x.is_zero() && y.is_zero() {
80            bn254_G1Affine::identity()
81        } else {
82            bn254_G1Affine::new(x, y)
83        }
84    }
85
86    pub fn from_bn254(p: &bn254_G1Affine) -> Self {
87        let p_z = p.into_group();
88        Self {
89            x: point_to_biguint(p.x),
90            y: point_to_biguint(p.y),
91            z: point_to_biguint(p_z.z),
92        }
93    }
94
95    // BLS12-381
96    pub fn to_bls12_381(self) -> bls12_381_G1Affine {
97        let x: bls12_381_fq = biguint_to_point(self.x);
98        let y: bls12_381_fq = biguint_to_point(self.y);
99        if x.is_zero() && y.is_zero() {
100            bls12_381_G1Affine::identity()
101        } else {
102            bls12_381_G1Affine::new(x, y)
103        }
104    }
105
106    pub fn from_bls12_381(p: &bls12_381_G1Affine) -> Self {
107        let p_z = p.into_group();
108        Self {
109            x: point_to_biguint(p.x),
110            y: point_to_biguint(p.y),
111            z: point_to_biguint(p_z.z),
112        }
113    }
114}
115
116#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
117pub struct G2 {
118    pub x: [BigUint; 2],
119    pub y: [BigUint; 2],
120    pub z: [BigUint; 2],
121}
122
123type G2Tup = ([BigUint; 2], [BigUint; 2], [BigUint; 2]);
124
125impl G2 {
126    // NB: Serialize the c1 limb first.
127    pub fn as_tuple(&self) -> G2Tup {
128        (
129            [self.x[1].clone(), self.x[0].clone()],
130            [self.y[1].clone(), self.y[0].clone()],
131            [self.z[1].clone(), self.z[0].clone()],
132        )
133    }
134
135    // BN254
136    pub fn to_bn254(self) -> bn254_G2Affine {
137        let c0 = biguint_to_point(self.x[0].clone());
138        let c1 = biguint_to_point(self.x[1].clone());
139        let x = bn254_Fq2::new(c0, c1);
140
141        let c0 = biguint_to_point(self.y[0].clone());
142        let c1 = biguint_to_point(self.y[1].clone());
143        let y = bn254_Fq2::new(c0, c1);
144
145        if x.is_zero() && y.is_zero() {
146            bn254_G2Affine::identity()
147        } else {
148            bn254_G2Affine::new(x, y)
149        }
150    }
151
152    pub fn from_bn254(p: &bn254_G2Affine) -> Self {
153        let p_z = p.into_group();
154        Self {
155            x: [point_to_biguint(p.x.c0), point_to_biguint(p.x.c1)],
156            y: [point_to_biguint(p.y.c0), point_to_biguint(p.y.c1)],
157            z: [point_to_biguint(p_z.z.c0), point_to_biguint(p_z.z.c1)],
158        }
159    }
160
161    // BLS12-381
162    pub fn to_bls12_381(self) -> bls12_381_G2Affine {
163        let c0 = biguint_to_point(self.x[0].clone());
164        let c1 = biguint_to_point(self.x[1].clone());
165        let x = bls12_381_Fq2::new(c0, c1);
166
167        let c0 = biguint_to_point(self.y[0].clone());
168        let c1 = biguint_to_point(self.y[1].clone());
169        let y = bls12_381_Fq2::new(c0, c1);
170
171        if x.is_zero() && y.is_zero() {
172            bls12_381_G2Affine::identity()
173        } else {
174            bls12_381_G2Affine::new(x, y)
175        }
176    }
177
178    pub fn from_bls12_381(p: &bls12_381_G2Affine) -> Self {
179        let p_z = p.into_group();
180        Self {
181            x: [point_to_biguint(p.x.c0), point_to_biguint(p.x.c1)],
182            y: [point_to_biguint(p.y.c0), point_to_biguint(p.y.c1)],
183            z: [point_to_biguint(p_z.z.c0), point_to_biguint(p_z.z.c1)],
184        }
185    }
186}
187
188#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
189pub struct Proof {
190    pub a: G1,
191    pub b: G2,
192    pub c: G1,
193    pub protocol: String,
194    pub curve: String,
195}
196
197impl Proof {
198    pub fn as_tuple(&self) -> (G1Tup, G2Tup, G1Tup) {
199        (self.a.as_tuple(), self.b.as_tuple(), self.c.as_tuple())
200    }
201}
202
203impl From<ark_groth16::Proof<Bn254>> for Proof {
204    fn from(proof: ark_groth16::Proof<Bn254>) -> Self {
205        Self {
206            a: G1::from_bn254(&proof.a),
207            b: G2::from_bn254(&proof.b),
208            c: G1::from_bn254(&proof.c),
209            protocol: PROTOCOL_GROTH16.to_string(),
210            curve: CURVE_BN254.to_string(),
211        }
212    }
213}
214
215impl From<ark_groth16::Proof<Bls12_381>> for Proof {
216    fn from(proof: ark_groth16::Proof<Bls12_381>) -> Self {
217        Self {
218            a: G1::from_bls12_381(&proof.a),
219            b: G2::from_bls12_381(&proof.b),
220            c: G1::from_bls12_381(&proof.c),
221            protocol: PROTOCOL_GROTH16.to_string(),
222            curve: CURVE_BLS12_381.to_string(),
223        }
224    }
225}
226
227impl From<Proof> for ark_groth16::Proof<Bn254> {
228    fn from(src: Proof) -> Self {
229        ark_groth16::Proof {
230            a: src.a.to_bn254(),
231            b: src.b.to_bn254(),
232            c: src.c.to_bn254(),
233        }
234    }
235}
236
237impl From<Proof> for ark_groth16::Proof<Bls12_381> {
238    fn from(src: Proof) -> Self {
239        ark_groth16::Proof {
240            a: src.a.to_bls12_381(),
241            b: src.b.to_bls12_381(),
242            c: src.c.to_bls12_381(),
243        }
244    }
245}
246
247// Helper for converting a PrimeField to its BigUint representation for Ethereum compatibility
248fn biguint_to_point<F: PrimeField>(point: BigUint) -> F {
249    let buf_size: usize = F::MODULUS_BIT_SIZE.div_ceil(8).try_into().unwrap(); // Rounds up the division
250    let mut buf = point.to_bytes_le();
251    buf.resize(buf_size, 0u8);
252    let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect("always works");
253    F::from_bigint(bigint).expect("always works")
254}
255
256// Helper for converting a PrimeField to its BigUint representation for Ethereum compatibility
257// (BigUint reads data as big endian)
258fn point_to_biguint<F: PrimeField>(point: F) -> BigUint {
259    let point = point.into_bigint();
260    let point_bytes = point.to_bytes_be();
261    BigUint::from_bytes_be(&point_bytes[..])
262}
263
264#[cfg(test)]
265mod tests {
266    use crate::prover::circom::{biguint_to_point, point_to_biguint, Inputs, Proof, G1, G2};
267    use num::BigUint;
268
269    mod bn254 {
270        use super::*;
271        use ark_bn254::{Bn254, Fq, Fr, G1Affine, G2Affine};
272        use ark_std::UniformRand;
273
274        fn fq() -> Fq {
275            Fq::from(2)
276        }
277
278        fn fr() -> Fr {
279            Fr::from(2)
280        }
281
282        fn g1() -> G1Affine {
283            let rng = &mut ark_std::test_rng();
284            G1Affine::rand(rng)
285        }
286
287        fn g2() -> G2Affine {
288            let rng = &mut ark_std::test_rng();
289            G2Affine::rand(rng)
290        }
291
292        #[test]
293        fn convert_fq() {
294            let el = fq();
295            let el2 = point_to_biguint(el);
296            let el3: Fq = biguint_to_point(el2.clone());
297            let el4 = point_to_biguint(el3);
298            assert_eq!(el, el3);
299            assert_eq!(el2, el4);
300        }
301
302        #[test]
303        fn convert_fr() {
304            let el = fr();
305            let el2 = point_to_biguint(el);
306            let el3: Fr = biguint_to_point(el2.clone());
307            let el4 = point_to_biguint(el3);
308            assert_eq!(el, el3);
309            assert_eq!(el2, el4);
310        }
311
312        #[test]
313        fn convert_g1() {
314            let el = g1();
315            let el2 = G1::from_bn254(&el);
316            let el3: G1Affine = el2.clone().to_bn254();
317            let el4 = G1::from_bn254(&el3);
318            assert_eq!(el, el3);
319            assert_eq!(el2, el4);
320        }
321
322        #[test]
323        fn convert_g2() {
324            let el = g2();
325            let el2 = G2::from_bn254(&el);
326            let el3: G2Affine = el2.clone().to_bn254();
327            let el4 = G2::from_bn254(&el3);
328            assert_eq!(el, el3);
329            assert_eq!(el2, el4);
330        }
331
332        #[test]
333        fn convert_proof() {
334            let p = ark_groth16::Proof::<Bn254> {
335                a: g1(),
336                b: g2(),
337                c: g1(),
338            };
339            let p2 = Proof::from(p.clone());
340            let p3 = ark_groth16::Proof::from(p2);
341            assert_eq!(p, p3);
342        }
343
344        #[test]
345        fn convert_fr_inputs_to_biguint() {
346            let bn254_1 = Fr::from(1u32);
347            let bn254_2 = Fr::from(2u32);
348            let bn254_3 = Fr::from(3u32);
349
350            let bn254_slice: &[Fr] = &[bn254_1, bn254_2, bn254_3];
351
352            let result: Inputs = bn254_slice.into();
353
354            assert_eq!(result.0.len(), 3);
355
356            assert_eq!(result.0[0], BigUint::from(1u32));
357            assert_eq!(result.0[1], BigUint::from(2u32));
358            assert_eq!(result.0[2], BigUint::from(3u32));
359        }
360
361        #[test]
362        fn convert_biguint_to_fr_inputs() {
363            let biguint1 = BigUint::from(1u32);
364            let biguint2 = BigUint::from(2u32);
365            let biguint3 = BigUint::from(3u32);
366
367            let inputs = Inputs(vec![biguint1, biguint2, biguint3]);
368
369            let result: Vec<Fr> = inputs.into();
370
371            assert_eq!(result.len(), 3);
372
373            assert_eq!(result[0], Fr::from(BigUint::from(1u32)));
374            assert_eq!(result[1], Fr::from(BigUint::from(2u32)));
375            assert_eq!(result[2], Fr::from(BigUint::from(3u32)));
376        }
377    }
378
379    mod bls12_381 {
380        use super::*;
381        use ark_bls12_381::{Bls12_381, Fq, Fr, G1Affine, G2Affine};
382        use ark_std::UniformRand;
383
384        fn fq() -> Fq {
385            Fq::from(2)
386        }
387
388        fn fr() -> Fr {
389            Fr::from(2)
390        }
391
392        fn g1() -> G1Affine {
393            let rng = &mut ark_std::test_rng();
394            G1Affine::rand(rng)
395        }
396
397        fn g2() -> G2Affine {
398            let rng = &mut ark_std::test_rng();
399            G2Affine::rand(rng)
400        }
401
402        #[test]
403        fn convert_fq() {
404            let el = fq();
405            let el2 = point_to_biguint(el);
406            let el3: Fq = biguint_to_point(el2.clone());
407            let el4 = point_to_biguint(el3);
408            assert_eq!(el, el3);
409            assert_eq!(el2, el4);
410        }
411
412        #[test]
413        fn convert_fr() {
414            let el = fr();
415            let el2 = point_to_biguint(el);
416            let el3: Fr = biguint_to_point(el2.clone());
417            let el4 = point_to_biguint(el3);
418            assert_eq!(el, el3);
419            assert_eq!(el2, el4);
420        }
421
422        #[test]
423        fn convert_g1() {
424            let el = g1();
425            let el2 = G1::from_bls12_381(&el);
426            let el3: G1Affine = el2.clone().to_bls12_381();
427            let el4 = G1::from_bls12_381(&el3);
428            assert_eq!(el, el3);
429            assert_eq!(el2, el4);
430        }
431
432        #[test]
433        fn convert_g2() {
434            let el = g2();
435            let el2 = G2::from_bls12_381(&el);
436            let el3: G2Affine = el2.clone().to_bls12_381();
437            let el4 = G2::from_bls12_381(&el3);
438            assert_eq!(el, el3);
439            assert_eq!(el2, el4);
440        }
441
442        #[test]
443        fn convert_proof() {
444            let p = ark_groth16::Proof::<Bls12_381> {
445                a: g1(),
446                b: g2(),
447                c: g1(),
448            };
449            let p2 = Proof::from(p.clone());
450            let p3 = ark_groth16::Proof::from(p2);
451            assert_eq!(p, p3);
452        }
453
454        #[test]
455        fn convert_fr_inputs_to_biguint() {
456            let bn254_1 = Fr::from(1u32);
457            let bn254_2 = Fr::from(2u32);
458            let bn254_3 = Fr::from(3u32);
459
460            let bn254_slice: &[Fr] = &[bn254_1, bn254_2, bn254_3];
461
462            let result: Inputs = bn254_slice.into();
463
464            assert_eq!(result.0.len(), 3);
465
466            assert_eq!(result.0[0], BigUint::from(1u32));
467            assert_eq!(result.0[1], BigUint::from(2u32));
468            assert_eq!(result.0[2], BigUint::from(3u32));
469        }
470
471        #[test]
472        fn convert_biguint_to_fr_inputs() {
473            let biguint1 = BigUint::from(1u32);
474            let biguint2 = BigUint::from(2u32);
475            let biguint3 = BigUint::from(3u32);
476
477            let inputs = Inputs(vec![biguint1, biguint2, biguint3]);
478
479            let result: Vec<Fr> = inputs.into();
480
481            assert_eq!(result.len(), 3);
482
483            assert_eq!(result[0], Fr::from(BigUint::from(1u32)));
484            assert_eq!(result[1], Fr::from(BigUint::from(2u32)));
485            assert_eq!(result[2], Fr::from(BigUint::from(3u32)));
486        }
487    }
488}