aurora_engine_precompiles/
alt_bn256.rs

1use aurora_engine_sdk::bn128::PAIR_ELEMENT_LEN;
2use aurora_evm::{Context, ExitError};
3use core::num::{NonZeroU64, NonZeroUsize};
4
5use crate::prelude::types::{Address, EthGas, make_address};
6use crate::prelude::{PhantomData, Vec};
7use crate::utils;
8use crate::{Byzantium, EvmPrecompileResult, HardFork, Istanbul, Precompile, PrecompileOutput};
9
10/// bn128 costs.
11mod costs {
12    use crate::prelude::types::EthGas;
13
14    /// Cost of the Byzantium `alt_bn128_add` operation.
15    pub(super) const BYZANTIUM_ADD: EthGas = EthGas::new(500);
16
17    /// Cost of the Byzantium `alt_bn128_mul` operation.
18    pub(super) const BYZANTIUM_MUL: EthGas = EthGas::new(40_000);
19
20    /// Cost of the `alt_bn128_pair` per point.
21    pub(super) const BYZANTIUM_PAIR_PER_POINT: EthGas = EthGas::new(80_000);
22
23    /// Cost of the `alt_bn128_pair` operation.
24    pub(super) const BYZANTIUM_PAIR_BASE: EthGas = EthGas::new(100_000);
25
26    /// Cost of the Istanbul `alt_bn128_add` operation.
27    pub(super) const ISTANBUL_ADD: EthGas = EthGas::new(150);
28
29    /// Cost of the Istanbul `alt_bn128_mul` operation.
30    pub(super) const ISTANBUL_MUL: EthGas = EthGas::new(6_000);
31
32    /// Cost of the Istanbul `alt_bn128_pair` per point.
33    pub(super) const ISTANBUL_PAIR_PER_POINT: EthGas = EthGas::new(34_000);
34
35    /// Cost of the Istanbul `alt_bn128_pair` operation.
36    pub(super) const ISTANBUL_PAIR_BASE: EthGas = EthGas::new(45_000);
37}
38
39#[derive(Default)]
40pub struct Bn256Add<HF: HardFork>(PhantomData<HF>);
41
42impl<HF: HardFork> Bn256Add<HF> {
43    pub const ADDRESS: Address = make_address(0, 6);
44
45    #[must_use]
46    pub const fn new() -> Self {
47        Self(PhantomData)
48    }
49}
50
51impl<HF: HardFork> Bn256Add<HF> {
52    fn run_inner(input: &[u8], _context: &Context) -> Result<Vec<u8>, ExitError> {
53        let output = aurora_engine_sdk::bn128::alt_bn128_g1_sum(input)
54            .map_err(|err| ExitError::Other(err.into()))?;
55
56        Ok(output.to_vec())
57    }
58}
59
60impl Precompile for Bn256Add<Byzantium> {
61    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
62        Ok(costs::BYZANTIUM_ADD)
63    }
64
65    /// Takes in two points on the elliptic curve `alt_bn128` and calculates the sum
66    /// of them.
67    ///
68    /// See: `https://eips.ethereum.org/EIPS/eip-196`
69    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000006`
70    fn run(
71        &self,
72        input: &[u8],
73        target_gas: Option<EthGas>,
74        context: &Context,
75        _is_static: bool,
76    ) -> EvmPrecompileResult {
77        let cost = Self::required_gas(input)?;
78        if let Some(target_gas) = target_gas {
79            if cost > target_gas {
80                return Err(ExitError::OutOfGas);
81            }
82        }
83
84        let output = Self::run_inner(input, context)?;
85        Ok(PrecompileOutput::without_logs(cost, output))
86    }
87}
88
89impl Precompile for Bn256Add<Istanbul> {
90    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
91        Ok(costs::ISTANBUL_ADD)
92    }
93
94    /// Takes in two points on the elliptic curve `alt_bn128` and calculates the sum
95    /// of them.
96    ///
97    /// See: `https://eips.ethereum.org/EIPS/eip-196`
98    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000006`
99    fn run(
100        &self,
101        input: &[u8],
102        target_gas: Option<EthGas>,
103        context: &Context,
104        _is_static: bool,
105    ) -> EvmPrecompileResult {
106        let cost = Self::required_gas(input)?;
107        if let Some(target_gas) = target_gas {
108            if cost > target_gas {
109                return Err(ExitError::OutOfGas);
110            }
111        }
112        let output = Self::run_inner(input, context)?;
113        Ok(PrecompileOutput::without_logs(cost, output))
114    }
115}
116
117#[derive(Default)]
118pub struct Bn256Mul<HF: HardFork>(PhantomData<HF>);
119
120impl<HF: HardFork> Bn256Mul<HF> {
121    pub const ADDRESS: Address = make_address(0, 7);
122
123    #[must_use]
124    pub const fn new() -> Self {
125        Self(PhantomData)
126    }
127}
128
129impl<HF: HardFork> Bn256Mul<HF> {
130    fn run_inner(input: &[u8], _context: &Context) -> Result<Vec<u8>, ExitError> {
131        let output = aurora_engine_sdk::bn128::alt_bn128_g1_scalar_multiple(input)
132            .map_err(|err| ExitError::Other(err.into()))?;
133
134        Ok(output.to_vec())
135    }
136}
137
138impl Precompile for Bn256Mul<Byzantium> {
139    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
140        Ok(costs::BYZANTIUM_MUL)
141    }
142
143    /// Takes in two points on the elliptic curve `alt_bn128` and multiples them.
144    ///
145    /// See: `https://eips.ethereum.org/EIPS/eip-196`
146    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000007`
147    fn run(
148        &self,
149        input: &[u8],
150        target_gas: Option<EthGas>,
151        context: &Context,
152        _is_static: bool,
153    ) -> EvmPrecompileResult {
154        let cost = Self::required_gas(input)?;
155        if let Some(target_gas) = target_gas {
156            if cost > target_gas {
157                return Err(ExitError::OutOfGas);
158            }
159        }
160
161        let output = Self::run_inner(input, context)?;
162        Ok(PrecompileOutput::without_logs(cost, output))
163    }
164}
165
166impl Precompile for Bn256Mul<Istanbul> {
167    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
168        Ok(costs::ISTANBUL_MUL)
169    }
170
171    /// Takes in two points on the elliptic curve `alt_bn128` and multiples them.
172    ///
173    /// See: `https://eips.ethereum.org/EIPS/eip-196`
174    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000007`
175    fn run(
176        &self,
177        input: &[u8],
178        target_gas: Option<EthGas>,
179        context: &Context,
180        _is_static: bool,
181    ) -> EvmPrecompileResult {
182        let cost = Self::required_gas(input)?;
183        if let Some(target_gas) = target_gas {
184            if cost > target_gas {
185                return Err(ExitError::OutOfGas);
186            }
187        }
188
189        let output = Self::run_inner(input, context)?;
190        Ok(PrecompileOutput::without_logs(cost, output))
191    }
192}
193
194#[derive(Default)]
195pub struct Bn256Pair<HF: HardFork>(PhantomData<HF>);
196
197impl<HF: HardFork> Bn256Pair<HF> {
198    pub const ADDRESS: Address = make_address(0, 8);
199
200    #[must_use]
201    pub const fn new() -> Self {
202        Self(PhantomData)
203    }
204}
205
206impl<HF: HardFork> Bn256Pair<HF> {
207    fn run_inner(input: &[u8], _context: &Context) -> Result<Vec<u8>, ExitError> {
208        // Default result is 0 (false)
209        let mut pairing_result = crate::vec![0u8; 32];
210        if aurora_engine_sdk::bn128::alt_bn128_pairing(input)
211            .map_err(|err| ExitError::Other(err.into()))?
212        {
213            // If valid, set output to 1 (true)
214            pairing_result[31] = 1;
215        }
216
217        Ok(pairing_result)
218    }
219}
220
221impl Precompile for Bn256Pair<Byzantium> {
222    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
223        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
224        let pair_element_len = NonZeroUsize::try_from(PAIR_ELEMENT_LEN)
225            .and_then(NonZeroU64::try_from)
226            .map_err(utils::err_usize_conv)?;
227        Ok(
228            costs::BYZANTIUM_PAIR_PER_POINT * input_len / pair_element_len
229                + costs::BYZANTIUM_PAIR_BASE,
230        )
231    }
232
233    /// Takes in elements and calculates the pair.
234    ///
235    /// See: `https://eips.ethereum.org/EIPS/eip-197`
236    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000008`
237    fn run(
238        &self,
239        input: &[u8],
240        target_gas: Option<EthGas>,
241        context: &Context,
242        _is_static: bool,
243    ) -> EvmPrecompileResult {
244        let cost = Self::required_gas(input)?;
245        if let Some(target_gas) = target_gas {
246            if cost > target_gas {
247                return Err(ExitError::OutOfGas);
248            }
249        }
250
251        let output = Self::run_inner(input, context)?;
252        Ok(PrecompileOutput::without_logs(cost, output))
253    }
254}
255
256impl Precompile for Bn256Pair<Istanbul> {
257    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
258        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
259        let pair_element_len = NonZeroUsize::try_from(PAIR_ELEMENT_LEN)
260            .and_then(NonZeroU64::try_from)
261            .map_err(utils::err_usize_conv)?;
262        Ok(
263            costs::ISTANBUL_PAIR_PER_POINT * input_len / pair_element_len
264                + costs::ISTANBUL_PAIR_BASE,
265        )
266    }
267
268    /// Takes in elements and calculates the pair.
269    ///
270    /// See: `https://eips.ethereum.org/EIPS/eip-197`
271    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000008`
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
286        let output = Self::run_inner(input, context)?;
287        Ok(PrecompileOutput::without_logs(cost, output))
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use crate::utils::new_context;
294    use std::borrow::Cow::Borrowed;
295
296    use super::*;
297
298    #[test]
299    fn test_alt_bn128_add() {
300        let input = hex::decode(
301            "\
302             18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\
303             063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\
304             07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\
305             06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7",
306        )
307        .unwrap();
308        let expected = hex::decode(
309            "\
310            2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\
311            301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915",
312        )
313        .unwrap();
314
315        let res = Bn256Add::<Byzantium>::new()
316            .run(&input, Some(EthGas::new(500)), &new_context(), false)
317            .unwrap()
318            .output;
319        assert_eq!(res, expected);
320
321        // zero sum test
322        let input = hex::decode(
323            "\
324            0000000000000000000000000000000000000000000000000000000000000000\
325            0000000000000000000000000000000000000000000000000000000000000000\
326            0000000000000000000000000000000000000000000000000000000000000000\
327            0000000000000000000000000000000000000000000000000000000000000000",
328        )
329        .unwrap();
330        let expected = hex::decode(
331            "\
332            0000000000000000000000000000000000000000000000000000000000000000\
333            0000000000000000000000000000000000000000000000000000000000000000",
334        )
335        .unwrap();
336
337        let res = Bn256Add::<Byzantium>::new()
338            .run(&input, Some(EthGas::new(500)), &new_context(), false)
339            .unwrap()
340            .output;
341        assert_eq!(res, expected);
342
343        // out of gas test
344        let input = hex::decode(
345            "\
346            0000000000000000000000000000000000000000000000000000000000000000\
347            0000000000000000000000000000000000000000000000000000000000000000\
348            0000000000000000000000000000000000000000000000000000000000000000\
349            0000000000000000000000000000000000000000000000000000000000000000",
350        )
351        .unwrap();
352        let res =
353            Bn256Add::<Byzantium>::new().run(&input, Some(EthGas::new(499)), &new_context(), false);
354        assert!(matches!(res, Err(ExitError::OutOfGas)));
355
356        // no input test
357        let input = [0u8; 0];
358        let expected = hex::decode(
359            "\
360            0000000000000000000000000000000000000000000000000000000000000000\
361            0000000000000000000000000000000000000000000000000000000000000000",
362        )
363        .unwrap();
364
365        let res = Bn256Add::<Byzantium>::new()
366            .run(&input, Some(EthGas::new(500)), &new_context(), false)
367            .unwrap()
368            .output;
369        assert_eq!(res, expected);
370
371        // point not on curve fail
372        let input = hex::decode(
373            "\
374            1111111111111111111111111111111111111111111111111111111111111111\
375            1111111111111111111111111111111111111111111111111111111111111111\
376            1111111111111111111111111111111111111111111111111111111111111111\
377            1111111111111111111111111111111111111111111111111111111111111111",
378        )
379        .unwrap();
380
381        let res =
382            Bn256Add::<Byzantium>::new().run(&input, Some(EthGas::new(500)), &new_context(), false);
383
384        assert!(matches!(
385            res,
386            Err(ExitError::Other(Borrowed(
387                "ERR_BN_AFFINE_G_FAILED_TO_CREATE"
388            )))
389        ));
390    }
391
392    #[test]
393    fn test_alt_bn128_mul() {
394        let input = hex::decode(
395            "\
396            2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\
397            21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\
398            00000000000000000000000000000000000000000000000011138ce750fa15c2",
399        )
400        .unwrap();
401        let expected = hex::decode(
402            "\
403            070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\
404            031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc",
405        )
406        .unwrap();
407
408        let res = Bn256Mul::<Byzantium>::new()
409            .run(&input, Some(EthGas::new(40_000)), &new_context(), false)
410            .unwrap()
411            .output;
412        assert_eq!(res, expected);
413
414        // out of gas test
415        let input = hex::decode(
416            "\
417            0000000000000000000000000000000000000000000000000000000000000000\
418            0000000000000000000000000000000000000000000000000000000000000000\
419            0200000000000000000000000000000000000000000000000000000000000000",
420        )
421        .unwrap();
422        let res = Bn256Mul::<Byzantium>::new().run(
423            &input,
424            Some(EthGas::new(39_999)),
425            &new_context(),
426            false,
427        );
428        assert!(matches!(res, Err(ExitError::OutOfGas)));
429
430        // zero multiplication test
431        let input = hex::decode(
432            "\
433            0000000000000000000000000000000000000000000000000000000000000000\
434            0000000000000000000000000000000000000000000000000000000000000000\
435            0200000000000000000000000000000000000000000000000000000000000000",
436        )
437        .unwrap();
438        let expected = hex::decode(
439            "\
440            0000000000000000000000000000000000000000000000000000000000000000\
441            0000000000000000000000000000000000000000000000000000000000000000",
442        )
443        .unwrap();
444
445        let res = Bn256Mul::<Byzantium>::new()
446            .run(&input, Some(EthGas::new(40_000)), &new_context(), false)
447            .unwrap()
448            .output;
449        assert_eq!(res, expected);
450
451        // no input test
452        let input = [0u8; 0];
453        let expected = hex::decode(
454            "\
455            0000000000000000000000000000000000000000000000000000000000000000\
456            0000000000000000000000000000000000000000000000000000000000000000",
457        )
458        .unwrap();
459
460        let res = Bn256Mul::<Byzantium>::new()
461            .run(&input, Some(EthGas::new(40_000)), &new_context(), false)
462            .unwrap()
463            .output;
464        assert_eq!(res, expected);
465
466        // point not on curve fail
467        let input = hex::decode(
468            "\
469            1111111111111111111111111111111111111111111111111111111111111111\
470            1111111111111111111111111111111111111111111111111111111111111111\
471            0f00000000000000000000000000000000000000000000000000000000000000",
472        )
473        .unwrap();
474
475        let res = Bn256Mul::<Byzantium>::new().run(
476            &input,
477            Some(EthGas::new(40_000)),
478            &new_context(),
479            false,
480        );
481        assert!(matches!(
482            res,
483            Err(ExitError::Other(Borrowed(
484                "ERR_BN_AFFINE_G_FAILED_TO_CREATE"
485            )))
486        ));
487    }
488
489    #[test]
490    #[allow(clippy::too_many_lines)]
491    fn test_alt_bn128_pair() {
492        let input = hex::decode(
493            "\
494            1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
495            3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
496            209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
497            04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
498            2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
499            120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
500            111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
501            2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
502            198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
503            1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
504            090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
505            12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
506        )
507        .unwrap();
508        let expected =
509            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
510                .unwrap();
511
512        let res = Bn256Pair::<Byzantium>::new()
513            .run(&input, Some(EthGas::new(260_000)), &new_context(), false)
514            .unwrap()
515            .output;
516        assert_eq!(res, expected);
517
518        // out of gas test
519        let input = hex::decode(
520            "\
521            1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\
522            3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\
523            209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\
524            04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\
525            2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\
526            120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\
527            111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\
528            2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\
529            198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\
530            1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\
531            090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\
532            12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa",
533        )
534        .unwrap();
535        let res = Bn256Pair::<Byzantium>::new().run(
536            &input,
537            Some(EthGas::new(259_999)),
538            &new_context(),
539            false,
540        );
541        assert!(matches!(res, Err(ExitError::OutOfGas)));
542
543        // no input test
544        let input = [0u8; 0];
545        let expected =
546            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
547                .unwrap();
548
549        let res = Bn256Pair::<Byzantium>::new()
550            .run(&input, Some(EthGas::new(260_000)), &new_context(), false)
551            .unwrap()
552            .output;
553        assert_eq!(res, expected);
554
555        // point not on curve fail
556        let input = hex::decode(
557            "\
558            1111111111111111111111111111111111111111111111111111111111111111\
559            1111111111111111111111111111111111111111111111111111111111111111\
560            1111111111111111111111111111111111111111111111111111111111111111\
561            1111111111111111111111111111111111111111111111111111111111111111\
562            1111111111111111111111111111111111111111111111111111111111111111\
563            1111111111111111111111111111111111111111111111111111111111111111",
564        )
565        .unwrap();
566
567        let res = Bn256Pair::<Byzantium>::new().run(
568            &input,
569            Some(EthGas::new(260_000)),
570            &new_context(),
571            false,
572        );
573        assert!(matches!(
574            res,
575            Err(ExitError::Other(Borrowed(
576                "ERR_BN_AFFINE_G_FAILED_TO_CREATE"
577            )))
578        ));
579
580        // invalid input length
581        let input = hex::decode(
582            "\
583            1111111111111111111111111111111111111111111111111111111111111111\
584            1111111111111111111111111111111111111111111111111111111111111111\
585            111111111111111111111111111111\
586        ",
587        )
588        .unwrap();
589
590        let res = Bn256Pair::<Byzantium>::new().run(
591            &input,
592            Some(EthGas::new(260_000)),
593            &new_context(),
594            false,
595        );
596        assert!(matches!(
597            res,
598            Err(ExitError::Other(Borrowed("ERR_BN_INVALID_PAIR_LEN",)))
599        ));
600
601        // on curve
602        let input = hex::decode(
603            "\
604            0000000000000000000000000000000000000000000000000000000000000000\
605            0000000000000000000000000000000000000000000000000000000000000000\
606            0000000000000000000000000000000000000000000000000000000000000000\
607            0000000000000000000000000000000000000000000000000000000000000000\
608            0000000000000000000000000000000000000000000000000000000000000000\
609            0000000000000000000000000000000000000000000000000000000000000000",
610        )
611        .unwrap();
612        let expected =
613            hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
614                .unwrap();
615
616        let res = Bn256Pair::<Byzantium>::new()
617            .run(&input, Some(EthGas::new(260_000)), &new_context(), false)
618            .unwrap()
619            .output;
620        assert_eq!(res, expected);
621    }
622}