Skip to content

Commit

Permalink
GenesisAccount : implement deserialize_private_key (#1447)
Browse files Browse the repository at this point in the history
* GenesisAccount : implement deserialize_private_key

* fix no std
  • Loading branch information
tcoratger authored Oct 10, 2024
1 parent 7645ad4 commit ef9b58b
Showing 1 changed file with 94 additions and 3 deletions.
97 changes: 94 additions & 3 deletions crates/genesis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

extern crate alloc;

use alloc::collections::BTreeMap;
use alloc::{collections::BTreeMap, string::String};
use alloy_primitives::{Address, Bytes, B256, U256};
use alloy_serde::{storage::deserialize_storage_map, ttd::deserialize_json_ttd_opt, OtherFields};
use serde::{Deserialize, Serialize};
use core::str::FromStr;
use serde::{de::Error as DeError, Deserialize, Deserializer, Serialize};

/// The genesis block specification.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -216,7 +217,12 @@ pub struct GenesisAccount {
)]
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")]
#[serde(
rename = "secretKey",
default,
skip_serializing_if = "Option::is_none",
deserialize_with = "deserialize_private_key"
)]
pub private_key: Option<B256>,
}

Expand Down Expand Up @@ -246,6 +252,28 @@ impl GenesisAccount {
}
}

/// Custom deserialization function for the private key.
///
/// This function allows the private key to be deserialized from a string or a `null` value.
///
/// We need a custom function here especially to handle the case where the private key is `0x` and
/// should be deserialized as `None`.
fn deserialize_private_key<'de, D>(deserializer: D) -> Result<Option<B256>, D::Error>
where
D: Deserializer<'de>,
{
let opt_str: Option<String> = Option::deserialize(deserializer)?;

if let Some(ref s) = opt_str {
if s == "0x" {
return Ok(None);
}
B256::from_str(s).map(Some).map_err(D::Error::custom)
} else {
Ok(None)
}
}

/// Defines core blockchain settings per block.
///
/// Tailors unique settings for each network based on its genesis block.
Expand Down Expand Up @@ -588,6 +616,7 @@ mod tests {
use alloc::vec;
use alloy_primitives::hex;
use core::str::FromStr;
use serde_json::json;

#[test]
fn genesis_defaults_config() {
Expand Down Expand Up @@ -1530,4 +1559,66 @@ mod tests {
let actual_object_value = genesis.config.extra_fields.get("object_field").unwrap();
assert_eq!(actual_object_value, &serde_json::json!({"sub_field": "sub_value"}));
}

#[test]
fn deserialize_private_key_as_none_when_0x() {
// Test case where "secretKey" is "0x", expecting None
let json_data = json!({
"balance": "0x0",
"secretKey": "0x"
});

let account: GenesisAccount = serde_json::from_value(json_data).unwrap();
assert_eq!(account.private_key, None);
}

#[test]
fn deserialize_private_key_with_valid_hex() {
// Test case where "secretKey" is a valid hex string
let json_data = json!({
"balance": "0x0",
"secretKey": "0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234"
});

let account: GenesisAccount = serde_json::from_value(json_data).unwrap();
let expected_key =
B256::from_str("123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234")
.unwrap();
assert_eq!(account.private_key, Some(expected_key));
}

#[test]
fn deserialize_private_key_as_none_when_null() {
// Test case where "secretKey" is null, expecting None
let json_data = json!({
"balance": "0x0",
"secretKey": null
});

let account: GenesisAccount = serde_json::from_value(json_data).unwrap();
assert_eq!(account.private_key, None);
}

#[test]
fn deserialize_private_key_with_invalid_hex_fails() {
// Test case where "secretKey" is an invalid hex string, expecting an error
let json_data = json!({
"balance": "0x0",
"secretKey": "0xINVALIDHEX"
});

let result: Result<GenesisAccount, _> = serde_json::from_value(json_data);
assert!(result.is_err()); // The deserialization should fail due to invalid hex
}

#[test]
fn deserialize_private_key_with_empty_string_fails() {
// Test case where "secretKey" is an empty string, expecting an error
let json_data = json!({
"secretKey": ""
});

let result: Result<GenesisAccount, _> = serde_json::from_value(json_data);
assert!(result.is_err()); // The deserialization should fail due to an empty string
}
}

0 comments on commit ef9b58b

Please sign in to comment.