aurora_engine_precompiles/
hash.rs

1#[cfg(feature = "contract")]
2use crate::prelude::sdk;
3use crate::prelude::types::{make_address, Address, EthGas};
4use crate::prelude::vec;
5use crate::{utils, EvmPrecompileResult, Precompile, PrecompileOutput};
6use aurora_evm::{Context, ExitError};
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    // TODO: should be enable from `RUst 1.84`
35    // #[allow(clippy::manual_div_ceil)]
36    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
37        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
38        Ok(
39            (input_len + consts::SHA256_WORD_LEN - 1) / consts::SHA256_WORD_LEN
40                * costs::SHA256_PER_WORD
41                + costs::SHA256_BASE,
42        )
43    }
44
45    /// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
46    /// See: `https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions`
47    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000002`
48    #[cfg(not(feature = "contract"))]
49    fn run(
50        &self,
51        input: &[u8],
52        target_gas: Option<EthGas>,
53        _context: &Context,
54        _is_static: bool,
55    ) -> EvmPrecompileResult {
56        use sha2::Digest;
57
58        let cost = Self::required_gas(input)?;
59        if let Some(target_gas) = target_gas {
60            if cost > target_gas {
61                return Err(ExitError::OutOfGas);
62            }
63        }
64
65        let output = sha2::Sha256::digest(input).to_vec();
66        Ok(PrecompileOutput::without_logs(cost, output))
67    }
68
69    /// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
70    /// See: `https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions`
71    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000002`
72    #[cfg(feature = "contract")]
73    fn run(
74        &self,
75        input: &[u8],
76        target_gas: Option<EthGas>,
77        _context: &Context,
78        _is_static: bool,
79    ) -> EvmPrecompileResult {
80        let cost = Self::required_gas(input)?;
81        if let Some(target_gas) = target_gas {
82            if cost > target_gas {
83                return Err(ExitError::OutOfGas);
84            }
85        }
86
87        let output = sdk::sha256(input).as_bytes().to_vec();
88        Ok(PrecompileOutput::without_logs(cost, output))
89    }
90}
91
92/// RIPEMD160 precompile.
93pub struct RIPEMD160;
94
95impl RIPEMD160 {
96    pub const ADDRESS: Address = make_address(0, 3);
97
98    #[cfg(not(feature = "contract"))]
99    fn internal_impl(input: &[u8]) -> [u8; 20] {
100        use ripemd::{Digest, Ripemd160};
101
102        let hash = Ripemd160::digest(input);
103        let mut output = [0u8; 20];
104        output.copy_from_slice(&hash);
105        output
106    }
107}
108
109impl Precompile for RIPEMD160 {
110    // TODO: should be enable from `RUst 1.84`
111    // #[allow(clippy::manual_div_ceil)]
112    fn required_gas(input: &[u8]) -> Result<EthGas, ExitError> {
113        let input_len = u64::try_from(input.len()).map_err(utils::err_usize_conv)?;
114        Ok(
115            (input_len + consts::RIPEMD_WORD_LEN - 1) / consts::RIPEMD_WORD_LEN
116                * costs::RIPEMD160_PER_WORD
117                + costs::RIPEMD160_BASE,
118        )
119    }
120
121    /// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
122    /// See: `https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions`
123    /// See: `https://etherscan.io/address/0000000000000000000000000000000000000003`
124    fn run(
125        &self,
126        input: &[u8],
127        target_gas: Option<EthGas>,
128        _context: &Context,
129        _is_static: bool,
130    ) -> EvmPrecompileResult {
131        let cost = Self::required_gas(input)?;
132        if let Some(target_gas) = target_gas {
133            if cost > target_gas {
134                return Err(ExitError::OutOfGas);
135            }
136        }
137
138        #[cfg(not(feature = "contract"))]
139        let hash = Self::internal_impl(input);
140        #[cfg(feature = "contract")]
141        let hash = sdk::ripemd160(input);
142        // The result needs to be padded with leading zeros because it is only 20 bytes, but
143        // the evm works with 32-byte words.
144        let mut output = vec![0u8; 32];
145        output[12..].copy_from_slice(&hash);
146        Ok(PrecompileOutput::without_logs(cost, output))
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use crate::utils::new_context;
153
154    use super::*;
155
156    #[test]
157    fn test_sha256() {
158        let input = b"";
159        let expected =
160            hex::decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
161                .unwrap();
162
163        let res = SHA256
164            .run(input, Some(EthGas::new(60)), &new_context(), false)
165            .unwrap()
166            .output;
167        assert_eq!(res, expected);
168    }
169
170    #[test]
171    fn test_ripemd160() {
172        let input = b"";
173        let expected =
174            hex::decode("0000000000000000000000009c1185a5c5e9fc54612808977ee8f548b2258d31")
175                .unwrap();
176
177        let res = RIPEMD160
178            .run(input, Some(EthGas::new(600)), &new_context(), false)
179            .unwrap()
180            .output;
181        assert_eq!(res, expected);
182    }
183}