diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml
index ff222ca..686e7f7 100644
--- a/.github/workflows/cont_integration.yml
+++ b/.github/workflows/cont_integration.yml
@@ -11,7 +11,7 @@ jobs:
matrix:
rust:
- 1.60.0 # STABLE
- - 1.56.0 # MSRV
+ - 1.57.0 # MSRV
features:
- default
- electrum,sqlite-db
@@ -80,7 +80,7 @@ jobs:
- run: sudo apt-get update || exit 1
- run: sudo apt-get install -y libclang-common-10-dev clang-10 libc6-dev-i386 || exit 1
- name: Set default toolchain
- run: rustup default 1.56.1 # MSRV
+ run: rustup default 1.57.0 # MSRV
- name: Set profile
run: rustup set profile minimal
- name: Add target wasm32
diff --git a/Cargo.lock b/Cargo.lock
index 160b5cf..9e516b7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -28,15 +28,6 @@ dependencies = [
"memchr",
]
-[[package]]
-name = "ansi_term"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
-dependencies = [
- "winapi",
-]
-
[[package]]
name = "async-trait"
version = "0.1.58"
@@ -134,6 +125,7 @@ dependencies = [
"bdk",
"bdk-macros",
"bdk-reserves",
+ "clap",
"dirs-next",
"electrsd",
"env_logger",
@@ -147,7 +139,6 @@ dependencies = [
"secp256k1 0.22.1",
"serde",
"serde_json",
- "structopt",
"tokio",
"wasm-bindgen",
"wasm-bindgen-futures",
@@ -382,17 +373,41 @@ dependencies = [
[[package]]
name = "clap"
-version = "2.34.0"
+version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
- "ansi_term",
"atty",
"bitflags",
+ "clap_derive",
+ "clap_lex",
+ "indexmap",
+ "once_cell",
"strsim",
+ "termcolor",
"textwrap",
- "unicode-width",
- "vec_map",
+]
+
+[[package]]
+name = "clap_derive"
+version = "3.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
+dependencies = [
+ "os_str_bytes",
]
[[package]]
@@ -885,12 +900,9 @@ dependencies = [
[[package]]
name = "heck"
-version = "0.3.3"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
@@ -1318,6 +1330,12 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "os_str_bytes"
+version = "6.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9"
+
[[package]]
name = "parking_lot"
version = "0.11.2"
@@ -2115,33 +2133,9 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "strsim"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-
-[[package]]
-name = "structopt"
-version = "0.3.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
-dependencies = [
- "clap",
- "lazy_static",
- "structopt-derive",
-]
-
-[[package]]
-name = "structopt-derive"
-version = "0.4.18"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
-dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
@@ -2190,12 +2184,9 @@ dependencies = [
[[package]]
name = "textwrap"
-version = "0.11.0"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
-]
+checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
@@ -2463,12 +2454,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.4"
diff --git a/Cargo.toml b/Cargo.toml
index fb08e91..4e4c0eb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,7 @@ license = "MIT"
[dependencies]
bdk = { version = "0.24", default-features = false, features = ["all-keys"] }
bdk-macros = "0.6"
-structopt = "0.3"
+clap = { version = "3.2.22", features = ["derive"] }
serde_json = "1.0"
log = "0.4"
zeroize = "<1.4.0"
diff --git a/src/commands.rs b/src/commands.rs
index 91c3ab6..6bc5999 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -13,23 +13,21 @@
//! All subcommands are defined in the below enums.
#![allow(clippy::large_enum_variant)]
-use structopt::clap::AppSettings;
-
-use structopt::StructOpt;
+use clap::{AppSettings, Parser, Subcommand};
use bdk::bitcoin::util::bip32::{DerivationPath, ExtendedPrivKey};
use bdk::bitcoin::{Address, Network, OutPoint, Script};
+use crate::utils::{parse_outpoint, parse_recipient};
#[cfg(any(
feature = "compact_filters",
feature = "electrum",
feature = "esplora",
feature = "rpc"
))]
-use crate::utils::parse_proxy_auth;
-use crate::utils::{parse_outpoint, parse_recipient};
+use {crate::utils::parse_proxy_auth, clap::Args};
-#[derive(PartialEq, Clone, Debug, StructOpt)]
+#[derive(PartialEq, Clone, Debug, Parser)]
/// The BDK Command Line Wallet App
///
/// bdk-cli is a light weight command line bitcoin wallet, powered by BDK.
@@ -41,13 +39,13 @@ use crate::utils::{parse_outpoint, parse_recipient};
/// bdk-cli is also a fully functioning Bitcoin wallet with taproot support!
///
/// For more information checkout
-#[structopt(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
+#[clap(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
pub struct CliOpts {
/// Sets the network.
- #[structopt(
+ #[clap(
name = "NETWORK",
- short = "n",
+ short = 'n',
long = "network",
default_value = "testnet",
possible_values = &["bitcoin", "testnet", "signet", "regtest"]
@@ -55,16 +53,16 @@ pub struct CliOpts {
pub network: Network,
/// Sets the wallet data directory.
/// Default value : "~/.bdk-bitcoin
- #[structopt(name = "DATADIR", short = "d", long = "datadir")]
+ #[clap(name = "DATADIR", short = 'd', long = "datadir")]
pub datadir: Option,
/// Top level cli sub-commands.
- #[structopt(subcommand)]
+ #[clap(subcommand)]
pub subcommand: CliSubCommand,
}
/// Top level cli sub-commands.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
-#[structopt(rename_all = "snake")]
+#[derive(Debug, Subcommand, Clone, PartialEq)]
+#[clap(rename_all = "snake")]
pub enum CliSubCommand {
/// Node operation subcommands.
///
@@ -75,9 +73,9 @@ pub enum CliSubCommand {
/// Feel free to open a feature request issue in
/// if you need extra rpc calls not covered in the command list.
#[cfg(feature = "regtest-node")]
- #[structopt(long_about = "Regtest Node mode")]
+ #[clap(long_about = "Regtest Node mode")]
Node {
- #[structopt(subcommand)]
+ #[clap(subcommand)]
subcommand: NodeSubCommand,
},
/// Wallet operations.
@@ -87,9 +85,9 @@ pub enum CliSubCommand {
/// needs backend like `sync` and `broadcast`, compile the binary with specific backend feature
/// and use the configuration options below to configure for that backend.
Wallet {
- #[structopt(flatten)]
+ #[clap(flatten)]
wallet_opts: WalletOpts,
- #[structopt(subcommand)]
+ #[clap(subcommand)]
subcommand: WalletSubCommand,
},
/// Key management operations.
@@ -100,18 +98,18 @@ pub enum CliSubCommand {
/// These sub-commands are **EXPERIMENTAL** and should only be used for testing. Do not use this
/// feature to create keys that secure actual funds on the Bitcoin mainnet.
Key {
- #[structopt(subcommand)]
+ #[clap(subcommand)]
subcommand: KeySubCommand,
},
/// Compile a miniscript policy to an output descriptor.
#[cfg(feature = "compiler")]
- #[structopt(long_about = "Miniscript policy compiler")]
+ #[clap(long_about = "Miniscript policy compiler")]
Compile {
/// Sets the spending policy to compile.
- #[structopt(name = "POLICY", required = true, index = 1)]
+ #[clap(name = "POLICY", required = true, index = 1)]
policy: String,
/// Sets the script type used to embed the compiled policy.
- #[structopt(name = "TYPE", short = "t", long = "type", default_value = "wsh", possible_values = &["sh","wsh", "sh-wsh"])]
+ #[clap(name = "TYPE", short = 't', long = "type", default_value = "wsh", possible_values = &["sh","wsh", "sh-wsh"])]
script_type: String,
},
#[cfg(feature = "repl")]
@@ -120,7 +118,7 @@ pub enum CliSubCommand {
/// REPL command loop can be used to make recurring callbacks to an already loaded wallet.
/// This mode is useful for hands on live testing of wallet operations.
Repl {
- #[structopt(flatten)]
+ #[clap(flatten)]
wallet_opts: WalletOpts,
},
/// Proof of reserves operations.
@@ -129,25 +127,25 @@ pub enum CliSubCommand {
#[cfg(all(feature = "reserves", feature = "electrum"))]
ExternalReserves {
/// Sets the challenge message with which the proof was produced.
- #[structopt(name = "MESSAGE", required = true, index = 1)]
+ #[clap(name = "MESSAGE", required = true, index = 1)]
message: String,
/// Sets the proof in form of a PSBT to verify.
- #[structopt(name = "PSBT", required = true, index = 2)]
+ #[clap(name = "PSBT", required = true, index = 2)]
psbt: String,
/// Sets the number of block confirmations for UTXOs to be considered.
- #[structopt(name = "CONFIRMATIONS", required = true, index = 3)]
+ #[clap(name = "CONFIRMATIONS", required = true, index = 3)]
confirmations: usize,
/// Sets the addresses for which the proof was produced.
- #[structopt(name = "ADDRESSES", required = true, index = 4)]
+ #[clap(name = "ADDRESSES", required = true, index = 4)]
addresses: Vec,
- #[structopt(flatten)]
+ #[clap(flatten)]
electrum_opts: ElectrumOpts,
},
}
/// Backend Node operation subcommands.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
-#[structopt(rename_all = "lower")]
+#[derive(Debug, Subcommand, Clone, PartialEq)]
+#[clap(rename_all = "lower")]
#[cfg(any(feature = "regtest-node"))]
pub enum NodeSubCommand {
/// Get info.
@@ -161,12 +159,12 @@ pub enum NodeSubCommand {
/// Send to an external wallet address.
SendToAddress { address: String, amount: u64 },
/// Execute any bitcoin-cli commands.
- #[structopt(external_subcommand)]
+ #[clap(external_subcommand)]
BitcoinCli(Vec),
}
/// Wallet operation subcommands.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Subcommand, Clone, PartialEq)]
pub enum WalletSubCommand {
#[cfg(any(
feature = "electrum",
@@ -174,60 +172,60 @@ pub enum WalletSubCommand {
feature = "compact_filters",
feature = "rpc"
))]
- #[structopt(flatten)]
+ #[clap(flatten)]
OnlineWalletSubCommand(OnlineWalletSubCommand),
- #[structopt(flatten)]
+ #[clap(flatten)]
OfflineWalletSubCommand(OfflineWalletSubCommand),
}
/// Config options wallet operations can take.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Parser, Clone, PartialEq)]
pub struct WalletOpts {
/// Selects the wallet to use.
- #[structopt(name = "WALLET_NAME", short = "w", long = "wallet")]
+ #[clap(name = "WALLET_NAME", short = 'w', long = "wallet")]
pub wallet: Option,
/// Adds verbosity, returns PSBT in JSON format alongside serialized, displays expanded objects.
- #[structopt(name = "VERBOSE", short = "v", long = "verbose")]
+ #[clap(name = "VERBOSE", short = 'v', long = "verbose")]
pub verbose: bool,
/// Sets the descriptor to use for the external addresses.
- #[structopt(name = "DESCRIPTOR", short = "d", long = "descriptor", required = true)]
+ #[clap(name = "DESCRIPTOR", short = 'd', long = "descriptor", required = true)]
pub descriptor: String,
/// Sets the descriptor to use for internal addresses.
- #[structopt(name = "CHANGE_DESCRIPTOR", short = "c", long = "change_descriptor")]
+ #[clap(name = "CHANGE_DESCRIPTOR", short = 'c', long = "change_descriptor")]
pub change_descriptor: Option,
#[cfg(feature = "electrum")]
- #[structopt(flatten)]
+ #[clap(flatten)]
pub electrum_opts: ElectrumOpts,
#[cfg(feature = "esplora")]
- #[structopt(flatten)]
+ #[clap(flatten)]
pub esplora_opts: EsploraOpts,
#[cfg(feature = "compact_filters")]
- #[structopt(flatten)]
+ #[clap(flatten)]
pub compactfilter_opts: CompactFilterOpts,
#[cfg(feature = "rpc")]
- #[structopt(flatten)]
+ #[clap(flatten)]
pub rpc_opts: RpcOpts,
#[cfg(any(feature = "compact_filters", feature = "electrum", feature = "esplora"))]
- #[structopt(flatten)]
+ #[clap(flatten)]
pub proxy_opts: ProxyOpts,
}
/// Options to configure a SOCKS5 proxy for a blockchain client connection.
#[cfg(any(feature = "compact_filters", feature = "electrum", feature = "esplora"))]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Args, Clone, PartialEq)]
pub struct ProxyOpts {
/// Sets the SOCKS5 proxy for a blockchain client.
- #[structopt(name = "PROXY_ADDRS:PORT", long = "proxy", short = "p")]
+ #[clap(name = "PROXY_ADDRS:PORT", long = "proxy", short = 'p')]
pub proxy: Option,
/// Sets the SOCKS5 proxy credential.
- #[structopt(name="PROXY_USER:PASSWD", long="proxy_auth", short="a", parse(try_from_str = parse_proxy_auth))]
+ #[clap(name="PROXY_USER:PASSWD", long="proxy_auth", short='a', value_parser = parse_proxy_auth)]
pub proxy_auth: Option<(String, String)>,
/// Sets the SOCKS5 proxy retries for the blockchain client.
- #[structopt(
+ #[clap(
name = "PROXY_RETRIES",
- short = "r",
+ short = 'r',
long = "retries",
default_value = "5"
)]
@@ -236,25 +234,25 @@ pub struct ProxyOpts {
/// Options to configure a BIP157 Compact Filter backend.
#[cfg(feature = "compact_filters")]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Args, Clone, PartialEq)]
pub struct CompactFilterOpts {
/// Sets the full node network address.
- #[structopt(
+ #[clap(
name = "ADDRESS:PORT",
- short = "n",
+ short = 'n',
long = "node",
default_value = "127.0.0.1:18444"
)]
pub address: Vec,
/// Sets the number of parallel node connections.
- #[structopt(name = "CONNECTIONS", long = "conn_count", default_value = "4")]
+ #[clap(name = "CONNECTIONS", long = "conn_count", default_value = "4")]
pub conn_count: usize,
/// Optionally skip initial `skip_blocks` blocks.
- #[structopt(
+ #[clap(
name = "SKIP_BLOCKS",
- short = "k",
+ short = 'k',
long = "skip_blocks",
default_value = "0"
)]
@@ -263,35 +261,35 @@ pub struct CompactFilterOpts {
/// Options to configure a bitcoin core rpc backend.
#[cfg(feature = "rpc")]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Args, Clone, PartialEq)]
pub struct RpcOpts {
/// Sets the full node address for rpc connection.
- #[structopt(
+ #[clap(
name = "ADDRESS:PORT",
- short = "n",
+ short = 'n',
long = "node",
default_value = "127.0.0.1:18443"
)]
pub address: String,
/// Sets the rpc basic authentication.
- #[structopt(
+ #[clap(
name = "USER:PASSWD",
- short = "a",
+ short = 'a',
long = "basic-auth",
- parse(try_from_str = parse_proxy_auth),
+ value_parser = parse_proxy_auth,
default_value = "user:password",
)]
pub basic_auth: (String, String),
/// Sets an optional cookie authentication.
- #[structopt(name = "COOKIE", long = "cookie")]
+ #[clap(name = "COOKIE", long = "cookie")]
pub cookie: Option,
/// Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
- #[structopt(
+ #[clap(
name = "RPC_START_TIME",
- short = "s",
+ short = 's',
long = "start-time",
default_value = "0"
)]
@@ -300,25 +298,25 @@ pub struct RpcOpts {
/// Options to configure electrum backend.
#[cfg(feature = "electrum")]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Args, Clone, PartialEq)]
pub struct ElectrumOpts {
/// Sets the SOCKS5 proxy timeout for the Electrum client.
- #[structopt(name = "PROXY_TIMEOUT", short = "t", long = "timeout")]
+ #[clap(name = "PROXY_TIMEOUT", short = 't', long = "timeout")]
pub timeout: Option,
/// Sets the Electrum server to use.
- #[structopt(
+ #[clap(
name = "ELECTRUM_URL",
- short = "s",
+ short = 's',
long = "server",
default_value = "ssl://electrum.blockstream.info:60002"
)]
pub server: String,
/// Stop searching addresses for transactions after finding an unused gap of this length.
- #[structopt(
+ #[clap(
name = "STOP_GAP",
long = "stop_gap",
- short = "g",
+ short = 'g',
default_value = "10"
)]
pub stop_gap: usize,
@@ -326,38 +324,38 @@ pub struct ElectrumOpts {
/// Options to configure Esplora backend.
#[cfg(feature = "esplora")]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Args, Clone, PartialEq)]
pub struct EsploraOpts {
/// Use the esplora server if given as parameter.
- #[structopt(
+ #[clap(
name = "ESPLORA_URL",
- short = "s",
+ short = 's',
long = "server",
default_value = "https://blockstream.info/testnet/api/"
)]
pub server: String,
/// Socket timeout.
- #[structopt(name = "TIMEOUT", long = "timeout", default_value = "5")]
+ #[clap(name = "TIMEOUT", long = "timeout", default_value = "5")]
pub timeout: u64,
/// Stop searching addresses for transactions after finding an unused gap of this length.
- #[structopt(
+ #[clap(
name = "STOP_GAP",
long = "stop_gap",
- short = "g",
+ short = 'g',
default_value = "10"
)]
pub stop_gap: usize,
/// Number of parallel requests sent to the esplora service.
- #[structopt(name = "CONCURRENCY", long = "conc", default_value = "4")]
+ #[clap(name = "CONCURRENCY", long = "conc", default_value = "4")]
pub conc: u8,
}
/// Wallet subcommands that can be issued without a blockchain backend.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
-#[structopt(rename_all = "snake")]
+#[derive(Debug, Subcommand, Clone, PartialEq)]
+#[clap(rename_all = "snake")]
pub enum OfflineWalletSubCommand {
/// Generates a new external address.
GetNewAddress,
@@ -370,45 +368,47 @@ pub enum OfflineWalletSubCommand {
/// Creates a new unsigned transaction.
CreateTx {
/// Adds a recipient to the transaction.
- #[structopt(name = "ADDRESS:SAT", long = "to", required = true, parse(try_from_str = parse_recipient))]
+ // Clap Doesn't support complex vector parsing https://github.com/clap-rs/clap/issues/1704.
+ // Address and amount parsing is done at run time in handler function.
+ #[clap(name = "ADDRESS:SAT", long = "to", required = true, value_parser = parse_recipient)]
recipients: Vec<(Script, u64)>,
/// Sends all the funds (or all the selected utxos). Requires only one recipient with value 0.
- #[structopt(short = "all", long = "send_all")]
+ #[clap(long = "send_all", short = 'a')]
send_all: bool,
/// Enables Replace-By-Fee (BIP125).
- #[structopt(short = "rbf", long = "enable_rbf")]
+ #[clap(long = "enable_rbf", short = 'r')]
enable_rbf: bool,
/// Make a PSBT that can be signed by offline signers and hardware wallets. Forces the addition of `non_witness_utxo` and more details to let the signer identify the change output.
- #[structopt(long = "offline_signer")]
+ #[clap(long = "offline_signer")]
offline_signer: bool,
/// Selects which utxos *must* be spent.
- #[structopt(name = "MUST_SPEND_TXID:VOUT", long = "utxos", parse(try_from_str = parse_outpoint))]
+ #[clap(name = "MUST_SPEND_TXID:VOUT", long = "utxos", value_parser = parse_outpoint)]
utxos: Option>,
/// Marks a utxo as unspendable.
- #[structopt(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", parse(try_from_str = parse_outpoint))]
+ #[clap(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", value_parser = parse_outpoint)]
unspendable: Option>,
/// Fee rate to use in sat/vbyte.
- #[structopt(name = "SATS_VBYTE", short = "fee", long = "fee_rate")]
+ #[clap(name = "SATS_VBYTE", short = 'f', long = "fee_rate")]
fee_rate: Option,
/// Selects which policy should be used to satisfy the external descriptor.
- #[structopt(name = "EXT_POLICY", long = "external_policy")]
+ #[clap(name = "EXT_POLICY", long = "external_policy")]
external_policy: Option,
/// Selects which policy should be used to satisfy the internal descriptor.
- #[structopt(name = "INT_POLICY", long = "internal_policy")]
+ #[clap(name = "INT_POLICY", long = "internal_policy")]
internal_policy: Option,
/// Optionally create an OP_RETURN output containing given String in utf8 encoding (max 80 bytes)
- #[structopt(
+ #[clap(
name = "ADD_STRING",
long = "add_string",
- short = "s",
+ short = 's',
conflicts_with = "ADD_DATA"
)]
add_string: Option,
/// Optionally create an OP_RETURN output containing given base64 encoded String. (max 80 bytes)
- #[structopt(
+ #[clap(
name = "ADD_DATA",
long = "add_data",
- short = "o",
+ short = 'o',
conflicts_with = "ADD_STRING"
)]
add_data: Option, //base 64 econding
@@ -416,22 +416,22 @@ pub enum OfflineWalletSubCommand {
/// Bumps the fees of an RBF transaction.
BumpFee {
/// TXID of the transaction to update.
- #[structopt(name = "TXID", short = "txid", long = "txid")]
+ #[clap(name = "TXID", long = "txid")]
txid: String,
/// Allows the wallet to reduce the amount to the specified address in order to increase fees.
- #[structopt(name = "SHRINK_ADDRESS", short = "s", long = "shrink")]
+ #[clap(name = "SHRINK_ADDRESS", long = "shrink")]
shrink_address: Option,
/// Make a PSBT that can be signed by offline signers and hardware wallets. Forces the addition of `non_witness_utxo` and more details to let the signer identify the change output.
- #[structopt(long = "offline_signer")]
+ #[clap(long = "offline_signer")]
offline_signer: bool,
/// Selects which utxos *must* be added to the tx. Unconfirmed utxos cannot be used.
- #[structopt(name = "MUST_SPEND_TXID:VOUT", long = "utxos", parse(try_from_str = parse_outpoint))]
+ #[clap(name = "MUST_SPEND_TXID:VOUT", long = "utxos", value_parser = parse_outpoint)]
utxos: Option>,
/// Marks an utxo as unspendable, in case more inputs are needed to cover the extra fees.
- #[structopt(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", parse(try_from_str = parse_outpoint))]
+ #[clap(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", value_parser = parse_outpoint)]
unspendable: Option>,
/// The new targeted fee rate in sat/vbyte.
- #[structopt(name = "SATS_VBYTE", short = "fee", long = "fee_rate")]
+ #[clap(name = "SATS_VBYTE", short = 'f', long = "fee_rate")]
fee_rate: f32,
},
/// Returns the available spending policies for the descriptor.
@@ -441,44 +441,44 @@ pub enum OfflineWalletSubCommand {
/// Signs and tries to finalize a PSBT.
Sign {
/// Sets the PSBT to sign.
- #[structopt(name = "BASE64_PSBT", long = "psbt")]
+ #[clap(name = "BASE64_PSBT", long = "psbt")]
psbt: String,
/// Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor.
- #[structopt(name = "HEIGHT", long = "assume_height")]
+ #[clap(name = "HEIGHT", long = "assume_height")]
assume_height: Option,
/// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided.
- #[structopt(name = "WITNESS", long = "trust_witness_utxo")]
+ #[clap(name = "WITNESS", long = "trust_witness_utxo")]
trust_witness_utxo: Option,
},
/// Extracts a raw transaction from a PSBT.
ExtractPsbt {
/// Sets the PSBT to extract
- #[structopt(name = "BASE64_PSBT", long = "psbt")]
+ #[clap(name = "BASE64_PSBT", long = "psbt")]
psbt: String,
},
/// Finalizes a PSBT.
FinalizePsbt {
/// Sets the PSBT to finalize.
- #[structopt(name = "BASE64_PSBT", long = "psbt")]
+ #[clap(name = "BASE64_PSBT", long = "psbt")]
psbt: String,
/// Assume the blockchain has reached a specific height.
- #[structopt(name = "HEIGHT", long = "assume_height")]
+ #[clap(name = "HEIGHT", long = "assume_height")]
assume_height: Option,
/// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided.
- #[structopt(name = "WITNESS", long = "trust_witness_utxo")]
+ #[clap(name = "WITNESS", long = "trust_witness_utxo")]
trust_witness_utxo: Option,
},
/// Combines multiple PSBTs into one.
CombinePsbt {
/// Add one PSBT to combine. This option can be repeated multiple times, one for each PSBT.
- #[structopt(name = "BASE64_PSBT", long = "psbt", required = true)]
+ #[clap(name = "BASE64_PSBT", long = "psbt", required = true)]
psbt: Vec,
},
}
/// Wallet subcommands that needs a blockchain backend.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
-#[structopt(rename_all = "snake")]
+#[derive(Debug, Subcommand, Clone, PartialEq)]
+#[clap(rename_all = "snake")]
#[cfg(any(
feature = "electrum",
feature = "esplora",
@@ -491,7 +491,7 @@ pub enum OnlineWalletSubCommand {
/// Broadcasts a transaction to the network. Takes either a raw transaction or a PSBT to extract.
Broadcast {
/// Sets the PSBT to sign.
- #[structopt(
+ #[clap(
name = "BASE64_PSBT",
long = "psbt",
required_unless = "RAWTX",
@@ -499,7 +499,7 @@ pub enum OnlineWalletSubCommand {
)]
psbt: Option,
/// Sets the raw transaction to broadcast.
- #[structopt(
+ #[clap(
name = "RAWTX",
long = "tx",
required_unless = "BASE64_PSBT",
@@ -511,81 +511,81 @@ pub enum OnlineWalletSubCommand {
#[cfg(feature = "reserves")]
ProduceProof {
/// Sets the message.
- #[structopt(name = "MESSAGE", long = "message")]
+ #[clap(name = "MESSAGE", long = "message")]
msg: String,
},
/// Verify a proof of reserves for our wallet.
#[cfg(feature = "reserves")]
VerifyProof {
/// Sets the PSBT to verify.
- #[structopt(name = "BASE64_PSBT", long = "psbt")]
+ #[clap(name = "BASE64_PSBT", long = "psbt")]
psbt: String,
/// Sets the message to verify.
- #[structopt(name = "MESSAGE", long = "message")]
+ #[clap(name = "MESSAGE", long = "message")]
msg: String,
/// Sets the number of block confirmations for UTXOs to be considered.
- #[structopt(name = "CONFIRMATIONS", long = "confirmations", default_value = "6")]
+ #[clap(name = "CONFIRMATIONS", long = "confirmations", default_value = "6")]
confirmations: u32,
},
}
/// Subcommands for Key operations.
-#[derive(Debug, StructOpt, Clone, PartialEq)]
+#[derive(Debug, Subcommand, Clone, PartialEq)]
pub enum KeySubCommand {
/// Generates new random seed mnemonic phrase and corresponding master extended key.
Generate {
/// Entropy level based on number of random seed mnemonic words.
- #[structopt(
+ #[clap(
name = "WORD_COUNT",
- short = "e",
+ short = 'e',
long = "entropy",
default_value = "24",
possible_values = &["12","24"],
)]
word_count: usize,
/// Seed password.
- #[structopt(name = "PASSWORD", short = "p", long = "password")]
+ #[clap(name = "PASSWORD", short = 'p', long = "password")]
password: Option,
},
/// Restore a master extended key from seed backup mnemonic words.
Restore {
/// Seed mnemonic words, must be quoted (eg. "word1 word2 ...").
- #[structopt(name = "MNEMONIC", short = "m", long = "mnemonic")]
+ #[clap(name = "MNEMONIC", short = 'm', long = "mnemonic")]
mnemonic: String,
/// Seed password.
- #[structopt(name = "PASSWORD", short = "p", long = "password")]
+ #[clap(name = "PASSWORD", short = 'p', long = "password")]
password: Option,
},
/// Derive a child key pair from a master extended key and a derivation path string (eg. "m/84'/1'/0'/0" or "m/84h/1h/0h/0").
Derive {
/// Extended private key to derive from.
- #[structopt(name = "XPRV", short = "x", long = "xprv")]
+ #[clap(name = "XPRV", short = 'x', long = "xprv")]
xprv: ExtendedPrivKey,
/// Path to use to derive extended public key from extended private key.
- #[structopt(name = "PATH", short = "p", long = "path")]
+ #[clap(name = "PATH", short = 'p', long = "path")]
path: DerivationPath,
},
}
/// Subcommands available in REPL mode.
#[cfg(any(feature = "repl", target_arch = "wasm32"))]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
-#[structopt(global_settings =&[AppSettings::NoBinaryName], rename_all = "lower")]
+#[derive(Debug, Parser, Clone, PartialEq)]
+#[clap(global_settings =&[AppSettings::NoBinaryName], rename_all = "lower")]
pub enum ReplSubCommand {
/// Execute wallet commands.
Wallet {
- #[structopt(subcommand)]
+ #[clap(subcommand)]
subcommand: WalletSubCommand,
},
/// Execute key commands.
Key {
- #[structopt(subcommand)]
+ #[clap(subcommand)]
subcommand: KeySubCommand,
},
/// Execute node commands.
#[cfg(feature = "regtest-node")]
Node {
- #[structopt(subcommand)]
+ #[clap(subcommand)]
subcommand: NodeSubCommand,
},
/// Exit REPL loop.
@@ -611,7 +611,6 @@ mod test {
SyncOptions, Wallet,
};
use std::str::{self, FromStr};
- use structopt::StructOpt;
use super::OfflineWalletSubCommand::{BumpFee, CreateTx, GetNewAddress};
#[cfg(any(
@@ -632,6 +631,12 @@ mod test {
#[cfg(feature = "repl")]
use regex::Regex;
+ #[test]
+ fn test_clap_args() {
+ use clap::CommandFactory;
+ CliOpts::command().debug_assert();
+ }
+
#[test]
fn test_parse_wallet_get_new_address() {
let cli_args = vec!["bdk-cli", "--network", "bitcoin", "wallet",
@@ -857,7 +862,7 @@ mod test {
"--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)",
"--proxy", "127.0.0.1:9005",
"--proxy_auth", "random_user:random_passwd",
- "--node", "127.0.0.1:18444", "127.2.3.1:19695",
+ "--node", "127.0.0.1:18444",
"--conn_count", "4",
"--skip_blocks", "5",
"get_new_address"];
@@ -874,7 +879,7 @@ mod test {
descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
compactfilter_opts: CompactFilterOpts{
- address: vec!["127.0.0.1:18444".to_string(), "127.2.3.1:19695".to_string()],
+ address: vec!["127.0.0.1:18444".to_string()],
conn_count: 4,
skip_blocks: 5,
},
@@ -959,20 +964,22 @@ mod test {
let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet",
"--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)",
"--change_descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)",
- "create_tx", "--to", "n2Z3YNXtceeJhFkTknVaNjT1mnCGWesykJ:123456","mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf:78910",
+ "create_tx", "--to", "n2Z3YNXtceeJhFkTknVaNjT1mnCGWesykJ:123456", "--to", "mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf:78910",
"--utxos","87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:1",
"--utxos","87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:2",
"--add_string","Hello BDK",
];
- let cli_opts = CliOpts::from_iter(&cli_args);
+ let cli_opts = CliOpts::parse_from(&cli_args);
let script1 = Address::from_str("n2Z3YNXtceeJhFkTknVaNjT1mnCGWesykJ")
.unwrap()
.script_pubkey();
+
let script2 = Address::from_str("mjDZ34icH4V2k9GmC8niCrhzVuR3z8Mgkf")
.unwrap()
.script_pubkey();
+
let outpoint1 = OutPoint::from_str(
"87345e46bfd702d24d54890cc094d08a005f773b27c8f965dfe0eb1e23eef88e:1",
)
@@ -1356,6 +1363,7 @@ mod test {
let expected_cli_opts = CliOpts {
network: Network::Bitcoin,
+ datadir: None,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
wallet: None,
@@ -1375,7 +1383,7 @@ mod test {
retries: 5,
},
},
- subcommand: OnlineWalletSubCommand(VerifyProof {
+ subcommand: OnlineWalletSubCommand(OnlineWalletSubCommand::VerifyProof {
psbt: psbt.to_string(),
msg: message.to_string(),
confirmations: 6,
@@ -1411,6 +1419,7 @@ mod test {
let expected_cli_opts = CliOpts {
network: Network::Bitcoin,
+ datadir: None,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
wallet: None,
@@ -1430,7 +1439,7 @@ mod test {
retries: 5,
},
},
- subcommand: OnlineWalletSubCommand(VerifyProof {
+ subcommand: OnlineWalletSubCommand(OnlineWalletSubCommand::VerifyProof {
psbt: psbt.to_string(),
msg: message.to_string(),
confirmations: 0,
diff --git a/src/handlers.rs b/src/handlers.rs
index c630571..422657e 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -24,7 +24,7 @@ use crate::commands::*;
use crate::utils::*;
use bdk::{database::BatchDatabase, wallet::AddressIndex, Error, FeeRate, KeychainKind, Wallet};
-use structopt::StructOpt;
+use clap::Parser;
use bdk::bitcoin::consensus::encode::{deserialize, serialize, serialize_hex};
#[cfg(any(
@@ -272,7 +272,7 @@ where
let mut psbts = psbt
.iter()
.map(|s| {
- let psbt = base64::decode(&s).map_err(|e| Error::Generic(e.to_string()))?;
+ let psbt = base64::decode(s).map_err(|e| Error::Generic(e.to_string()))?;
let psbt: PartiallySignedTransaction = deserialize(&psbt)?;
Ok(psbt)
})
diff --git a/src/main.rs b/src/main.rs
index eeebddf..ced420f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -25,7 +25,7 @@ use crate::commands::CliOpts;
use crate::handlers::*;
use bdk::{bitcoin, Error};
use bdk_macros::{maybe_async, maybe_await};
-use structopt::StructOpt;
+use clap::Parser;
#[cfg(any(feature = "repl", target_arch = "wasm32"))]
const REPL_LINE_SPLIT_REGEX: &str = r#""([^"]*)"|'([^']*)'|([\w\-]+)"#;
@@ -36,7 +36,7 @@ const REPL_LINE_SPLIT_REGEX: &str = r#""([^"]*)"|'([^']*)'|([\w\-]+)"#;
fn main() {
env_logger::init();
- let cli_opts: CliOpts = CliOpts::from_args();
+ let cli_opts: CliOpts = CliOpts::parse();
let network = cli_opts.network;
debug!("network: {:?}", network);
diff --git a/src/wasm.rs b/src/wasm.rs
index 0a9e7ad..5e69ffc 100644
--- a/src/wasm.rs
+++ b/src/wasm.rs
@@ -9,6 +9,7 @@ use bitcoin::*;
use bdk::blockchain::AnyBlockchain;
use bdk::database::AnyDatabase;
use bdk::miniscript::{MiniscriptKey, Translator};
+use clap::Parser;
use js_sys::Promise;
use regex::Regex;
use std::collections::HashMap;
@@ -17,7 +18,6 @@ use std::ops::Deref;
use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr;
-use structopt::StructOpt;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::future_to_promise;