Skip to main content

ark_circom/
ethereum.rs

1//! Helpers for converting Arkworks types to U256-tuples as expected by the
2//! Solidity Groth16 Verifier smart contracts
3use ark_ff::{BigInteger, PrimeField};
4use ethers_core::types::U256;
5use num_traits::Zero;
6
7use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
8use ark_serialize::CanonicalDeserialize;
9
10pub struct Inputs(pub Vec<U256>);
11
12impl From<&[Fr]> for Inputs {
13    fn from(src: &[Fr]) -> Self {
14        let els = src.iter().map(|point| point_to_u256(*point)).collect();
15
16        Self(els)
17    }
18}
19
20#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21pub struct G1 {
22    pub x: U256,
23    pub y: U256,
24}
25
26impl From<G1> for G1Affine {
27    fn from(src: G1) -> G1Affine {
28        let x: Fq = u256_to_point(src.x);
29        let y: Fq = u256_to_point(src.y);
30        if x.is_zero() && y.is_zero() {
31            G1Affine::identity()
32        } else {
33            G1Affine::new(x, y)
34        }
35    }
36}
37
38type G1Tup = (U256, U256);
39
40impl G1 {
41    pub fn as_tuple(&self) -> (U256, U256) {
42        (self.x, self.y)
43    }
44}
45
46impl From<&G1Affine> for G1 {
47    fn from(p: &G1Affine) -> Self {
48        Self {
49            x: point_to_u256(p.x),
50            y: point_to_u256(p.y),
51        }
52    }
53}
54
55#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
56pub struct G2 {
57    pub x: [U256; 2],
58    pub y: [U256; 2],
59}
60
61impl From<G2> for G2Affine {
62    fn from(src: G2) -> G2Affine {
63        let c0 = u256_to_point(src.x[0]);
64        let c1 = u256_to_point(src.x[1]);
65        let x = Fq2::new(c0, c1);
66
67        let c0 = u256_to_point(src.y[0]);
68        let c1 = u256_to_point(src.y[1]);
69        let y = Fq2::new(c0, c1);
70
71        if x.is_zero() && y.is_zero() {
72            G2Affine::identity()
73        } else {
74            G2Affine::new(x, y)
75        }
76    }
77}
78
79type G2Tup = ([U256; 2], [U256; 2]);
80
81impl G2 {
82    // NB: Serialize the c1 limb first.
83    pub fn as_tuple(&self) -> G2Tup {
84        ([self.x[1], self.x[0]], [self.y[1], self.y[0]])
85    }
86}
87
88impl From<&G2Affine> for G2 {
89    fn from(p: &G2Affine) -> Self {
90        Self {
91            x: [point_to_u256(p.x.c0), point_to_u256(p.x.c1)],
92            y: [point_to_u256(p.y.c0), point_to_u256(p.y.c1)],
93        }
94    }
95}
96
97#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
98pub struct Proof {
99    pub a: G1,
100    pub b: G2,
101    pub c: G1,
102}
103
104impl Proof {
105    pub fn as_tuple(&self) -> (G1Tup, G2Tup, G1Tup) {
106        (self.a.as_tuple(), self.b.as_tuple(), self.c.as_tuple())
107    }
108}
109
110impl From<ark_groth16::Proof<Bn254>> for Proof {
111    fn from(proof: ark_groth16::Proof<Bn254>) -> Self {
112        Self {
113            a: G1::from(&proof.a),
114            b: G2::from(&proof.b),
115            c: G1::from(&proof.c),
116        }
117    }
118}
119
120impl From<Proof> for ark_groth16::Proof<Bn254> {
121    fn from(src: Proof) -> ark_groth16::Proof<Bn254> {
122        ark_groth16::Proof {
123            a: src.a.into(),
124            b: src.b.into(),
125            c: src.c.into(),
126        }
127    }
128}
129
130#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
131pub struct VerifyingKey {
132    pub alpha1: G1,
133    pub beta2: G2,
134    pub gamma2: G2,
135    pub delta2: G2,
136    pub ic: Vec<G1>,
137}
138
139impl VerifyingKey {
140    pub fn as_tuple(&self) -> (G1Tup, G2Tup, G2Tup, G2Tup, Vec<G1Tup>) {
141        (
142            self.alpha1.as_tuple(),
143            self.beta2.as_tuple(),
144            self.gamma2.as_tuple(),
145            self.delta2.as_tuple(),
146            self.ic.iter().map(|i| i.as_tuple()).collect(),
147        )
148    }
149}
150
151impl From<ark_groth16::VerifyingKey<Bn254>> for VerifyingKey {
152    fn from(vk: ark_groth16::VerifyingKey<Bn254>) -> Self {
153        Self {
154            alpha1: G1::from(&vk.alpha_g1),
155            beta2: G2::from(&vk.beta_g2),
156            gamma2: G2::from(&vk.gamma_g2),
157            delta2: G2::from(&vk.delta_g2),
158            ic: vk.gamma_abc_g1.iter().map(G1::from).collect(),
159        }
160    }
161}
162
163impl From<VerifyingKey> for ark_groth16::VerifyingKey<Bn254> {
164    fn from(src: VerifyingKey) -> ark_groth16::VerifyingKey<Bn254> {
165        ark_groth16::VerifyingKey {
166            alpha_g1: src.alpha1.into(),
167            beta_g2: src.beta2.into(),
168            gamma_g2: src.gamma2.into(),
169            delta_g2: src.delta2.into(),
170            gamma_abc_g1: src.ic.into_iter().map(Into::into).collect(),
171        }
172    }
173}
174
175// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility
176fn u256_to_point<F: PrimeField>(point: U256) -> F {
177    let mut buf = [0; 32];
178    point.to_little_endian(&mut buf);
179    let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect("always works");
180    F::from_bigint(bigint).expect("always works")
181}
182
183// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility
184// (U256 reads data as big endian)
185fn point_to_u256<F: PrimeField>(point: F) -> U256 {
186    let point = point.into_bigint();
187    let point_bytes = point.to_bytes_be();
188    U256::from(&point_bytes[..])
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194    use ark_bn254::Fq;
195    use ark_std::UniformRand;
196
197    fn fq() -> Fq {
198        Fq::from(2)
199    }
200
201    fn fr() -> Fr {
202        Fr::from(2)
203    }
204
205    fn g1() -> G1Affine {
206        let rng = &mut ark_std::test_rng();
207        G1Affine::rand(rng)
208    }
209
210    fn g2() -> G2Affine {
211        let rng = &mut ark_std::test_rng();
212        G2Affine::rand(rng)
213    }
214
215    #[test]
216    fn convert_fq() {
217        let el = fq();
218        let el2 = point_to_u256(el);
219        let el3: Fq = u256_to_point(el2);
220        let el4 = point_to_u256(el3);
221        assert_eq!(el, el3);
222        assert_eq!(el2, el4);
223    }
224
225    #[test]
226    fn convert_fr() {
227        let el = fr();
228        let el2 = point_to_u256(el);
229        let el3: Fr = u256_to_point(el2);
230        let el4 = point_to_u256(el3);
231        assert_eq!(el, el3);
232        assert_eq!(el2, el4);
233    }
234
235    #[test]
236    fn convert_g1() {
237        let el = g1();
238        let el2 = G1::from(&el);
239        let el3: G1Affine = el2.into();
240        let el4 = G1::from(&el3);
241        assert_eq!(el, el3);
242        assert_eq!(el2, el4);
243    }
244
245    #[test]
246    fn convert_g2() {
247        let el = g2();
248        let el2 = G2::from(&el);
249        let el3: G2Affine = el2.into();
250        let el4 = G2::from(&el3);
251        assert_eq!(el, el3);
252        assert_eq!(el2, el4);
253    }
254
255    #[test]
256    fn convert_vk() {
257        let vk = ark_groth16::VerifyingKey::<Bn254> {
258            alpha_g1: g1(),
259            beta_g2: g2(),
260            gamma_g2: g2(),
261            delta_g2: g2(),
262            gamma_abc_g1: vec![g1(), g1(), g1()],
263        };
264        let vk_ethers = VerifyingKey::from(vk.clone());
265        let ark_vk: ark_groth16::VerifyingKey<Bn254> = vk_ethers.into();
266        assert_eq!(ark_vk, vk);
267    }
268
269    #[test]
270    fn convert_proof() {
271        let p = ark_groth16::Proof::<Bn254> {
272            a: g1(),
273            b: g2(),
274            c: g1(),
275        };
276        let p2 = Proof::from(p.clone());
277        let p3 = ark_groth16::Proof::from(p2);
278        assert_eq!(p, p3);
279    }
280}