1#![no_std]
48
49extern crate alloc;
50
51pub use sails_reflect_hash_derive::ReflectHash;
53
54#[doc(hidden)]
56pub use keccak_const;
57
58use alloc::{collections::BTreeMap, string::String, vec::Vec};
59use core::num::{
60 NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroU8, NonZeroU16, NonZeroU32,
61 NonZeroU64, NonZeroU128,
62};
63use gprimitives::{ActorId, CodeId, H160, H256, MessageId, NonZeroU256, U256};
64use keccak_const::Keccak256;
65
66pub trait ReflectHash {
76 const HASH: [u8; 32];
78}
79
80macro_rules! impl_reflect_hash_for_primitives {
81 ($($t:ty => $discriminant:literal),* $(,)?) => {
82 $(
83 impl ReflectHash for $t {
84 const HASH: [u8; 32] = Keccak256::new()
85 .update($discriminant)
86 .finalize();
87 }
88 )*
89 };
90}
91
92impl_reflect_hash_for_primitives! {
95 u8 => b"u8",
96 u16 => b"u16",
97 u32 => b"u32",
98 u64 => b"u64",
99 u128 => b"u128",
100 i8 => b"i8",
101 i16 => b"i16",
102 i32 => b"i32",
103 i64 => b"i64",
104 i128 => b"i128",
105 bool => b"bool",
106 char => b"char",
107 str => b"String",
108 String => b"String",
109}
110
111impl ReflectHash for NonZeroU8 {
112 const HASH: [u8; 32] = Keccak256::new()
113 .update(b"NonZeroU8")
114 .update(&<u8 as ReflectHash>::HASH)
115 .finalize();
116}
117
118impl ReflectHash for NonZeroU16 {
119 const HASH: [u8; 32] = Keccak256::new()
120 .update(b"NonZeroU16")
121 .update(&<u16 as ReflectHash>::HASH)
122 .finalize();
123}
124
125impl ReflectHash for NonZeroU32 {
126 const HASH: [u8; 32] = Keccak256::new()
127 .update(b"NonZeroU32")
128 .update(&<u32 as ReflectHash>::HASH)
129 .finalize();
130}
131
132impl ReflectHash for NonZeroU64 {
133 const HASH: [u8; 32] = Keccak256::new()
134 .update(b"NonZeroU64")
135 .update(&<u64 as ReflectHash>::HASH)
136 .finalize();
137}
138
139impl ReflectHash for NonZeroU128 {
140 const HASH: [u8; 32] = Keccak256::new()
141 .update(b"NonZeroU128")
142 .update(&<u128 as ReflectHash>::HASH)
143 .finalize();
144}
145
146impl ReflectHash for NonZeroI8 {
147 const HASH: [u8; 32] = Keccak256::new()
148 .update(b"NonZeroI8")
149 .update(&<i8 as ReflectHash>::HASH)
150 .finalize();
151}
152
153impl ReflectHash for NonZeroI16 {
154 const HASH: [u8; 32] = Keccak256::new()
155 .update(b"NonZeroI16")
156 .update(&<i16 as ReflectHash>::HASH)
157 .finalize();
158}
159
160impl ReflectHash for NonZeroI32 {
161 const HASH: [u8; 32] = Keccak256::new()
162 .update(b"NonZeroI32")
163 .update(&<i32 as ReflectHash>::HASH)
164 .finalize();
165}
166
167impl ReflectHash for NonZeroI64 {
168 const HASH: [u8; 32] = Keccak256::new()
169 .update(b"NonZeroI64")
170 .update(&<i64 as ReflectHash>::HASH)
171 .finalize();
172}
173
174impl ReflectHash for NonZeroI128 {
175 const HASH: [u8; 32] = Keccak256::new()
176 .update(b"NonZeroI128")
177 .update(&<i128 as ReflectHash>::HASH)
178 .finalize();
179}
180
181impl<T: ReflectHash> ReflectHash for [T] {
183 const HASH: [u8; 32] = {
184 Keccak256::new()
185 .update(b"[")
186 .update(&T::HASH)
187 .update(b"]")
188 .finalize()
189 };
190}
191
192impl<T: ReflectHash + ?Sized> ReflectHash for &T {
195 const HASH: [u8; 32] = T::HASH;
196}
197
198impl<T: ReflectHash + ?Sized> ReflectHash for &mut T {
201 const HASH: [u8; 32] = T::HASH;
202}
203
204impl ReflectHash for () {
205 const HASH: [u8; 32] = Keccak256::new().update(b"()").finalize();
206}
207
208impl<T: ReflectHash> ReflectHash for Option<T> {
209 const HASH: [u8; 32] = {
210 Keccak256::new()
211 .update(b"Option")
212 .update(&T::HASH)
213 .finalize()
214 };
215}
216
217impl<T: ReflectHash, E: ReflectHash> ReflectHash for Result<T, E> {
218 const HASH: [u8; 32] = {
219 Keccak256::new()
220 .update(b"Result")
221 .update(&T::HASH)
222 .update(&E::HASH)
223 .finalize()
224 };
225}
226
227macro_rules! impl_reflect_hash_for_tuples {
228 () => {};
229 ($first:ident $(, $rest:ident)*) => {
230 impl<$first: ReflectHash, $($rest: ReflectHash),*> ReflectHash for ($first, $($rest),*) {
231 const HASH: [u8; 32] = {
232 Keccak256::new()
233 .update(&$first::HASH)
234 $(
235 .update(&$rest::HASH)
236 )*
237 .finalize()
238 };
239 }
240 impl_reflect_hash_for_tuples!($($rest),*);
241 };
242}
243
244impl_reflect_hash_for_tuples!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
246
247macro_rules! impl_reflect_hash_for_bytes_arrays {
248 ($($n:expr),* $(,)?) => {
249 $(
250 impl<T: ReflectHash> ReflectHash for [T; $n] {
251 const HASH: [u8; 32] = {
252 let n_str = stringify!($n);
253 Keccak256::new()
254 .update(&T::HASH)
255 .update(n_str.as_bytes())
256 .finalize()
257 };
258 }
259 )*
260 };
261}
262
263impl_reflect_hash_for_bytes_arrays!(
265 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
266 26, 27, 28, 29, 30, 31, 32,
267);
268
269impl ReflectHash for ActorId {
270 const HASH: [u8; 32] = Keccak256::new().update(b"ActorId").finalize();
271}
272
273impl ReflectHash for MessageId {
274 const HASH: [u8; 32] = Keccak256::new().update(b"MessageId").finalize();
275}
276
277impl ReflectHash for CodeId {
278 const HASH: [u8; 32] = Keccak256::new().update(b"CodeId").finalize();
279}
280
281impl ReflectHash for H256 {
282 const HASH: [u8; 32] = Keccak256::new().update(b"H256").finalize();
283}
284
285impl ReflectHash for H160 {
286 const HASH: [u8; 32] = Keccak256::new().update(b"H160").finalize();
287}
288
289#[cfg(feature = "alloy-primitives")]
290impl ReflectHash for alloy_primitives::Address {
291 const HASH: [u8; 32] = H160::HASH;
292}
293
294#[cfg(feature = "alloy-primitives")]
295impl ReflectHash for alloy_primitives::B256 {
296 const HASH: [u8; 32] = H256::HASH;
297}
298
299impl ReflectHash for U256 {
300 const HASH: [u8; 32] = Keccak256::new().update(b"U256").finalize();
301}
302
303impl ReflectHash for NonZeroU256 {
304 const HASH: [u8; 32] = Keccak256::new()
305 .update(b"NonZeroU256")
306 .update(&U256::HASH)
307 .finalize();
308}
309
310impl<T: ReflectHash> ReflectHash for Vec<T> {
311 const HASH: [u8; 32] = {
312 Keccak256::new()
313 .update(b"[")
314 .update(&T::HASH)
315 .update(b"]")
316 .finalize()
317 };
318}
319
320impl<K: ReflectHash, V: ReflectHash> ReflectHash for BTreeMap<K, V> {
321 const HASH: [u8; 32] = {
322 Keccak256::new()
323 .update(b"[")
324 .update(&<(K, V) as ReflectHash>::HASH)
325 .update(b"]")
326 .finalize()
327 };
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn same_hash_types() {
336 assert_eq!(str::HASH, String::HASH);
337 assert_eq!(<[u8] as ReflectHash>::HASH, Vec::<u8>::HASH);
338 assert_eq!(<&u32 as ReflectHash>::HASH, u32::HASH);
339 assert_eq!(<&mut u32 as ReflectHash>::HASH, u32::HASH);
340 assert_eq!(<&'static str as ReflectHash>::HASH, str::HASH);
341 }
342
343 #[test]
345 fn crate_paths() {
346 use crate as reflect_hash_crate;
347
348 #[derive(ReflectHash)]
349 #[reflect_hash(crate = reflect_hash_crate)]
350 #[allow(dead_code)]
351 struct TestStruct(String);
352 }
353
354 #[cfg(feature = "alloy-primitives")]
355 #[test]
356 fn alloy_primitives_reuse_h160_h256_hashes() {
357 assert_eq!(
358 <alloy_primitives::Address as ReflectHash>::HASH,
359 <H160 as ReflectHash>::HASH,
360 );
361 assert_eq!(
362 <alloy_primitives::B256 as ReflectHash>::HASH,
363 <H256 as ReflectHash>::HASH,
364 );
365 }
366}