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