1use crate::{
2 utilities::{bool_to_bytes32, right_pad},
3 Address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress,
4};
5use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2};
6use std::vec::Vec;
7
8pub mod add {
9 use super::*;
10
11 const ADDRESS: Address = crate::u64_to_address(6);
12
13 pub const ISTANBUL_ADD_GAS_COST: u64 = 150;
14 pub const ISTANBUL: PrecompileWithAddress =
15 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
16 run_add(input, ISTANBUL_ADD_GAS_COST, gas_limit)
17 });
18
19 pub const BYZANTIUM_ADD_GAS_COST: u64 = 500;
20 pub const BYZANTIUM: PrecompileWithAddress =
21 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
22 run_add(input, BYZANTIUM_ADD_GAS_COST, gas_limit)
23 });
24}
25
26pub mod mul {
27 use super::*;
28
29 const ADDRESS: Address = crate::u64_to_address(7);
30
31 pub const ISTANBUL_MUL_GAS_COST: u64 = 6_000;
32 pub const ISTANBUL: PrecompileWithAddress =
33 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
34 run_mul(input, ISTANBUL_MUL_GAS_COST, gas_limit)
35 });
36
37 pub const BYZANTIUM_MUL_GAS_COST: u64 = 40_000;
38 pub const BYZANTIUM: PrecompileWithAddress =
39 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
40 run_mul(input, BYZANTIUM_MUL_GAS_COST, gas_limit)
41 });
42}
43
44pub mod pair {
45 use super::*;
46
47 pub const ADDRESS: Address = crate::u64_to_address(8);
48
49 pub const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
50 pub const ISTANBUL_PAIR_BASE: u64 = 45_000;
51 pub const ISTANBUL: PrecompileWithAddress =
52 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
53 run_pair(
54 input,
55 ISTANBUL_PAIR_PER_POINT,
56 ISTANBUL_PAIR_BASE,
57 gas_limit,
58 )
59 });
60
61 pub const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000;
62 pub const BYZANTIUM_PAIR_BASE: u64 = 100_000;
63 pub const BYZANTIUM: PrecompileWithAddress =
64 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
65 run_pair(
66 input,
67 BYZANTIUM_PAIR_PER_POINT,
68 BYZANTIUM_PAIR_BASE,
69 gas_limit,
70 )
71 });
72}
73
74pub const ADD_INPUT_LEN: usize = 64 + 64;
77
78pub const MUL_INPUT_LEN: usize = 64 + 32;
81
82pub const PAIR_ELEMENT_LEN: usize = 64 + 128;
86
87#[inline]
93pub fn read_fq(input: &[u8]) -> Result<Fq, PrecompileError> {
94 Fq::from_slice(&input[..32]).map_err(|_| PrecompileError::Bn128FieldPointNotAMember)
95}
96
97#[inline]
103pub fn read_point(input: &[u8]) -> Result<G1, PrecompileError> {
104 let px = read_fq(&input[0..32])?;
105 let py = read_fq(&input[32..64])?;
106 new_g1_point(px, py)
107}
108
109pub fn new_g1_point(px: Fq, py: Fq) -> Result<G1, PrecompileError> {
111 if px == Fq::zero() && py == Fq::zero() {
112 Ok(G1::zero())
113 } else {
114 AffineG1::new(px, py)
115 .map(Into::into)
116 .map_err(|_| PrecompileError::Bn128AffineGFailedToCreate)
117 }
118}
119
120pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult {
121 if gas_cost > gas_limit {
122 return Err(PrecompileError::OutOfGas);
123 }
124
125 let input = right_pad::<ADD_INPUT_LEN>(input);
126
127 let p1 = read_point(&input[..64])?;
128 let p2 = read_point(&input[64..])?;
129
130 let mut output = [0u8; 64];
131 if let Some(sum) = AffineG1::from_jacobian(p1 + p2) {
132 sum.x().to_big_endian(&mut output[..32]).unwrap();
133 sum.y().to_big_endian(&mut output[32..]).unwrap();
134 }
135 Ok(PrecompileOutput::new(gas_cost, output.into()))
136}
137
138pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult {
139 if gas_cost > gas_limit {
140 return Err(PrecompileError::OutOfGas);
141 }
142
143 let input = right_pad::<MUL_INPUT_LEN>(input);
144
145 let p = read_point(&input[..64])?;
146
147 let fr = bn::Fr::from_slice(&input[64..96]).unwrap();
149
150 let mut output = [0u8; 64];
151 if let Some(mul) = AffineG1::from_jacobian(p * fr) {
152 mul.x().to_big_endian(&mut output[..32]).unwrap();
153 mul.y().to_big_endian(&mut output[32..]).unwrap();
154 }
155 Ok(PrecompileOutput::new(gas_cost, output.into()))
156}
157
158pub fn run_pair(
159 input: &[u8],
160 pair_per_point_cost: u64,
161 pair_base_cost: u64,
162 gas_limit: u64,
163) -> PrecompileResult {
164 let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost;
165 if gas_used > gas_limit {
166 return Err(PrecompileError::OutOfGas);
167 }
168
169 if input.len() % PAIR_ELEMENT_LEN != 0 {
170 return Err(PrecompileError::Bn128PairLength);
171 }
172
173 let success = if input.is_empty() {
174 true
175 } else {
176 let elements = input.len() / PAIR_ELEMENT_LEN;
177
178 let mut points = Vec::with_capacity(elements);
179
180 for idx in 0..elements {
182 let read_fq_at = |n: usize| {
183 debug_assert!(n < PAIR_ELEMENT_LEN / 32);
184 let start = idx * PAIR_ELEMENT_LEN + n * 32;
185 let slice = unsafe { input.get_unchecked(start..start + 32) };
188 Fq::from_slice(slice).map_err(|_| PrecompileError::Bn128FieldPointNotAMember)
189 };
190 let ax = read_fq_at(0)?;
191 let ay = read_fq_at(1)?;
192 let bay = read_fq_at(2)?;
193 let bax = read_fq_at(3)?;
194 let bby = read_fq_at(4)?;
195 let bbx = read_fq_at(5)?;
196
197 let a = new_g1_point(ax, ay)?;
198 let b = {
199 let ba = Fq2::new(bax, bay);
200 let bb = Fq2::new(bbx, bby);
201 if ba.is_zero() && bb.is_zero() {
203 G2::zero()
204 } else {
205 G2::from(
206 AffineG2::new(ba, bb)
207 .map_err(|_| PrecompileError::Bn128AffineGFailedToCreate)?,
208 )
209 }
210 };
211
212 points.push((a, b));
213 }
214
215 let mul = bn::pairing_batch(&points);
216
217 mul == Gt::one()
218 };
219 Ok(PrecompileOutput::new(gas_used, bool_to_bytes32(success)))
220}
221
222#[cfg(test)]
223mod tests {
224 use crate::{
225 bn128::{
226 add::BYZANTIUM_ADD_GAS_COST,
227 mul::BYZANTIUM_MUL_GAS_COST,
228 pair::{BYZANTIUM_PAIR_BASE, BYZANTIUM_PAIR_PER_POINT},
229 },
230 PrecompileError,
231 };
232 use primitives::hex;
233
234 use super::*;
235
236 #[test]
237 fn test_alt_bn128_add() {
238 let input = hex::decode(
239 "\
240 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\
241 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\
242 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\
243 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7",
244 )
245 .unwrap();
246 let expected = hex::decode(
247 "\
248 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\
249 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915",
250 )
251 .unwrap();
252
253 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
254 assert_eq!(outcome.bytes, expected);
255
256 let input = hex::decode(
258 "\
259 0000000000000000000000000000000000000000000000000000000000000000\
260 0000000000000000000000000000000000000000000000000000000000000000\
261 0000000000000000000000000000000000000000000000000000000000000000\
262 0000000000000000000000000000000000000000000000000000000000000000",
263 )
264 .unwrap();
265 let expected = hex::decode(
266 "\
267 0000000000000000000000000000000000000000000000000000000000000000\
268 0000000000000000000000000000000000000000000000000000000000000000",
269 )
270 .unwrap();
271
272 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
273 assert_eq!(outcome.bytes, expected);
274
275 let input = hex::decode(
277 "\
278 0000000000000000000000000000000000000000000000000000000000000000\
279 0000000000000000000000000000000000000000000000000000000000000000\
280 0000000000000000000000000000000000000000000000000000000000000000\
281 0000000000000000000000000000000000000000000000000000000000000000",
282 )
283 .unwrap();
284
285 let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 499);
286
287 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
288
289 let input = [0u8; 0];
291 let expected = hex::decode(
292 "\
293 0000000000000000000000000000000000000000000000000000000000000000\
294 0000000000000000000000000000000000000000000000000000000000000000",
295 )
296 .unwrap();
297
298 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
299 assert_eq!(outcome.bytes, expected);
300
301 let input = hex::decode(
303 "\
304 1111111111111111111111111111111111111111111111111111111111111111\
305 1111111111111111111111111111111111111111111111111111111111111111\
306 1111111111111111111111111111111111111111111111111111111111111111\
307 1111111111111111111111111111111111111111111111111111111111111111",
308 )
309 .unwrap();
310
311 let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500);
312 assert!(matches!(
313 res,
314 Err(PrecompileError::Bn128AffineGFailedToCreate)
315 ));
316 }
317
318 #[test]
319 fn test_alt_bn128_mul() {
320 let input = hex::decode(
321 "\
322 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\
323 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\
324 00000000000000000000000000000000000000000000000011138ce750fa15c2",
325 )
326 .unwrap();
327 let expected = hex::decode(
328 "\
329 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\
330 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc",
331 )
332 .unwrap();
333
334 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
335 assert_eq!(outcome.bytes, expected);
336
337 let input = hex::decode(
339 "\
340 0000000000000000000000000000000000000000000000000000000000000000\
341 0000000000000000000000000000000000000000000000000000000000000000\
342 0200000000000000000000000000000000000000000000000000000000000000",
343 )
344 .unwrap();
345
346 let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 39_999);
347 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
348
349 let input = hex::decode(
351 "\
352 0000000000000000000000000000000000000000000000000000000000000000\
353 0000000000000000000000000000000000000000000000000000000000000000\
354 0200000000000000000000000000000000000000000000000000000000000000",
355 )
356 .unwrap();
357 let expected = hex::decode(
358 "\
359 0000000000000000000000000000000000000000000000000000000000000000\
360 0000000000000000000000000000000000000000000000000000000000000000",
361 )
362 .unwrap();
363
364 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
365 assert_eq!(outcome.bytes, expected);
366
367 let input = [0u8; 0];
369 let expected = hex::decode(
370 "\
371 0000000000000000000000000000000000000000000000000000000000000000\
372 0000000000000000000000000000000000000000000000000000000000000000",
373 )
374 .unwrap();
375
376 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
377 assert_eq!(outcome.bytes, expected);
378
379 let input = hex::decode(
381 "\
382 1111111111111111111111111111111111111111111111111111111111111111\
383 1111111111111111111111111111111111111111111111111111111111111111\
384 0f00000000000000000000000000000000000000000000000000000000000000",
385 )
386 .unwrap();
387
388 let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000);
389 assert!(matches!(
390 res,
391 Err(PrecompileError::Bn128AffineGFailedToCreate)
392 ));
393 }
394
395 #[test]
396 fn test_alt_bn128_pair() {
397 let input = hex::decode(
398 "\
399 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
400 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
401 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
402 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
403 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
404 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
405 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
406 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
407 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
408 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
409 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
410 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
411 )
412 .unwrap();
413 let expected =
414 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
415 .unwrap();
416
417 let outcome = run_pair(
418 &input,
419 BYZANTIUM_PAIR_PER_POINT,
420 BYZANTIUM_PAIR_BASE,
421 260_000,
422 )
423 .unwrap();
424 assert_eq!(outcome.bytes, expected);
425
426 let input = hex::decode(
428 "\
429 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
430 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
431 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
432 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
433 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
434 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
435 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
436 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
437 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
438 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
439 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
440 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
441 )
442 .unwrap();
443
444 let res = run_pair(
445 &input,
446 BYZANTIUM_PAIR_PER_POINT,
447 BYZANTIUM_PAIR_BASE,
448 259_999,
449 );
450 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
451
452 let input = [0u8; 0];
454 let expected =
455 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
456 .unwrap();
457
458 let outcome = run_pair(
459 &input,
460 BYZANTIUM_PAIR_PER_POINT,
461 BYZANTIUM_PAIR_BASE,
462 260_000,
463 )
464 .unwrap();
465 assert_eq!(outcome.bytes, expected);
466
467 let input = hex::decode(
469 "\
470 1111111111111111111111111111111111111111111111111111111111111111\
471 1111111111111111111111111111111111111111111111111111111111111111\
472 1111111111111111111111111111111111111111111111111111111111111111\
473 1111111111111111111111111111111111111111111111111111111111111111\
474 1111111111111111111111111111111111111111111111111111111111111111\
475 1111111111111111111111111111111111111111111111111111111111111111",
476 )
477 .unwrap();
478
479 let res = run_pair(
480 &input,
481 BYZANTIUM_PAIR_PER_POINT,
482 BYZANTIUM_PAIR_BASE,
483 260_000,
484 );
485 assert!(matches!(
486 res,
487 Err(PrecompileError::Bn128AffineGFailedToCreate)
488 ));
489
490 let input = hex::decode(
492 "\
493 1111111111111111111111111111111111111111111111111111111111111111\
494 1111111111111111111111111111111111111111111111111111111111111111\
495 111111111111111111111111111111\
496 ",
497 )
498 .unwrap();
499
500 let res = run_pair(
501 &input,
502 BYZANTIUM_PAIR_PER_POINT,
503 BYZANTIUM_PAIR_BASE,
504 260_000,
505 );
506 assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
507 }
508}