aurora_engine_precompiles/
hash.rs

1use crate::prelude::sdk;
2use crate::prelude::types::{make_address, Address, EthGas};
3use crate::prelude::vec;
4use crate::{utils, EvmPrecompileResult, Precompile, PrecompileOutput};
5use aurora_evm::{Context, ExitError};
6
7mod costs {
8    use crate::prelude::types::EthGas;
9
10    pub(super) const SHA256_BASE: EthGas = EthGas::new(60);
11
12    pub(super) const SHA256_PER_WORD: EthGas = EthGas::new(12);
13
14    pub(super) const RIPEMD160_BASE: EthGas = EthGas::new(600);
15
16    pub(super) const RIPEMD160_PER_WORD: EthGas = EthGas::new(120);
17}
18
19mod consts {
20    pub(super) const SHA256_WORD_LEN: u64 = 32;
21
22    pub(super) const RIPEMD_WORD_LEN: u64 = 32;
23}
24
25/// SHA256 precompile.
26pub struct SHA256;
27
28impl SHA256 {
29    pub const ADDRESS: Address = make_address(0, 2);
30}
31
32impl Precompile for SHA256 {
33    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
34        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
35        Ok(
36            input_len.div_ceil(consts::SHA256_WORD_LEN) * costs::SHA256_PER_WORD
37                + costs::SHA256_BASE,
38        )
39    }
40
41    /// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
42    /// See: `https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions`
43    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000002`
44    fn run(
45        &self,
46        input: &[u8],
47        target_gas: Option<EthGas>,
48        _context: &Context,
49        _is_static: bool,
50    ) -> EvmPrecompileResult {
51        let cost = Self::required_gas(input)?;
52        if let Some(target_gas) = target_gas {
53            if cost > target_gas {
54                return Err(ExitError::OutOfGas);
55            }
56        }
57
58        let output = sdk::sha256(input).as_bytes().to_vec();
59        Ok(PrecompileOutput::without_logs(cost, output))
60    }
61}
62
63/// RIPEMD160 precompile.
64pub struct RIPEMD160;
65
66impl RIPEMD160 {
67    pub const ADDRESS: Address = make_address(0, 3);
68}
69
70impl Precompile for RIPEMD160 {
71    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
72        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
73        Ok(
74            input_len.div_ceil(consts::RIPEMD_WORD_LEN) * costs::RIPEMD160_PER_WORD
75                + costs::RIPEMD160_BASE,
76        )
77    }
78
79    /// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
80    /// See: `https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions`
81    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000003`
82    fn run(
83        &self,
84        input: &[u8],
85        target_gas: Option<EthGas>,
86        _context: &Context,
87        _is_static: bool,
88    ) -> EvmPrecompileResult {
89        let cost = Self::required_gas(input)?;
90        if let Some(target_gas) = target_gas {
91            if cost > target_gas {
92                return Err(ExitError::OutOfGas);
93            }
94        }
95
96        let hash = sdk::ripemd160(input);
97        // The result needs to be padded with leading zeros because it is only 20 bytes, but
98        // the evm works with 32-byte words.
99        let mut output = vec![0u8; 32];
100        output[12..].copy_from_slice(&hash);
101        Ok(PrecompileOutput::without_logs(cost, output))
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use crate::utils::new_context;
108
109    use super::*;
110
111    #[test]
112    fn test_sha256() {
113        let input = b"";
114        let expected =
115            hex::decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
116                .unwrap();
117
118        let res = SHA256
119            .run(input, Some(EthGas::new(60)), &new_context(), false)
120            .unwrap()
121            .output;
122        assert_eq!(res, expected);
123    }
124
125    #[test]
126    fn test_ripemd160() {
127        let input = b"";
128        let expected =
129            hex::decode("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")
130                .unwrap();
131
132        let res = RIPEMD160
133            .run(input, Some(EthGas::new(600)), &new_context(), false)
134            .unwrap()
135            .output;
136        assert_eq!(res, expected);
137    }
138}