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