aurora_engine_precompiles/
alt_bn256.rs

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