aurora_engine_precompiles/
hash.rs

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