ethcontract_common/
hash.rs

1//! Keccak256 hash utilities.
2
3use tiny_keccak::{Hasher, Keccak};
4
5/// Perform a Keccak256 hash of data and return its 32-byte result.
6pub fn keccak256<B>(data: B) -> [u8; 32]
7where
8    B: AsRef<[u8]>,
9{
10    let mut output = [0u8; 32];
11    let mut hasher = Keccak::v256();
12    hasher.update(data.as_ref());
13    hasher.finalize(&mut output);
14    output
15}
16
17/// A 32-bit prefix of a standard 256-bit Keccak hash.
18///
19/// This 32-bit prefix is generally used as the first 4 bytes of transaction
20/// data in order to select which Solidity method will be called.
21pub type H32 = [u8; 4];
22
23/// Calculates the function selector as per the contract ABI specification. This
24/// is definied as the first 4 bytes of the Keccak256 hash of the function
25/// signature.
26pub fn function_selector<S>(signature: S) -> H32
27where
28    S: AsRef<str>,
29{
30    let hash = keccak256(signature.as_ref());
31    let mut selector = H32::default();
32    selector.copy_from_slice(&hash[0..4]);
33    selector
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn simple_keccak_hash() {
42        // test vector retrieved from
43        // https://web3js.readthedocs.io/en/v1.2.4/web3-utils.html#sha3
44        assert_eq!(
45            &keccak256([0xea]),
46            b"\x2f\x20\x67\x74\x59\x12\x06\x77\x48\x4f\x71\x04\xc7\x6d\xeb\x68\
47              \x46\xa2\xc0\x71\xf9\xb3\x15\x2c\x10\x3b\xb1\x2c\xd5\x4d\x1a\x4a"
48        );
49    }
50
51    #[test]
52    fn simple_function_signature() {
53        // test vector retrieved from
54        // https://web3js.readthedocs.io/en/v1.2.4/web3-eth-abi.html#encodefunctionsignature
55        assert_eq!(
56            function_selector("myMethod(uint256,string)"),
57            [0x24, 0xee, 0x00, 0x97],
58        );
59    }
60
61    #[test]
62    fn revert_function_signature() {
63        assert_eq!(function_selector("Error(string)"), [0x08, 0xc3, 0x79, 0xa0]);
64    }
65}