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