feat: add --p2p-secret-key-hex which is similiar nodekeyhex in geth (#19670)

Co-authored-by: weixie.cui <weixie.cui@okg.com>
This commit is contained in:
cui
2025-11-12 20:15:03 +08:00
committed by GitHub
parent d9537a416a
commit 8479f286ea
11 changed files with 178 additions and 12 deletions

1
Cargo.lock generated
View File

@@ -9058,7 +9058,6 @@ dependencies = [
"reth-basic-payload-builder",
"reth-chain-state",
"reth-chainspec",
"reth-cli-util",
"reth-config",
"reth-consensus",
"reth-consensus-debug-client",

View File

@@ -8,7 +8,7 @@ use backon::{ConstantBuilder, Retryable};
use clap::{Parser, Subcommand};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_util::{get_secret_key, hash_or_num_value_parser};
use reth_cli_util::hash_or_num_value_parser;
use reth_config::Config;
use reth_network::{BlockDownloaderProvider, NetworkConfigBuilder};
use reth_network_p2p::bodies::client::BodiesClient;
@@ -183,9 +183,7 @@ impl<C: ChainSpecParser> DownloadArgs<C> {
config.peers.trusted_nodes_only = self.network.trusted_only;
let default_secret_key_path = data_dir.p2p_secret();
let secret_key_path =
self.network.p2p_secret_key.clone().unwrap_or(default_secret_key_path);
let p2p_secret_key = get_secret_key(&secret_key_path)?;
let p2p_secret_key = self.network.secret_key(default_secret_key_path)?;
let rlpx_socket = (self.network.addr, self.network.port).into();
let boot_nodes = self.chain.bootnodes().unwrap_or_default();

View File

@@ -12,7 +12,7 @@ pub mod allocator;
/// Helper function to load a secret key from a file.
pub mod load_secret_key;
pub use load_secret_key::get_secret_key;
pub use load_secret_key::{get_secret_key, parse_secret_key_from_hex};
/// Cli parsers functions.
pub mod parsers;

View File

@@ -30,6 +30,10 @@ pub enum SecretKeyError {
/// Path to the secret key file.
secret_file: PathBuf,
},
/// Invalid hex string format.
#[error("invalid hex string: {0}")]
InvalidHexString(String),
}
/// Attempts to load a [`SecretKey`] from a specified path. If no file exists there, then it
@@ -60,3 +64,75 @@ pub fn get_secret_key(secret_key_path: &Path) -> Result<SecretKey, SecretKeyErro
}),
}
}
/// Parses a [`SecretKey`] from a hex string.
///
/// The hex string can optionally start with "0x".
pub fn parse_secret_key_from_hex(hex_str: &str) -> Result<SecretKey, SecretKeyError> {
// Remove "0x" prefix if present
let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
// Decode the hex string
let bytes = alloy_primitives::hex::decode(hex_str)
.map_err(|e| SecretKeyError::InvalidHexString(e.to_string()))?;
// Parse into SecretKey
SecretKey::from_slice(&bytes).map_err(SecretKeyError::SecretKeyDecodeError)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_secret_key_from_hex_without_prefix() {
// Valid 32-byte hex string (64 characters)
let hex = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_ok());
let secret_key = result.unwrap();
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), hex);
}
#[test]
fn test_parse_secret_key_from_hex_with_0x_prefix() {
// Valid 32-byte hex string with 0x prefix
let hex = "0x4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_ok());
let secret_key = result.unwrap();
let expected = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), expected);
}
#[test]
fn test_parse_secret_key_from_hex_invalid_length() {
// Invalid length (not 32 bytes)
let hex = "4c0883a69102937d";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_err());
}
#[test]
fn test_parse_secret_key_from_hex_invalid_chars() {
// Invalid hex characters
let hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_err());
if let Err(SecretKeyError::InvalidHexString(_)) = result {
// Expected error type
} else {
panic!("Expected InvalidHexString error");
}
}
#[test]
fn test_parse_secret_key_from_hex_empty() {
let hex = "";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_err());
}
}

View File

@@ -15,7 +15,6 @@ workspace = true
## reth
reth-chain-state.workspace = true
reth-chainspec.workspace = true
reth-cli-util.workspace = true
reth-config.workspace = true
reth-consensus-debug-client.workspace = true
reth-consensus.workspace = true

View File

@@ -12,7 +12,6 @@ use crate::{
use alloy_eips::eip4844::env_settings::EnvKzgSettings;
use futures::Future;
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli_util::get_secret_key;
use reth_db_api::{database::Database, database_metrics::DatabaseMetrics};
use reth_exex::ExExContext;
use reth_network::{
@@ -869,9 +868,7 @@ impl<Node: FullNodeTypes> BuilderContext<Node> {
/// Get the network secret from the given data dir
fn network_secret(&self, data_dir: &ChainPath<DataDirPath>) -> eyre::Result<SecretKey> {
let network_secret_path =
self.config().network.p2p_secret_key.clone().unwrap_or_else(|| data_dir.p2p_secret());
let secret_key = get_secret_key(&network_secret_path)?;
let secret_key = self.config().network.secret_key(data_dir.p2p_secret())?;
Ok(secret_key)
}

View File

@@ -10,6 +10,7 @@ use std::{
use crate::version::version_metadata;
use clap::Args;
use reth_chainspec::EthChainSpec;
use reth_cli_util::{get_secret_key, load_secret_key::SecretKeyError};
use reth_config::Config;
use reth_discv4::{NodeRecord, DEFAULT_DISCOVERY_ADDR, DEFAULT_DISCOVERY_PORT};
use reth_discv5::{
@@ -81,9 +82,16 @@ pub struct NetworkArgs {
///
/// This will also deterministically set the peer ID. If not specified, it will be set in the
/// data dir for the chain being used.
#[arg(long, value_name = "PATH")]
#[arg(long, value_name = "PATH", conflicts_with = "p2p_secret_key_hex")]
pub p2p_secret_key: Option<PathBuf>,
/// Hex encoded secret key to use for this node.
///
/// This will also deterministically set the peer ID. Cannot be used together with
/// `--p2p-secret-key`.
#[arg(long, value_name = "HEX", conflicts_with = "p2p_secret_key")]
pub p2p_secret_key_hex: Option<B256>,
/// Do not persist peers.
#[arg(long, verbatim_doc_comment)]
pub no_persist_peers: bool,
@@ -351,6 +359,25 @@ impl NetworkArgs {
)
.await
}
/// Load the p2p secret key from the provided options.
///
/// If `p2p_secret_key_hex` is provided, it will be used directly.
/// If `p2p_secret_key` is provided, it will be loaded from the file.
/// If neither is provided, the `default_secret_key_path` will be used.
pub fn secret_key(
&self,
default_secret_key_path: PathBuf,
) -> Result<SecretKey, SecretKeyError> {
if let Some(b256) = &self.p2p_secret_key_hex {
// Use the B256 value directly (already validated as 32 bytes)
SecretKey::from_slice(b256.as_slice()).map_err(SecretKeyError::SecretKeyDecodeError)
} else {
// Load from file (either provided path or default)
let secret_key_path = self.p2p_secret_key.clone().unwrap_or(default_secret_key_path);
get_secret_key(&secret_key_path)
}
}
}
impl Default for NetworkArgs {
@@ -364,6 +391,7 @@ impl Default for NetworkArgs {
peers_file: None,
identity: version_metadata().p2p_client_version.to_string(),
p2p_secret_key: None,
p2p_secret_key_hex: None,
no_persist_peers: false,
nat: NatResolver::Any,
addr: DEFAULT_DISCOVERY_ADDR,
@@ -698,4 +726,53 @@ mod tests {
let args = CommandParser::<NetworkArgs>::parse_from(["reth"]).args;
assert!(args.required_block_hashes.is_empty());
}
#[test]
fn parse_p2p_secret_key_hex() {
let hex = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let args =
CommandParser::<NetworkArgs>::parse_from(["reth", "--p2p-secret-key-hex", hex]).args;
let expected: B256 = hex.parse().unwrap();
assert_eq!(args.p2p_secret_key_hex, Some(expected));
assert_eq!(args.p2p_secret_key, None);
}
#[test]
fn parse_p2p_secret_key_hex_with_0x_prefix() {
let hex = "0x4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let args =
CommandParser::<NetworkArgs>::parse_from(["reth", "--p2p-secret-key-hex", hex]).args;
let expected: B256 = hex.parse().unwrap();
assert_eq!(args.p2p_secret_key_hex, Some(expected));
assert_eq!(args.p2p_secret_key, None);
}
#[test]
fn test_p2p_secret_key_and_hex_are_mutually_exclusive() {
let result = CommandParser::<NetworkArgs>::try_parse_from([
"reth",
"--p2p-secret-key",
"/path/to/key",
"--p2p-secret-key-hex",
"4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f",
]);
assert!(result.is_err());
}
#[test]
fn test_secret_key_method_with_hex() {
let hex = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let args =
CommandParser::<NetworkArgs>::parse_from(["reth", "--p2p-secret-key-hex", hex]).args;
let temp_dir = std::env::temp_dir();
let default_path = temp_dir.join("default_key");
let secret_key = args.secret_key(default_path).unwrap();
// Verify the secret key matches the hex input
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), hex);
}
}

View File

@@ -160,6 +160,11 @@ Networking:
This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used.
--p2p-secret-key-hex <HEX>
Hex encoded secret key to use for this node.
This will also deterministically set the peer ID. Cannot be used together with `--p2p-secret-key`.
--no-persist-peers
Do not persist peers.

View File

@@ -106,6 +106,11 @@ Networking:
This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used.
--p2p-secret-key-hex <HEX>
Hex encoded secret key to use for this node.
This will also deterministically set the peer ID. Cannot be used together with `--p2p-secret-key`.
--no-persist-peers
Do not persist peers.

View File

@@ -106,6 +106,11 @@ Networking:
This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used.
--p2p-secret-key-hex <HEX>
Hex encoded secret key to use for this node.
This will also deterministically set the peer ID. Cannot be used together with `--p2p-secret-key`.
--no-persist-peers
Do not persist peers.

View File

@@ -210,6 +210,11 @@ Networking:
This will also deterministically set the peer ID. If not specified, it will be set in the data dir for the chain being used.
--p2p-secret-key-hex <HEX>
Hex encoded secret key to use for this node.
This will also deterministically set the peer ID. Cannot be used together with `--p2p-secret-key`.
--no-persist-peers
Do not persist peers.