gemachain_program/
hash.rs1use crate::sanitize::Sanitize;
4use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
5use sha2::{Digest, Sha256};
6use std::{convert::TryFrom, fmt, mem, str::FromStr};
7use thiserror::Error;
8
9pub const HASH_BYTES: usize = 32;
10const MAX_BASE58_LEN: usize = 44;
12#[derive(
13 Serialize,
14 Deserialize,
15 BorshSerialize,
16 BorshDeserialize,
17 BorshSchema,
18 Clone,
19 Copy,
20 Default,
21 Eq,
22 PartialEq,
23 Ord,
24 PartialOrd,
25 Hash,
26 AbiExample,
27)]
28#[repr(transparent)]
29pub struct Hash(pub [u8; HASH_BYTES]);
30
31#[derive(Clone, Default)]
32pub struct Hasher {
33 hasher: Sha256,
34}
35
36impl Hasher {
37 pub fn hash(&mut self, val: &[u8]) {
38 self.hasher.update(val);
39 }
40 pub fn hashv(&mut self, vals: &[&[u8]]) {
41 for val in vals {
42 self.hash(val);
43 }
44 }
45 pub fn result(self) -> Hash {
46 Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.finalize().as_slice()).unwrap())
49 }
50}
51
52impl Sanitize for Hash {}
53
54impl AsRef<[u8]> for Hash {
55 fn as_ref(&self) -> &[u8] {
56 &self.0[..]
57 }
58}
59
60impl fmt::Debug for Hash {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 write!(f, "{}", bs58::encode(self.0).into_string())
63 }
64}
65
66impl fmt::Display for Hash {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 write!(f, "{}", bs58::encode(self.0).into_string())
69 }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Error)]
73pub enum ParseHashError {
74 #[error("string decoded to wrong size for hash")]
75 WrongSize,
76 #[error("failed to decoded string to hash")]
77 Invalid,
78}
79
80impl FromStr for Hash {
81 type Err = ParseHashError;
82
83 fn from_str(s: &str) -> Result<Self, Self::Err> {
84 if s.len() > MAX_BASE58_LEN {
85 return Err(ParseHashError::WrongSize);
86 }
87 let bytes = bs58::decode(s)
88 .into_vec()
89 .map_err(|_| ParseHashError::Invalid)?;
90 if bytes.len() != mem::size_of::<Hash>() {
91 Err(ParseHashError::WrongSize)
92 } else {
93 Ok(Hash::new(&bytes))
94 }
95 }
96}
97
98impl Hash {
99 pub fn new(hash_slice: &[u8]) -> Self {
100 Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap())
101 }
102
103 pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self {
104 Self(hash_array)
105 }
106
107 pub fn new_unique() -> Self {
109 use std::sync::atomic::{AtomicU64, Ordering};
110 static I: AtomicU64 = AtomicU64::new(1);
111
112 let mut b = [0u8; HASH_BYTES];
113 let i = I.fetch_add(1, Ordering::Relaxed);
114 b[0..8].copy_from_slice(&i.to_le_bytes());
115 Self::new(&b)
116 }
117
118 pub fn to_bytes(self) -> [u8; HASH_BYTES] {
119 self.0
120 }
121}
122
123pub fn hashv(vals: &[&[u8]]) -> Hash {
125 #[cfg(not(target_arch = "bpf"))]
128 {
129 let mut hasher = Hasher::default();
130 hasher.hashv(vals);
131 hasher.result()
132 }
133 #[cfg(target_arch = "bpf")]
135 {
136 extern "C" {
137 fn gema_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64;
138 }
139 let mut hash_result = [0; HASH_BYTES];
140 unsafe {
141 gema_sha256(
142 vals as *const _ as *const u8,
143 vals.len() as u64,
144 &mut hash_result as *mut _ as *mut u8,
145 );
146 }
147 Hash::new_from_array(hash_result)
148 }
149}
150
151pub fn hash(val: &[u8]) -> Hash {
153 hashv(&[val])
154}
155
156pub fn extend_and_hash(id: &Hash, val: &[u8]) -> Hash {
158 let mut hash_data = id.as_ref().to_vec();
159 hash_data.extend_from_slice(val);
160 hash(&hash_data)
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_new_unique() {
169 assert!(Hash::new_unique() != Hash::new_unique());
170 }
171
172 #[test]
173 fn test_hash_fromstr() {
174 let hash = hash(&[1u8]);
175
176 let mut hash_base58_str = bs58::encode(hash).into_string();
177
178 assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
179
180 hash_base58_str.push_str(&bs58::encode(hash.0).into_string());
181 assert_eq!(
182 hash_base58_str.parse::<Hash>(),
183 Err(ParseHashError::WrongSize)
184 );
185
186 hash_base58_str.truncate(hash_base58_str.len() / 2);
187 assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
188
189 hash_base58_str.truncate(hash_base58_str.len() / 2);
190 assert_eq!(
191 hash_base58_str.parse::<Hash>(),
192 Err(ParseHashError::WrongSize)
193 );
194
195 let input_too_big = bs58::encode(&[0xffu8; HASH_BYTES + 1]).into_string();
196 assert!(input_too_big.len() > MAX_BASE58_LEN);
197 assert_eq!(
198 input_too_big.parse::<Hash>(),
199 Err(ParseHashError::WrongSize)
200 );
201
202 let mut hash_base58_str = bs58::encode(hash.0).into_string();
203 assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));
204
205 hash_base58_str.replace_range(..1, "I");
207 assert_eq!(
208 hash_base58_str.parse::<Hash>(),
209 Err(ParseHashError::Invalid)
210 );
211 }
212}