use color_eyre::Result;
use libp2p::Multiaddr;
use serde::{Deserialize, Serialize};
use std::{fmt::Display, net::IpAddr, path::PathBuf, str::FromStr};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(
name = "Gadget",
about = "An MPC executor that connects to the Tangle network to perform work"
)]
pub struct Opt {
#[structopt(global = true, parse(from_os_str), short = "c", long = "config")]
pub config: Option<PathBuf>,
#[structopt(long, short = "v", global = true, parse(from_occurrences))]
pub verbose: i32,
#[structopt(global = true, long)]
pub pretty: bool,
#[structopt(flatten)]
pub options: ShellTomlConfig,
}
#[derive(Copy, Clone, Default, Debug, StructOpt, Serialize, Deserialize)]
#[structopt(rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum SupportedChains {
#[default]
LocalTestnet,
LocalMainnet,
Testnet,
Mainnet,
}
impl FromStr for SupportedChains {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"local_testnet" => Ok(SupportedChains::LocalTestnet),
"local_mainnet" => Ok(SupportedChains::LocalMainnet),
"testnet" => Ok(SupportedChains::Testnet),
"mainnet" => Ok(SupportedChains::Mainnet),
_ => Err(format!("Invalid chain: {}", s)),
}
}
}
impl Display for SupportedChains {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SupportedChains::LocalTestnet => write!(f, "local_testnet"),
SupportedChains::LocalMainnet => write!(f, "local_mainnet"),
SupportedChains::Testnet => write!(f, "testnet"),
SupportedChains::Mainnet => write!(f, "mainnet"),
}
}
}
#[derive(Debug, StructOpt, Serialize, Deserialize)]
pub struct ShellTomlConfig {
#[structopt(long = "bind-ip", short = "i", default_value = defaults::BIND_IP)]
#[serde(default = "defaults::bind_ip")]
pub bind_ip: IpAddr,
#[structopt(long = "port", short = "p", default_value = defaults::BIND_PORT)]
#[serde(default = "defaults::bind_port")]
pub bind_port: u16,
#[structopt(long = "url", parse(try_from_str = url::Url::parse), default_value = defaults::RPC_URL)]
#[serde(default = "defaults::rpc_url")]
pub url: url::Url,
#[structopt(long = "bootnodes", parse(try_from_str = <Multiaddr as std::str::FromStr>::from_str))]
#[serde(default)]
pub bootnodes: Vec<Multiaddr>,
#[structopt(long = "node-key", env, parse(try_from_str = parse_node_key))]
#[serde(skip_serializing)]
pub node_key: Option<String>,
#[structopt(
parse(from_os_str),
long,
short = "d",
required_unless = "config",
default_value_if("config", None, ".")
)]
pub base_path: PathBuf,
#[structopt(long = "keystore-password", env)]
pub keystore_password: Option<String>,
#[structopt(
long,
default_value,
possible_values = &[
"local_testnet",
"local_mainnet",
"testnet",
"mainnet"
]
)]
#[serde(default)]
pub chain: SupportedChains,
#[structopt(long, short = "v", global = true, parse(from_occurrences))]
pub verbose: i32,
#[structopt(global = true, long)]
pub pretty: bool,
}
pub mod defaults {
pub const BIND_PORT: &str = "30555";
pub const BIND_IP: &str = "0.0.0.0";
pub const RPC_URL: &str = "ws://127.0.0.1:9944";
pub fn rpc_url() -> url::Url {
url::Url::parse(RPC_URL).expect("Default RPC URL is valid")
}
pub fn bind_ip() -> std::net::IpAddr {
BIND_IP.parse().expect("Default bind IP is valid")
}
pub fn bind_port() -> u16 {
BIND_PORT.parse().expect("Default bind port is valid")
}
pub fn generate_node_key() -> [u8; 32] {
use rand::Rng;
let mut rng = rand::thread_rng();
let mut array = [0u8; 32];
rng.fill(&mut array);
array
}
}
fn parse_node_key(s: &str) -> Result<String> {
let result: [u8; 32] = hex::decode(s.replace("0x", ""))?.try_into().map_err(|_| {
color_eyre::eyre::eyre!("Invalid node key length, expect 32 bytes hex string")
})?;
Ok(hex::encode(result))
}