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