feat: add secret-key command line option (#1946)

This commit is contained in:
Ryan Orendorff
2023-04-08 03:48:21 +01:00
committed by GitHub
parent 1bf5c7dce3
commit eca6dd01ae
11 changed files with 137 additions and 14 deletions

3
Cargo.lock generated
View File

@ -4447,6 +4447,7 @@ dependencies = [
"eyre", "eyre",
"fdlimit", "fdlimit",
"futures", "futures",
"hex",
"human_bytes", "human_bytes",
"hyper", "hyper",
"jsonrpsee", "jsonrpsee",
@ -4478,10 +4479,12 @@ dependencies = [
"reth-tasks", "reth-tasks",
"reth-tracing", "reth-tracing",
"reth-transaction-pool", "reth-transaction-pool",
"secp256k1 0.26.0",
"serde", "serde",
"serde_json", "serde_json",
"shellexpand", "shellexpand",
"tempfile", "tempfile",
"thiserror",
"tokio", "tokio",
"tracing", "tracing",
"tui", "tui",

View File

@ -34,6 +34,13 @@ reth-net-nat = { path = "../../crates/net/nat" }
reth-miner = { path = "../../crates/miner" } reth-miner = { path = "../../crates/miner" }
reth-discv4 = { path = "../../crates/net/discv4" } reth-discv4 = { path = "../../crates/net/discv4" }
# crypto
secp256k1 = { version = "0.26.0", features = [
"global-context",
"rand-std",
"recovery",
] }
# tracing # tracing
tracing = "0.1" tracing = "0.1"
@ -72,3 +79,5 @@ eyre = "0.6.8"
clap = { version = "4", features = ["derive", "cargo"] } clap = { version = "4", features = ["derive", "cargo"] }
tempfile = { version = "3.3.0" } tempfile = { version = "3.3.0" }
backon = "0.4" backon = "0.4"
hex = "0.4"
thiserror = "1.0"

View File

@ -11,3 +11,6 @@ pub use rpc_server_args::RpcServerArgs;
/// DebugArgs struct for debugging purposes /// DebugArgs struct for debugging purposes
mod debug_args; mod debug_args;
pub use debug_args::DebugArgs; pub use debug_args::DebugArgs;
mod secret_key;
pub use secret_key::{get_secret_key, SecretKeyError};

View File

@ -6,6 +6,7 @@ use reth_net_nat::NatResolver;
use reth_network::NetworkConfigBuilder; use reth_network::NetworkConfigBuilder;
use reth_primitives::{mainnet_nodes, ChainSpec, NodeRecord}; use reth_primitives::{mainnet_nodes, ChainSpec, NodeRecord};
use reth_staged_sync::Config; use reth_staged_sync::Config;
use secp256k1::SecretKey;
use std::{path::PathBuf, sync::Arc}; use std::{path::PathBuf, sync::Arc};
/// Parameters for configuring the network more granularity via CLI /// Parameters for configuring the network more granularity via CLI
@ -57,11 +58,12 @@ impl NetworkArgs {
&self, &self,
config: &Config, config: &Config,
chain_spec: Arc<ChainSpec>, chain_spec: Arc<ChainSpec>,
secret_key: SecretKey,
) -> NetworkConfigBuilder { ) -> NetworkConfigBuilder {
let chain_bootnodes = chain_spec.chain.bootnodes().unwrap_or_else(mainnet_nodes); let chain_bootnodes = chain_spec.chain.bootnodes().unwrap_or_else(mainnet_nodes);
let network_config_builder = config let network_config_builder = config
.network_config(self.nat, self.persistent_peers_file()) .network_config(self.nat, self.persistent_peers_file(), secret_key)
.boot_nodes(self.bootnodes.clone().unwrap_or(chain_bootnodes)) .boot_nodes(self.bootnodes.clone().unwrap_or(chain_bootnodes))
.chain_spec(chain_spec); .chain_spec(chain_spec);

View File

@ -0,0 +1,49 @@
use crate::dirs::{PlatformPath, SecretKeyPath};
use hex::encode as hex_encode;
use reth_network::config::rng_secret_key;
use secp256k1::{Error as SecretKeyBaseError, SecretKey};
use std::fs::read_to_string;
use thiserror::Error;
/// Errors returned by loading a [`SecretKey`][secp256k1::SecretKey], including IO errors.
#[derive(Error, Debug)]
#[allow(missing_docs)]
pub enum SecretKeyError {
#[error(transparent)]
SecretKeyDecodeError(#[from] SecretKeyBaseError),
#[error("An I/O error occurred: {0}")]
IOError(#[from] std::io::Error),
}
/// Attempts to load a [`SecretKey`] from a specified path. If no file exists
/// there, then it generates a secret key and stores it in the default path. I/O
/// errors might occur during write operations in the form of a
/// [`SecretKeyError`]
pub fn get_secret_key(
secret_key_path: &PlatformPath<SecretKeyPath>,
) -> Result<SecretKey, SecretKeyError> {
let fpath = secret_key_path.as_ref();
let exists = fpath.try_exists();
match exists {
Ok(true) => {
let contents = read_to_string(fpath)?;
(contents.as_str().parse::<SecretKey>()).map_err(SecretKeyError::SecretKeyDecodeError)
}
Ok(false) => {
let default_path = PlatformPath::<SecretKeyPath>::default();
let fpath = default_path.as_ref();
if let Some(dir) = fpath.parent() {
// Create parent directory
std::fs::create_dir_all(dir)?
}
let secret = rng_secret_key();
let hex = hex_encode(secret.as_ref());
std::fs::write(fpath, hex)?;
Ok(secret)
}
Err(e) => Err(SecretKeyError::IOError(e)),
}
}

View File

@ -56,6 +56,13 @@ pub fn net_dir() -> Option<PathBuf> {
data_dir().map(|root| root.join("net")) data_dir().map(|root| root.join("net"))
} }
/// Returns the path to the reth secret key directory.
///
/// Refer to [dirs_next::data_dir] for cross-platform behavior.
pub fn p2p_secret_key_dir() -> Option<PathBuf> {
data_dir().map(|root| root.join("p2p"))
}
/// Returns the path to the reth database. /// Returns the path to the reth database.
/// ///
/// Refer to [dirs_next::data_dir] for cross-platform behavior. /// Refer to [dirs_next::data_dir] for cross-platform behavior.
@ -121,6 +128,19 @@ impl XdgPath for LogsDir {
} }
} }
/// Returns the path to the default reth secret key directory.
///
/// Refer to [dirs_next::data_dir] for cross-platform behavior.
#[derive(Default, Debug, Clone)]
#[non_exhaustive]
pub struct SecretKeyPath;
impl XdgPath for SecretKeyPath {
fn resolve() -> Option<PathBuf> {
p2p_secret_key_dir().map(|p| p.join("secret"))
}
}
/// A small helper trait for unit structs that represent a standard path following the XDG /// A small helper trait for unit structs that represent a standard path following the XDG
/// path specification. /// path specification.
pub trait XdgPath { pub trait XdgPath {

View File

@ -2,8 +2,8 @@
//! //!
//! Starts the client //! Starts the client
use crate::{ use crate::{
args::{DebugArgs, NetworkArgs, RpcServerArgs}, args::{get_secret_key, DebugArgs, NetworkArgs, RpcServerArgs},
dirs::{ConfigPath, DbPath, PlatformPath}, dirs::{ConfigPath, DbPath, PlatformPath, SecretKeyPath},
prometheus_exporter, prometheus_exporter,
runner::CliContext, runner::CliContext,
utils::get_single_header, utils::get_single_header,
@ -59,6 +59,7 @@ use reth_stages::{
}; };
use reth_tasks::TaskExecutor; use reth_tasks::TaskExecutor;
use reth_transaction_pool::{EthTransactionValidator, TransactionPool}; use reth_transaction_pool::{EthTransactionValidator, TransactionPool};
use secp256k1::SecretKey;
use std::{ use std::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4}, net::{Ipv4Addr, SocketAddr, SocketAddrV4},
path::PathBuf, path::PathBuf,
@ -109,6 +110,12 @@ pub struct Command {
)] )]
chain: Arc<ChainSpec>, chain: Arc<ChainSpec>,
/// Secret key to use for this node.
///
/// This also will deterministically set the peer ID.
#[arg(long, value_name = "PATH", global = true, required = false, default_value_t)]
p2p_secret_key: PlatformPath<SecretKeyPath>,
/// Enable Prometheus metrics. /// Enable Prometheus metrics.
/// ///
/// The metrics will be served at the given interface and port. /// The metrics will be served at the given interface and port.
@ -168,8 +175,13 @@ impl Command {
info!(target: "reth::cli", "Test transaction pool initialized"); info!(target: "reth::cli", "Test transaction pool initialized");
info!(target: "reth::cli", "Connecting to P2P network"); info!(target: "reth::cli", "Connecting to P2P network");
let network_config = let secret_key = get_secret_key(&self.p2p_secret_key)?;
self.load_network_config(&config, Arc::clone(&db), ctx.task_executor.clone()); let network_config = self.load_network_config(
&config,
Arc::clone(&db),
ctx.task_executor.clone(),
secret_key,
);
let network = self let network = self
.start_network(network_config, &ctx.task_executor, transaction_pool.clone()) .start_network(network_config, &ctx.task_executor, transaction_pool.clone())
.await?; .await?;
@ -548,11 +560,12 @@ impl Command {
config: &Config, config: &Config,
db: Arc<Env<WriteMap>>, db: Arc<Env<WriteMap>>,
executor: TaskExecutor, executor: TaskExecutor,
secret_key: SecretKey,
) -> NetworkConfig<ShareableDatabase<Arc<Env<WriteMap>>>> { ) -> NetworkConfig<ShareableDatabase<Arc<Env<WriteMap>>>> {
let head = self.lookup_head(Arc::clone(&db)).expect("the head block is missing"); let head = self.lookup_head(Arc::clone(&db)).expect("the head block is missing");
self.network self.network
.network_config(config, self.chain.clone()) .network_config(config, self.chain.clone(), secret_key)
.with_task_executor(Box::new(executor)) .with_task_executor(Box::new(executor))
.set_head(head) .set_head(head)
.listener_addr(SocketAddr::V4(SocketAddrV4::new( .listener_addr(SocketAddr::V4(SocketAddrV4::new(

View File

@ -1,7 +1,7 @@
//! P2P Debugging tool //! P2P Debugging tool
use crate::{ use crate::{
args::DiscoveryArgs, args::{get_secret_key, DiscoveryArgs},
dirs::{ConfigPath, PlatformPath}, dirs::{ConfigPath, PlatformPath, SecretKeyPath},
utils::get_single_header, utils::get_single_header,
}; };
use backon::{ConstantBuilder, Retryable}; use backon::{ConstantBuilder, Retryable};
@ -41,6 +41,12 @@ pub struct Command {
)] )]
chain: Arc<ChainSpec>, chain: Arc<ChainSpec>,
/// Secret key to use for this node.
///
/// This also will deterministically set the peer ID.
#[arg(long, value_name = "PATH", global = true, required = false, default_value_t)]
p2p_secret_key: PlatformPath<SecretKeyPath>,
/// Disable the discovery service. /// Disable the discovery service.
#[command(flatten)] #[command(flatten)]
pub discovery: DiscoveryArgs, pub discovery: DiscoveryArgs,
@ -98,8 +104,10 @@ impl Command {
config.peers.connect_trusted_nodes_only = self.trusted_only; config.peers.connect_trusted_nodes_only = self.trusted_only;
let p2p_secret_key = get_secret_key(&self.p2p_secret_key)?;
let mut network_config_builder = let mut network_config_builder =
config.network_config(self.nat, None).chain_spec(self.chain.clone()); config.network_config(self.nat, None, p2p_secret_key).chain_spec(self.chain.clone());
network_config_builder = self.discovery.apply_to_builder(network_config_builder); network_config_builder = self.discovery.apply_to_builder(network_config_builder);

View File

@ -2,8 +2,8 @@
//! //!
//! Stage debugging tool //! Stage debugging tool
use crate::{ use crate::{
args::NetworkArgs, args::{get_secret_key, NetworkArgs},
dirs::{ConfigPath, DbPath, PlatformPath}, dirs::{ConfigPath, DbPath, PlatformPath, SecretKeyPath},
prometheus_exporter, StageEnum, prometheus_exporter, StageEnum,
}; };
use clap::Parser; use clap::Parser;
@ -56,6 +56,12 @@ pub struct Command {
)] )]
chain: Arc<ChainSpec>, chain: Arc<ChainSpec>,
/// Secret key to use for this node.
///
/// This also will deterministically set the peer ID.
#[arg(long, value_name = "PATH", global = true, required = false, default_value_t)]
p2p_secret_key: PlatformPath<SecretKeyPath>,
/// Enable Prometheus metrics. /// Enable Prometheus metrics.
/// ///
/// The metrics will be served at the given interface and port. /// The metrics will be served at the given interface and port.
@ -125,9 +131,11 @@ impl Command {
}); });
} }
let p2p_secret_key = get_secret_key(&self.p2p_secret_key)?;
let network = self let network = self
.network .network
.network_config(&config, self.chain.clone()) .network_config(&config, self.chain.clone(), p2p_secret_key)
.build(Arc::new(ShareableDatabase::new(db.clone(), self.chain.clone()))) .build(Arc::new(ShareableDatabase::new(db.clone(), self.chain.clone())))
.start_network() .start_network()
.await?; .await?;

View File

@ -30,6 +30,11 @@ tracing = "0.1.37"
# crypto # crypto
rand = { version = "0.8", optional = true } rand = { version = "0.8", optional = true }
secp256k1 = { version = "0.26.0", features = [
"global-context",
"rand-std",
"recovery",
] }
# errors # errors
thiserror = "1" thiserror = "1"

View File

@ -4,7 +4,8 @@ use reth_downloaders::{
bodies::bodies::BodiesDownloaderBuilder, bodies::bodies::BodiesDownloaderBuilder,
headers::reverse_headers::ReverseHeadersDownloaderBuilder, headers::reverse_headers::ReverseHeadersDownloaderBuilder,
}; };
use reth_network::{config::rng_secret_key, NetworkConfigBuilder, PeersConfig}; use reth_network::{NetworkConfigBuilder, PeersConfig};
use secp256k1::SecretKey;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
@ -25,15 +26,17 @@ impl Config {
&self, &self,
nat_resolution_method: reth_net_nat::NatResolver, nat_resolution_method: reth_net_nat::NatResolver,
peers_file: Option<PathBuf>, peers_file: Option<PathBuf>,
secret_key: SecretKey,
) -> NetworkConfigBuilder { ) -> NetworkConfigBuilder {
let peer_config = self let peer_config = self
.peers .peers
.clone() .clone()
.with_basic_nodes_from_file(peers_file) .with_basic_nodes_from_file(peers_file)
.unwrap_or_else(|_| self.peers.clone()); .unwrap_or_else(|_| self.peers.clone());
let discv4 = let discv4 =
Discv4Config::builder().external_ip_resolver(Some(nat_resolution_method)).clone(); Discv4Config::builder().external_ip_resolver(Some(nat_resolution_method)).clone();
NetworkConfigBuilder::new(rng_secret_key()).peer_config(peer_config).discovery(discv4) NetworkConfigBuilder::new(secret_key).peer_config(peer_config).discovery(discv4)
} }
} }