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