diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 420f4cadb..8a6cbe7b2 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -25,6 +25,7 @@ rustdoc-args = [ workspace = true [dependencies] +arrayvec.workspace=true bytes.workspace = true hex.workspace = true itoa.workspace = true @@ -114,6 +115,7 @@ serde_json.workspace = true [features] default = ["std", "map", "map-foldhash"] std = [ + "alloc", "bytes/std", "hex/std", "ruint/std", @@ -129,6 +131,12 @@ std = [ "serde?/std", "sha3?/std", ] +alloc = [ + "proptest?/alloc", + "ruint/alloc", + "serde?/alloc", + "hex/alloc", +] nightly = [ "foldhash?/nightly", "hashbrown?/nightly", diff --git a/crates/primitives/src/bits/address.rs b/crates/primitives/src/bits/address.rs index d6b122881..47c105067 100644 --- a/crates/primitives/src/bits/address.rs +++ b/crates/primitives/src/bits/address.rs @@ -1,6 +1,7 @@ use crate::{FixedBytes, aliases::U160, utils::keccak256}; +use core::borrow::Borrow; +#[cfg(feature = "alloc")] use alloc::{ - borrow::Borrow, string::{String, ToString}, }; use core::{fmt, mem::MaybeUninit, str}; @@ -579,6 +580,7 @@ impl AddressChecksumBuffer { } /// Returns the checksum of a formatted address. + #[cfg(feature = "alloc")] #[inline] #[allow(clippy::inherent_to_string_shadow_display)] pub fn to_string(&self) -> String { diff --git a/crates/primitives/src/bits/serde.rs b/crates/primitives/src/bits/serde.rs index ce9c70e90..c2b7533ec 100644 --- a/crates/primitives/src/bits/serde.rs +++ b/crates/primitives/src/bits/serde.rs @@ -38,7 +38,15 @@ impl<'de, const N: usize> Deserialize<'de> for FixedBytes { fn visit_seq>(self, mut seq: A) -> Result { let len_error = - |i| de::Error::invalid_length(i, &format!("exactly {N} bytes").as_str()); + |i| { + // "exactly N bytes" string has len of 15, so we handle N up to 10**17 here safely. Should cover all reasonable N. + let mut string = arrayvec::ArrayString::<32>::new(); + if fmt::write(&mut string, format_args!("exactly {N} bytes")).is_ok() { + de::Error::invalid_length(i, &string.as_str()) + } else { + de::Error::invalid_length(i, &"exactly N bytes") + } + }; let mut bytes = [0u8; N]; for (i, byte) in bytes.iter_mut().enumerate() { diff --git a/crates/primitives/src/bytes/mod.rs b/crates/primitives/src/bytes/mod.rs index b26fe5a82..c53c96b24 100644 --- a/crates/primitives/src/bytes/mod.rs +++ b/crates/primitives/src/bytes/mod.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "alloc")] + use crate::FixedBytes; use alloc::{boxed::Box, vec::Vec}; use core::{ diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 2d5ef25cd..3c90e46c4 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -8,6 +8,8 @@ #![cfg_attr(feature = "nightly", feature(hasher_prefixfree_extras))] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(feature = "alloc")] +#[allow(unused_imports)] #[macro_use] extern crate alloc; @@ -70,6 +72,7 @@ pub use signature::PrimitiveSignature; pub use signature::{Signature, SignatureError, normalize_v, to_eip155_v}; pub mod utils; +#[cfg(feature = "alloc")] pub use utils::{KECCAK256_EMPTY, Keccak256, eip191_hash_message, keccak256}; #[doc(hidden)] // Use `hex` directly instead! @@ -107,6 +110,7 @@ pub type B160 = FixedBytes<20>; // Not public API. #[doc(hidden)] pub mod private { + #[cfg(feature = "alloc")] pub use alloc::vec::Vec; pub use core::{ self, diff --git a/crates/primitives/src/log/mod.rs b/crates/primitives/src/log/mod.rs index d5d095770..b33c9df07 100644 --- a/crates/primitives/src/log/mod.rs +++ b/crates/primitives/src/log/mod.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "alloc")] // logs are a wrapper over Vec basically, so there is no point in having them without alloc + use crate::{Address, B256, Bloom, Bytes}; use alloc::vec::Vec; diff --git a/crates/primitives/src/signature/sig.rs b/crates/primitives/src/signature/sig.rs index ff9cb5155..f388d4ecb 100644 --- a/crates/primitives/src/signature/sig.rs +++ b/crates/primitives/src/signature/sig.rs @@ -1,6 +1,7 @@ #![allow(clippy::missing_const_for_fn)] // On purpose for forward compatibility. use crate::{B256, U256, hex, normalize_v, signature::SignatureError, uint}; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use core::{fmt::Display, str::FromStr}; @@ -60,6 +61,7 @@ impl From for [u8; 65] { } } +#[cfg(feature = "alloc")] impl From<&Signature> for Vec { #[inline] fn from(value: &Signature) -> Self { @@ -67,6 +69,7 @@ impl From<&Signature> for Vec { } } +#[cfg(feature = "alloc")] impl From for Vec { #[inline] fn from(value: Signature) -> Self { diff --git a/crates/primitives/src/signed/conversions.rs b/crates/primitives/src/signed/conversions.rs index 9f0d57f92..50023cd29 100644 --- a/crates/primitives/src/signed/conversions.rs +++ b/crates/primitives/src/signed/conversions.rs @@ -1,4 +1,5 @@ use super::{BigIntConversionError, ParseSignedError, Sign, Signed, utils::twos_complement}; +#[cfg(feature = "alloc")] use alloc::string::String; use core::str::FromStr; use ruint::{ToUintError, Uint, UintTryFrom}; @@ -95,6 +96,7 @@ impl TryFrom<&str> for Signed TryFrom<&String> for Signed { type Error = ParseSignedError; @@ -104,6 +106,7 @@ impl TryFrom<&String> for Signed TryFrom for Signed { type Error = ParseSignedError; diff --git a/crates/primitives/src/signed/int.rs b/crates/primitives/src/signed/int.rs index f1d10d77b..6376f5739 100644 --- a/crates/primitives/src/signed/int.rs +++ b/crates/primitives/src/signed/int.rs @@ -1,4 +1,5 @@ use super::{ParseSignedError, Sign, utils::*}; +#[cfg(feature = "alloc")] use alloc::string::String; use core::fmt; use ruint::{BaseConvertError, Uint, UintTryFrom, UintTryTo}; @@ -367,6 +368,7 @@ impl Signed { } /// Convert to a decimal string. + #[cfg(feature = "alloc")] pub fn to_dec_string(&self) -> String { let sign = self.sign(); let abs = self.unsigned_abs(); @@ -393,6 +395,7 @@ impl Signed { } /// Convert to a hex string. + #[cfg(feature = "alloc")] pub fn to_hex_string(&self) -> String { let sign = self.sign(); let abs = self.unsigned_abs(); diff --git a/crates/primitives/src/signed/serde.rs b/crates/primitives/src/signed/serde.rs index d51630dcf..5e99c39e8 100644 --- a/crates/primitives/src/signed/serde.rs +++ b/crates/primitives/src/signed/serde.rs @@ -1,4 +1,5 @@ use super::Signed; +#[cfg(feature = "alloc")] use alloc::string::String; use core::{fmt, str}; use ruint::Uint; @@ -55,6 +56,7 @@ fn deserialize<'de, const BITS: usize, const LIMBS: usize, D: Deserializer<'de>> v.parse().map_err(serde::de::Error::custom) } + #[cfg(feature = "alloc")] fn visit_string(self, v: String) -> Result { self.visit_str(&v) } diff --git a/crates/primitives/src/utils/mod.rs b/crates/primitives/src/utils/mod.rs index 4d0f66692..05f668222 100644 --- a/crates/primitives/src/utils/mod.rs +++ b/crates/primitives/src/utils/mod.rs @@ -1,23 +1,28 @@ //! Common Ethereum utilities. use crate::B256; + +#[cfg(feature = "alloc")] use alloc::{boxed::Box, collections::TryReserveError, vec::Vec}; use cfg_if::cfg_if; use core::{ fmt, - mem::{ManuallyDrop, MaybeUninit}, + mem::MaybeUninit, }; mod units; +#[cfg(feature = "alloc")] pub use units::{ DecimalSeparator, ParseUnits, Unit, UnitsError, format_ether, format_units, format_units_with, parse_ether, parse_units, }; +#[cfg(feature = "alloc")] #[doc(hidden)] #[deprecated(since = "0.5.0", note = "use `Unit::ETHER.wei()` instead")] pub const WEI_IN_ETHER: crate::U256 = Unit::ETHER.wei_const(); +#[cfg(feature = "alloc")] #[doc(hidden)] #[deprecated(since = "0.5.0", note = "use `Unit` instead")] pub type Units = Unit; @@ -51,6 +56,7 @@ macro_rules! try_vec { /// /// Stable version of `Box::try_new`. #[inline] +#[cfg(feature = "alloc")] pub fn box_try_new(value: T) -> Result, TryReserveError> { let mut boxed = box_try_new_uninit::()?; unsafe { @@ -64,6 +70,7 @@ pub fn box_try_new(value: T) -> Result, TryReserveError> { /// allocation fails. /// /// Stable version of `Box::try_new_uninit`. +#[cfg(feature = "alloc")] #[inline] pub fn box_try_new_uninit() -> Result>, TryReserveError> { let mut vec = Vec::>::new(); @@ -75,13 +82,14 @@ pub fn box_try_new_uninit() -> Result>, TryReserveError> { // Make sure we got exactly 1 element. vec.shrink_to(1); - let mut vec = ManuallyDrop::new(vec); + let mut vec = core::mem::ManuallyDrop::new(vec); // SAFETY: `vec` is exactly one element long and has not been deallocated. Ok(unsafe { Box::from_raw(vec.as_mut_ptr()) }) } /// Tries to collect the elements of an iterator into a `Vec`. +#[cfg(feature = "alloc")] pub fn try_collect_vec, T>(iter: I) -> Result, TryReserveError> { let mut vec = Vec::new(); if let Some(size_hint) = iter.size_hint().1 { @@ -92,6 +100,7 @@ pub fn try_collect_vec, T>(iter: I) -> Result, TryR } /// Tries to create a `Vec` with the given capacity. +#[cfg(feature = "alloc")] #[inline] pub fn vec_try_with_capacity(capacity: usize) -> Result, TryReserveError> { let mut vec = Vec::new(); @@ -100,6 +109,7 @@ pub fn vec_try_with_capacity(capacity: usize) -> Result, TryReserveErr /// Tries to create a `Vec` of `n` elements, each initialized to `elem`. // Not public API. Use `try_vec!` instead. +#[cfg(feature = "alloc")] #[doc(hidden)] pub fn vec_try_from_elem(elem: T, n: usize) -> Result, TryReserveError> { let mut vec = Vec::new(); @@ -116,6 +126,7 @@ pub fn vec_try_from_elem(elem: T, n: usize) -> Result, TryReser /// This message is then hashed using [Keccak-256](keccak256). /// /// [EIP-191]: https://eips.ethereum.org/EIPS/eip-191 +#[cfg(feature = "alloc")] pub fn eip191_hash_message>(message: T) -> B256 { keccak256(eip191_message(message)) } @@ -126,6 +137,7 @@ pub fn eip191_hash_message>(message: T) -> B256 { /// `"\x19Ethereum Signed Message:\n" + message.length + message` /// /// [EIP-191]: https://eips.ethereum.org/EIPS/eip-191 +#[cfg(feature = "alloc")] pub fn eip191_message>(message: T) -> Vec { fn eip191_message(message: &[u8]) -> Vec { let len = message.len(); diff --git a/crates/primitives/src/utils/units.rs b/crates/primitives/src/utils/units.rs index fc561b1b9..b79f81856 100644 --- a/crates/primitives/src/utils/units.rs +++ b/crates/primitives/src/utils/units.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "alloc")] + use crate::{I256, ParseSignedError, U256}; use alloc::string::{String, ToString}; use core::fmt;