1use crate::{
3 utilities::{bool_to_bytes32, right_pad},
4 Address, PrecompileError, PrecompileOutput, PrecompileResult, PrecompileWithAddress,
5};
6use std::vec::Vec;
7
8cfg_if::cfg_if! {
9 if #[cfg(feature = "bn")]{
10 mod substrate;
11 use substrate::{
12 encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point,
13 read_scalar,
14 };
15 } else {
16 mod arkworks;
17 use arkworks::{
18 encode_g1_point, g1_point_add, g1_point_mul, pairing_check, read_g1_point, read_g2_point,
19 read_scalar,
20 };
21 }
22}
23
24pub mod add {
26 use super::*;
27
28 pub const ADDRESS: Address = crate::u64_to_address(6);
30
31 pub const ISTANBUL_ADD_GAS_COST: u64 = 150;
33
34 pub const ISTANBUL: PrecompileWithAddress =
36 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
37 run_add(input, ISTANBUL_ADD_GAS_COST, gas_limit)
38 });
39
40 pub const BYZANTIUM_ADD_GAS_COST: u64 = 500;
42
43 pub const BYZANTIUM: PrecompileWithAddress =
45 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
46 run_add(input, BYZANTIUM_ADD_GAS_COST, gas_limit)
47 });
48}
49
50pub mod mul {
52 use super::*;
53
54 pub const ADDRESS: Address = crate::u64_to_address(7);
56
57 pub const ISTANBUL_MUL_GAS_COST: u64 = 6_000;
59
60 pub const ISTANBUL: PrecompileWithAddress =
62 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
63 run_mul(input, ISTANBUL_MUL_GAS_COST, gas_limit)
64 });
65
66 pub const BYZANTIUM_MUL_GAS_COST: u64 = 40_000;
68
69 pub const BYZANTIUM: PrecompileWithAddress =
71 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
72 run_mul(input, BYZANTIUM_MUL_GAS_COST, gas_limit)
73 });
74}
75
76pub mod pair {
78 use super::*;
79
80 pub const ADDRESS: Address = crate::u64_to_address(8);
82
83 pub const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
85
86 pub const ISTANBUL_PAIR_BASE: u64 = 45_000;
88
89 pub const ISTANBUL: PrecompileWithAddress =
91 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
92 run_pair(
93 input,
94 ISTANBUL_PAIR_PER_POINT,
95 ISTANBUL_PAIR_BASE,
96 gas_limit,
97 )
98 });
99
100 pub const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000;
102
103 pub const BYZANTIUM_PAIR_BASE: u64 = 100_000;
105
106 pub const BYZANTIUM: PrecompileWithAddress =
108 PrecompileWithAddress(ADDRESS, |input, gas_limit| {
109 run_pair(
110 input,
111 BYZANTIUM_PAIR_PER_POINT,
112 BYZANTIUM_PAIR_BASE,
113 gas_limit,
114 )
115 });
116}
117
118const FQ_LEN: usize = 32;
123
124const SCALAR_LEN: usize = 32;
127
128const FQ2_LEN: usize = 2 * FQ_LEN;
134
135const G1_LEN: usize = 2 * FQ_LEN;
139const G2_LEN: usize = 2 * FQ2_LEN;
143
144pub const ADD_INPUT_LEN: usize = 2 * G1_LEN;
147
148pub const MUL_INPUT_LEN: usize = G1_LEN + SCALAR_LEN;
151
152pub const PAIR_ELEMENT_LEN: usize = G1_LEN + G2_LEN;
156
157pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult {
159 if gas_cost > gas_limit {
160 return Err(PrecompileError::OutOfGas);
161 }
162
163 let input = right_pad::<ADD_INPUT_LEN>(input);
164
165 let p1 = read_g1_point(&input[..G1_LEN])?;
166 let p2 = read_g1_point(&input[G1_LEN..])?;
167 let result = g1_point_add(p1, p2);
168
169 let output = encode_g1_point(result);
170
171 Ok(PrecompileOutput::new(gas_cost, output.into()))
172}
173
174pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult {
176 if gas_cost > gas_limit {
177 return Err(PrecompileError::OutOfGas);
178 }
179
180 let input = right_pad::<MUL_INPUT_LEN>(input);
181
182 let p = read_g1_point(&input[..G1_LEN])?;
183
184 let scalar = read_scalar(&input[G1_LEN..G1_LEN + SCALAR_LEN]);
185 let result = g1_point_mul(p, scalar);
186
187 let output = encode_g1_point(result);
188
189 Ok(PrecompileOutput::new(gas_cost, output.into()))
190}
191
192pub fn run_pair(
194 input: &[u8],
195 pair_per_point_cost: u64,
196 pair_base_cost: u64,
197 gas_limit: u64,
198) -> PrecompileResult {
199 let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost;
200 if gas_used > gas_limit {
201 return Err(PrecompileError::OutOfGas);
202 }
203
204 if input.len() % PAIR_ELEMENT_LEN != 0 {
205 return Err(PrecompileError::Bn128PairLength);
206 }
207
208 let elements = input.len() / PAIR_ELEMENT_LEN;
209
210 let mut points = Vec::with_capacity(elements);
211
212 for idx in 0..elements {
213 let start = idx * PAIR_ELEMENT_LEN;
215 let g1_start = start;
216 let g2_start = start + G1_LEN;
219
220 let encoded_g1_element = &input[g1_start..g2_start];
221 let encoded_g2_element = &input[g2_start..g2_start + G2_LEN];
222
223 let g1_is_zero = encoded_g1_element.iter().all(|i| *i == 0);
233 let g2_is_zero = encoded_g2_element.iter().all(|i| *i == 0);
234
235 let a = read_g1_point(encoded_g1_element)?;
237 let b = read_g2_point(encoded_g2_element)?;
238
239 if !g1_is_zero && !g2_is_zero {
240 points.push((a, b));
241 }
242 }
243
244 let success = pairing_check(&points);
245
246 Ok(PrecompileOutput::new(gas_used, bool_to_bytes32(success)))
247}
248
249#[cfg(test)]
250mod tests {
251 use crate::{
252 bn128::{
253 add::BYZANTIUM_ADD_GAS_COST,
254 mul::BYZANTIUM_MUL_GAS_COST,
255 pair::{BYZANTIUM_PAIR_BASE, BYZANTIUM_PAIR_PER_POINT},
256 },
257 PrecompileError,
258 };
259 use primitives::hex;
260
261 use super::*;
262
263 #[test]
264 fn test_alt_bn128_add() {
265 let input = hex::decode(
266 "\
267 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\
268 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\
269 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\
270 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7",
271 )
272 .unwrap();
273 let expected = hex::decode(
274 "\
275 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\
276 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915",
277 )
278 .unwrap();
279
280 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
281 assert_eq!(outcome.bytes, expected);
282
283 let input = hex::decode(
285 "\
286 0000000000000000000000000000000000000000000000000000000000000000\
287 0000000000000000000000000000000000000000000000000000000000000000\
288 0000000000000000000000000000000000000000000000000000000000000000\
289 0000000000000000000000000000000000000000000000000000000000000000",
290 )
291 .unwrap();
292 let expected = hex::decode(
293 "\
294 0000000000000000000000000000000000000000000000000000000000000000\
295 0000000000000000000000000000000000000000000000000000000000000000",
296 )
297 .unwrap();
298
299 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
300 assert_eq!(outcome.bytes, expected);
301
302 let input = hex::decode(
304 "\
305 0000000000000000000000000000000000000000000000000000000000000000\
306 0000000000000000000000000000000000000000000000000000000000000000\
307 0000000000000000000000000000000000000000000000000000000000000000\
308 0000000000000000000000000000000000000000000000000000000000000000",
309 )
310 .unwrap();
311
312 let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 499);
313
314 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
315
316 let input = [0u8; 0];
318 let expected = hex::decode(
319 "\
320 0000000000000000000000000000000000000000000000000000000000000000\
321 0000000000000000000000000000000000000000000000000000000000000000",
322 )
323 .unwrap();
324
325 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
326 assert_eq!(outcome.bytes, expected);
327
328 let input = hex::decode(
330 "\
331 1111111111111111111111111111111111111111111111111111111111111111\
332 1111111111111111111111111111111111111111111111111111111111111111\
333 1111111111111111111111111111111111111111111111111111111111111111\
334 1111111111111111111111111111111111111111111111111111111111111111",
335 )
336 .unwrap();
337
338 let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500);
339 assert!(matches!(
340 res,
341 Err(PrecompileError::Bn128AffineGFailedToCreate)
342 ));
343 }
344
345 #[test]
346 fn test_alt_bn128_mul() {
347 let input = hex::decode(
348 "\
349 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\
350 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\
351 00000000000000000000000000000000000000000000000011138ce750fa15c2",
352 )
353 .unwrap();
354 let expected = hex::decode(
355 "\
356 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\
357 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc",
358 )
359 .unwrap();
360
361 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
362 assert_eq!(outcome.bytes, expected);
363
364 let input = hex::decode(
366 "\
367 0000000000000000000000000000000000000000000000000000000000000000\
368 0000000000000000000000000000000000000000000000000000000000000000\
369 0200000000000000000000000000000000000000000000000000000000000000",
370 )
371 .unwrap();
372
373 let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 39_999);
374 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
375
376 let input = hex::decode(
378 "\
379 0000000000000000000000000000000000000000000000000000000000000000\
380 0000000000000000000000000000000000000000000000000000000000000000\
381 0200000000000000000000000000000000000000000000000000000000000000",
382 )
383 .unwrap();
384 let expected = hex::decode(
385 "\
386 0000000000000000000000000000000000000000000000000000000000000000\
387 0000000000000000000000000000000000000000000000000000000000000000",
388 )
389 .unwrap();
390
391 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
392 assert_eq!(outcome.bytes, expected);
393
394 let input = [0u8; 0];
396 let expected = hex::decode(
397 "\
398 0000000000000000000000000000000000000000000000000000000000000000\
399 0000000000000000000000000000000000000000000000000000000000000000",
400 )
401 .unwrap();
402
403 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
404 assert_eq!(outcome.bytes, expected);
405
406 let input = hex::decode(
408 "\
409 1111111111111111111111111111111111111111111111111111111111111111\
410 1111111111111111111111111111111111111111111111111111111111111111\
411 0f00000000000000000000000000000000000000000000000000000000000000",
412 )
413 .unwrap();
414
415 let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000);
416 assert!(matches!(
417 res,
418 Err(PrecompileError::Bn128AffineGFailedToCreate)
419 ));
420 }
421
422 #[test]
423 fn test_alt_bn128_pair() {
424 let input = hex::decode(
425 "\
426 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
427 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
428 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
429 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
430 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
431 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
432 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
433 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
434 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
435 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
436 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
437 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
438 )
439 .unwrap();
440 let expected =
441 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
442 .unwrap();
443
444 let outcome = run_pair(
445 &input,
446 BYZANTIUM_PAIR_PER_POINT,
447 BYZANTIUM_PAIR_BASE,
448 260_000,
449 )
450 .unwrap();
451 assert_eq!(outcome.bytes, expected);
452
453 let input = hex::decode(
455 "\
456 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
457 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
458 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
459 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
460 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
461 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
462 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
463 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
464 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
465 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
466 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
467 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
468 )
469 .unwrap();
470
471 let res = run_pair(
472 &input,
473 BYZANTIUM_PAIR_PER_POINT,
474 BYZANTIUM_PAIR_BASE,
475 259_999,
476 );
477 assert!(matches!(res, Err(PrecompileError::OutOfGas)));
478
479 let input = [0u8; 0];
481 let expected =
482 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
483 .unwrap();
484
485 let outcome = run_pair(
486 &input,
487 BYZANTIUM_PAIR_PER_POINT,
488 BYZANTIUM_PAIR_BASE,
489 260_000,
490 )
491 .unwrap();
492 assert_eq!(outcome.bytes, expected);
493
494 let input = hex::decode(
496 "\
497 1111111111111111111111111111111111111111111111111111111111111111\
498 1111111111111111111111111111111111111111111111111111111111111111\
499 1111111111111111111111111111111111111111111111111111111111111111\
500 1111111111111111111111111111111111111111111111111111111111111111\
501 1111111111111111111111111111111111111111111111111111111111111111\
502 1111111111111111111111111111111111111111111111111111111111111111",
503 )
504 .unwrap();
505
506 let res = run_pair(
507 &input,
508 BYZANTIUM_PAIR_PER_POINT,
509 BYZANTIUM_PAIR_BASE,
510 260_000,
511 );
512 assert!(matches!(
513 res,
514 Err(PrecompileError::Bn128AffineGFailedToCreate)
515 ));
516
517 let input = hex::decode(
519 "\
520 1111111111111111111111111111111111111111111111111111111111111111\
521 1111111111111111111111111111111111111111111111111111111111111111\
522 111111111111111111111111111111\
523 ",
524 )
525 .unwrap();
526
527 let res = run_pair(
528 &input,
529 BYZANTIUM_PAIR_PER_POINT,
530 BYZANTIUM_PAIR_BASE,
531 260_000,
532 );
533 assert!(matches!(res, Err(PrecompileError::Bn128PairLength)));
534 }
535}