aurora_engine_precompiles/
identity.rs

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