diff --git a/Cargo.toml b/Cargo.toml index 75d3eed..dbe6022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,13 @@ categories = ["cryptography::cryptocurrencies"] rust-version = "1.83.0" [dependencies] +spin = { version = "0.9", default-features = false, features = ["lazy", "once"], optional = true } ring = { version = "0.17", optional = true } -sha2 = { version = "0.10", optional = true } +sha2 = { version = "0.10", default-features = false, optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] cpufeatures = "0.2" -sha2 = "0.10" +sha2 = { version = "0.10", default-features = false } [dev-dependencies] rustc-hex = "2" @@ -26,8 +27,10 @@ rustc-hex = "2" wasm-bindgen-test = "0.3.33" [features] -default = ["zero_hash_cache", "ring"] -zero_hash_cache = [] +default = ["std", "zero_hash_cache", "ring"] +std = ["sha2?/std", "ring?/std"] +# spin backs the zero hash cache only under no_std; std builds use std::sync::LazyLock. +zero_hash_cache = ["dep:spin"] ring = ["dep:ring"] sha2 = ["dep:sha2"] # Enable ring's WASM support (wasm32-unknown-unknown target). diff --git a/src/lib.rs b/src/lib.rs index a139d3f..c078fbd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,24 @@ //! Now this crate serves primarily as a wrapper over two SHA256 crates: `sha2` and `ring` – which //! it switches between at runtime based on the availability of SHA intrinsics. +#![cfg_attr(not(feature = "std"), no_std)] + +// Selecting a backend is the consumer's choice, so say so once and clearly rather than letting +// the failure surface as cascading type errors from an empty `DynamicImpl`. +#[cfg(all( + not(feature = "ring"), + not(feature = "sha2"), + not(target_arch = "x86_64") +))] +compile_error!( + "ethereum_hashing needs a sha256 backend: enable the `sha2` feature (portable, required for \ + wasm) or the `ring` feature. With --no-default-features neither is on by default." +); + +extern crate alloc; + +use alloc::{vec, vec::Vec}; + #[cfg(test)] mod tests; @@ -17,8 +35,12 @@ pub use self::DynamicContext as Context; #[cfg(any(target_arch = "x86_64", feature = "sha2"))] use sha2_impl::Sha2CrateImpl; -#[cfg(feature = "zero_hash_cache")] -use std::sync::LazyLock; +#[cfg(all(feature = "zero_hash_cache", feature = "std"))] +use std::sync::LazyLock as Lazy; +// `spin` only where there is no `std::sync`: a spin lock is strictly worse under contention, so +// std builds keep the futex backed one. +#[cfg(all(feature = "zero_hash_cache", not(feature = "std")))] +use spin::Lazy; /// Length of a SHA256 hash in bytes. pub const HASH_LEN: usize = 32; @@ -151,18 +173,6 @@ impl DynamicImpl { { Self::Ring } - - // Compile error if no implementation available - #[cfg(all( - not(feature = "ring"), - not(target_arch = "x86_64"), - not(feature = "sha2") - ))] - { - compile_error!( - "Either 'ring' or 'sha2' feature must be enabled on non-x86_64 architectures" - ); - } } } @@ -235,7 +245,7 @@ pub const ZERO_HASHES_MAX_INDEX: usize = 48; #[cfg(feature = "zero_hash_cache")] /// Cached zero hashes where `ZERO_HASHES[i]` is the hash of a Merkle tree with 2^i zero leaves. -pub static ZERO_HASHES: LazyLock> = LazyLock::new(|| { +pub static ZERO_HASHES: Lazy> = Lazy::new(|| { let mut hashes = vec![[0; HASH_LEN]; ZERO_HASHES_MAX_INDEX + 1]; for i in 0..ZERO_HASHES_MAX_INDEX { diff --git a/src/sha2_impl.rs b/src/sha2_impl.rs index 85f5b10..3a8df9a 100644 --- a/src/sha2_impl.rs +++ b/src/sha2_impl.rs @@ -2,6 +2,7 @@ #![cfg(any(target_arch = "x86_64", feature = "sha2"))] use crate::{Sha256, Sha256Context, HASH_LEN}; +use alloc::vec::Vec; use sha2::Digest; /// Implementation of SHA256 using the `sha2` crate (fastest on x86_64 CPUs with SHA extensions).