Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 2 additions & 22 deletions blake3-hasher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,19 @@ all-features = true
rustdoc-args = ["--cfg=docsrs"]

[features]
borsh = ["dep:borsh", "std"]
dev-context-only-utils = ["std"]
frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro", "std"]
serde = ["dep:serde", "dep:serde_derive"]
blake3 = ["dep:blake3"]
std = ["solana-hash/std"]

[dependencies]
borsh = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
solana-frozen-abi = { workspace = true, optional = true, features = [
"frozen-abi",
] }
solana-frozen-abi-macro = { workspace = true, optional = true, features = [
"frozen-abi",
] }
solana-hash = { workspace = true }
solana-sanitize = { workspace = true }

[target.'cfg(not(target_os = "solana"))'.dependencies]
blake3 = { workspace = true }
blake3 = { workspace = true, optional = true }

[target.'cfg(target_os = "solana")'.dependencies]
# blake3 should be removed in the next breaking release,
# as there's no reason to use the crate instead of the syscall
# onchain
blake3 = { workspace = true, optional = true }
solana-define-syscall = { workspace = true }

[dev-dependencies]
bs58 = { workspace = true, features = ["std"] }
solana-blake3-hasher = { path = ".", features = ["dev-context-only-utils"] }
solana-blake3-hasher = { path = ".", features = ["blake3"] }

[lints]
workspace = true
174 changes: 21 additions & 153 deletions blake3-hasher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,17 @@
//!
//! [blake3]: https://github.com/BLAKE3-team/BLAKE3
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
#![no_std]
#[cfg(feature = "std")]
extern crate std;

pub use solana_hash::{ParseHashError, HASH_BYTES, MAX_BASE58_LEN};
#[cfg(feature = "borsh")]
use {
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
std::string::ToString,
};
use {
core::{fmt, str::FromStr},
solana_sanitize::Sanitize,
};
pub use solana_hash::{Hash, ParseHashError, HASH_BYTES, MAX_BASE58_LEN};

// TODO: replace this with `solana_hash::Hash` in the
// next breaking change.
// It's a breaking change because the field is public
// here and private in `solana_hash`, and making
// it public in `solana_hash` would break wasm-bindgen
#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
#[cfg_attr(
feature = "borsh",
derive(BorshSerialize, BorshDeserialize, BorshSchema),
borsh(crate = "borsh")
)]
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[derive(Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Hash(pub [u8; HASH_BYTES]);

#[cfg(any(feature = "blake3", not(target_os = "solana")))]
#[derive(Clone, Default)]
#[cfg(all(feature = "blake3", not(target_os = "solana")))]
pub struct Hasher {
hasher: blake3::Hasher,
}

#[cfg(any(feature = "blake3", not(target_os = "solana")))]
#[cfg(all(feature = "blake3", not(target_os = "solana")))]
impl Hasher {
pub fn hash(&mut self, val: &[u8]) {
self.hasher.update(val);
Expand All @@ -54,65 +23,7 @@ impl Hasher {
}
}
pub fn result(self) -> Hash {
Hash(*self.hasher.finalize().as_bytes())
}
}

impl From<solana_hash::Hash> for Hash {
fn from(val: solana_hash::Hash) -> Self {
Self(val.to_bytes())
}
}

impl From<Hash> for solana_hash::Hash {
fn from(val: Hash) -> Self {
Self::new_from_array(val.0)
}
}

impl Sanitize for Hash {}

impl AsRef<[u8]> for Hash {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}

impl fmt::Debug for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let converted: solana_hash::Hash = (*self).into();
fmt::Debug::fmt(&converted, f)
}
}

impl fmt::Display for Hash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let converted: solana_hash::Hash = (*self).into();
fmt::Display::fmt(&converted, f)
}
}

impl FromStr for Hash {
type Err = ParseHashError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let unconverted = solana_hash::Hash::from_str(s)?;
Ok(unconverted.into())
}
}

impl Hash {
pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self {
Self(hash_array)
}

/// unique Hash for tests and benchmarks.
pub fn new_unique() -> Self {
Self::from(solana_hash::Hash::new_unique())
}

pub fn to_bytes(self) -> [u8; HASH_BYTES] {
self.0
Hash::new_from_array(*self.hasher.finalize().as_bytes())
}
}

Expand All @@ -122,9 +33,17 @@ pub fn hashv(vals: &[&[u8]]) -> Hash {
// not supported
#[cfg(not(target_os = "solana"))]
{
let mut hasher = Hasher::default();
hasher.hashv(vals);
hasher.result()
#[cfg(feature = "blake3")]
{
let mut hasher = Hasher::default();
hasher.hashv(vals);
hasher.result()
}
#[cfg(not(feature = "blake3"))]
{
core::hint::black_box(vals);
panic!("hashv is only available on target `solana` or with the `blake3` feature enabled on this crate")
}
}
// Call via a system call to perform the calculation
#[cfg(target_os = "solana")]
Expand All @@ -146,71 +65,20 @@ pub fn hash(val: &[u8]) -> Hash {
hashv(&[val])
}

#[cfg(feature = "std")]
/// Return the hash of the given hash extended with the given value.
pub fn extend_and_hash(id: &Hash, val: &[u8]) -> Hash {
let mut hash_data = id.as_ref().to_vec();
hash_data.extend_from_slice(val);
hash(&hash_data)
}
Comment thread
febo marked this conversation as resolved.

#[cfg(test)]
#[cfg(feature = "blake3")]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that the feature is enabled on the dev-dependencies, so we probably don't need this. I might have missed that on my first review. Not sure if the idea is to have this pattern, but it can get a bit verbose for crates that have many features.

mod tests {
use super::*;

#[test]
fn test_new_unique() {
assert!(Hash::new_unique() != Hash::new_unique());
}

#[test]
fn test_hash_fromstr() {
let hash = hash(&[1u8]);

let mut hash_base58_str = bs58::encode(hash).into_string();

assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));

hash_base58_str.push_str(&bs58::encode(hash.0).into_string());
assert_eq!(
hash_base58_str.parse::<Hash>(),
Err(ParseHashError::WrongSize)
);

hash_base58_str.truncate(hash_base58_str.len() / 2);
assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));

hash_base58_str.truncate(hash_base58_str.len() / 2);
assert_eq!(
hash_base58_str.parse::<Hash>(),
Err(ParseHashError::WrongSize)
);

let input_too_big = bs58::encode(&[0xffu8; HASH_BYTES + 1]).into_string();
assert!(input_too_big.len() > MAX_BASE58_LEN);
assert_eq!(
input_too_big.parse::<Hash>(),
Err(ParseHashError::WrongSize)
);

let mut hash_base58_str = bs58::encode(hash.0).into_string();
assert_eq!(hash_base58_str.parse::<Hash>(), Ok(hash));

// throw some non-base58 stuff in there
hash_base58_str.replace_range(..1, "I");
assert_eq!(
hash_base58_str.parse::<Hash>(),
Err(ParseHashError::Invalid)
);
}

#[test]
fn test_extend_and_hash() {
fn test_hashv() {
let val = "gHiljKpq";
let val_hash = hash(val.as_bytes());

let ext = "lM890t";
let hash_ext = [&val_hash.0, ext.as_bytes()].concat();
let ext_hash = extend_and_hash(&val_hash, ext.as_bytes());
let ext_hash = hashv(&[&val_hash.to_bytes(), ext.as_bytes()]);
Comment thread
febo marked this conversation as resolved.

let hash_ext = [&val_hash.to_bytes(), ext.as_bytes()].concat();
assert!(ext_hash == hash(&hash_ext));
}
}
6 changes: 5 additions & 1 deletion hash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,13 @@ impl Hash {
Self::new_from_array(b)
}

pub fn to_bytes(self) -> [u8; HASH_BYTES] {
pub const fn to_bytes(self) -> [u8; HASH_BYTES] {
self.0
}

pub const fn as_bytes(&self) -> &[u8; HASH_BYTES] {
&self.0
}
}

#[cfg(target_arch = "wasm32")]
Expand Down
20 changes: 1 addition & 19 deletions keccak-hasher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,15 @@ all-features = true
rustdoc-args = ["--cfg=docsrs"]

[features]
borsh = ["dep:borsh", "std"]
frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro", "std"]
serde = ["dep:serde", "dep:serde_derive"]
sha3 = ["dep:sha3"]
std = ["solana-hash/std"]

[dependencies]
borsh = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
solana-frozen-abi = { workspace = true, optional = true, features = [
"frozen-abi",
] }
solana-frozen-abi-macro = { workspace = true, optional = true, features = [
"frozen-abi",
] }
solana-hash = { workspace = true }
solana-sanitize = { workspace = true }

[target.'cfg(not(target_os = "solana"))'.dependencies]
sha3 = { workspace = true }
sha3 = { workspace = true, optional = true }

[target.'cfg(target_os = "solana")'.dependencies]
# sha3 should be removed in the next breaking release,
# as there's no reason to use the crate instead of the syscall
# onchain
sha3 = { workspace = true, optional = true }
solana-define-syscall = { workspace = true }

[lints]
Expand Down
Loading
Loading