Skip to content

Commit ec39e22

Browse files
authored
From proof (#367)
* feat(circom-prover): simplify `point_to_biguint` interface * feat: add `from_ethereum_proof` and `from_ethereum_inputs` in mopro-ffi * feat: add `fromEthereumProof`, `fromEthereumInputs` in test-e2e * fix: fix clippy * chore: add comment
1 parent da02fc4 commit ec39e22

File tree

6 files changed

+222
-44
lines changed

6 files changed

+222
-44
lines changed

Diff for: circom-prover/src/prover/ethereum.rs

+119-28
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,60 @@
33
//! Helpers for converting Arkworks types to BigUint-tuples as expected by the
44
//! Solidity Groth16 Verifier smart contracts
55
use ark_bls12_381::{
6-
Bls12_381, Fq as bls12_381_fq, Fq2 as bls12_381_Fq2, G1Affine as bls12_381_G1Affine,
7-
G2Affine as bls12_381_G2Affine,
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,
88
};
99
use ark_ec::AffineRepr;
1010
use ark_ff::{BigInteger, PrimeField};
1111
use num::BigUint;
1212
use num_traits::Zero;
1313

1414
use ark_bn254::{
15-
Bn254, Fq as bn254_Fq, Fq2 as bn254_Fq2, Fr, G1Affine as bn254_G1Affine,
15+
Bn254, Fq as bn254_Fq, Fq2 as bn254_Fq2, Fr as bn254_Fr, G1Affine as bn254_G1Affine,
1616
G2Affine as bn254_G2Affine,
1717
};
1818
use ark_serialize::CanonicalDeserialize;
1919

20-
const BN254_BUF_SIZE: usize = 32;
21-
const BLS12_381_BUF_SIZE: usize = 48;
22-
2320
pub const PROTOCOL_GROTH16: &str = "groth16";
2421
pub const CURVE_BN254: &str = "bn128";
2522
pub const CURVE_BLS12_381: &str = "bls12381";
2623

2724
pub struct Inputs(pub Vec<BigUint>);
2825

29-
impl From<&[Fr]> for Inputs {
30-
fn from(src: &[Fr]) -> Self {
26+
impl From<&[bn254_Fr]> for Inputs {
27+
fn from(src: &[bn254_Fr]) -> Self {
3128
let els = src.iter().map(|point| point_to_biguint(*point)).collect();
29+
Self(els)
30+
}
31+
}
3232

33+
impl 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();
3336
Self(els)
3437
}
3538
}
3639

40+
impl 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+
50+
impl 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+
3760
// Follow the interface: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/snarkjs/index.d.cts
3861
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
3962
pub struct G1 {
@@ -51,8 +74,8 @@ impl G1 {
5174

5275
// BN254
5376
pub fn to_bn254(self) -> bn254_G1Affine {
54-
let x: bn254_Fq = biguint_to_point(self.x, BN254_BUF_SIZE);
55-
let y: bn254_Fq = biguint_to_point(self.y, BN254_BUF_SIZE);
77+
let x: bn254_Fq = biguint_to_point(self.x);
78+
let y: bn254_Fq = biguint_to_point(self.y);
5679
if x.is_zero() && y.is_zero() {
5780
bn254_G1Affine::identity()
5881
} else {
@@ -71,8 +94,8 @@ impl G1 {
7194

7295
// BLS12-381
7396
pub fn to_bls12_381(self) -> bls12_381_G1Affine {
74-
let x: bls12_381_fq = biguint_to_point(self.x, BLS12_381_BUF_SIZE);
75-
let y: bls12_381_fq = biguint_to_point(self.y, BLS12_381_BUF_SIZE);
97+
let x: bls12_381_fq = biguint_to_point(self.x);
98+
let y: bls12_381_fq = biguint_to_point(self.y);
7699
if x.is_zero() && y.is_zero() {
77100
bls12_381_G1Affine::identity()
78101
} else {
@@ -111,12 +134,12 @@ impl G2 {
111134

112135
// BN254
113136
pub fn to_bn254(self) -> bn254_G2Affine {
114-
let c0 = biguint_to_point(self.x[0].clone(), BN254_BUF_SIZE);
115-
let c1 = biguint_to_point(self.x[1].clone(), BN254_BUF_SIZE);
137+
let c0 = biguint_to_point(self.x[0].clone());
138+
let c1 = biguint_to_point(self.x[1].clone());
116139
let x = bn254_Fq2::new(c0, c1);
117140

118-
let c0 = biguint_to_point(self.y[0].clone(), BN254_BUF_SIZE);
119-
let c1 = biguint_to_point(self.y[1].clone(), BN254_BUF_SIZE);
141+
let c0 = biguint_to_point(self.y[0].clone());
142+
let c1 = biguint_to_point(self.y[1].clone());
120143
let y = bn254_Fq2::new(c0, c1);
121144

122145
if x.is_zero() && y.is_zero() {
@@ -137,12 +160,12 @@ impl G2 {
137160

138161
// BLS12-381
139162
pub fn to_bls12_381(self) -> bls12_381_G2Affine {
140-
let c0 = biguint_to_point(self.x[0].clone(), BLS12_381_BUF_SIZE);
141-
let c1 = biguint_to_point(self.x[1].clone(), BLS12_381_BUF_SIZE);
163+
let c0 = biguint_to_point(self.x[0].clone());
164+
let c1 = biguint_to_point(self.x[1].clone());
142165
let x = bls12_381_Fq2::new(c0, c1);
143166

144-
let c0 = biguint_to_point(self.y[0].clone(), BLS12_381_BUF_SIZE);
145-
let c1 = biguint_to_point(self.y[1].clone(), BLS12_381_BUF_SIZE);
167+
let c0 = biguint_to_point(self.y[0].clone());
168+
let c1 = biguint_to_point(self.y[1].clone());
146169
let y = bls12_381_Fq2::new(c0, c1);
147170

148171
if x.is_zero() && y.is_zero() {
@@ -222,7 +245,8 @@ impl From<Proof> for ark_groth16::Proof<Bls12_381> {
222245
}
223246

224247
// Helper for converting a PrimeField to its BigUint representation for Ethereum compatibility
225-
fn biguint_to_point<F: PrimeField>(point: BigUint, buf_size: usize) -> F {
248+
fn biguint_to_point<F: PrimeField>(point: BigUint) -> F {
249+
let buf_size: usize = ((F::MODULUS_BIT_SIZE + 7) / 8).try_into().unwrap(); // Rounds up the division
226250
let mut buf = point.to_bytes_le();
227251
buf.resize(buf_size, 0u8);
228252
let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect("always works");
@@ -239,9 +263,8 @@ fn point_to_biguint<F: PrimeField>(point: F) -> BigUint {
239263

240264
#[cfg(test)]
241265
mod tests {
242-
use crate::prover::ethereum::{
243-
biguint_to_point, point_to_biguint, Proof, BLS12_381_BUF_SIZE, BN254_BUF_SIZE, G1, G2,
244-
};
266+
use crate::prover::ethereum::{biguint_to_point, point_to_biguint, Inputs, Proof, G1, G2};
267+
use num::BigUint;
245268

246269
mod bn254 {
247270
use super::*;
@@ -270,7 +293,7 @@ mod tests {
270293
fn convert_fq() {
271294
let el = fq();
272295
let el2 = point_to_biguint(el);
273-
let el3: Fq = biguint_to_point(el2.clone(), BN254_BUF_SIZE);
296+
let el3: Fq = biguint_to_point(el2.clone());
274297
let el4 = point_to_biguint(el3);
275298
assert_eq!(el, el3);
276299
assert_eq!(el2, el4);
@@ -280,7 +303,7 @@ mod tests {
280303
fn convert_fr() {
281304
let el = fr();
282305
let el2 = point_to_biguint(el);
283-
let el3: Fr = biguint_to_point(el2.clone(), BN254_BUF_SIZE);
306+
let el3: Fr = biguint_to_point(el2.clone());
284307
let el4 = point_to_biguint(el3);
285308
assert_eq!(el, el3);
286309
assert_eq!(el2, el4);
@@ -317,6 +340,40 @@ mod tests {
317340
let p3 = ark_groth16::Proof::from(p2);
318341
assert_eq!(p, p3);
319342
}
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+
}
320377
}
321378

322379
mod bls12_381 {
@@ -346,7 +403,7 @@ mod tests {
346403
fn convert_fq() {
347404
let el = fq();
348405
let el2 = point_to_biguint(el);
349-
let el3: Fq = biguint_to_point(el2.clone(), BLS12_381_BUF_SIZE);
406+
let el3: Fq = biguint_to_point(el2.clone());
350407
let el4 = point_to_biguint(el3);
351408
assert_eq!(el, el3);
352409
assert_eq!(el2, el4);
@@ -356,7 +413,7 @@ mod tests {
356413
fn convert_fr() {
357414
let el = fr();
358415
let el2 = point_to_biguint(el);
359-
let el3: Fr = biguint_to_point(el2.clone(), BLS12_381_BUF_SIZE);
416+
let el3: Fr = biguint_to_point(el2.clone());
360417
let el4 = point_to_biguint(el3);
361418
assert_eq!(el, el3);
362419
assert_eq!(el2, el4);
@@ -393,5 +450,39 @@ mod tests {
393450
let p3 = ark_groth16::Proof::from(p2);
394451
assert_eq!(p, p3);
395452
}
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+
}
396487
}
397488
}

Diff for: mopro-ffi/src/circom/ethereum.rs

+58-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
use ark_bn254::Bn254;
1+
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
22
use circom_prover::prover::{
3-
ethereum,
4-
serialization::{deserialize_inputs, deserialize_proof},
3+
ethereum::{self, CURVE_BN254, PROTOCOL_GROTH16},
4+
serialization::{
5+
self, deserialize_inputs, deserialize_proof, SerializableInputs, SerializableProof,
6+
},
57
};
8+
use num_bigint::BigUint;
9+
use std::str::FromStr;
610

711
#[derive(Debug, Clone, Default)]
812
pub struct G1 {
@@ -51,6 +55,44 @@ pub fn to_ethereum_inputs(inputs: Vec<u8>) -> Vec<String> {
5155
inputs
5256
}
5357

58+
// Only supports bn254 for now
59+
pub fn from_ethereum_inputs(inputs: Vec<String>) -> Vec<u8> {
60+
let inputs = inputs
61+
.iter()
62+
.map(|x| BigUint::from_str(x).unwrap())
63+
.collect::<Vec<BigUint>>();
64+
let fr_inputs: Vec<Fr> = ethereum::Inputs(inputs).into();
65+
serialization::serialize_inputs(&SerializableInputs::<Bn254>(fr_inputs))
66+
}
67+
68+
// Only supports bn254 for now
69+
pub fn from_ethereum_proof(proof: ProofCalldata) -> Vec<u8> {
70+
let a_x = Fq::from_str(&proof.a.x).unwrap();
71+
let a_y = Fq::from_str(&proof.a.y).unwrap();
72+
let a = G1Affine::new_unchecked(a_x, a_y);
73+
let a_biguint = ethereum::G1::from_bn254(&a);
74+
let c_x = Fq::from_str(&proof.c.x).unwrap();
75+
let c_y = Fq::from_str(&proof.c.y).unwrap();
76+
let c = G1Affine::new_unchecked(c_x, c_y);
77+
let c_biguint = ethereum::G1::from_bn254(&c);
78+
let b1_x = Fq::from_str(&proof.b.x[0]).unwrap();
79+
let b1_y = Fq::from_str(&proof.b.x[1]).unwrap();
80+
let b1 = Fq2::new(b1_x, b1_y);
81+
let b2_x = Fq::from_str(&proof.b.y[0]).unwrap();
82+
let b2_y = Fq::from_str(&proof.b.y[1]).unwrap();
83+
let b2 = Fq2::new(b2_x, b2_y);
84+
let b = G2Affine::new_unchecked(b1, b2);
85+
let b_biguint = ethereum::G2::from_bn254(&b);
86+
let proof = ethereum::Proof {
87+
a: a_biguint,
88+
b: b_biguint,
89+
c: c_biguint,
90+
protocol: PROTOCOL_GROTH16.to_string(),
91+
curve: CURVE_BN254.to_string(),
92+
};
93+
serialization::serialize_proof(&SerializableProof::<Bn254>(proof.into()))
94+
}
95+
5496
#[cfg(test)]
5597
mod tests {
5698

@@ -79,13 +121,16 @@ mod tests {
79121
109, 154,
80122
];
81123

82-
let proof = to_ethereum_proof(raw_proof);
124+
let proof = to_ethereum_proof(raw_proof.clone());
83125
assert!(!proof.a.x.is_empty());
84126
assert!(!proof.a.y.is_empty());
85127
assert!(!proof.b.x.is_empty());
86128
assert!(!proof.b.y.is_empty());
87129
assert!(!proof.c.x.is_empty());
88130
assert!(!proof.c.y.is_empty());
131+
132+
let converted_proof = from_ethereum_proof(proof);
133+
assert_eq!(raw_proof, converted_proof);
89134
}
90135

91136
#[test]
@@ -96,23 +141,29 @@ mod tests {
96141
48, 0, 0, 0, 240, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88,
97142
129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48,
98143
];
99-
let inputs = to_ethereum_inputs(raw_inputs);
144+
let inputs = to_ethereum_inputs(raw_inputs.clone());
100145
let expected_inputs = vec![
101146
"21888242871839275222246405745257275088548364400416034343698204186575808495616",
102147
"21888242871839275222246405745257275088548364400416034343698204186575808495616",
103148
];
104149
assert_eq!(inputs, expected_inputs);
150+
151+
let converted_inputs = from_ethereum_inputs(inputs);
152+
assert_eq!(raw_inputs, converted_inputs);
105153
}
106154

107155
#[test]
108156
fn test_to_ethereum_inputs_with_zero() {
109157
let raw_inputs = vec![
110158
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
111-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
159+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112160
];
113-
let inputs = to_ethereum_inputs(raw_inputs);
161+
let inputs = to_ethereum_inputs(raw_inputs.clone());
114162
let expected_inputs = vec!["0".to_string()];
115163
assert_eq!(inputs, expected_inputs);
164+
165+
let converted_inputs = from_ethereum_inputs(expected_inputs);
166+
assert_eq!(raw_inputs, converted_inputs);
116167
}
117168
}
118169
}

Diff for: mopro-ffi/src/circom/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ macro_rules! circom_app {
8181
fn to_ethereum_inputs(inputs: Vec<u8>) -> Vec<String> {
8282
mopro_ffi::to_ethereum_inputs(inputs)
8383
}
84+
85+
#[allow(dead_code)]
86+
#[cfg_attr(not(disable_uniffi_export), uniffi::export)]
87+
fn from_ethereum_proof(proof: $proof_call_data) -> Vec<u8> {
88+
mopro_ffi::from_ethereum_proof(proof.into())
89+
}
90+
91+
#[allow(dead_code)]
92+
#[cfg_attr(not(disable_uniffi_export), uniffi::export)]
93+
fn from_ethereum_inputs(inputs: Vec<String>) -> Vec<u8> {
94+
mopro_ffi::from_ethereum_inputs(inputs)
95+
}
8496
};
8597
}
8698

0 commit comments

Comments
 (0)