aurora_engine_precompiles/
identity.rs

1use aurora_evm::{Context, ExitError};
2
3use crate::prelude::types::{Address, EthGas, make_address};
4use crate::{EvmPrecompileResult, Precompile, PrecompileOutput, utils};
5
6/// Identity precompile costs.
7mod costs {
8    use aurora_engine_types::types::EthGas;
9
10    /// The base cost of the operation.
11    pub(super) const IDENTITY_BASE: EthGas = EthGas::new(15);
12
13    /// The cost per word.
14    pub(super) const IDENTITY_PER_WORD: EthGas = EthGas::new(3);
15}
16
17mod consts {
18    /// Length of the identity word.
19    pub(super) const IDENTITY_WORD_LEN: u64 = 32;
20}
21
22pub struct Identity;
23
24impl Identity {
25    pub const ADDRESS: Address = make_address(0, 4);
26}
27
28impl Precompile for Identity {
29    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
30        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
31        Ok(
32            input_len.div_ceil(consts::IDENTITY_WORD_LEN) * costs::IDENTITY_PER_WORD
33                + costs::IDENTITY_BASE,
34        )
35    }
36
37    /// Takes the input bytes, copies them, and returns it as the output.
38    ///
39    /// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
40    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000004`
41    fn run(
42        &self,
43        input: &[u8],
44        target_gas: Option<EthGas>,
45        _context: &Context,
46        _is_static: bool,
47    ) -> EvmPrecompileResult {
48        let cost = Self::required_gas(input)?;
49        if let Some(target_gas) = target_gas {
50            if cost > target_gas {
51                return Err(ExitError::OutOfGas);
52            }
53        }
54
55        Ok(PrecompileOutput::without_logs(cost, input.to_vec()))
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use aurora_evm::ExitError;
62
63    use crate::utils::new_context;
64
65    use super::*;
66
67    #[test]
68    fn test_identity() {
69        let input = [0u8, 1, 2, 3];
70
71        let expected = input[0..2].to_vec();
72        let res = Identity
73            .run(&input[0..2], Some(EthGas::new(18)), &new_context(), false)
74            .unwrap()
75            .output;
76        assert_eq!(res, expected);
77
78        let expected = input.to_vec();
79        let res = Identity
80            .run(&input, Some(EthGas::new(18)), &new_context(), false)
81            .unwrap()
82            .output;
83        assert_eq!(res, expected);
84
85        // gas fail
86        let res = Identity.run(&input[0..2], Some(EthGas::new(17)), &new_context(), false);
87
88        assert!(matches!(res, Err(ExitError::OutOfGas)));
89
90        // larger input
91        let input = [
92            0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
93            24, 25, 26, 27, 28, 29, 30, 31, 32,
94        ];
95        let res = Identity
96            .run(&input, Some(EthGas::new(21)), &new_context(), false)
97            .unwrap()
98            .output;
99        assert_eq!(res, input.to_vec());
100    }
101}