From 2111dc9bb042f0c3e2e9d8f3d2db994fa8631d56 Mon Sep 17 00:00:00 2001 From: shekohex Date: Fri, 25 Oct 2024 21:13:12 +0300 Subject: [PATCH] fix(cargo-tangle): CLI bugs (#409) --- Cargo.lock | 105 +---------- Cargo.toml | 6 +- blueprint-manager/Cargo.toml | 2 +- blueprint-manager/src/config.rs | 22 +-- blueprint-manager/src/main.rs | 4 +- blueprint-manager/src/sdk/entry.rs | 2 +- blueprint-test-utils/src/lib.rs | 2 +- .../incredible-squaring-eigenlayer/Cargo.toml | 2 +- .../contracts/lib/forge-std | 2 +- .../src/runner.rs | 4 +- blueprints/incredible-squaring/Cargo.toml | 1 - .../contracts/lib/forge-std | 2 +- blueprints/periodic-web-poller/Cargo.toml | 1 - cli/src/create.rs | 6 +- cli/src/keys.rs | 8 +- cli/src/main.rs | 45 +++-- gadget-io/Cargo.toml | 4 +- gadget-io/src/imp/standard/shell.rs | 49 ++---- gadget-io/src/shared/shell.rs | 4 +- macros/blueprint-proc-macro/src/lib.rs | 13 ++ macros/blueprint-proc-macro/src/sdk_main.rs | 26 ++- sdk/Cargo.toml | 3 +- sdk/src/config.rs | 163 +++++++++++------- sdk/src/keystore/backend/mod.rs | 16 ++ sdk/src/lib.rs | 2 +- 25 files changed, 242 insertions(+), 252 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c36399a..23a4c617 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1379,17 +1379,6 @@ dependencies = [ "url", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "auth-git2" version = "0.5.5" @@ -1936,6 +1925,7 @@ version = "0.1.1" dependencies = [ "async-trait", "auto_impl", + "clap", "color-eyre", "failure", "futures", @@ -1949,7 +1939,6 @@ dependencies = [ "serde", "sha2 0.10.8", "sp-core", - "structopt", "tangle-subxt", "tokio", "toml", @@ -2183,7 +2172,7 @@ dependencies = [ "anstyle", "anyhow", "auth-git2", - "clap 4.5.20", + "clap", "console", "dialoguer", "env_logger", @@ -2237,7 +2226,7 @@ dependencies = [ "alloy-signer-local", "cargo-generate", "cargo_metadata", - "clap 4.5.20", + "clap", "clap-cargo", "color-eyre", "escargot", @@ -2402,21 +2391,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "clap" version = "4.5.20" @@ -2434,7 +2408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2ea69cefa96b848b73ad516ad1d59a195cdf9263087d977f648a818c8b43e" dependencies = [ "anstyle", - "clap 4.5.20", + "clap", ] [[package]] @@ -4739,6 +4713,7 @@ name = "gadget-io" version = "0.0.4" dependencies = [ "cfg-if 1.0.0", + "clap", "hex", "js-sys", "multiaddr", @@ -4750,7 +4725,6 @@ dependencies = [ "sp-application-crypto", "sp-core", "sp-keystore", - "structopt", "thiserror", "tokio", "tracing", @@ -4784,6 +4758,7 @@ dependencies = [ "backon", "bincode", "bollard", + "clap", "ed25519-zebra 4.0.3", "eigensdk", "elliptic-curve", @@ -4814,7 +4789,6 @@ dependencies = [ "sp-core", "sp-io", "sqlx", - "structopt", "subxt", "subxt-core", "subxt-signer", @@ -5344,15 +5318,6 @@ dependencies = [ "hashbrown 0.14.5", ] -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "heck" version = "0.4.1" @@ -5368,15 +5333,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.3.9" @@ -5944,7 +5900,6 @@ dependencies = [ "parking_lot 0.12.3", "serde_json", "sp-core", - "structopt", "subxt-signer", "tokio", "tokio-util 0.7.12", @@ -5976,6 +5931,7 @@ dependencies = [ "async-trait", "bip39", "blueprint-test-utils", + "clap", "color-eyre", "ed25519-zebra 4.0.3", "eigensdk", @@ -5993,7 +5949,6 @@ dependencies = [ "serde", "serde_json", "sp-core", - "structopt", "subxt-signer", "thiserror", "tokio", @@ -8567,7 +8522,6 @@ dependencies = [ "reqwest 0.12.8", "serde_json", "sp-core", - "structopt", "subxt-signer", "tokio", "tokio-util 0.7.12", @@ -11512,12 +11466,6 @@ dependencies = [ "unicode-properties", ] -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - [[package]] name = "strsim" version = "0.10.0" @@ -11553,30 +11501,6 @@ dependencies = [ "syn 2.0.85", ] -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "strum" version = "0.26.3" @@ -12057,15 +11981,6 @@ dependencies = [ "url", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thin-vec" version = "0.2.13" @@ -12853,12 +12768,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index bc20356f..6d93d028 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,7 @@ members = [ "macros/playground", "macros/context-derive", ] -exclude = [ - "tangle-test-utils", - "example", -] +exclude = ["tangle-test-utils", "example"] [workspace.package] authors = ["Webb Technologies Inc."] @@ -123,7 +120,6 @@ serde = { version = "1.0.208", default-features = false } serde_json = "1.0" sha2 = "0.10.8" sqlx = "=0.7.3" -structopt = "0.3.26" syn = "2.0.75" sysinfo = "0.31.2" thiserror = { version = "1.0.64", default-features = false } diff --git a/blueprint-manager/Cargo.toml b/blueprint-manager/Cargo.toml index bc8eec78..8ec9f5e6 100644 --- a/blueprint-manager/Cargo.toml +++ b/blueprint-manager/Cargo.toml @@ -12,9 +12,9 @@ path = "src/main.rs" sp-core = { workspace = true } gadget-io = { workspace = true } gadget-sdk = { workspace = true, default-features = true } +clap = { workspace = true, features = ["derive", "wrap_help"] } color-eyre = { workspace = true, features = ["tracing-error", "color-spantrace", "issue-url"] } serde = { workspace = true } -structopt = { workspace = true } tangle-subxt = { workspace = true } toml = { workspace = true } hex = { workspace = true } diff --git a/blueprint-manager/src/config.rs b/blueprint-manager/src/config.rs index f54e0ab0..112e01d0 100644 --- a/blueprint-manager/src/config.rs +++ b/blueprint-manager/src/config.rs @@ -1,31 +1,31 @@ +use clap::Parser; use std::path::PathBuf; -use structopt::StructOpt; -#[derive(Debug, StructOpt)] -#[structopt( +#[derive(Debug, Parser)] +#[command( name = "Blueprint Manager", about = "An program executor that connects to the Tangle network and runs protocols dynamically on the fly" )] pub struct BlueprintManagerConfig { /// The path to the gadget configuration file - #[structopt(parse(from_os_str), short = "s", long)] + #[arg(short = 's', long)] pub gadget_config: Option, /// The path to the keystore - #[structopt(short = "k", long)] + #[arg(short = 'k', long)] pub keystore_uri: String, /// The directory in which all gadgets will store their data - #[structopt(long, short = "d", parse(from_os_str), default_value = "./data")] + #[arg(long, short = 'd', default_value = "./data")] pub data_dir: PathBuf, /// The verbosity level, can be used multiple times - #[structopt(long, short = "v", parse(from_occurrences))] - pub verbose: i32, + #[arg(long, short = 'v', action = clap::ArgAction::Count)] + pub verbose: u8, /// Whether to use pretty logging - #[structopt(long)] + #[arg(long)] pub pretty: bool, /// An optional unique string identifier for the blueprint manager to differentiate between multiple /// running instances of a BlueprintManager (mostly for debugging purposes) - #[structopt(long, short = "id")] + #[arg(long, alias = "id")] pub instance_id: Option, - #[structopt(long, short = "t")] + #[arg(long, short = 't')] pub test_mode: bool, } diff --git a/blueprint-manager/src/main.rs b/blueprint-manager/src/main.rs index d248f90b..11c2f6a6 100644 --- a/blueprint-manager/src/main.rs +++ b/blueprint-manager/src/main.rs @@ -2,15 +2,15 @@ use blueprint_manager::config::BlueprintManagerConfig; use blueprint_manager::run_blueprint_manager; use blueprint_manager::sdk; use blueprint_manager::sdk::utils::msg_to_error; +use clap::Parser; use gadget_io::GadgetConfig; use sdk::entry; -use structopt::StructOpt; #[tokio::main] #[allow(clippy::needless_return)] async fn main() -> color_eyre::Result<()> { color_eyre::install()?; - let mut blueprint_manager_config = BlueprintManagerConfig::from_args(); + let mut blueprint_manager_config = BlueprintManagerConfig::parse(); blueprint_manager_config.data_dir = std::path::absolute(&blueprint_manager_config.data_dir)?; diff --git a/blueprint-manager/src/sdk/entry.rs b/blueprint-manager/src/sdk/entry.rs index 5ae99f6a..7a1b0798 100644 --- a/blueprint-manager/src/sdk/entry.rs +++ b/blueprint-manager/src/sdk/entry.rs @@ -21,7 +21,7 @@ impl<'a, F: Send + Future + 'a, T> SendFuture<'a, T> for F {} /// Sets up the logger for the blueprint manager, based on the verbosity level passed in. pub fn setup_blueprint_manager_logger( - verbose: i32, + verbose: u8, pretty: bool, filter: &str, ) -> color_eyre::Result<()> { diff --git a/blueprint-test-utils/src/lib.rs b/blueprint-test-utils/src/lib.rs index a6dbc17f..6bef8f04 100644 --- a/blueprint-test-utils/src/lib.rs +++ b/blueprint-test-utils/src/lib.rs @@ -46,7 +46,7 @@ pub struct PerTestNodeInput { bind_ip: IpAddr, bind_port: u16, bootnodes: Vec, - verbose: i32, + verbose: u8, pretty: bool, #[allow(dead_code)] extra_input: T, diff --git a/blueprints/incredible-squaring-eigenlayer/Cargo.toml b/blueprints/incredible-squaring-eigenlayer/Cargo.toml index 380142ee..bf17399f 100644 --- a/blueprints/incredible-squaring-eigenlayer/Cargo.toml +++ b/blueprints/incredible-squaring-eigenlayer/Cargo.toml @@ -42,7 +42,7 @@ ark-ec = { workspace = true } parking_lot = { workspace = true } libp2p = { workspace = true } ed25519-zebra = { workspace = true, features = ["pkcs8", "default", "der", "std", "serde", "pem"] } -structopt = { workspace = true } +clap = { workspace = true, features = ["derive", "wrap_help"] } hex = { workspace = true } k256 = { workspace = true } reqwest = { workspace = true } diff --git a/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std b/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std index 035de35f..1de6eecf 160000 --- a/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std +++ b/blueprints/incredible-squaring-eigenlayer/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 +Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f diff --git a/blueprints/incredible-squaring-eigenlayer/src/runner.rs b/blueprints/incredible-squaring-eigenlayer/src/runner.rs index 718b7da2..3450b1a9 100644 --- a/blueprints/incredible-squaring-eigenlayer/src/runner.rs +++ b/blueprints/incredible-squaring-eigenlayer/src/runner.rs @@ -22,10 +22,10 @@ use eigensdk::client_elcontracts::writer::ELChainWriter; use eigensdk::crypto_bls::BlsKeyPair; use eigensdk::logging::get_test_logger; use eigensdk::types::operator::Operator; +use gadget_sdk::clap::Parser; use gadget_sdk::events_watcher::InitializableEventHandler; use gadget_sdk::info; use gadget_sdk::run::GadgetRunner; -use gadget_sdk::structopt::StructOpt; use gadget_sdk::{ config::{ContextConfig, GadgetConfiguration}, events_watcher::evm::DefaultNodeConfig, @@ -211,7 +211,7 @@ impl GadgetRunner for EigenlayerGadgetRunner { pub async fn execute_runner() -> Result<()> { gadget_sdk::logging::setup_log(); - let config = ContextConfig::from_args(); + let config = ContextConfig::parse(); let env = gadget_sdk::config::load(config).expect("Failed to load environment"); let mut runner = Box::new(EigenlayerGadgetRunner::new(env.clone()).await); diff --git a/blueprints/incredible-squaring/Cargo.toml b/blueprints/incredible-squaring/Cargo.toml index 55e1264b..ec765f71 100644 --- a/blueprints/incredible-squaring/Cargo.toml +++ b/blueprints/incredible-squaring/Cargo.toml @@ -26,7 +26,6 @@ ark-ec = { workspace = true } parking_lot = { workspace = true } libp2p = { workspace = true } ed25519-zebra = { workspace = true, features = ["pkcs8", "default", "der", "std", "serde", "pem"] } -structopt = { workspace = true } hex = { workspace = true } k256 = { workspace = true } serde_json = { workspace = true } diff --git a/blueprints/incredible-squaring/contracts/lib/forge-std b/blueprints/incredible-squaring/contracts/lib/forge-std index 035de35f..1de6eecf 160000 --- a/blueprints/incredible-squaring/contracts/lib/forge-std +++ b/blueprints/incredible-squaring/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 035de35f5e366c8d6ed142aec4ccb57fe2dd87d4 +Subproject commit 1de6eecf821de7fe2c908cc48d3ab3dced20717f diff --git a/blueprints/periodic-web-poller/Cargo.toml b/blueprints/periodic-web-poller/Cargo.toml index 67cc6624..b83aef24 100644 --- a/blueprints/periodic-web-poller/Cargo.toml +++ b/blueprints/periodic-web-poller/Cargo.toml @@ -21,7 +21,6 @@ sp-core = { workspace = true } subxt-signer = { workspace = true, features = ["sr25519", "subxt", "std"] } parking_lot = { workspace = true } ed25519-zebra = { workspace = true, features = ["pkcs8", "default", "der", "std", "serde", "pem"] } -structopt = { workspace = true } hex = { workspace = true } k256 = { workspace = true } serde_json = { workspace = true } diff --git a/cli/src/create.rs b/cli/src/create.rs index cc1b621d..1bb31cc7 100644 --- a/cli/src/create.rs +++ b/cli/src/create.rs @@ -14,11 +14,11 @@ pub struct Source { #[derive(Args, Debug, Clone)] #[group(requires = "repo")] pub struct RepoArgs { - #[arg(short, long, required = false, group = "source")] + #[arg(short, long, env, required = false, group = "source")] repo: String, - #[arg(short, long)] + #[arg(short, long, env)] branch: Option, - #[arg(short, long, conflicts_with = "branch")] + #[arg(short, long, env, conflicts_with = "branch")] tag: Option, } diff --git a/cli/src/keys.rs b/cli/src/keys.rs index a11dbf38..ae585420 100644 --- a/cli/src/keys.rs +++ b/cli/src/keys.rs @@ -136,10 +136,10 @@ pub fn generate_key( } }; - println!("Generated {:?} key:", key_type); - println!("Public key: {}", public); - if show_secret { - println!("Private key: {}", secret); + eprintln!("Generated {:?} key:", key_type); + eprintln!("Public key: {}", public); + if show_secret || keystore.is_mem() { + eprintln!("Private key: {}", secret); } Ok(()) diff --git a/cli/src/main.rs b/cli/src/main.rs index d5221415..e44c83ef 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -37,7 +37,7 @@ pub enum GadgetCommands { #[command(visible_alias = "c")] Create { /// The name of the blueprint - #[arg(short, long)] + #[arg(short, long, value_name = "NAME", env = "NAME")] name: String, #[command(flatten)] @@ -48,13 +48,23 @@ pub enum GadgetCommands { #[command(visible_alias = "d")] Deploy { /// HTTP RPC URL to use - #[arg(long, value_name = "URL", default_value = "https://rpc.tangle.tools")] + #[arg( + long, + value_name = "URL", + default_value = "https://rpc.tangle.tools", + env + )] http_rpc_url: String, /// Tangle RPC URL to use - #[arg(long, value_name = "URL", default_value = "wss://rpc.tangle.tools")] + #[arg( + long, + value_name = "URL", + default_value = "wss://rpc.tangle.tools", + env + )] ws_rpc_url: String, /// The package to deploy (if the workspace has multiple packages). - #[arg(short, long, value_name = "PACKAGE")] + #[arg(short, long, value_name = "PACKAGE", env = "CARGO_PACKAGE")] package: Option, }, /// Generate a key @@ -63,12 +73,14 @@ pub enum GadgetCommands { #[arg(short, long, value_enum)] key_type: KeyType, - /// The path to save the key (optional) + /// The path to save the key to, if not provided, the key will be printed + /// to the console instead #[arg(short, long)] path: Option, - /// The SURI or seed to use for the generation of the key (optional) - #[arg(short, long)] + /// The seed to use for the generation of the key in hex format without 0x prefix, + /// if not provided, a random seed will be generated + #[arg(short, long, value_name = "SEED_HEX", env = "SEED")] seed: Option, /// If true, the secret key will be printed along with the public key @@ -127,12 +139,8 @@ async fn main() -> color_eyre::Result<()> { seed, show_secret, } => { - keys::generate_key( - key_type, - path, - seed.as_deref().map(str::as_bytes), - show_secret, - )?; + let seed = seed.map(hex::decode).transpose()?; + keys::generate_key(key_type, path, seed.as_deref(), show_secret)?; } }, } @@ -153,3 +161,14 @@ fn init_tracing_subscriber() { .with(fmt_layer) .init(); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert(); + } +} diff --git a/gadget-io/Cargo.toml b/gadget-io/Cargo.toml index 2b58485f..7328f842 100644 --- a/gadget-io/Cargo.toml +++ b/gadget-io/Cargo.toml @@ -8,6 +8,7 @@ repository.workspace = true homepage.workspace = true [dependencies] +clap = { workspace = true, features = ["derive", "wrap_help", "env"], optional = true } cfg-if = { workspace = true } hex = { workspace = true } multiaddr = { workspace = true, default-features = false } @@ -18,7 +19,6 @@ serde = { workspace = true } sp-application-crypto = { workspace = true, default-features = false, features = ["full_crypto"] } sp-core = { workspace = true, features = ["serde", "full_crypto"] } sp-keystore = { workspace = true, optional = true } -structopt = { workspace = true, optional = true } thiserror = { workspace = true } tokio = { workspace = true, optional = true, features = ["time", "rt", "sync", "macros"] } tracing = { workspace = true, default-features = false, features = ["log", "attributes"] } @@ -37,9 +37,9 @@ default = ["std"] std = [ "dep:tokio", "sp-application-crypto/std", + "dep:clap", "dep:sp-keystore", "dep:sc-keystore", - "dep:structopt", "dep:scale-info", "dep:parity-scale-codec", "tracing/std", diff --git a/gadget-io/src/imp/standard/shell.rs b/gadget-io/src/imp/standard/shell.rs index 4dc7af8d..8b97ac24 100644 --- a/gadget-io/src/imp/standard/shell.rs +++ b/gadget-io/src/imp/standard/shell.rs @@ -1,77 +1,62 @@ use crate::shared::shell::SupportedChains; +use clap::Parser; use multiaddr::Multiaddr; use serde::{Deserialize, Serialize}; use std::{net::IpAddr, path::PathBuf}; -use structopt::StructOpt; -#[derive(Debug, StructOpt)] -#[structopt( +#[derive(Debug, Parser)] +#[command( name = "Gadget", about = "An MPC executor that connects to the Tangle network to perform work" )] pub struct Opt { /// The path to the configuration file. If not provided, the default configuration will be used. /// Note that if the configuration file is provided, the command line arguments will be ignored. - #[structopt(global = true, parse(from_os_str), short = "c", long = "config")] + #[arg(global = true, short = 'c', long = "config")] pub config: Option, - /// The verbosity level, can be used multiple times - #[structopt(long, short = "v", global = true, parse(from_occurrences))] - pub verbose: i32, - /// Whether to use pretty logging - #[structopt(global = true, long)] - pub pretty: bool, /// The options for the shell - #[structopt(flatten)] + #[command(flatten)] pub options: GadgetConfig, } -#[derive(Debug, StructOpt, Serialize, Deserialize)] +#[derive(Debug, Parser, Serialize, Deserialize)] /// All shells should expect this as CLI input. The Blueprint Manager will be responsible for passing these values to this gadget binary pub struct GadgetConfig { /// The IP address to bind to for the libp2p node. - #[structopt(long = "bind-ip", short = "i", default_value = defaults::BIND_IP)] + #[arg(long = "bind-ip", short = 'i', default_value = defaults::BIND_IP)] #[serde(default = "defaults::bind_ip")] pub bind_addr: IpAddr, /// The port to bind to for the libp2p node. - #[structopt(long = "port", short = "p", default_value = defaults::BIND_PORT)] + #[arg(long = "port", short = 'p', default_value = defaults::BIND_PORT)] #[serde(default = "defaults::bind_port")] pub bind_port: u16, /// The HTTP RPC URL of the Tangle Node. - #[structopt(long = "url", parse(try_from_str = url::Url::parse), default_value = defaults::HTTP_RPC_URL)] + #[arg(long = "url", value_parser = url::Url::parse, default_value = defaults::HTTP_RPC_URL)] #[serde(default = "defaults::http_rpc_url")] pub http_rpc_url: url::Url, /// The WS RPC URL of the Tangle Node. - #[structopt(long = "url", parse(try_from_str = url::Url::parse), default_value = defaults::WS_RPC_URL)] + #[arg(long = "url", value_parser = url::Url::parse, default_value = defaults::WS_RPC_URL)] #[serde(default = "defaults::ws_rpc_url")] pub ws_rpc_url: url::Url, /// The List of bootnodes to connect to - #[structopt(long = "bootnodes", parse(try_from_str = ::from_str))] + #[arg(long = "bootnodes", value_parser = ::from_str, action = clap::ArgAction::Append)] #[serde(default)] pub bootnodes: Vec, /// The base path to store the blueprint manager data, and read data from the keystore. - #[structopt(long, short = "d")] + #[arg(long, short = 'd')] pub keystore_uri: String, /// Keystore Password, if not provided, the password will be read from the environment variable. - #[structopt(long = "keystore-password", env)] + #[arg(long = "keystore-password", env)] pub keystore_password: Option, /// The chain to connect to, must be one of the supported chains. - #[structopt( - long, - default_value, - possible_values = &[ - "local_testnet", - "local_mainnet", - "testnet", - "mainnet" - ] - )] + #[arg(long, default_value_t = SupportedChains::LocalTestnet)] #[serde(default)] pub chain: SupportedChains, /// The verbosity level, can be used multiple times - #[structopt(long, short = "v", global = true, parse(from_occurrences))] - pub verbose: i32, + #[arg(long, short = 'v', global = true, action = clap::ArgAction::Count)] + pub verbose: u8, /// Whether to use pretty logging - #[structopt(global = true, long)] + #[arg(global = true, long)] pub pretty: bool, } diff --git a/gadget-io/src/shared/shell.rs b/gadget-io/src/shared/shell.rs index 889f5dbd..6ce883c5 100644 --- a/gadget-io/src/shared/shell.rs +++ b/gadget-io/src/shared/shell.rs @@ -7,8 +7,8 @@ use core::str::FromStr; use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)] -#[cfg_attr(feature = "std", derive(structopt::StructOpt))] -#[cfg_attr(feature = "std", structopt(rename_all = "snake_case"))] +#[cfg_attr(feature = "std", derive(clap::ValueEnum))] +#[cfg_attr(feature = "std", clap(rename_all = "snake_case"))] #[serde(rename_all = "snake_case")] pub enum SupportedChains { #[default] diff --git a/macros/blueprint-proc-macro/src/lib.rs b/macros/blueprint-proc-macro/src/lib.rs index caf59443..6579dacb 100644 --- a/macros/blueprint-proc-macro/src/lib.rs +++ b/macros/blueprint-proc-macro/src/lib.rs @@ -122,6 +122,19 @@ pub fn load_abi(input: TokenStream) -> TokenStream { } /// A procedural macro that annotates a function as a main function for the blueprint. +/// +/// ```rust,no_run +/// # use gadget_sdk as sdk; +/// #[sdk::main(env)] +/// pub async fn main() { +/// // Your main function code here +/// } +/// ``` +/// +/// # Parameters +/// - `env`: Sets up the environment for the main function. +/// - `skip_logging`: A flag to skip the logging setup for the main function. +/// - ...: are passes as-is to the `#[tokio::main]` attribute. #[proc_macro_attribute] pub fn main(args: TokenStream, input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as syn::ItemFn); diff --git a/macros/blueprint-proc-macro/src/sdk_main.rs b/macros/blueprint-proc-macro/src/sdk_main.rs index 1ae56f1e..3c637ab6 100644 --- a/macros/blueprint-proc-macro/src/sdk_main.rs +++ b/macros/blueprint-proc-macro/src/sdk_main.rs @@ -13,12 +13,15 @@ use syn::{Expr, ItemFn, Token}; mod kw { // Presented as #[sdk::main(env)] syn::custom_keyword!(env); + // Presented as #[sdk::main(skip_logger)] + syn::custom_keyword!(skip_logger); } // Add fields which are passed onto tokio, and those that are reserved for the sdk_main macro pub(crate) struct SdkMainArgs { tokio_args: Option>, env: bool, + skip_logger: bool, } pub(crate) fn sdk_main_impl(args: &SdkMainArgs, input: &ItemFn) -> syn::Result { @@ -43,7 +46,7 @@ pub(crate) fn sdk_main_impl(args: &SdkMainArgs, input: &ItemFn) -> syn::Result syn::Result syn::Result Result<(), Box> { - gadget_sdk::logging::setup_log(); + #logger #standard_setup inner_main(#env_passed_var).await?; Ok(()) @@ -79,6 +90,7 @@ impl Parse for SdkMainArgs { // to the SdkMainArgs struct. Otherwise, add it to the tokio args let mut tokio_args = vec![]; let mut env = false; + let mut skip_logger = false; // Parse through everything while !input.is_empty() { if input.peek(Token![,]) { @@ -88,7 +100,11 @@ impl Parse for SdkMainArgs { if input.peek(kw::env) { let _ = input.parse::()?; env = true; + } else if input.peek(kw::skip_logger) { + let _ = input.parse::()?; + skip_logger = true; } else { + // Parse the input as an expression tokio_args.push(input.parse()?); } } @@ -99,6 +115,10 @@ impl Parse for SdkMainArgs { Some(tokio_args) }; - Ok(SdkMainArgs { tokio_args, env }) + Ok(SdkMainArgs { + tokio_args, + env, + skip_logger, + }) } } diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 75ca7a33..1a481bd8 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -23,7 +23,7 @@ parking_lot = { workspace = true, optional = true } rand = { workspace = true, features = ["alloc"] } thiserror = { workspace = true } tokio-stream = { workspace = true, features = ["io-util", "sync"] } -structopt = { workspace = true } +clap = { workspace = true, features = ["derive", "wrap_help"] } url = { workspace = true, features = ["serde"] } uuid = { workspace = true } failure = { workspace = true } @@ -93,7 +93,6 @@ gadget-blueprint-proc-macro-core = { workspace = true, default-features = false sysinfo = { workspace = true } - [target.'cfg(not(target_family = "wasm"))'.dependencies.libp2p] workspace = true features = [ diff --git a/sdk/src/config.rs b/sdk/src/config.rs index cb74c108..dfb68d88 100644 --- a/sdk/src/config.rs +++ b/sdk/src/config.rs @@ -12,11 +12,15 @@ use gadget_io::SupportedChains; use libp2p::Multiaddr; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use structopt::StructOpt; use url::Url; /// The protocol on which a gadget will be executed. #[derive(Default, Debug, Clone, Copy, Serialize, Deserialize)] +#[cfg_attr( + feature = "std", + derive(clap::ValueEnum), + clap(rename_all = "lowercase") +)] pub enum Protocol { #[default] Tangle, @@ -45,14 +49,18 @@ impl Protocol { pub fn from_env() -> Result { Ok(Protocol::default()) } + + pub fn as_str(&self) -> &'static str { + match self { + Self::Tangle => "tangle", + Self::Eigenlayer => "eigenlayer", + } + } } impl core::fmt::Display for Protocol { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - match self { - Self::Tangle => write!(f, "tangle"), - Self::Eigenlayer => write!(f, "eigenlayer"), - } + write!(f, "{}", self.as_str()) } } @@ -110,6 +118,7 @@ pub struct GadgetConfiguration { pub bind_port: u16, /// The Address of the Network that will be interacted with pub bind_addr: IpAddr, + /// Specifies custom tracing span for the gadget pub span: tracing::Span, /// Whether the gadget is in test mode pub test_mode: bool, @@ -118,37 +127,18 @@ pub struct GadgetConfiguration { _lock: core::marker::PhantomData, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize)] pub struct EigenlayerContractAddresses { + /// The address of the registry coordinator contract pub registry_coordinator_addr: Address, + /// The address of the operator state retriever contract pub operator_state_retriever_addr: Address, + /// The address of the operator registry contract pub delegation_manager_addr: Address, + /// The address of the strategy manager contract pub strategy_manager_addr: Address, } -impl Default for EigenlayerContractAddresses { - fn default() -> Self { - EigenlayerContractAddresses { - registry_coordinator_addr: std::env::var("REGISTRY_COORDINATOR_ADDR") - .unwrap_or_default() - .parse() - .unwrap(), - operator_state_retriever_addr: std::env::var("OPERATOR_STATE_RETRIEVER_ADDR") - .unwrap_or_default() - .parse() - .unwrap(), - delegation_manager_addr: std::env::var("DELEGATION_MANAGER_ADDR") - .unwrap_or_default() - .parse() - .unwrap(), - strategy_manager_addr: std::env::var("STRATEGY_MANAGER_ADDR") - .unwrap_or_default() - .parse() - .unwrap(), - } - } -} - impl Debug for GadgetConfiguration { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("GadgetConfiguration") @@ -261,72 +251,95 @@ pub enum Error { /// Invalid ECDSA keypair found in the keystore. #[error("Invalid ECDSA keypair found in the keystore")] InvalidEcdsaKeypair, - /// Missing `KEYSTORE_URI` environment - #[error("Missing keystore URI")] + /// Test setup error + #[error("Test setup error: {0}")] TestSetup(String), /// Missing `EigenlayerContractAddresses` #[error("Missing EigenlayerContractAddresses")] MissingEigenlayerContractAddresses, } -#[derive(Debug, Clone, StructOpt, Serialize, Deserialize)] -#[structopt(name = "General CLI Context")] +#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize)] +#[command(name = "General CLI Context")] #[cfg(feature = "std")] pub struct ContextConfig { /// Pass through arguments to another command - #[structopt(subcommand)] + #[command(subcommand)] pub gadget_core_settings: GadgetCLICoreSettings, } -#[derive(Debug, Clone, StructOpt, Serialize, Deserialize)] +#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize)] #[cfg(feature = "std")] pub enum GadgetCLICoreSettings { - #[structopt(name = "run")] + #[command(name = "run")] Run { - #[structopt(long, short = "b", parse(try_from_str), env)] + #[arg(long, short = 'b', env)] bind_addr: IpAddr, - #[structopt(long, short = "p", env)] + #[arg(long, short = 'p', env)] bind_port: u16, - #[structopt(long, short = "t", env)] + #[arg(long, short = 't', env)] test_mode: bool, - #[structopt(long, short = "l", env)] + #[arg(long, short = 'l', env)] log_id: Option, - #[structopt(long, short = "u", parse(try_from_str = url::Url::parse), env)] + #[arg(long, env)] #[serde(default = "gadget_io::defaults::http_rpc_url")] http_rpc_url: Url, - #[structopt(long, short = "ws", parse(try_from_str = url::Url::parse), env)] + #[arg(long, env)] #[serde(default = "gadget_io::defaults::ws_rpc_url")] ws_rpc_url: Url, - #[structopt(long, parse(try_from_str = ::from_str), env)] + #[arg(long, value_parser = ::from_str, action = clap::ArgAction::Append, env)] #[serde(default)] bootnodes: Option>, - #[structopt(long, short = "d", env)] + #[arg(long, short = 'd', env)] keystore_uri: String, - #[structopt( - long, - default_value, - possible_values = &[ - "local_testnet", - "local_mainnet", - "testnet", - "mainnet" - ], - env - )] + #[arg(long, value_enum, env)] chain: SupportedChains, - #[structopt(long, short = "v", parse(from_occurrences), env)] - verbose: i32, + #[arg(long, short = 'v', action = clap::ArgAction::Count, env)] + verbose: u8, /// Whether to use pretty logging - #[structopt(long, env)] + #[arg(long, env)] pretty: bool, - #[structopt(long, env)] + #[arg(long, env)] keystore_password: Option, - #[structopt(long, env)] + #[arg(long, env)] blueprint_id: u64, - #[structopt(long, env)] + #[arg(long, env)] service_id: Option, - #[structopt(long, parse(try_from_str), env)] + /// The protocol to use + #[arg(long, value_enum, env)] protocol: Protocol, + /// The address of the registry coordinator + #[arg( + long, + value_name = "ADDR", + env = "REGISTRY_COORDINATOR_ADDR", + required_if_eq("protocol", Protocol::Eigenlayer.as_str()), + )] + registry_coordinator: Option
, + /// The address of the operator state retriever + #[arg( + long, + value_name = "ADDR", + env = "OPERATOR_STATE_RETRIEVER_ADDR", + required_if_eq("protocol", Protocol::Eigenlayer.as_str()) + )] + operator_state_retriever: Option
, + /// The address of the delegation manager + #[arg( + long, + value_name = "ADDR", + env = "DELEGATION_MANAGER_ADDR", + required_if_eq("protocol", Protocol::Eigenlayer.as_str()) + )] + delegation_manager: Option
, + /// The address of the strategy manager + #[arg( + long, + value_name = "ADDR", + env = "STRATEGY_MANAGER_ADDR", + required_if_eq("protocol", Protocol::Eigenlayer.as_str()) + )] + strategy_manager: Option
, }, } @@ -373,6 +386,10 @@ fn load_inner( blueprint_id, service_id, protocol, + registry_coordinator, + operator_state_retriever, + delegation_manager, + strategy_manager, .. }, .. @@ -402,7 +419,14 @@ fn load_inner( }, is_registration, protocol, - eigenlayer_contract_addrs: Default::default(), + // Eigenlayer contract addresses will be None if the protocol is not Eigenlayer + // otherwise, they will be the values provided by the user + eigenlayer_contract_addrs: EigenlayerContractAddresses { + registry_coordinator_addr: registry_coordinator.unwrap_or_default(), + operator_state_retriever_addr: operator_state_retriever.unwrap_or_default(), + delegation_manager_addr: delegation_manager.unwrap_or_default(), + strategy_manager_addr: strategy_manager.unwrap_or_default(), + }, _lock: core::marker::PhantomData, }) } @@ -494,9 +518,20 @@ impl GadgetConfiguration { pub async fn client(&self) -> Result { let client = subxt::OnlineClient::::from_url( - self.http_rpc_endpoint.clone(), + self.ws_rpc_endpoint.clone(), ) .await?; Ok(client) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_cli() { + use clap::CommandFactory; + ContextConfig::command().debug_assert(); + } +} diff --git a/sdk/src/keystore/backend/mod.rs b/sdk/src/keystore/backend/mod.rs index 4197a853..ce643211 100644 --- a/sdk/src/keystore/backend/mod.rs +++ b/sdk/src/keystore/backend/mod.rs @@ -72,6 +72,22 @@ impl GenericKeyStore { ), }) } + + /// Returns `true` if the generic key store is [`Mem`]. + /// + /// [`Mem`]: GenericKeyStore::Mem + #[must_use] + pub fn is_mem(&self) -> bool { + matches!(self, Self::Mem(..)) + } + + /// Returns `true` if the generic key store is [`Fs`]. + /// + /// [`Fs`]: GenericKeyStore::Fs + #[must_use] + pub fn is_fs(&self) -> bool { + matches!(self, Self::Fs(..)) + } } impl super::Backend for GenericKeyStore { diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 9d6db244..30acaebc 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -70,12 +70,12 @@ pub mod utils; // Re-exports pub use alloy_rpc_types; +pub use clap; pub use error::Error; pub use futures; pub use gadget_blueprint_proc_macro::*; pub use libp2p; pub use parking_lot; -pub use structopt; pub use subxt_core; pub use tangle_subxt; pub use tokio;