1use crate::{
3 crypto, eth_precompile_fn,
4 utilities::{bool_to_bytes32, right_pad},
5 Address, EthPrecompileOutput, EthPrecompileResult, Precompile, PrecompileHalt, PrecompileId,
6};
7use std::vec::Vec;
8
9#[cfg_attr(feature = "bn", expect(dead_code))]
10pub mod arkworks;
11
12cfg_if::cfg_if! {
13 if #[cfg(feature = "bn")]{
14 pub(crate) mod substrate;
15 pub(crate) use substrate as crypto_backend;
16 } else {
17 pub(crate) use arkworks as crypto_backend;
18 }
19}
20
21pub mod add {
23 use super::*;
24
25 pub const ADDRESS: Address = crate::u64_to_address(6);
27
28 pub const ISTANBUL_ADD_GAS_COST: u64 = 150;
30
31 pub const ISTANBUL: Precompile = Precompile::new(PrecompileId::Bn254Add, ADDRESS, istanbul_add);
33
34 pub const BYZANTIUM_ADD_GAS_COST: u64 = 500;
36
37 pub const BYZANTIUM: Precompile =
39 Precompile::new(PrecompileId::Bn254Add, ADDRESS, byzantium_add);
40
41 eth_precompile_fn!(istanbul_add, |i, g| run_add(i, ISTANBUL_ADD_GAS_COST, g));
42 eth_precompile_fn!(byzantium_add, |i, g| run_add(i, BYZANTIUM_ADD_GAS_COST, g));
43}
44
45pub mod mul {
47 use super::*;
48
49 pub const ADDRESS: Address = crate::u64_to_address(7);
51
52 pub const ISTANBUL_MUL_GAS_COST: u64 = 6_000;
54
55 pub const ISTANBUL: Precompile = Precompile::new(PrecompileId::Bn254Mul, ADDRESS, istanbul_mul);
57
58 pub const BYZANTIUM_MUL_GAS_COST: u64 = 40_000;
60
61 pub const BYZANTIUM: Precompile =
63 Precompile::new(PrecompileId::Bn254Mul, ADDRESS, byzantium_mul);
64
65 eth_precompile_fn!(istanbul_mul, |i, g| run_mul(i, ISTANBUL_MUL_GAS_COST, g));
66 eth_precompile_fn!(byzantium_mul, |i, g| run_mul(i, BYZANTIUM_MUL_GAS_COST, g));
67}
68
69pub mod pair {
71 use super::*;
72
73 pub const ADDRESS: Address = crate::u64_to_address(8);
75
76 pub const ISTANBUL_PAIR_PER_POINT: u64 = 34_000;
78
79 pub const ISTANBUL_PAIR_BASE: u64 = 45_000;
81
82 pub const ISTANBUL: Precompile =
84 Precompile::new(PrecompileId::Bn254Pairing, ADDRESS, istanbul_pair);
85
86 pub const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000;
88
89 pub const BYZANTIUM_PAIR_BASE: u64 = 100_000;
91
92 pub const BYZANTIUM: Precompile =
94 Precompile::new(PrecompileId::Bn254Pairing, ADDRESS, byzantium_pair);
95
96 eth_precompile_fn!(istanbul_pair, |i, g| run_pair(
97 i,
98 ISTANBUL_PAIR_PER_POINT,
99 ISTANBUL_PAIR_BASE,
100 g
101 ));
102 eth_precompile_fn!(byzantium_pair, |i, g| run_pair(
103 i,
104 BYZANTIUM_PAIR_PER_POINT,
105 BYZANTIUM_PAIR_BASE,
106 g
107 ));
108}
109
110const FQ_LEN: usize = 32;
115
116const SCALAR_LEN: usize = 32;
119
120const FQ2_LEN: usize = 2 * FQ_LEN;
126
127const G1_LEN: usize = 2 * FQ_LEN;
131const G2_LEN: usize = 2 * FQ2_LEN;
135
136pub const ADD_INPUT_LEN: usize = 2 * G1_LEN;
139
140pub const MUL_INPUT_LEN: usize = G1_LEN + SCALAR_LEN;
143
144pub const PAIR_ELEMENT_LEN: usize = G1_LEN + G2_LEN;
148
149pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> EthPrecompileResult {
151 if gas_cost > gas_limit {
152 return Err(PrecompileHalt::OutOfGas);
153 }
154
155 let input = right_pad::<ADD_INPUT_LEN>(input);
156
157 let p1_bytes = &input[..G1_LEN];
158 let p2_bytes = &input[G1_LEN..];
159 let output = crypto().bn254_g1_add(p1_bytes, p2_bytes)?;
160
161 Ok(EthPrecompileOutput::new(gas_cost, output.into()))
162}
163
164pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> EthPrecompileResult {
166 if gas_cost > gas_limit {
167 return Err(PrecompileHalt::OutOfGas);
168 }
169
170 let input = right_pad::<MUL_INPUT_LEN>(input);
171
172 let point_bytes = &input[..G1_LEN];
173 let scalar_bytes = &input[G1_LEN..G1_LEN + SCALAR_LEN];
174 let output = crypto().bn254_g1_mul(point_bytes, scalar_bytes)?;
175
176 Ok(EthPrecompileOutput::new(gas_cost, output.into()))
177}
178
179pub fn run_pair(
181 input: &[u8],
182 pair_per_point_cost: u64,
183 pair_base_cost: u64,
184 gas_limit: u64,
185) -> EthPrecompileResult {
186 let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost;
187 if gas_used > gas_limit {
188 return Err(PrecompileHalt::OutOfGas);
189 }
190
191 if !input.len().is_multiple_of(PAIR_ELEMENT_LEN) {
192 return Err(PrecompileHalt::Bn254PairLength);
193 }
194
195 let elements = input.len() / PAIR_ELEMENT_LEN;
196
197 let mut points = Vec::with_capacity(elements);
198
199 for idx in 0..elements {
200 let start = idx * PAIR_ELEMENT_LEN;
202 let g1_start = start;
203 let g2_start = start + G1_LEN;
206
207 let encoded_g1_element = &input[g1_start..g2_start];
209 let encoded_g2_element = &input[g2_start..g2_start + G2_LEN];
210 points.push((encoded_g1_element, encoded_g2_element));
211 }
212
213 let pairing_result = crypto().bn254_pairing_check(&points)?;
214 Ok(EthPrecompileOutput::new(
215 gas_used,
216 bool_to_bytes32(pairing_result),
217 ))
218}
219
220#[cfg(test)]
221mod tests {
222 use crate::{
223 bn254::{
224 add::BYZANTIUM_ADD_GAS_COST,
225 mul::BYZANTIUM_MUL_GAS_COST,
226 pair::{BYZANTIUM_PAIR_BASE, BYZANTIUM_PAIR_PER_POINT},
227 },
228 PrecompileHalt,
229 };
230 use primitives::hex;
231
232 use super::*;
233
234 #[test]
235 fn test_bn254_add() {
236 let input = hex::decode(
237 "\
238 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\
239 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\
240 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\
241 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7",
242 )
243 .unwrap();
244 let expected = hex::decode(
245 "\
246 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\
247 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915",
248 )
249 .unwrap();
250
251 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
252 assert_eq!(outcome.bytes, expected);
253
254 let input = hex::decode(
256 "\
257 0000000000000000000000000000000000000000000000000000000000000000\
258 0000000000000000000000000000000000000000000000000000000000000000\
259 0000000000000000000000000000000000000000000000000000000000000000\
260 0000000000000000000000000000000000000000000000000000000000000000",
261 )
262 .unwrap();
263 let expected = hex::decode(
264 "\
265 0000000000000000000000000000000000000000000000000000000000000000\
266 0000000000000000000000000000000000000000000000000000000000000000",
267 )
268 .unwrap();
269
270 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
271 assert_eq!(outcome.bytes, expected);
272
273 let input = hex::decode(
275 "\
276 0000000000000000000000000000000000000000000000000000000000000000\
277 0000000000000000000000000000000000000000000000000000000000000000\
278 0000000000000000000000000000000000000000000000000000000000000000\
279 0000000000000000000000000000000000000000000000000000000000000000",
280 )
281 .unwrap();
282
283 let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 499);
284
285 assert!(matches!(res, Err(PrecompileHalt::OutOfGas)));
286
287 let input = [0u8; 0];
289 let expected = hex::decode(
290 "\
291 0000000000000000000000000000000000000000000000000000000000000000\
292 0000000000000000000000000000000000000000000000000000000000000000",
293 )
294 .unwrap();
295
296 let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap();
297 assert_eq!(outcome.bytes, expected);
298
299 let input = hex::decode(
301 "\
302 1111111111111111111111111111111111111111111111111111111111111111\
303 1111111111111111111111111111111111111111111111111111111111111111\
304 1111111111111111111111111111111111111111111111111111111111111111\
305 1111111111111111111111111111111111111111111111111111111111111111",
306 )
307 .unwrap();
308
309 let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500);
310 assert!(matches!(
311 res,
312 Err(ref f) if *f == PrecompileHalt::Bn254AffineGFailedToCreate
313 ));
314 }
315
316 #[test]
317 fn test_bn254_mul() {
318 let input = hex::decode(
319 "\
320 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\
321 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\
322 00000000000000000000000000000000000000000000000011138ce750fa15c2",
323 )
324 .unwrap();
325 let expected = hex::decode(
326 "\
327 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\
328 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc",
329 )
330 .unwrap();
331
332 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
333 assert_eq!(outcome.bytes, expected);
334
335 let input = hex::decode(
337 "\
338 0000000000000000000000000000000000000000000000000000000000000000\
339 0000000000000000000000000000000000000000000000000000000000000000\
340 0200000000000000000000000000000000000000000000000000000000000000",
341 )
342 .unwrap();
343
344 let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 39_999);
345 assert!(matches!(res, Err(PrecompileHalt::OutOfGas)));
346
347 let input = hex::decode(
349 "\
350 0000000000000000000000000000000000000000000000000000000000000000\
351 0000000000000000000000000000000000000000000000000000000000000000\
352 0200000000000000000000000000000000000000000000000000000000000000",
353 )
354 .unwrap();
355 let expected = hex::decode(
356 "\
357 0000000000000000000000000000000000000000000000000000000000000000\
358 0000000000000000000000000000000000000000000000000000000000000000",
359 )
360 .unwrap();
361
362 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
363 assert_eq!(outcome.bytes, expected);
364
365 let input = [0u8; 0];
367 let expected = hex::decode(
368 "\
369 0000000000000000000000000000000000000000000000000000000000000000\
370 0000000000000000000000000000000000000000000000000000000000000000",
371 )
372 .unwrap();
373
374 let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap();
375 assert_eq!(outcome.bytes, expected);
376
377 let input = hex::decode(
379 "\
380 1111111111111111111111111111111111111111111111111111111111111111\
381 1111111111111111111111111111111111111111111111111111111111111111\
382 0f00000000000000000000000000000000000000000000000000000000000000",
383 )
384 .unwrap();
385
386 let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000);
387 assert!(matches!(
388 res,
389 Err(ref f) if *f == PrecompileHalt::Bn254AffineGFailedToCreate
390 ));
391 }
392
393 #[test]
394 fn test_bn254_pair() {
395 let input = hex::decode(
396 "\
397 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
398 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
399 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
400 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
401 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
402 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
403 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
404 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
405 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
406 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
407 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
408 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
409 )
410 .unwrap();
411 let expected =
412 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
413 .unwrap();
414
415 let outcome = run_pair(
416 &input,
417 BYZANTIUM_PAIR_PER_POINT,
418 BYZANTIUM_PAIR_BASE,
419 260_000,
420 )
421 .unwrap();
422 assert_eq!(outcome.bytes, expected);
423
424 let input = hex::decode(
426 "\
427 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
428 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
429 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
430 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
431 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
432 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
433 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
434 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
435 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
436 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
437 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
438 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
439 )
440 .unwrap();
441
442 let res = run_pair(
443 &input,
444 BYZANTIUM_PAIR_PER_POINT,
445 BYZANTIUM_PAIR_BASE,
446 259_999,
447 );
448 assert!(matches!(res, Err(PrecompileHalt::OutOfGas)));
449
450 let input = [0u8; 0];
452 let expected =
453 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
454 .unwrap();
455
456 let outcome = run_pair(
457 &input,
458 BYZANTIUM_PAIR_PER_POINT,
459 BYZANTIUM_PAIR_BASE,
460 260_000,
461 )
462 .unwrap();
463 assert_eq!(outcome.bytes, expected);
464
465 let input = hex::decode(
467 "\
468 1111111111111111111111111111111111111111111111111111111111111111\
469 1111111111111111111111111111111111111111111111111111111111111111\
470 1111111111111111111111111111111111111111111111111111111111111111\
471 1111111111111111111111111111111111111111111111111111111111111111\
472 1111111111111111111111111111111111111111111111111111111111111111\
473 1111111111111111111111111111111111111111111111111111111111111111",
474 )
475 .unwrap();
476
477 let res = run_pair(
478 &input,
479 BYZANTIUM_PAIR_PER_POINT,
480 BYZANTIUM_PAIR_BASE,
481 260_000,
482 );
483 assert!(matches!(
484 res,
485 Err(ref f) if *f == PrecompileHalt::Bn254AffineGFailedToCreate
486 ));
487
488 let input = hex::decode(
490 "\
491 1111111111111111111111111111111111111111111111111111111111111111\
492 1111111111111111111111111111111111111111111111111111111111111111\
493 111111111111111111111111111111\
494 ",
495 )
496 .unwrap();
497
498 let res = run_pair(
499 &input,
500 BYZANTIUM_PAIR_PER_POINT,
501 BYZANTIUM_PAIR_BASE,
502 260_000,
503 );
504 assert!(matches!(res, Err(PrecompileHalt::Bn254PairLength)));
505
506 let input = hex::decode(
509 "\
510 0000000000000000000000000000000000000000000000000000000000000000\
511 0000000000000000000000000000000000000000000000000000000000000000\
512 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
513 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
514 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
515 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550",
516 )
517 .unwrap();
518 let expected =
519 hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
520 .unwrap();
521
522 let outcome = run_pair(
523 &input,
524 BYZANTIUM_PAIR_PER_POINT,
525 BYZANTIUM_PAIR_BASE,
526 260_000,
527 )
528 .unwrap();
529 assert_eq!(outcome.bytes, expected);
530
531 let input = hex::decode(
534 "\
535 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
536 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
537 0000000000000000000000000000000000000000000000000000000000000000\
538 0000000000000000000000000000000000000000000000000000000000000000\
539 0000000000000000000000000000000000000000000000000000000000000000\
540 0000000000000000000000000000000000000000000000000000000000000000",
541 )
542 .unwrap();
543
544 let outcome = run_pair(
545 &input,
546 BYZANTIUM_PAIR_PER_POINT,
547 BYZANTIUM_PAIR_BASE,
548 260_000,
549 )
550 .unwrap();
551 assert_eq!(outcome.bytes, expected);
552 }
553}