aurora_engine_precompiles/
alt_bn256.rs

1use crate::prelude::types::{make_address, Address, EthGas};
2use crate::prelude::{Borrowed, PhantomData, Vec};
3use crate::utils;
4use crate::{Byzantium, EvmPrecompileResult, HardFork, Istanbul, Precompile, PrecompileOutput};
5use aurora_evm::{Context, ExitError};
6use bn::Group;
7use core::num::{NonZeroU64, NonZeroUsize};
8
9/// bn128 costs.
10mod costs {
11    use crate::prelude::types::EthGas;
12
13    /// Cost of the Byzantium `alt_bn128_add` operation.
14    pub(super) const BYZANTIUM_ADD: EthGas = EthGas::new(500);
15
16    /// Cost of the Byzantium `alt_bn128_mul` operation.
17    pub(super) const BYZANTIUM_MUL: EthGas = EthGas::new(40_000);
18
19    /// Cost of the `alt_bn128_pair` per point.
20    pub(super) const BYZANTIUM_PAIR_PER_POINT: EthGas = EthGas::new(80_000);
21
22    /// Cost of the `alt_bn128_pair` operation.
23    pub(super) const BYZANTIUM_PAIR_BASE: EthGas = EthGas::new(100_000);
24
25    /// Cost of the Istanbul `alt_bn128_add` operation.
26    pub(super) const ISTANBUL_ADD: EthGas = EthGas::new(150);
27
28    /// Cost of the Istanbul `alt_bn128_mul` operation.
29    pub(super) const ISTANBUL_MUL: EthGas = EthGas::new(6_000);
30
31    /// Cost of the Istanbul `alt_bn128_pair` per point.
32    pub(super) const ISTANBUL_PAIR_PER_POINT: EthGas = EthGas::new(34_000);
33
34    /// Cost of the Istanbul `alt_bn128_pair` operation.
35    pub(super) const ISTANBUL_PAIR_BASE: EthGas = EthGas::new(45_000);
36}
37
38/// bn128 constants.
39mod consts {
40    use crate::prelude::Borrowed;
41    use aurora_evm::ExitError;
42
43    /// Input length for the add operation.
44    pub(super) const ADD_INPUT_LEN: usize = 128;
45
46    /// Input length for the multiplication operation.
47    pub(super) const MUL_INPUT_LEN: usize = 128;
48
49    /// Pair element length.
50    pub(super) const PAIR_ELEMENT_LEN: usize = 192;
51
52    /// Size of BN scalars.
53    pub(super) const SCALAR_LEN: usize = 32;
54
55    /// Size of BN points.
56    pub(super) const POINT_LEN: usize = 64;
57
58    /// Size of BN pairs.
59    #[cfg(feature = "contract")]
60    pub(super) const POINT_PAIR_LEN: usize = 128;
61
62    /// Output length.
63    pub(super) const OUTPUT_LEN: usize = 64;
64
65    // pub(super) const ERR_BIG_ENDIAN: &str = "ERR_BIG_ENDIAN";
66
67    pub(super) const ERR_BIG_ENDIAN: ExitError = ExitError::Other(Borrowed("ERR_BIG_ENDIAN"));
68}
69
70#[cfg(feature = "contract")]
71mod type_arith {
72    pub struct Double<const P: usize>;
73    pub trait Is<const S: usize> {}
74    impl Is<128> for Double<64> {}
75    impl Is<64> for Double<32> {}
76    impl Is<32> for Double<16> {}
77}
78
79#[cfg(feature = "contract")]
80trait HostFnEncode {
81    type Encoded;
82
83    fn host_fn_encode(self) -> Self::Encoded;
84}
85
86#[cfg(feature = "contract")]
87fn concat_low_high<const P: usize, const S: usize>(low: [u8; P], high: [u8; P]) -> [u8; S]
88where
89    type_arith::Double<P>: type_arith::Is<S>,
90{
91    let mut bytes = [0u8; S];
92    bytes[0..P].copy_from_slice(&low);
93    bytes[P..S].copy_from_slice(&high);
94    bytes
95}
96
97#[cfg(feature = "contract")]
98impl HostFnEncode for bn::Fr {
99    type Encoded = [u8; consts::SCALAR_LEN];
100
101    fn host_fn_encode(self) -> Self::Encoded {
102        let [low, high] = self.into_u256().0;
103        concat_low_high(low.to_le_bytes(), high.to_le_bytes())
104    }
105}
106
107#[cfg(feature = "contract")]
108impl HostFnEncode for bn::Fq {
109    type Encoded = [u8; consts::SCALAR_LEN];
110
111    fn host_fn_encode(self) -> Self::Encoded {
112        let [low, high] = self.into_u256().0;
113        concat_low_high(low.to_le_bytes(), high.to_le_bytes())
114    }
115}
116
117#[cfg(feature = "contract")]
118impl HostFnEncode for bn::Fq2 {
119    type Encoded = [u8; consts::SCALAR_LEN * 2];
120
121    fn host_fn_encode(self) -> Self::Encoded {
122        let [real_low, real_high] = self.real().into_u256().0;
123        let real: [u8; consts::SCALAR_LEN] =
124            concat_low_high(real_low.to_le_bytes(), real_high.to_le_bytes());
125
126        let [imaginary_low, imaginary_high] = self.imaginary().into_u256().0;
127        let imaginary: [u8; consts::SCALAR_LEN] =
128            concat_low_high(imaginary_low.to_le_bytes(), imaginary_high.to_le_bytes());
129        concat_low_high(real, imaginary)
130    }
131}
132
133#[cfg(feature = "contract")]
134impl HostFnEncode for bn::G1 {
135    type Encoded = [u8; consts::POINT_LEN];
136
137    fn host_fn_encode(self) -> Self::Encoded {
138        bn::AffineG1::from_jacobian(self).map_or_else(
139            || [0u8; consts::POINT_LEN],
140            |p| {
141                let (px, py) = (p.x().host_fn_encode(), p.y().host_fn_encode());
142                concat_low_high(px, py)
143            },
144        )
145    }
146}
147
148#[cfg(feature = "contract")]
149impl HostFnEncode for bn::G2 {
150    type Encoded = [u8; consts::POINT_PAIR_LEN];
151
152    fn host_fn_encode(self) -> Self::Encoded {
153        bn::AffineG2::from_jacobian(self).map_or_else(
154            || [0u8; consts::POINT_PAIR_LEN],
155            |g2| {
156                let x = g2.x().host_fn_encode();
157                let y = g2.y().host_fn_encode();
158                concat_low_high(x, y)
159            },
160        )
161    }
162}
163
164/// Reads the `x` and `y` points from an input at a given position.
165fn read_point(input: &[u8], pos: usize) -> Result<bn::G1, ExitError> {
166    use bn::{AffineG1, Fq, G1};
167    if input.len() < (pos + consts::SCALAR_LEN * 2) {
168        return Err(ExitError::Other(Borrowed("INVALID_INPUT_LENGTH")));
169    }
170
171    let px = Fq::from_slice(&input[pos..(pos + consts::SCALAR_LEN)])
172        .map_err(|_e| ExitError::Other(Borrowed("ERR_FQ_INCORRECT")))?;
173    let py = Fq::from_slice(&input[(pos + consts::SCALAR_LEN)..(pos + consts::SCALAR_LEN * 2)])
174        .map_err(|_e| ExitError::Other(Borrowed("ERR_FQ_INCORRECT")))?;
175
176    Ok(if px == Fq::zero() && py == Fq::zero() {
177        G1::zero()
178    } else {
179        AffineG1::new(px, py)
180            .map_err(|_| ExitError::Other(Borrowed("ERR_BN128_INVALID_POINT")))?
181            .into()
182    })
183}
184
185#[derive(Default)]
186pub struct Bn256Add<HF: HardFork>(PhantomData<HF>);
187
188impl<HF: HardFork> Bn256Add<HF> {
189    pub const ADDRESS: Address = make_address(0, 6);
190
191    #[must_use]
192    pub const fn new() -> Self {
193        Self(PhantomData)
194    }
195}
196
197impl<HF: HardFork> Bn256Add<HF> {
198    fn run_inner(input: &[u8], _context: &Context) -> Result<Vec<u8>, ExitError> {
199        let mut input = input.to_vec();
200        input.resize(consts::ADD_INPUT_LEN, 0);
201
202        let p1 = read_point(&input, 0)?;
203        let p2 = read_point(&input, consts::POINT_LEN)?;
204
205        let output = Self::execute(p1, p2)?;
206        Ok(output.to_vec())
207    }
208
209    #[cfg(not(feature = "contract"))]
210    fn execute(p1: bn::G1, p2: bn::G1) -> Result<[u8; consts::OUTPUT_LEN], ExitError> {
211        let mut output = [0u8; consts::POINT_LEN];
212        if let Some(sum) = bn::AffineG1::from_jacobian(p1 + p2) {
213            sum.x()
214                .to_big_endian(&mut output[0..consts::SCALAR_LEN])
215                .map_err(|_| consts::ERR_BIG_ENDIAN)?;
216            sum.y()
217                .to_big_endian(&mut output[consts::SCALAR_LEN..consts::SCALAR_LEN * 2])
218                .map_err(|_| consts::ERR_BIG_ENDIAN)?;
219        }
220        Ok(output)
221    }
222
223    #[cfg(feature = "contract")]
224    #[allow(clippy::unnecessary_wraps)]
225    fn execute(p1: bn::G1, p2: bn::G1) -> Result<[u8; consts::OUTPUT_LEN], ExitError> {
226        Ok(aurora_engine_sdk::alt_bn128_g1_sum(
227            p1.host_fn_encode(),
228            p2.host_fn_encode(),
229        ))
230    }
231}
232
233impl Precompile for Bn256Add<Byzantium> {
234    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
235        Ok(costs::BYZANTIUM_ADD)
236    }
237
238    /// Takes in two points on the elliptic curve `alt_bn128` and calculates the sum
239    /// of them.
240    ///
241    /// See: `https://eips.ethereum.org/EIPS/eip-196`
242    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000006`
243    fn run(
244        &self,
245        input: &[u8],
246        target_gas: Option<EthGas>,
247        context: &Context,
248        _is_static: bool,
249    ) -> EvmPrecompileResult {
250        let cost = Self::required_gas(input)?;
251        if let Some(target_gas) = target_gas {
252            if cost > target_gas {
253                return Err(ExitError::OutOfGas);
254            }
255        }
256
257        let output = Self::run_inner(input, context)?;
258        Ok(PrecompileOutput::without_logs(cost, output))
259    }
260}
261
262impl Precompile for Bn256Add<Istanbul> {
263    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
264        Ok(costs::ISTANBUL_ADD)
265    }
266
267    /// Takes in two points on the elliptic curve `alt_bn128` and calculates the sum
268    /// of them.
269    ///
270    /// See: `https://eips.ethereum.org/EIPS/eip-196`
271    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000006`
272    fn run(
273        &self,
274        input: &[u8],
275        target_gas: Option<EthGas>,
276        context: &Context,
277        _is_static: bool,
278    ) -> EvmPrecompileResult {
279        let cost = Self::required_gas(input)?;
280        if let Some(target_gas) = target_gas {
281            if cost > target_gas {
282                return Err(ExitError::OutOfGas);
283            }
284        }
285        let output = Self::run_inner(input, context)?;
286        Ok(PrecompileOutput::without_logs(cost, output))
287    }
288}
289
290#[derive(Default)]
291pub struct Bn256Mul<HF: HardFork>(PhantomData<HF>);
292
293impl<HF: HardFork> Bn256Mul<HF> {
294    pub const ADDRESS: Address = make_address(0, 7);
295
296    #[must_use]
297    pub const fn new() -> Self {
298        Self(PhantomData)
299    }
300}
301
302impl<HF: HardFork> Bn256Mul<HF> {
303    fn run_inner(input: &[u8], _context: &Context) -> Result<Vec<u8>, ExitError> {
304        let mut input = input.to_vec();
305        input.resize(consts::MUL_INPUT_LEN, 0);
306
307        let p = read_point(&input, 0)?;
308        let fr =
309            bn::Fr::from_slice(&input[consts::POINT_LEN..consts::POINT_LEN + consts::SCALAR_LEN])
310                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_FR")))?;
311
312        let output = Self::execute(p, fr)?;
313        Ok(output.to_vec())
314    }
315
316    #[cfg(not(feature = "contract"))]
317    fn execute(p: bn::G1, fr: bn::Fr) -> Result<[u8; consts::OUTPUT_LEN], ExitError> {
318        let mut output = [0u8; consts::OUTPUT_LEN];
319        if let Some(mul) = bn::AffineG1::from_jacobian(p * fr) {
320            mul.x()
321                .into_u256()
322                .to_big_endian(&mut output[0..consts::SCALAR_LEN])
323                .map_err(|_e| consts::ERR_BIG_ENDIAN)?;
324            mul.y()
325                .into_u256()
326                .to_big_endian(&mut output[consts::SCALAR_LEN..consts::SCALAR_LEN * 2])
327                .map_err(|_e| consts::ERR_BIG_ENDIAN)?;
328        }
329        Ok(output)
330    }
331
332    #[cfg(feature = "contract")]
333    #[allow(clippy::unnecessary_wraps)]
334    fn execute(g1: bn::G1, fr: bn::Fr) -> Result<[u8; consts::OUTPUT_LEN], ExitError> {
335        Ok(aurora_engine_sdk::alt_bn128_g1_scalar_multiple(
336            g1.host_fn_encode(),
337            fr.host_fn_encode(),
338        ))
339    }
340}
341
342impl Precompile for Bn256Mul<Byzantium> {
343    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
344        Ok(costs::BYZANTIUM_MUL)
345    }
346
347    /// Takes in two points on the elliptic curve `alt_bn128` and multiples them.
348    ///
349    /// See: `https://eips.ethereum.org/EIPS/eip-196`
350    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000007`
351    fn run(
352        &self,
353        input: &[u8],
354        target_gas: Option<EthGas>,
355        context: &Context,
356        _is_static: bool,
357    ) -> EvmPrecompileResult {
358        let cost = Self::required_gas(input)?;
359        if let Some(target_gas) = target_gas {
360            if cost > target_gas {
361                return Err(ExitError::OutOfGas);
362            }
363        }
364
365        let output = Self::run_inner(input, context)?;
366        Ok(PrecompileOutput::without_logs(cost, output))
367    }
368}
369
370impl Precompile for Bn256Mul<Istanbul> {
371    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
372        Ok(costs::ISTANBUL_MUL)
373    }
374
375    /// Takes in two points on the elliptic curve `alt_bn128` and multiples them.
376    ///
377    /// See: `https://eips.ethereum.org/EIPS/eip-196`
378    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000007`
379    fn run(
380        &self,
381        input: &[u8],
382        target_gas: Option<EthGas>,
383        context: &Context,
384        _is_static: bool,
385    ) -> EvmPrecompileResult {
386        let cost = Self::required_gas(input)?;
387        if let Some(target_gas) = target_gas {
388            if cost > target_gas {
389                return Err(ExitError::OutOfGas);
390            }
391        }
392
393        let output = Self::run_inner(input, context)?;
394        Ok(PrecompileOutput::without_logs(cost, output))
395    }
396}
397
398#[derive(Default)]
399pub struct Bn256Pair<HF: HardFork>(PhantomData<HF>);
400
401impl<HF: HardFork> Bn256Pair<HF> {
402    pub const ADDRESS: Address = make_address(0, 8);
403
404    #[must_use]
405    pub const fn new() -> Self {
406        Self(PhantomData)
407    }
408}
409
410impl<HF: HardFork> Bn256Pair<HF> {
411    fn run_inner(input: &[u8], _context: &Context) -> Result<Vec<u8>, ExitError> {
412        if input.len() % consts::PAIR_ELEMENT_LEN != 0 {
413            return Err(ExitError::Other(Borrowed("ERR_BN128_INVALID_LEN")));
414        }
415
416        let output = if input.is_empty() {
417            bn::arith::U256::one()
418        } else {
419            let elements = input.len() / consts::PAIR_ELEMENT_LEN;
420            let mut vals = Vec::with_capacity(elements);
421            for idx in 0..elements {
422                let ax = bn::Fq::from_slice(
423                    &input[(idx * consts::PAIR_ELEMENT_LEN)
424                        ..(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN)],
425                )
426                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_AX")))?;
427                let ay = bn::Fq::from_slice(
428                    &input[(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN)
429                        ..(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 2)],
430                )
431                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_AY")))?;
432                let bay = bn::Fq::from_slice(
433                    &input[(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 2)
434                        ..(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 3)],
435                )
436                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_BAY")))?;
437                let bax = bn::Fq::from_slice(
438                    &input[(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 3)
439                        ..(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 4)],
440                )
441                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_BAX")))?;
442                let bby = bn::Fq::from_slice(
443                    &input[(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 4)
444                        ..(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 5)],
445                )
446                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_BBY")))?;
447                let bbx = bn::Fq::from_slice(
448                    &input[(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 5)
449                        ..(idx * consts::PAIR_ELEMENT_LEN + consts::SCALAR_LEN * 6)],
450                )
451                .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_BBX")))?;
452
453                let g1_a = {
454                    if ax.is_zero() && ay.is_zero() {
455                        bn::G1::zero()
456                    } else {
457                        bn::AffineG1::new(ax, ay)
458                            .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_A")))?
459                            .into()
460                    }
461                };
462                let g1_b = {
463                    let ba = bn::Fq2::new(bax, bay);
464                    let bb = bn::Fq2::new(bbx, bby);
465
466                    if ba.is_zero() && bb.is_zero() {
467                        bn::G2::zero()
468                    } else {
469                        bn::AffineG2::new(ba, bb)
470                            .map_err(|_e| ExitError::Other(Borrowed("ERR_BN128_INVALID_B")))?
471                            .into()
472                    }
473                };
474                vals.push((g1_a, g1_b));
475            }
476
477            let result = Self::execute(vals);
478            if result {
479                bn::arith::U256::one()
480            } else {
481                bn::arith::U256::zero()
482            }
483        };
484
485        let mut res = crate::vec![0u8; 32];
486        output
487            .to_big_endian(&mut res[0..32])
488            .map_err(|_e| consts::ERR_BIG_ENDIAN)?;
489        Ok(res)
490    }
491
492    #[cfg(not(feature = "contract"))]
493    #[allow(clippy::needless_pass_by_value)]
494    fn execute(vals: Vec<(bn::G1, bn::G2)>) -> bool {
495        bn::pairing_batch(&vals) == bn::Gt::one()
496    }
497
498    #[cfg(feature = "contract")]
499    fn execute(vals: Vec<(bn::G1, bn::G2)>) -> bool {
500        let points = vals
501            .into_iter()
502            .map(|(g1, g2)| (g1.host_fn_encode(), g2.host_fn_encode()));
503        aurora_engine_sdk::alt_bn128_pairing(points)
504    }
505}
506
507impl Precompile for Bn256Pair<Byzantium> {
508    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
509        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
510        let pair_element_len = NonZeroUsize::try_from(consts::PAIR_ELEMENT_LEN)
511            .and_then(NonZeroU64::try_from)
512            .map_err(utils::err_usize_conv)?;
513        Ok(
514            costs::BYZANTIUM_PAIR_PER_POINT * input_len / pair_element_len
515                + costs::BYZANTIUM_PAIR_BASE,
516        )
517    }
518
519    /// Takes in elements and calculates the pair.
520    ///
521    /// See: `https://eips.ethereum.org/EIPS/eip-197`
522    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000008`
523    fn run(
524        &self,
525        input: &[u8],
526        target_gas: Option<EthGas>,
527        context: &Context,
528        _is_static: bool,
529    ) -> EvmPrecompileResult {
530        let cost = Self::required_gas(input)?;
531        if let Some(target_gas) = target_gas {
532            if cost > target_gas {
533                return Err(ExitError::OutOfGas);
534            }
535        }
536
537        let output = Self::run_inner(input, context)?;
538        Ok(PrecompileOutput::without_logs(cost, output))
539    }
540}
541
542impl Precompile for Bn256Pair<Istanbul> {
543    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
544        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
545        let pair_element_len = NonZeroUsize::try_from(consts::PAIR_ELEMENT_LEN)
546            .and_then(NonZeroU64::try_from)
547            .map_err(utils::err_usize_conv)?;
548        Ok(
549            costs::ISTANBUL_PAIR_PER_POINT * input_len / pair_element_len
550                + costs::ISTANBUL_PAIR_BASE,
551        )
552    }
553
554    /// Takes in elements and calculates the pair.
555    ///
556    /// See: `https://eips.ethereum.org/EIPS/eip-197`
557    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000008`
558    fn run(
559        &self,
560        input: &[u8],
561        target_gas: Option<EthGas>,
562        context: &Context,
563        _is_static: bool,
564    ) -> EvmPrecompileResult {
565        let cost = Self::required_gas(input)?;
566        if let Some(target_gas) = target_gas {
567            if cost > target_gas {
568                return Err(ExitError::OutOfGas);
569            }
570        }
571
572        let output = Self::run_inner(input, context)?;
573        Ok(PrecompileOutput::without_logs(cost, output))
574    }
575}
576
577#[cfg(test)]
578mod tests {
579    use crate::utils::new_context;
580
581    use super::*;
582
583    #[test]
584    fn test_alt_bn128_add() {
585        let input = hex::decode(
586            "\
587             18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\
588             063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\
589             07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\
590             06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7",
591        )
592        .unwrap();
593        let expected = hex::decode(
594            "\
595            2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\
596            301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915",
597        )
598        .unwrap();
599
600        let res = Bn256Add::<Byzantium>::new()
601            .run(&input, Some(EthGas::new(500)), &new_context(), false)
602            .unwrap()
603            .output;
604        assert_eq!(res, expected);
605
606        // zero sum test
607        let input = hex::decode(
608            "\
609            0000000000000000000000000000000000000000000000000000000000000000\
610            0000000000000000000000000000000000000000000000000000000000000000\
611            0000000000000000000000000000000000000000000000000000000000000000\
612            0000000000000000000000000000000000000000000000000000000000000000",
613        )
614        .unwrap();
615        let expected = hex::decode(
616            "\
617            0000000000000000000000000000000000000000000000000000000000000000\
618            0000000000000000000000000000000000000000000000000000000000000000",
619        )
620        .unwrap();
621
622        let res = Bn256Add::<Byzantium>::new()
623            .run(&input, Some(EthGas::new(500)), &new_context(), false)
624            .unwrap()
625            .output;
626        assert_eq!(res, expected);
627
628        // out of gas test
629        let input = hex::decode(
630            "\
631            0000000000000000000000000000000000000000000000000000000000000000\
632            0000000000000000000000000000000000000000000000000000000000000000\
633            0000000000000000000000000000000000000000000000000000000000000000\
634            0000000000000000000000000000000000000000000000000000000000000000",
635        )
636        .unwrap();
637        let res =
638            Bn256Add::<Byzantium>::new().run(&input, Some(EthGas::new(499)), &new_context(), false);
639        assert!(matches!(res, Err(ExitError::OutOfGas)));
640
641        // no input test
642        let input = [0u8; 0];
643        let expected = hex::decode(
644            "\
645            0000000000000000000000000000000000000000000000000000000000000000\
646            0000000000000000000000000000000000000000000000000000000000000000",
647        )
648        .unwrap();
649
650        let res = Bn256Add::<Byzantium>::new()
651            .run(&input, Some(EthGas::new(500)), &new_context(), false)
652            .unwrap()
653            .output;
654        assert_eq!(res, expected);
655
656        // point not on curve fail
657        let input = hex::decode(
658            "\
659            1111111111111111111111111111111111111111111111111111111111111111\
660            1111111111111111111111111111111111111111111111111111111111111111\
661            1111111111111111111111111111111111111111111111111111111111111111\
662            1111111111111111111111111111111111111111111111111111111111111111",
663        )
664        .unwrap();
665
666        let res =
667            Bn256Add::<Byzantium>::new().run(&input, Some(EthGas::new(500)), &new_context(), false);
668        assert!(matches!(
669            res,
670            Err(ExitError::Other(Borrowed("ERR_BN128_INVALID_POINT")))
671        ));
672    }
673
674    #[test]
675    fn test_alt_bn128_mul() {
676        let input = hex::decode(
677            "\
678            2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\
679            21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\
680            00000000000000000000000000000000000000000000000011138ce750fa15c2",
681        )
682        .unwrap();
683        let expected = hex::decode(
684            "\
685            070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\
686            031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc",
687        )
688        .unwrap();
689
690        let res = Bn256Mul::<Byzantium>::new()
691            .run(&input, Some(EthGas::new(40_000)), &new_context(), false)
692            .unwrap()
693            .output;
694        assert_eq!(res, expected);
695
696        // out of gas test
697        let input = hex::decode(
698            "\
699            0000000000000000000000000000000000000000000000000000000000000000\
700            0000000000000000000000000000000000000000000000000000000000000000\
701            0200000000000000000000000000000000000000000000000000000000000000",
702        )
703        .unwrap();
704        let res = Bn256Mul::<Byzantium>::new().run(
705            &input,
706            Some(EthGas::new(39_999)),
707            &new_context(),
708            false,
709        );
710        assert!(matches!(res, Err(ExitError::OutOfGas)));
711
712        // zero multiplication test
713        let input = hex::decode(
714            "\
715            0000000000000000000000000000000000000000000000000000000000000000\
716            0000000000000000000000000000000000000000000000000000000000000000\
717            0200000000000000000000000000000000000000000000000000000000000000",
718        )
719        .unwrap();
720        let expected = hex::decode(
721            "\
722            0000000000000000000000000000000000000000000000000000000000000000\
723            0000000000000000000000000000000000000000000000000000000000000000",
724        )
725        .unwrap();
726
727        let res = Bn256Mul::<Byzantium>::new()
728            .run(&input, Some(EthGas::new(40_000)), &new_context(), false)
729            .unwrap()
730            .output;
731        assert_eq!(res, expected);
732
733        // no input test
734        let input = [0u8; 0];
735        let expected = hex::decode(
736            "\
737            0000000000000000000000000000000000000000000000000000000000000000\
738            0000000000000000000000000000000000000000000000000000000000000000",
739        )
740        .unwrap();
741
742        let res = Bn256Mul::<Byzantium>::new()
743            .run(&input, Some(EthGas::new(40_000)), &new_context(), false)
744            .unwrap()
745            .output;
746        assert_eq!(res, expected);
747
748        // point not on curve fail
749        let input = hex::decode(
750            "\
751            1111111111111111111111111111111111111111111111111111111111111111\
752            1111111111111111111111111111111111111111111111111111111111111111\
753            0f00000000000000000000000000000000000000000000000000000000000000",
754        )
755        .unwrap();
756
757        let res = Bn256Mul::<Byzantium>::new().run(
758            &input,
759            Some(EthGas::new(40_000)),
760            &new_context(),
761            false,
762        );
763        assert!(matches!(
764            res,
765            Err(ExitError::Other(Borrowed("ERR_BN128_INVALID_POINT")))
766        ));
767    }
768
769    #[test]
770    #[allow(clippy::too_many_lines)]
771    fn test_alt_bn128_pair() {
772        let input = hex::decode(
773            "\
774            1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
775            3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
776            209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
777            04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
778            2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
779            120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
780            111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
781            2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
782            198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
783            1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
784            090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
785            12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
786        )
787        .unwrap();
788        let expected =
789            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
790                .unwrap();
791
792        let res = Bn256Pair::<Byzantium>::new()
793            .run(&input, Some(EthGas::new(260_000)), &new_context(), false)
794            .unwrap()
795            .output;
796        assert_eq!(res, expected);
797
798        // out of gas test
799        let input = hex::decode(
800            "\
801            1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
802            3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
803            209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
804            04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
805            2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
806            120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
807            111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
808            2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
809            198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
810            1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
811            090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
812            12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
813        )
814        .unwrap();
815        let res = Bn256Pair::<Byzantium>::new().run(
816            &input,
817            Some(EthGas::new(259_999)),
818            &new_context(),
819            false,
820        );
821        assert!(matches!(res, Err(ExitError::OutOfGas)));
822
823        // no input test
824        let input = [0u8; 0];
825        let expected =
826            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
827                .unwrap();
828
829        let res = Bn256Pair::<Byzantium>::new()
830            .run(&input, Some(EthGas::new(260_000)), &new_context(), false)
831            .unwrap()
832            .output;
833        assert_eq!(res, expected);
834
835        // point not on curve fail
836        let input = hex::decode(
837            "\
838            1111111111111111111111111111111111111111111111111111111111111111\
839            1111111111111111111111111111111111111111111111111111111111111111\
840            1111111111111111111111111111111111111111111111111111111111111111\
841            1111111111111111111111111111111111111111111111111111111111111111\
842            1111111111111111111111111111111111111111111111111111111111111111\
843            1111111111111111111111111111111111111111111111111111111111111111",
844        )
845        .unwrap();
846
847        let res = Bn256Pair::<Byzantium>::new().run(
848            &input,
849            Some(EthGas::new(260_000)),
850            &new_context(),
851            false,
852        );
853        assert!(matches!(
854            res,
855            Err(ExitError::Other(Borrowed("ERR_BN128_INVALID_A")))
856        ));
857
858        // invalid input length
859        let input = hex::decode(
860            "\
861            1111111111111111111111111111111111111111111111111111111111111111\
862            1111111111111111111111111111111111111111111111111111111111111111\
863            111111111111111111111111111111\
864        ",
865        )
866        .unwrap();
867
868        let res = Bn256Pair::<Byzantium>::new().run(
869            &input,
870            Some(EthGas::new(260_000)),
871            &new_context(),
872            false,
873        );
874        assert!(matches!(
875            res,
876            Err(ExitError::Other(Borrowed("ERR_BN128_INVALID_LEN",)))
877        ));
878
879        // on curve
880        let input = hex::decode(
881            "\
882            0000000000000000000000000000000000000000000000000000000000000000\
883            0000000000000000000000000000000000000000000000000000000000000000\
884            0000000000000000000000000000000000000000000000000000000000000000\
885            0000000000000000000000000000000000000000000000000000000000000000\
886            0000000000000000000000000000000000000000000000000000000000000000\
887            0000000000000000000000000000000000000000000000000000000000000000",
888        )
889        .unwrap();
890        let expected =
891            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
892                .unwrap();
893
894        let res = Bn256Pair::<Byzantium>::new()
895            .run(&input, Some(EthGas::new(260_000)), &new_context(), false)
896            .unwrap()
897            .output;
898        assert_eq!(res, expected);
899    }
900}