From 2801eb8da1b7f05e0ef238e844733ea9c6ec1403 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Thu, 31 Oct 2024 14:57:34 +0400 Subject: [PATCH 1/7] extract keccak-hasher crate --- Cargo.lock | 18 +++++ Cargo.toml | 2 + programs/sbf/Cargo.lock | 13 +++ sdk/keccak-hasher/Cargo.toml | 49 ++++++++++++ .../keccak.rs => keccak-hasher/src/lib.rs} | 79 ++++++++++--------- sdk/program/Cargo.toml | 1 + sdk/program/src/lib.rs | 3 +- 7 files changed, 126 insertions(+), 39 deletions(-) create mode 100644 sdk/keccak-hasher/Cargo.toml rename sdk/{program/src/keccak.rs => keccak-hasher/src/lib.rs} (61%) diff --git a/Cargo.lock b/Cargo.lock index 33543a024ae40a..cd2e2777a412b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7196,6 +7196,23 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "solana-keccak-hasher" +version = "2.2.0" +dependencies = [ + "borsh 1.5.3", + "bs58", + "serde", + "serde_derive", + "sha3", + "solana-atomic-u64", + "solana-define-syscall", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-hash", + "solana-sanitize", +] + [[package]] name = "solana-keygen" version = "2.2.0" @@ -7742,6 +7759,7 @@ dependencies = [ "solana-frozen-abi-macro", "solana-hash", "solana-instruction", + "solana-keccak-hasher", "solana-last-restart-slot", "solana-logger", "solana-msg", diff --git a/Cargo.toml b/Cargo.toml index 73f1183da72747..1693ffd9c88c73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,6 +124,7 @@ members = [ "sdk/hash", "sdk/inflation", "sdk/instruction", + "sdk/keccak-hasher", "sdk/keypair", "sdk/logger", "sdk/macro", @@ -463,6 +464,7 @@ solana-hash = { path = "sdk/hash", version = "=2.2.0", default-features = false solana-inflation = { path = "sdk/inflation", version = "=2.2.0" } solana-inline-spl = { path = "inline-spl", version = "=2.2.0" } solana-instruction = { path = "sdk/instruction", version = "=2.2.0", default-features = false } +solana-keccak-hasher = { path = "sdk/keccak-hasher", version = "=2.2.0" } solana-keypair = { path = "sdk/keypair", version = "=2.2.0" } solana-last-restart-slot = { path = "sdk/last-restart-slot", version = "=2.2.0" } solana-lattice-hash = { path = "lattice-hash", version = "=2.2.0" } diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 04379aeb571d24..8d29cb8c9e1af3 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5749,6 +5749,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "solana-keccak-hasher" +version = "2.2.0" +dependencies = [ + "bs58", + "sha3", + "solana-atomic-u64", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + [[package]] name = "solana-keypair" version = "2.2.0" @@ -6095,6 +6107,7 @@ dependencies = [ "solana-fee-calculator", "solana-hash", "solana-instruction", + "solana-keccak-hasher", "solana-last-restart-slot", "solana-msg", "solana-native-token", diff --git a/sdk/keccak-hasher/Cargo.toml b/sdk/keccak-hasher/Cargo.toml new file mode 100644 index 00000000000000..18fa2c11151acc --- /dev/null +++ b/sdk/keccak-hasher/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "solana-keccak-hasher" +description = "Solana Keccak hashing" +documentation = "https://docs.rs/solana-keccak-hasher" +version = { workspace = true } +authors = { workspace = true } +repository = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +edition = { workspace = true } + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] +all-features = true +rustdoc-args = ["--cfg=docsrs"] + +[dependencies] +borsh = { workspace = true, optional = true } +bs58 = { workspace = true } +solana-atomic-u64 = { workspace = 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 } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } + +[target.'cfg(not(target_os = "solana"))'.dependencies] +sha3 = { workspace = 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 } + +[features] +borsh = ["dep:borsh"] +frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] +serde = ["dep:serde", "dep:serde_derive"] +sha3 = ["dep:sha3"] + +[lints] +workspace = true diff --git a/sdk/program/src/keccak.rs b/sdk/keccak-hasher/src/lib.rs similarity index 61% rename from sdk/program/src/keccak.rs rename to sdk/keccak-hasher/src/lib.rs index cf94dfa4287722..d023523c84a271 100644 --- a/sdk/program/src/keccak.rs +++ b/sdk/keccak-hasher/src/lib.rs @@ -1,34 +1,45 @@ //! Hashing with the [keccak] (SHA-3) hash function. //! //! [keccak]: https://keccak.team/keccak.html +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![cfg_attr(feature = "frozen-abi", feature(min_specialization))] #[cfg(feature = "borsh")] use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +#[cfg(any(feature = "sha3", not(target_os = "solana")))] +use sha3::{Digest, Keccak256}; +pub use solana_hash::{ParseHashError, HASH_BYTES, MAX_BASE58_LEN}; use { - sha3::{Digest, Keccak256}, solana_sanitize::Sanitize, - std::{convert::TryFrom, fmt, str::FromStr}, - thiserror::Error, + std::{fmt, str::FromStr}, }; -pub const HASH_BYTES: usize = 32; -/// Maximum string length of a base58 encoded hash -const MAX_BASE58_LEN: usize = 44; -#[cfg_attr(feature = "frozen-abi", derive(AbiExample))] +// 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") )] -#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[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 = "sha3", not(target_os = "solana")))] #[derive(Clone, Default)] pub struct Hasher { hasher: Keccak256, } +#[cfg(any(feature = "sha3", not(target_os = "solana")))] impl Hasher { pub fn hash(&mut self, val: &[u8]) { self.hasher.update(val); @@ -43,6 +54,18 @@ impl Hasher { } } +impl From for Hash { + fn from(val: solana_hash::Hash) -> Self { + Self(val.to_bytes()) + } +} + +impl Into for Hash { + fn into(self) -> solana_hash::Hash { + solana_hash::Hash::new_from_array(self.0) + } +} + impl Sanitize for Hash {} impl AsRef<[u8]> for Hash { @@ -53,46 +76,32 @@ impl AsRef<[u8]> for Hash { impl fmt::Debug for Hash { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) + 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 { - write!(f, "{}", bs58::encode(self.0).into_string()) + let converted: solana_hash::Hash = (*self).into(); + fmt::Display::fmt(&converted, f) } } -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum ParseHashError { - #[error("string decoded to wrong size for hash")] - WrongSize, - #[error("failed to decoded string to hash")] - Invalid, -} - impl FromStr for Hash { type Err = ParseHashError; fn from_str(s: &str) -> Result { - if s.len() > MAX_BASE58_LEN { - return Err(ParseHashError::WrongSize); - } - bs58::decode(s) - .into_vec() - .map_err(|_| ParseHashError::Invalid) - .and_then(|bytes| { - <[u8; HASH_BYTES]>::try_from(bytes) - .map(Hash::new_from_array) - .map_err(|_| ParseHashError::WrongSize) - }) + let unconverted = solana_hash::Hash::from_str(s)?; + Ok(unconverted.into()) } } impl Hash { #[deprecated(since = "2.2.0", note = "Use 'Hash::new_from_array' instead")] pub fn new(hash_slice: &[u8]) -> Self { - Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap()) + #[allow(deprecated)] + Self::from(solana_hash::Hash::new(hash_slice)) } pub const fn new_from_array(hash_array: [u8; HASH_BYTES]) -> Self { @@ -101,13 +110,7 @@ impl Hash { /// unique Hash for tests and benchmarks. pub fn new_unique() -> Self { - use solana_atomic_u64::AtomicU64; - static I: AtomicU64 = AtomicU64::new(1); - - let mut b = [0u8; HASH_BYTES]; - let i = I.fetch_add(1); - b[0..8].copy_from_slice(&i.to_le_bytes()); - Self::new_from_array(b) + Self::from(solana_hash::Hash::new_unique()) } pub fn to_bytes(self) -> [u8; HASH_BYTES] { @@ -130,7 +133,7 @@ pub fn hashv(vals: &[&[u8]]) -> Hash { { let mut hash_result = [0; HASH_BYTES]; unsafe { - crate::syscalls::sol_keccak256( + solana_define_syscall::definitions::sol_keccak256( vals as *const _ as *const u8, vals.len() as u64, &mut hash_result as *mut _ as *mut u8, diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 141773c4f789df..4eba8b6d88d82e 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -54,6 +54,7 @@ solana-instruction = { workspace = true, default-features = false, features = [ "serde", "std", ] } +solana-keccak-hasher = { workspace = true } solana-last-restart-slot = { workspace = true, features = ["serde", "sysvar"] } solana-msg = { workspace = true } solana-native-token = { workspace = true } diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 55da6831369d8f..4cb41e6cbed3cd 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -484,7 +484,6 @@ pub mod feature; pub mod hash; pub mod incinerator; pub mod instruction; -pub mod keccak; pub mod lamports; pub mod loader_instruction; pub mod loader_upgradeable_instruction; @@ -522,6 +521,8 @@ pub use solana_borsh::v1 as borsh1; pub use solana_epoch_rewards as epoch_rewards; #[deprecated(since = "2.1.0", note = "Use `solana-fee-calculator` crate instead")] pub use solana_fee_calculator as fee_calculator; +#[deprecated(since = "2.2.0", note = "Use `solana-keccak-hasher` crate instead")] +pub use solana_keccak_hasher as keccak; #[deprecated(since = "2.1.0", note = "Use `solana-last-restart-slot` crate instead")] pub use solana_last_restart_slot as last_restart_slot; #[deprecated(since = "2.1.0", note = "Use `solana-program-memory` crate instead")] From 8ad2b988ba774adef48897f704ff7723893af706 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Thu, 31 Oct 2024 15:07:39 +0400 Subject: [PATCH 2/7] fix lint --- sdk/keccak-hasher/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/keccak-hasher/src/lib.rs b/sdk/keccak-hasher/src/lib.rs index d023523c84a271..5c2ec004ead454 100644 --- a/sdk/keccak-hasher/src/lib.rs +++ b/sdk/keccak-hasher/src/lib.rs @@ -60,9 +60,9 @@ impl From for Hash { } } -impl Into for Hash { - fn into(self) -> solana_hash::Hash { - solana_hash::Hash::new_from_array(self.0) +impl From for solana_hash::Hash { + fn from(val: Hash) -> Self { + Self::new_from_array(val.0) } } From 8f8758459a76eb59d3a2026aa77a164c56e5a50f Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Thu, 31 Oct 2024 15:36:52 +0400 Subject: [PATCH 3/7] sort deps --- sdk/keccak-hasher/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/keccak-hasher/Cargo.toml b/sdk/keccak-hasher/Cargo.toml index 18fa2c11151acc..19f80c4f117550 100644 --- a/sdk/keccak-hasher/Cargo.toml +++ b/sdk/keccak-hasher/Cargo.toml @@ -17,6 +17,8 @@ rustdoc-args = ["--cfg=docsrs"] [dependencies] borsh = { workspace = true, optional = true } bs58 = { workspace = true } +serde = { workspace = true, optional = true } +serde_derive = { workspace = true, optional = true } solana-atomic-u64 = { workspace = true } solana-frozen-abi = { workspace = true, optional = true, features = [ "frozen-abi", @@ -26,8 +28,6 @@ solana-frozen-abi-macro = { workspace = true, optional = true, features = [ ] } solana-hash = { workspace = true } solana-sanitize = { workspace = true } -serde = { workspace = true, optional = true } -serde_derive = { workspace = true, optional = true } [target.'cfg(not(target_os = "solana"))'.dependencies] sha3 = { workspace = true } From 00499e9205db7d5f8b8ed10a04afeb31c55e930f Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Thu, 31 Oct 2024 17:27:23 +0400 Subject: [PATCH 4/7] missing feature --- sdk/program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/program/Cargo.toml b/sdk/program/Cargo.toml index 4eba8b6d88d82e..1db2814a97d495 100644 --- a/sdk/program/Cargo.toml +++ b/sdk/program/Cargo.toml @@ -54,7 +54,7 @@ solana-instruction = { workspace = true, default-features = false, features = [ "serde", "std", ] } -solana-keccak-hasher = { workspace = true } +solana-keccak-hasher = { workspace = true, features = ["sha3"] } solana-last-restart-slot = { workspace = true, features = ["serde", "sysvar"] } solana-msg = { workspace = true } solana-native-token = { workspace = true } From 0688483372d12c3106d6ce374c853e103c18cb5e Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Mon, 18 Nov 2024 17:53:36 +0400 Subject: [PATCH 5/7] update lock file --- svm/examples/Cargo.lock | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/svm/examples/Cargo.lock b/svm/examples/Cargo.lock index 8e5b6843bd91c7..3a5a15e3354a5f 100644 --- a/svm/examples/Cargo.lock +++ b/svm/examples/Cargo.lock @@ -5570,6 +5570,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "solana-keccak-hasher" +version = "2.2.0" +dependencies = [ + "bs58", + "sha3", + "solana-atomic-u64", + "solana-define-syscall", + "solana-hash", + "solana-sanitize", +] + [[package]] name = "solana-keypair" version = "2.2.0" @@ -5916,6 +5928,7 @@ dependencies = [ "solana-fee-calculator", "solana-hash", "solana-instruction", + "solana-keccak-hasher", "solana-last-restart-slot", "solana-msg", "solana-native-token", From aa4077af004f819b52374adcee4a8e647a332b93 Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Wed, 20 Nov 2024 04:42:57 +0400 Subject: [PATCH 6/7] remove unused deps --- Cargo.lock | 2 -- programs/sbf/Cargo.lock | 2 -- sdk/keccak-hasher/Cargo.toml | 2 -- svm/examples/Cargo.lock | 2 -- 4 files changed, 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd2e2777a412b0..8fb169c4940812 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7201,11 +7201,9 @@ name = "solana-keccak-hasher" version = "2.2.0" dependencies = [ "borsh 1.5.3", - "bs58", "serde", "serde_derive", "sha3", - "solana-atomic-u64", "solana-define-syscall", "solana-frozen-abi", "solana-frozen-abi-macro", diff --git a/programs/sbf/Cargo.lock b/programs/sbf/Cargo.lock index 8d29cb8c9e1af3..69e4fb808ecba6 100644 --- a/programs/sbf/Cargo.lock +++ b/programs/sbf/Cargo.lock @@ -5753,9 +5753,7 @@ dependencies = [ name = "solana-keccak-hasher" version = "2.2.0" dependencies = [ - "bs58", "sha3", - "solana-atomic-u64", "solana-define-syscall", "solana-hash", "solana-sanitize", diff --git a/sdk/keccak-hasher/Cargo.toml b/sdk/keccak-hasher/Cargo.toml index 19f80c4f117550..4d77dba2eb8270 100644 --- a/sdk/keccak-hasher/Cargo.toml +++ b/sdk/keccak-hasher/Cargo.toml @@ -16,10 +16,8 @@ rustdoc-args = ["--cfg=docsrs"] [dependencies] borsh = { workspace = true, optional = true } -bs58 = { workspace = true } serde = { workspace = true, optional = true } serde_derive = { workspace = true, optional = true } -solana-atomic-u64 = { workspace = true } solana-frozen-abi = { workspace = true, optional = true, features = [ "frozen-abi", ] } diff --git a/svm/examples/Cargo.lock b/svm/examples/Cargo.lock index 3a5a15e3354a5f..851010dd0db9f6 100644 --- a/svm/examples/Cargo.lock +++ b/svm/examples/Cargo.lock @@ -5574,9 +5574,7 @@ dependencies = [ name = "solana-keccak-hasher" version = "2.2.0" dependencies = [ - "bs58", "sha3", - "solana-atomic-u64", "solana-define-syscall", "solana-hash", "solana-sanitize", From 5d5a49c7ecbbeab53c7036a53c8a8915113aa5cb Mon Sep 17 00:00:00 2001 From: kevinheavey Date: Wed, 20 Nov 2024 11:49:16 +0400 Subject: [PATCH 7/7] make std optional --- sdk/keccak-hasher/Cargo.toml | 5 +++-- sdk/keccak-hasher/src/lib.rs | 13 ++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sdk/keccak-hasher/Cargo.toml b/sdk/keccak-hasher/Cargo.toml index 4d77dba2eb8270..91fd59b41973ea 100644 --- a/sdk/keccak-hasher/Cargo.toml +++ b/sdk/keccak-hasher/Cargo.toml @@ -38,10 +38,11 @@ sha3 = { workspace = true, optional = true } solana-define-syscall = { workspace = true } [features] -borsh = ["dep:borsh"] -frozen-abi = ["dep:solana-frozen-abi", "dep:solana-frozen-abi-macro"] +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"] [lints] workspace = true diff --git a/sdk/keccak-hasher/src/lib.rs b/sdk/keccak-hasher/src/lib.rs index 5c2ec004ead454..b0a18694c50e5a 100644 --- a/sdk/keccak-hasher/src/lib.rs +++ b/sdk/keccak-hasher/src/lib.rs @@ -3,15 +3,21 @@ //! [keccak]: https://keccak.team/keccak.html #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(feature = "frozen-abi", feature(min_specialization))] +#![no_std] +#[cfg(feature = "std")] +extern crate std; -#[cfg(feature = "borsh")] -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; #[cfg(any(feature = "sha3", not(target_os = "solana")))] use sha3::{Digest, Keccak256}; 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, - std::{fmt, str::FromStr}, }; // TODO: replace this with `solana_hash::Hash` in the @@ -148,6 +154,7 @@ 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();