gemachain_program/
keccak.rs

1use crate::sanitize::Sanitize;
2use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
3use sha3::{Digest, Keccak256};
4use std::{convert::TryFrom, fmt, mem, str::FromStr};
5use thiserror::Error;
6
7pub const HASH_BYTES: usize = 32;
8/// Maximum string length of a base58 encoded hash
9const MAX_BASE58_LEN: usize = 44;
10#[derive(
11    Serialize,
12    Deserialize,
13    BorshSerialize,
14    BorshDeserialize,
15    BorshSchema,
16    Clone,
17    Copy,
18    Default,
19    Eq,
20    PartialEq,
21    Ord,
22    PartialOrd,
23    Hash,
24    AbiExample,
25)]
26#[repr(transparent)]
27pub struct Hash(pub [u8; HASH_BYTES]);
28
29#[derive(Clone, Default)]
30pub struct Hasher {
31    hasher: Keccak256,
32}
33
34impl Hasher {
35    pub fn hash(&mut self, val: &[u8]) {
36        self.hasher.update(val);
37    }
38    pub fn hashv(&mut self, vals: &[&[u8]]) {
39        for val in vals {
40            self.hash(val);
41        }
42    }
43    pub fn result(self) -> Hash {
44        // At the time of this writing, the sha3 library is stuck on an old version
45        // of generic_array (0.9.0). Decouple ourselves with a clone to our version.
46        Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.finalize().as_slice()).unwrap())
47    }
48}
49
50impl Sanitize for Hash {}
51
52impl AsRef<[u8]> for Hash {
53    fn as_ref(&self) -> &[u8] {
54        &self.0[..]
55    }
56}
57
58impl fmt::Debug for Hash {
59    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
60        write!(f, "{}", bs58::encode(self.0).into_string())
61    }
62}
63
64impl fmt::Display for Hash {
65    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66        write!(f, "{}", bs58::encode(self.0).into_string())
67    }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Error)]
71pub enum ParseHashError {
72    #[error("string decoded to wrong size for hash")]
73    WrongSize,
74    #[error("failed to decoded string to hash")]
75    Invalid,
76}
77
78impl FromStr for Hash {
79    type Err = ParseHashError;
80
81    fn from_str(s: &str) -> Result<Self, Self::Err> {
82        if s.len() > MAX_BASE58_LEN {
83            return Err(ParseHashError::WrongSize);
84        }
85        let bytes = bs58::decode(s)
86            .into_vec()
87            .map_err(|_| ParseHashError::Invalid)?;
88        if bytes.len() != mem::size_of::<Hash>() {
89            Err(ParseHashError::WrongSize)
90        } else {
91            Ok(Hash::new(&bytes))
92        }
93    }
94}
95
96impl Hash {
97    pub fn new(hash_slice: &[u8]) -> Self {
98        Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap())
99    }
100
101    pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self {
102        Self(hash_array)
103    }
104
105    /// unique Hash for tests and benchmarks.
106    pub fn new_unique() -> Self {
107        use std::sync::atomic::{AtomicU64, Ordering};
108        static I: AtomicU64 = AtomicU64::new(1);
109
110        let mut b = [0u8; HASH_BYTES];
111        let i = I.fetch_add(1, Ordering::Relaxed);
112        b[0..8].copy_from_slice(&i.to_le_bytes());
113        Self::new(&b)
114    }
115
116    pub fn to_bytes(self) -> [u8; HASH_BYTES] {
117        self.0
118    }
119}
120
121/// Return a Keccak256 hash for the given data.
122pub fn hashv(vals: &[&[u8]]) -> Hash {
123    // Perform the calculation inline, calling this from within a program is
124    // not supported
125    #[cfg(not(target_arch = "bpf"))]
126    {
127        let mut hasher = Hasher::default();
128        hasher.hashv(vals);
129        hasher.result()
130    }
131    // Call via a system call to perform the calculation
132    #[cfg(target_arch = "bpf")]
133    {
134        extern "C" {
135            fn gema_keccak256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
136        }
137        let mut hash_result = [0; HASH_BYTES];
138        unsafe {
139            gema_keccak256(
140                vals as *const _ as *const u8,
141                vals.len() as u64,
142                &mut hash_result as *mut _ as *mut u8,
143            );
144        }
145        Hash::new_from_array(hash_result)
146    }
147}
148
149/// Return a Keccak256 hash for the given data.
150pub fn hash(val: &[u8]) -> Hash {
151    hashv(&[val])
152}
153
154/// Return the hash of the given hash extended with the given value.
155pub fn extend_and_hash(id: &Hash, val: &[u8]) -> Hash {
156    let mut hash_data = id.as_ref().to_vec();
157    hash_data.extend_from_slice(val);
158    hash(&hash_data)
159}