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