solana_blake3_hasher/
lib.rs

1//! Hashing with the [blake3] hash function.
2//!
3//! [blake3]: https://github.com/BLAKE3-team/BLAKE3
4#![no_std]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7pub use solana_hash::{Hash, ParseHashError, HASH_BYTES, MAX_BASE58_LEN};
8
9#[derive(Clone, Default)]
10#[cfg(all(
11    feature = "blake3",
12    not(any(target_os = "solana", target_arch = "bpf"))
13))]
14pub struct Hasher {
15    hasher: blake3::Hasher,
16}
17
18#[cfg(all(
19    feature = "blake3",
20    not(any(target_os = "solana", target_arch = "bpf"))
21))]
22impl Hasher {
23    pub fn hash(&mut self, val: &[u8]) {
24        self.hasher.update(val);
25    }
26    pub fn hashv(&mut self, vals: &[&[u8]]) {
27        for val in vals {
28            self.hash(val);
29        }
30    }
31    pub fn result(self) -> Hash {
32        Hash::new_from_array(*self.hasher.finalize().as_bytes())
33    }
34}
35
36/// Return a Blake3 hash for the given data.
37#[cfg_attr(any(target_os = "solana", target_arch = "bpf"), inline(always))]
38pub fn hashv(vals: &[&[u8]]) -> Hash {
39    // Perform the calculation inline, calling this from within a program is
40    // not supported
41    #[cfg(not(any(target_os = "solana", target_arch = "bpf")))]
42    {
43        #[cfg(feature = "blake3")]
44        {
45            let mut hasher = Hasher::default();
46            hasher.hashv(vals);
47            hasher.result()
48        }
49        #[cfg(not(feature = "blake3"))]
50        {
51            core::hint::black_box(vals);
52            panic!("hashv is only available on target `solana` or with the `blake3` feature enabled on this crate")
53        }
54    }
55    // Call via a system call to perform the calculation
56    #[cfg(any(target_os = "solana", target_arch = "bpf"))]
57    {
58        let mut hash_result = core::mem::MaybeUninit::<[u8; solana_hash::HASH_BYTES]>::uninit();
59        // SAFETY: This is sound as sol_blake3 always fills all 32 bytes of our hash
60        unsafe {
61            solana_define_syscall::definitions::sol_blake3(
62                vals as *const _ as *const u8,
63                vals.len() as u64,
64                hash_result.as_mut_ptr() as *mut u8,
65            );
66            Hash::new_from_array(hash_result.assume_init())
67        }
68    }
69}
70
71/// Return a Blake3 hash for the given data.
72#[cfg_attr(any(target_os = "solana", target_arch = "bpf"), inline(always))]
73pub fn hash(val: &[u8]) -> Hash {
74    hashv(&[val])
75}
76
77#[cfg(test)]
78#[cfg(feature = "blake3")]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_hashv() {
84        let val = "gHiljKpq";
85        let val_hash = hash(val.as_bytes());
86
87        let ext = "lM890t";
88        let ext_hash = hashv(&[val_hash.as_bytes(), ext.as_bytes()]);
89
90        let hash_ext = [&val_hash.to_bytes(), ext.as_bytes()].concat();
91        assert!(ext_hash == hash(&hash_ext));
92    }
93}