Skip to content

Commit

Permalink
feat: support no_std for alloy-genesis/alloy-serde (#320)
Browse files Browse the repository at this point in the history
* feat: support no_std for alloy-genesis/alloy-serde

* remove unused file

* fmt

* fix for tests

* adjust a little

* clippy warnings

* clippy warnings

* use feature gate

* fix feature gate

* fmt

* Update Cargo.toml

* Update README.md

Co-authored-by: Oliver Nordbjerg <[email protected]>

---------

Co-authored-by: Matthias Seitz <[email protected]>
Co-authored-by: James Prestwich <[email protected]>
Co-authored-by: Oliver Nordbjerg <[email protected]>
  • Loading branch information
4 people authored Mar 21, 2024
1 parent fd8f065 commit d3af591
Show file tree
Hide file tree
Showing 10 changed files with 75 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ thiserror-no-std = "2.0.2"
url = "2.5"

## serde
serde = { version = "1.0", features = ["derive"], default-features = false }
serde = { version = "1.0", default-features = false, features = ["derive"] }
serde_json = "1.0"

## misc-testing
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ contribution follows the linting rules and passes clippy.
## Note on `no_std`

Because these crates are primarily json-rpc focused, we do not intend to support
`no_std` for them at this time.
`no_std` for most of them at this time.
The following crates support `no_std`:
- alloy-eips
- alloy-genesis
- alloy-serde

## Credits

Expand Down
9 changes: 9 additions & 0 deletions crates/genesis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,12 @@ serde.workspace = true

[dev-dependencies]
serde_json.workspace = true

[features]
default = ["std"]
std = [
"alloy-primitives/std",
"alloy-serde/std",
# serde
"serde/std",
]
33 changes: 19 additions & 14 deletions crates/genesis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

use alloc::collections::BTreeMap;

use alloy_primitives::{Address, Bytes, B256, U256};
use alloy_serde::{
Expand All @@ -24,7 +29,6 @@ use alloy_serde::{
storage::deserialize_storage_map,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// The genesis block specification.
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
Expand Down Expand Up @@ -52,7 +56,7 @@ pub struct Genesis {
/// The genesis header coinbase address.
pub coinbase: Address,
/// The initial state of accounts in the genesis block.
pub alloc: HashMap<Address, GenesisAccount>,
pub alloc: BTreeMap<Address, GenesisAccount>,
// NOTE: the following fields:
// * base_fee_per_gas
// * excess_blob_gas
Expand Down Expand Up @@ -102,7 +106,7 @@ impl Genesis {
};

// fund account
let mut alloc = HashMap::new();
let mut alloc = BTreeMap::default();
alloc.insert(
signer_addr,
GenesisAccount {
Expand Down Expand Up @@ -226,7 +230,7 @@ pub struct GenesisAccount {
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_storage_map"
)]
pub storage: Option<HashMap<B256, B256>>,
pub storage: Option<BTreeMap<B256, B256>>,
/// The account's private key. Should only be used for testing.
#[serde(rename = "secretKey", default, skip_serializing_if = "Option::is_none")]
pub private_key: Option<B256>,
Expand All @@ -252,7 +256,7 @@ impl GenesisAccount {
}

/// Set the storage.
pub fn with_storage(mut self, storage: Option<HashMap<B256, B256>>) -> Self {
pub fn with_storage(mut self, storage: Option<BTreeMap<B256, B256>>) -> Self {
self.storage = storage;
self
}
Expand Down Expand Up @@ -538,8 +542,9 @@ pub struct CliqueConfig {
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
use alloy_primitives::hex;
use std::str::FromStr;
use core::str::FromStr;

#[test]
fn test_genesis() {
Expand All @@ -555,7 +560,7 @@ mod tests {
let coinbase = hex!("265873b6faf3258b3ab0827805386a2a20ed040e").into();
// create dummy account
let first_address: Address = hex!("7618a8c597b89e01c66a1f662078992c52a30c9a").into();
let mut account = HashMap::default();
let mut account = BTreeMap::default();
account.insert(first_address, GenesisAccount::default());

// check values updated
Expand Down Expand Up @@ -587,18 +592,18 @@ mod tests {
nonce: Some(1),
balance: U256::from(1),
code: Some(Bytes::from(b"code")),
storage: Some(HashMap::default()),
storage: Some(BTreeMap::default()),
private_key: None,
};
let mut updated_account = HashMap::default();
let mut updated_account = BTreeMap::default();
updated_account.insert(same_address, new_alloc_account);
let custom_genesis = custom_genesis.extend_accounts(updated_account.clone());
assert_ne!(account, updated_account);
assert_eq!(custom_genesis.alloc.len(), 1);

// add second account
let different_address = hex!("94e0681e3073dd71cec54b53afe988f39078fd1a").into();
let more_accounts = HashMap::from([(different_address, GenesisAccount::default())]);
let more_accounts = BTreeMap::from([(different_address, GenesisAccount::default())]);
let custom_genesis = custom_genesis.extend_accounts(more_accounts);
assert_eq!(custom_genesis.alloc.len(), 2);

Expand All @@ -619,7 +624,7 @@ mod tests {
let code = Some(Bytes::from(b"code"));
let root = hex!("9474ddfcea39c5a690d2744103e39d1ff1b03d18db10fc147d970ad24699395a").into();
let value = hex!("58eb8294d9bb16832a9dabfcb270fff99ab8ee1d8764e4f3d9fdf59ec1dee469").into();
let mut map = HashMap::default();
let mut map = BTreeMap::default();
map.insert(root, value);
let storage = Some(map);

Expand Down Expand Up @@ -1054,7 +1059,7 @@ mod tests {
.get(&Address::from_str("0000000000000000000000000000000000000314").unwrap())
.expect("missing account for parsed genesis");
let storage = alloc_entry.storage.as_ref().expect("missing storage for parsed genesis");
let expected_storage = HashMap::from_iter(vec![
let expected_storage = BTreeMap::from_iter(vec![
(
B256::from_str(
"0x0000000000000000000000000000000000000000000000000000000000000000",
Expand Down Expand Up @@ -1162,7 +1167,7 @@ mod tests {
excess_blob_gas: None,
blob_gas_used: None,
number: None,
alloc: HashMap::from_iter(vec![
alloc: BTreeMap::from_iter(vec![
(
Address::from_str("0xdbdbdb2cbd23b783741e8d7fcf51e459b497e4a6").unwrap(),
GenesisAccount {
Expand Down Expand Up @@ -1190,7 +1195,7 @@ mod tests {
balance: U256::from_str("0x21").unwrap(),
nonce: None,
code: None,
storage: Some(HashMap::from_iter(vec![
storage: Some(BTreeMap::from_iter(vec![
(

B256::from_str("0x0000000000000000000000000000000000000000000000000000000000000001").
Expand Down
16 changes: 12 additions & 4 deletions crates/serde/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ repository.workspace = true
exclude.workspace = true

[dependencies]
# ethereum
alloy-primitives = { workspace = true, features = ["rlp", "serde"] }

serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
# serde
serde = { workspace = true }
serde_json = { workspace = true, default-features = false, features = ["alloc"] }

[dev-dependencies]
alloy-primitives = { workspace = true, features = [
Expand All @@ -25,3 +24,12 @@ alloy-primitives = { workspace = true, features = [
"serde",
"arbitrary",
] }

[features]
default = ["std"]
std = [
"alloy-primitives/std",
# serde
"serde/std",
"serde_json/std",
]
9 changes: 8 additions & 1 deletion crates/serde/src/json_u256.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
//! Json U256 serde helpers.
#[cfg(not(feature = "std"))]
use alloc::string::String;
use alloc::{fmt, format};
use core::str::FromStr;

use alloy_primitives::U256;
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json::Value;
use std::{fmt, str::FromStr};

/// Wrapper around primitive U256 type that also supports deserializing numbers
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
Expand Down Expand Up @@ -175,6 +180,8 @@ where
#[cfg(test)]
mod test {
use super::JsonU256;
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use alloy_primitives::U256;
use serde::{Deserialize, Serialize};

Expand Down
11 changes: 8 additions & 3 deletions crates/serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,25 @@
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![deny(unused_must_use, rust_2018_idioms)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]

extern crate alloc;

use alloc::format;

use alloy_primitives::B256;
use serde::Serializer;

pub mod json_u256;
pub use json_u256::JsonU256;
pub use self::json_u256::JsonU256;

/// Helpers for dealing with numbers.
pub mod num;
pub use num::*;
pub use self::num::*;

/// Storage related helpers.
pub mod storage;
pub use storage::JsonStorageKey;
pub use self::storage::JsonStorageKey;

/// Serialize a byte vec as a hex string _without_ the "0x" prefix.
///
Expand Down
5 changes: 4 additions & 1 deletion crates/serde/src/num.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! Numeric serde helpers.
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use core::str::FromStr;

use alloy_primitives::{U256, U64};
use serde::{de, Deserialize, Deserializer, Serialize};
use std::str::FromStr;

/// A `u64` wrapper type that deserializes from hex or a u64 and serializes as hex.
///
Expand Down
15 changes: 9 additions & 6 deletions crates/serde/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
use alloc::{collections::BTreeMap, fmt::Write};

use alloy_primitives::{Bytes, B256, U256};
use serde::{Deserialize, Deserializer, Serialize};
use std::{collections::HashMap, fmt::Write};

/// A storage key type that can be serialized to and from a hex string up to 32 bytes. Used for
/// `eth_getStorageAt` and `eth_getProof` RPCs.
Expand Down Expand Up @@ -87,15 +90,15 @@ where
/// ```
pub fn deserialize_storage_map<'de, D>(
deserializer: D,
) -> Result<Option<HashMap<B256, B256>>, D::Error>
) -> Result<Option<BTreeMap<B256, B256>>, D::Error>
where
D: Deserializer<'de>,
{
let map = Option::<HashMap<Bytes, Bytes>>::deserialize(deserializer)?;
let map = Option::<BTreeMap<Bytes, Bytes>>::deserialize(deserializer)?;
match map {
Some(mut map) => {
let mut res_map = HashMap::with_capacity(map.len());
for (k, v) in map.drain() {
Some(map) => {
let mut res_map = BTreeMap::new();
for (k, v) in map {
let k_deserialized = from_bytes_to_b256::<'de, D>(k)?;
let v_deserialized = from_bytes_to_b256::<'de, D>(v)?;
res_map.insert(k_deserialized, v_deserialized);
Expand Down
18 changes: 0 additions & 18 deletions crates/serde/src/u64_hex.rs

This file was deleted.

0 comments on commit d3af591

Please sign in to comment.