mirror of
https://github.com/logos-co/nomos-node.git
synced 2026-08-27 17:41:11 +00:00
478 lines
14 KiB
Rust
478 lines
14 KiB
Rust
pub mod config;
|
|
pub mod get_peer_id;
|
|
pub mod keys;
|
|
pub mod participate;
|
|
|
|
use std::{
|
|
net::{Ipv4Addr, SocketAddr},
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use clap::{Parser, Subcommand};
|
|
use color_eyre::eyre::Result;
|
|
use lb_tui_zone::cli::NodeKeyArgs;
|
|
use lb_utils::yaml::{OnUnknownKeys, deserialize_value_at_path};
|
|
use libp2p::Multiaddr;
|
|
|
|
use crate::{
|
|
cli::{
|
|
config::migrate_0_1_2,
|
|
keys::{AddKeyArgs, GenerateKeyArgs, RemoveKeyArgs},
|
|
},
|
|
config::{
|
|
ApiArgs, BlendArgs, CryptarchiaArgs, DeploymentArgs, DeploymentSettings, LogArgs,
|
|
NetworkArgs, RunConfig, SdpArgs, StateArgs, UserConfig, api::serde::AxumBackendSettings,
|
|
blend::serde::core::BackendConfig as BlendCoreConfig, network::serde::SwarmConfig,
|
|
update_api, update_blend, update_cryptarchia, update_network, update_sdp, update_state,
|
|
update_tracing,
|
|
},
|
|
};
|
|
|
|
fn long_version() -> String {
|
|
let head_commit_hash = env!("HEAD_COMMIT_HASH");
|
|
let head_tag_name = env!("HEAD_TAG_NAME");
|
|
let pkg_version = env!("PKG_VERSION");
|
|
let target = env!("TARGET");
|
|
let profile = env!("PROFILE");
|
|
let rustc_version = env!("RUSTC_VERSION");
|
|
|
|
let commit_line = match (head_commit_hash, head_tag_name) {
|
|
(commit_hash, tag_name) if !commit_hash.is_empty() && !tag_name.is_empty() => {
|
|
format!("commit: {commit_hash} (tag {tag_name})")
|
|
}
|
|
(commit_hash, _) if !commit_hash.is_empty() => {
|
|
format!("commit: {commit_hash}")
|
|
}
|
|
_ => "commit: unknown".to_owned(),
|
|
};
|
|
|
|
format!(
|
|
"\
|
|
{pkg_version}
|
|
{commit_line}
|
|
target: {target}
|
|
profile: {profile}
|
|
rustc: {rustc_version}"
|
|
)
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(author, version, long_version = long_version(), about, long_about = None,
|
|
args_conflicts_with_subcommands = true,
|
|
subcommand_negates_reqs = true)]
|
|
pub struct CliArgs {
|
|
#[command(subcommand)]
|
|
pub command: Option<Command>,
|
|
|
|
/// Path for a yaml-encoded network config file
|
|
config: Option<PathBuf>,
|
|
/// Dry-run flag. If active, the binary will try to deserialize the config
|
|
/// file and then exit.
|
|
#[clap(long = "check-config", action)]
|
|
check_config_only: bool,
|
|
/// Overrides log config.
|
|
#[clap(flatten)]
|
|
log: LogArgs,
|
|
/// Overrides network config.
|
|
#[clap(flatten)]
|
|
network: NetworkArgs,
|
|
/// Overrides blend config.
|
|
#[clap(flatten)]
|
|
blend: BlendArgs,
|
|
/// Overrides cryptarchia config.
|
|
#[clap(flatten)]
|
|
cryptarchia: CryptarchiaArgs,
|
|
/// Overrides sdp config.
|
|
#[clap(flatten)]
|
|
sdp: SdpArgs,
|
|
/// Overrides http config.
|
|
#[clap(flatten)]
|
|
api: ApiArgs,
|
|
#[clap(flatten)]
|
|
deployment: DeploymentArgs,
|
|
#[clap(flatten)]
|
|
state: StateArgs,
|
|
}
|
|
|
|
impl CliArgs {
|
|
#[must_use]
|
|
pub fn user_config_path(&self) -> &Path {
|
|
self.config
|
|
.as_deref()
|
|
.expect("config path is required when not using a subcommand")
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn deployment_config_path(&self) -> &Option<PathBuf> {
|
|
&self.deployment.custom_deployment_path
|
|
}
|
|
|
|
#[must_use]
|
|
pub const fn dry_run(&self) -> bool {
|
|
self.check_config_only
|
|
}
|
|
}
|
|
|
|
#[derive(Subcommand, Debug)]
|
|
pub enum Command {
|
|
/// Initialize a new user config with generated keys
|
|
InitConfig(Box<InitArgs>),
|
|
/// Update existing user config with keys from keystore
|
|
UpdateConfig(Box<UpdateArgs>),
|
|
/// Migrates a new user config with generated keys
|
|
MigrateConfig(Box<MigrateArgs>),
|
|
/// Migrates 0.1.2 config to a new user config
|
|
#[command(name = "migrate-from-0.1.2")]
|
|
Migrate0_1_2(Box<migrate_0_1_2::MigrateArgs>),
|
|
/// Generate a new key of type.
|
|
GenerateKey(Box<GenerateKeyArgs>),
|
|
/// Add a key of type to a keystore.
|
|
AddKey(Box<AddKeyArgs>),
|
|
/// Remove a key with title from a keystore.
|
|
RemoveKey(Box<RemoveKeyArgs>),
|
|
/// Publish text inscriptions as zone blocks
|
|
Inscribe(NodeKeyArgs),
|
|
/// Generate stakeholder.yaml and provider.yaml from a user config
|
|
Participate(ParticipateArgs),
|
|
/// Print the libp2p `PeerId` derived from the node key in a user config
|
|
GetPeerId(GetPeerIdArgs),
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
pub struct InitArgs {
|
|
/// Output file path for the generated config.
|
|
#[clap(long = "output", short = 'o', default_value = "user_config.yaml")]
|
|
pub output: PathBuf,
|
|
|
|
/// Path for the generated keystore file.
|
|
/// Defaults to 'keystore.yaml' in the same directory as --output.
|
|
#[clap(long = "keystore", short = 'k')]
|
|
pub keystore: Option<PathBuf>,
|
|
|
|
/// Overwrite existing user config and keystore.
|
|
#[arg(long, default_value_t = false)]
|
|
pub overwrite: bool,
|
|
|
|
#[clap(flatten)]
|
|
pub log: LogArgs,
|
|
|
|
#[clap(flatten)]
|
|
pub network: NetworkArgs,
|
|
|
|
#[clap(flatten)]
|
|
pub blend: BlendArgs,
|
|
|
|
#[clap(flatten)]
|
|
pub cryptarchia: CryptarchiaArgs,
|
|
|
|
#[clap(flatten)]
|
|
pub sdp: SdpArgs,
|
|
|
|
#[clap(flatten)]
|
|
pub api: ApiArgs,
|
|
|
|
#[clap(flatten)]
|
|
pub state: StateArgs,
|
|
|
|
/// Name (or path, relative to the state folder) of the storage DB folder.
|
|
#[clap(long = "storage-path")]
|
|
pub storage_path: Option<PathBuf>,
|
|
}
|
|
|
|
/// Set of arguments for use in c-bindings crate.
|
|
#[derive(Debug)]
|
|
pub struct EmbeddedInitArgs {
|
|
/// Trusted peers to bootstrap from (multiaddr format).
|
|
/// If `--skip-ibd` is not set, peers whose multiaddrs include a `PeerId`
|
|
/// are also used as IBD peers.
|
|
pub initial_peers: Vec<Multiaddr>,
|
|
|
|
/// Output file path for the generated config
|
|
pub output: PathBuf,
|
|
|
|
/// Network listen port
|
|
pub net_port: u16,
|
|
|
|
/// Blend listen port
|
|
pub blend_port: u16,
|
|
|
|
/// HTTP API listen address
|
|
pub http_addr: SocketAddr,
|
|
|
|
/// External address for nodes with a known public IP (disables NAT
|
|
/// traversal). Format: /ip4/<public-ip>/udp/<port>/quic-v1
|
|
pub external_address: Option<Multiaddr>,
|
|
|
|
pub state_path: Option<PathBuf>,
|
|
pub storage_path: Option<PathBuf>,
|
|
pub logs_path: Option<PathBuf>,
|
|
|
|
/// Disable Initial Block Download (IBD) by leaving the IBD peer list
|
|
/// empty, regardless of any peers passed via `--initial-peers`/`-p`.
|
|
pub skip_ibd: bool,
|
|
|
|
/// Log filter directives to write into the generated config, e.g.
|
|
/// `warn,logos_blockchain=debug,libp2p_gossipsub::behaviour=error`.
|
|
pub log_filter: Option<String>,
|
|
|
|
/// Path for the generated KMS keys YAML file.
|
|
/// Defaults to 'kms.yaml' in the same directory as --output.
|
|
pub kms_file: Option<PathBuf>,
|
|
}
|
|
|
|
impl From<EmbeddedInitArgs> for InitArgs {
|
|
fn from(args: EmbeddedInitArgs) -> Self {
|
|
let mut init_args = Self {
|
|
output: args.output.clone(),
|
|
keystore: args.kms_file.clone(),
|
|
..Default::default()
|
|
};
|
|
|
|
init_args.log.filter.clone_from(&args.log_filter);
|
|
init_args.network.port = Some(args.net_port);
|
|
init_args
|
|
.network
|
|
.external_address
|
|
.clone_from(&args.external_address);
|
|
init_args.network.initial_peers = Some(args.initial_peers.clone());
|
|
|
|
init_args.blend.blend_addr =
|
|
Some(BlendCoreConfig::default_listening_address(args.blend_port));
|
|
|
|
init_args.cryptarchia.skip_ibd = args.skip_ibd;
|
|
init_args.api.addr = Some(args.http_addr);
|
|
init_args.state.path.clone_from(&args.state_path);
|
|
init_args.storage_path.clone_from(&args.storage_path);
|
|
init_args.log.directory.clone_from(&args.logs_path);
|
|
|
|
init_args
|
|
}
|
|
}
|
|
|
|
impl Default for EmbeddedInitArgs {
|
|
fn default() -> Self {
|
|
Self {
|
|
initial_peers: Vec::new(),
|
|
output: PathBuf::from("user_config.yaml"),
|
|
net_port: SwarmConfig::default_port(),
|
|
blend_port: BlendCoreConfig::default_port(),
|
|
http_addr: AxumBackendSettings::default_listening_address(
|
|
AxumBackendSettings::default_port(),
|
|
),
|
|
external_address: None,
|
|
state_path: None,
|
|
storage_path: None,
|
|
logs_path: None,
|
|
skip_ibd: false,
|
|
log_filter: None,
|
|
kms_file: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
pub struct UpdateArgs {
|
|
/// Output file path for the generated config.
|
|
#[clap(long = "user-config", short = 'u', default_value = "user_config.yaml")]
|
|
pub user_config: PathBuf,
|
|
|
|
/// Path for the keystore file.
|
|
#[clap(long = "keystore")]
|
|
pub keystore: PathBuf,
|
|
|
|
/// Auto approve interactive promps.
|
|
#[arg(short, long, default_value_t = false)]
|
|
pub yes: bool,
|
|
|
|
#[clap(flatten)]
|
|
log: LogArgs,
|
|
|
|
#[clap(flatten)]
|
|
network: NetworkArgs,
|
|
|
|
#[clap(flatten)]
|
|
blend: BlendArgs,
|
|
|
|
#[clap(flatten)]
|
|
cryptarchia: CryptarchiaArgs,
|
|
|
|
#[clap(flatten)]
|
|
sdp: SdpArgs,
|
|
|
|
#[clap(flatten)]
|
|
api: ApiArgs,
|
|
|
|
#[clap(flatten)]
|
|
state: StateArgs,
|
|
}
|
|
|
|
impl UpdateArgs {
|
|
/// Creates arguments programmatically (e.g. from the c-bindings crate),
|
|
/// leaving all config overrides at their defaults. `auto_approve` skips
|
|
/// interactive prompts.
|
|
#[must_use]
|
|
pub fn new(user_config: PathBuf, keystore: PathBuf, auto_approve: bool) -> Self {
|
|
Self {
|
|
user_config,
|
|
keystore,
|
|
yes: auto_approve,
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Custom default implementation to require keystore path initialized from clap.
|
|
impl Default for UpdateArgs {
|
|
fn default() -> Self {
|
|
Self {
|
|
user_config: "user_config.yaml".into(),
|
|
keystore: "keystore.yaml".into(),
|
|
yes: false,
|
|
log: LogArgs::default(),
|
|
network: NetworkArgs::default(),
|
|
blend: BlendArgs::default(),
|
|
cryptarchia: CryptarchiaArgs::default(),
|
|
sdp: SdpArgs::default(),
|
|
api: ApiArgs::default(),
|
|
state: StateArgs::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
pub struct MigrateArgs {
|
|
/// Output file path for the generated config.
|
|
#[clap(long = "output", short = 'o', default_value = "user_config.yaml")]
|
|
pub output: PathBuf,
|
|
|
|
/// Path for the keystore file.
|
|
#[clap(long = "keystore")]
|
|
pub keystore: PathBuf,
|
|
|
|
#[clap(flatten)]
|
|
log: LogArgs,
|
|
|
|
#[clap(flatten)]
|
|
network: NetworkArgs,
|
|
|
|
#[clap(flatten)]
|
|
blend: BlendArgs,
|
|
|
|
#[clap(flatten)]
|
|
cryptarchia: CryptarchiaArgs,
|
|
|
|
#[clap(flatten)]
|
|
sdp: SdpArgs,
|
|
|
|
#[clap(flatten)]
|
|
api: ApiArgs,
|
|
|
|
#[clap(flatten)]
|
|
state: StateArgs,
|
|
|
|
/// Name (or path, relative to the state folder) of the storage DB folder.
|
|
#[clap(long = "storage-path")]
|
|
storage_path: Option<PathBuf>,
|
|
}
|
|
|
|
impl MigrateArgs {
|
|
/// Creates arguments programmatically (e.g. from the c-bindings crate),
|
|
/// leaving all config overrides at their defaults.
|
|
#[must_use]
|
|
pub fn new(output: PathBuf, keystore: PathBuf) -> Self {
|
|
Self {
|
|
output,
|
|
keystore,
|
|
log: LogArgs::default(),
|
|
network: NetworkArgs::default(),
|
|
blend: BlendArgs::default(),
|
|
cryptarchia: CryptarchiaArgs::default(),
|
|
sdp: SdpArgs::default(),
|
|
api: ApiArgs::default(),
|
|
state: StateArgs::default(),
|
|
storage_path: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<MigrateArgs> for InitArgs {
|
|
fn from(migrate: MigrateArgs) -> Self {
|
|
Self {
|
|
output: migrate.output,
|
|
keystore: Some(migrate.keystore),
|
|
log: migrate.log,
|
|
network: migrate.network,
|
|
blend: migrate.blend,
|
|
cryptarchia: migrate.cryptarchia,
|
|
sdp: migrate.sdp,
|
|
api: migrate.api,
|
|
state: migrate.state,
|
|
storage_path: migrate.storage_path,
|
|
overwrite: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for InitArgs {
|
|
fn default() -> Self {
|
|
Self::parse_from::<Vec<String>, String>(vec![])
|
|
}
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
pub struct ParticipateArgs {
|
|
/// Path to the user config YAML file
|
|
#[arg(long, default_value = "user_config.yaml")]
|
|
pub config: PathBuf,
|
|
/// Path to the keystore YAML file
|
|
#[arg(long, default_value = "keystore.yaml")]
|
|
pub keystore: PathBuf,
|
|
/// Output directory for `participation_data.yaml`
|
|
#[arg(long, default_value = ".")]
|
|
pub output: PathBuf,
|
|
/// Node's public IPv4 address, required when the blend listening address
|
|
/// is 0.0.0.0
|
|
#[arg(long)]
|
|
pub external_address: Option<Ipv4Addr>,
|
|
}
|
|
|
|
#[derive(Parser, Debug)]
|
|
pub struct GetPeerIdArgs {
|
|
/// Path to the user config YAML file
|
|
#[arg(long, default_value = "user_config.yaml")]
|
|
pub config: PathBuf,
|
|
}
|
|
|
|
/// Applies CLI overrides from `args` to `user_config` and returns a
|
|
/// `RunConfig` ready to start the node.
|
|
pub fn build_run_config(mut user_config: UserConfig, args: CliArgs) -> Result<RunConfig> {
|
|
let CliArgs {
|
|
log: log_args,
|
|
api: api_args,
|
|
network: network_args,
|
|
blend: blend_args,
|
|
cryptarchia: cryptarchia_args,
|
|
sdp: sdp_args,
|
|
deployment: deployment_args,
|
|
state: state_args,
|
|
..
|
|
} = args;
|
|
update_tracing(&mut user_config.tracing, log_args)?;
|
|
update_network(&mut user_config.network, network_args)?;
|
|
update_blend(&mut user_config.blend, blend_args);
|
|
update_cryptarchia(&mut user_config.cryptarchia, cryptarchia_args);
|
|
update_sdp(&mut user_config.sdp, sdp_args);
|
|
update_api(&mut user_config.api, api_args);
|
|
update_state(&mut user_config.state, state_args);
|
|
|
|
let deployment_settings = match deployment_args.custom_deployment_path {
|
|
None => DeploymentSettings::default(),
|
|
Some(path) => deserialize_value_at_path::<DeploymentSettings>(&path, OnUnknownKeys::Fail)?,
|
|
};
|
|
|
|
Ok(RunConfig {
|
|
deployment: deployment_settings,
|
|
user: user_config,
|
|
})
|
|
}
|