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