From: Steve Myers Date: Tue, 9 Feb 2021 03:16:01 +0000 (-0800) Subject: Add ElectrumOpts and EsploraOpts structops; set no default dependencies X-Git-Tag: v0.2.0~11 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/database/scripts/struct.Height.html?a=commitdiff_plain;h=3211af06021f0382400ab8f02d7f347a50185eaf;p=bdk-cli Add ElectrumOpts and EsploraOpts structops; set no default dependencies --- diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index c5e7536..d1c5203 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -14,7 +14,10 @@ jobs: - 1.45.0 # MSRV features: - default - - default,esplora + - repl + - electrum + - esplora + - repl,electrum,esplora steps: - name: Checkout uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 1e09f81..056f512 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ readme = "README.md" license = "MIT" [dependencies] -bdk = { git = "https://github.com/bitcoindevkit/bdk.git", rev = "c4f2179", features = ["all-keys"]} +bdk = { git = "https://github.com/bitcoindevkit/bdk.git", rev = "c4f2179", default-features = false, features = ["all-keys"]} bdk-macros = { git = "https://github.com/bitcoindevkit/bdk.git", rev = "c4f2179" } structopt = "^0.3" serde_json = { version = "^1.0" } @@ -28,8 +28,8 @@ clap = { version = "2.33", optional = true } regex = {version = "1", optional = true } [features] -default = ["repl", "electrum"] -repl = ["async-trait", "rustyline", "dirs-next", "env_logger", "clap", "regex"] +default = [] +repl = ["async-trait", "bdk/key-value-db", "clap", "dirs-next", "env_logger", "regex", "rustyline"] electrum = ["bdk/electrum"] esplora = ["bdk/esplora"] compiler = ["bdk/compiler"] @@ -37,4 +37,7 @@ compiler = ["bdk/compiler"] [[bin]] name = "bdk-cli" path = "src/bdk_cli.rs" -required-features = ["repl"] +required-features = ["repl", "electrum"] + +[package.metadata.docs.rs] +all-features = true diff --git a/README.md b/README.md index 46deea3..e5d4886 100644 --- a/README.md +++ b/README.md @@ -14,23 +14,32 @@ To get usage information for the `bdk-cli` bin use the below command which retur available wallet options and commands: ```shell -cargo run +cargo run --features repl,electrum ``` To sync a wallet to the default electrum server: ```shell -cargo run -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync +cargo run --features repl,electrum,esplora -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync ``` To get a wallet balance with customized logging: ```shell -RUST_LOG=debug,sled=info,rustls=info cargo run -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" get_balance +RUST_LOG=debug,sled=info,rustls=info cargo run --features repl,electrum,esplora -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" get_balance ``` To generate a new extended master key, suitable for using in a descriptor: ```shell -cargo run -- key generate +cargo run --features repl,electrum -- key generate +``` + +To install dev version of `bdk-cli` from local git repo: + +```shell +cd +cargo install --path . --features repl,electrum,esplora + +bdk-cli help # to verify it worked ``` \ No newline at end of file diff --git a/src/bdk_cli.rs b/src/bdk_cli.rs index 80661c7..4e63b1a 100644 --- a/src/bdk_cli.rs +++ b/src/bdk_cli.rs @@ -100,8 +100,8 @@ where // Try to use Esplora config if "esplora" feature is enabled #[cfg(feature = "esplora")] let config_esplora: Option = { - let esplora_concurrency = wallet_opts.esplora_concurrency; - wallet_opts.esplora.clone().map(|base_url| { + let esplora_concurrency = wallet_opts.esplora_opts.esplora_concurrency; + wallet_opts.esplora_opts.esplora.clone().map(|base_url| { AnyBlockchainConfig::Esplora(EsploraBlockchainConfig { base_url, concurrency: Some(esplora_concurrency), @@ -112,10 +112,10 @@ where let config_esplora = None; let config_electrum = AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { - url: wallet_opts.electrum.clone(), - socks5: wallet_opts.proxy.clone(), - retry: wallet_opts.retries, - timeout: wallet_opts.timeout, + url: wallet_opts.electrum_opts.electrum.clone(), + socks5: wallet_opts.electrum_opts.proxy.clone(), + retry: wallet_opts.electrum_opts.retries, + timeout: wallet_opts.electrum_opts.timeout, }); // Fall back to Electrum config if Esplora config isn't provided diff --git a/src/lib.rs b/src/lib.rs index 75089f4..7eaab72 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,9 @@ //! //! # Example //! -//! ``` +//! ```no_run +//! # #[cfg(feature = "electrum")] +//! # { //! # use bdk::bitcoin::Network; //! # use bdk::blockchain::{AnyBlockchain, ConfigurableBlockchain}; //! # use bdk::blockchain::{AnyBlockchainConfig, ElectrumBlockchainConfig}; @@ -72,8 +74,8 @@ //! let database = MemoryDatabase::new(); //! //! let config = AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { -//! url: wallet_opts.electrum, -//! socks5: wallet_opts.proxy, +//! url: wallet_opts.electrum_opts.electrum, +//! socks5: wallet_opts.electrum_opts.proxy, //! retry: 3, //! timeout: None, //! }); @@ -89,6 +91,7 @@ //! let result = bdk_cli::handle_online_wallet_subcommand(&wallet, online_subcommand).unwrap(); //! println!("{}", serde_json::to_string_pretty(&result).unwrap()); //! } +//! # } //! ``` pub extern crate bdk; @@ -131,6 +134,10 @@ use bdk::{FeeRate, KeychainKind, Wallet}; /// # use bdk::bitcoin::Network; /// # use structopt::StructOpt; /// # use bdk_cli::{CliOpts, WalletOpts, CliSubCommand, WalletSubCommand}; +/// # #[cfg(feature = "electrum")] +/// # use bdk_cli::ElectrumOpts; +/// # #[cfg(feature = "esplora")] +/// # use bdk_cli::EsploraOpts; /// # use bdk_cli::OnlineWalletSubCommand::Sync; /// /// let cli_args = vec!["bdk-cli", "--network", "testnet", "wallet", @@ -148,18 +155,18 @@ use bdk::{FeeRate, KeychainKind, Wallet}; /// wallet: "main".to_string(), /// descriptor: "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/44'/1'/0'/0/*)".to_string(), /// change_descriptor: None, -/// #[cfg(feature = "electrum")] -/// proxy: None, -/// #[cfg(feature = "electrum")] -/// retries: 5, -/// #[cfg(feature = "electrum")] -/// timeout: None, -/// #[cfg(feature = "electrum")] -/// electrum: "ssl://electrum.blockstream.info:60002".to_string(), -/// #[cfg(feature = "esplora")] -/// esplora: None, -/// #[cfg(feature = "esplora")] -/// esplora_concurrency: 4, +/// #[cfg(feature = "electrum")] +/// electrum_opts: ElectrumOpts { +/// proxy: None, +/// retries: 5, +/// timeout: None, +/// electrum: "ssl://electrum.blockstream.info:60002".to_string(), +/// }, +/// #[cfg(feature = "esplora")] +/// esplora_opts: EsploraOpts { +/// esplora: None, +/// esplora_concurrency: 4, +/// } /// }, /// subcommand: WalletSubCommand::OnlineWalletSubCommand(Sync { /// max_addresses: Some(50) @@ -237,7 +244,7 @@ pub enum WalletSubCommand { /// Wallet options /// /// The wallet options required for all [`CliSubCommand::Wallet`] or [`CliSubCommand::Repl`] -/// sub-commands. These options capture wallet descriptor and blockchain client information. The \ +/// sub-commands. These options capture wallet descriptor and blockchain client information. The /// blockchain client details are only used for [`OnlineWalletSubCommand`]s. /// /// # Example @@ -245,7 +252,11 @@ pub enum WalletSubCommand { /// ``` /// # use bdk::bitcoin::Network; /// # use structopt::StructOpt; -/// # use bdk_cli:: WalletOpts; +/// # use bdk_cli::WalletOpts; +/// # #[cfg(feature = "electrum")] +/// # use bdk_cli::ElectrumOpts; +/// # #[cfg(feature = "esplora")] +/// # use bdk_cli::EsploraOpts; /// /// let cli_args = vec!["wallet", /// "--descriptor", "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/44'/1'/0'/0/*)"]; @@ -260,17 +271,17 @@ pub enum WalletSubCommand { /// descriptor: "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/44'/1'/0'/0/*)".to_string(), /// change_descriptor: None, /// #[cfg(feature = "electrum")] -/// proxy: None, -/// #[cfg(feature = "electrum")] -/// retries: 5, -/// #[cfg(feature = "electrum")] -/// timeout: None, -/// #[cfg(feature = "electrum")] -/// electrum: "ssl://electrum.blockstream.info:60002".to_string(), -/// #[cfg(feature = "esplora")] -/// esplora: None, +/// electrum_opts: ElectrumOpts { +/// proxy: None, +/// retries: 5, +/// timeout: None, +/// electrum: "ssl://electrum.blockstream.info:60002".to_string(), +/// }, /// #[cfg(feature = "esplora")] -/// esplora_concurrency: 4, +/// esplora_opts: EsploraOpts { +/// esplora: None, +/// esplora_concurrency: 4, +/// } /// }; /// /// assert_eq!(expected_wallet_opts, wallet_opts); @@ -291,12 +302,24 @@ pub struct WalletOpts { /// Sets the descriptor to use for internal addresses #[structopt(name = "CHANGE_DESCRIPTOR", short = "c", long = "change_descriptor")] pub change_descriptor: Option, - /// Sets the SOCKS5 proxy for the Electrum client #[cfg(feature = "electrum")] + #[structopt(flatten)] + pub electrum_opts: ElectrumOpts, + #[cfg(feature = "esplora")] + #[structopt(flatten)] + pub esplora_opts: EsploraOpts, +} + +/// Electrum options +/// +/// Electrum blockchain client information used by [`OnlineWalletSubCommand`]s. +#[cfg(feature = "electrum")] +#[derive(Debug, StructOpt, Clone, PartialEq)] +pub struct ElectrumOpts { + /// Sets the SOCKS5 proxy for the Electrum client #[structopt(name = "PROXY_SERVER:PORT", short = "p", long = "proxy")] pub proxy: Option, /// Sets the SOCKS5 proxy retries for the Electrum client - #[cfg(feature = "electrum")] #[structopt( name = "PROXY_RETRIES", short = "r", @@ -305,11 +328,9 @@ pub struct WalletOpts { )] pub retries: u8, /// Sets the SOCKS5 proxy timeout for the Electrum client - #[cfg(feature = "electrum")] #[structopt(name = "PROXY_TIMEOUT", short = "t", long = "timeout")] pub timeout: Option, /// Sets the Electrum server to use - #[cfg(feature = "electrum")] #[structopt( name = "SERVER:PORT", short = "s", @@ -317,12 +338,18 @@ pub struct WalletOpts { default_value = "ssl://electrum.blockstream.info:60002" )] pub electrum: String, +} + +/// Esplora options +/// +/// Esplora blockchain client information used by [`OnlineWalletSubCommand`]s. +#[cfg(feature = "esplora")] +#[derive(Debug, StructOpt, Clone, PartialEq)] +pub struct EsploraOpts { /// Use the esplora server if given as parameter - #[cfg(feature = "esplora")] #[structopt(name = "ESPLORA_URL", short = "e", long = "esplora")] pub esplora: Option, /// Concurrency of requests made to the esplora server - #[cfg(feature = "esplora")] #[structopt( name = "ESPLORA_CONCURRENCY", long = "esplora_concurrency", @@ -359,6 +386,11 @@ To capture wallet sub-commands from a string vector without a preceeding binary create a custom struct the includes the `NoBinaryName` clap setting and wraps the WalletSubCommand enum. See also the [`bdk-cli`](https://github.com/bitcoindevkit/bdk-cli/blob/master/src/bdkcli.rs) example app. +"# +)] +#[cfg_attr( + all(doc, feature = "repl"), + doc = r#" # Example ``` @@ -836,9 +868,14 @@ pub fn handle_key_subcommand( #[cfg(test)] mod test { use super::{CliOpts, WalletOpts}; + #[cfg(feature = "electrum")] + use crate::ElectrumOpts; + #[cfg(feature = "esplora")] + use crate::EsploraOpts; use crate::OfflineWalletSubCommand::{CreateTx, GetNewAddress}; use crate::OnlineWalletSubCommand::{Broadcast, Sync}; use crate::{handle_key_subcommand, CliSubCommand, KeySubCommand, WalletSubCommand}; + use bdk::bitcoin::{Address, Network, OutPoint}; use bdk::miniscript::bitcoin::network::constants::Network::Testnet; use std::str::FromStr; @@ -861,17 +898,17 @@ mod test { descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), #[cfg(feature = "electrum")] - proxy: None, - #[cfg(feature = "electrum")] - retries: 5, - #[cfg(feature = "electrum")] - timeout: None, - #[cfg(feature = "electrum")] - electrum: "ssl://electrum.blockstream.info:60002".to_string(), - #[cfg(feature = "esplora")] - esplora: None, + electrum_opts: ElectrumOpts { + proxy: None, + retries: 5, + timeout: None, + electrum: "ssl://electrum.blockstream.info:60002".to_string() + }, #[cfg(feature = "esplora")] - esplora_concurrency: 4 + esplora_opts: EsploraOpts { + esplora: None, + esplora_concurrency: 4 + }, }, subcommand: WalletSubCommand::OfflineWalletSubCommand(GetNewAddress), }, @@ -899,14 +936,18 @@ mod test { wallet: "main".to_string(), descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), - proxy: Some("127.0.0.1:9150".to_string()), - retries: 3, - timeout: Some(10), - electrum: "ssl://electrum.blockstream.info:50002".to_string(), - #[cfg(feature = "esplora")] - esplora: None, + #[cfg(feature = "electrum")] + electrum_opts: ElectrumOpts { + proxy: Some("127.0.0.1:9150".to_string()), + retries: 3, + timeout: Some(10), + electrum: "ssl://electrum.blockstream.info:50002".to_string(), + }, #[cfg(feature = "esplora")] - esplora_concurrency: 4, + esplora_opts: EsploraOpts { + esplora: None, + esplora_concurrency: 4, + } }, subcommand: WalletSubCommand::OfflineWalletSubCommand(GetNewAddress), }, @@ -935,15 +976,17 @@ mod test { descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), #[cfg(feature = "electrum")] - proxy: None, - #[cfg(feature = "electrum")] - retries: 5, - #[cfg(feature = "electrum")] - timeout: None, - #[cfg(feature = "electrum")] - electrum: "ssl://electrum.blockstream.info:60002".to_string(), - esplora: Some("https://blockstream.info/api/".to_string()), - esplora_concurrency: 5, + electrum_opts: ElectrumOpts { + proxy: None, + retries: 5, + timeout: None, + electrum: "ssl://electrum.blockstream.info:60002".to_string(), + }, + #[cfg(feature = "esplora")] + esplora_opts: EsploraOpts { + esplora: Some("https://blockstream.info/api/".to_string()), + esplora_concurrency: 5, + } }, subcommand: WalletSubCommand::OfflineWalletSubCommand(GetNewAddress), }, @@ -968,17 +1011,17 @@ mod test { descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), change_descriptor: None, #[cfg(feature = "electrum")] - proxy: None, - #[cfg(feature = "electrum")] - retries: 5, - #[cfg(feature = "electrum")] - timeout: None, - #[cfg(feature = "electrum")] - electrum: "ssl://electrum.blockstream.info:60002".to_string(), - #[cfg(feature = "esplora")] - esplora: None, + electrum_opts: ElectrumOpts { + proxy: None, + retries: 5, + timeout: None, + electrum: "ssl://electrum.blockstream.info:60002".to_string(), + }, #[cfg(feature = "esplora")] - esplora_concurrency: 4, + esplora_opts: EsploraOpts { + esplora: None, + esplora_concurrency: 4, + } }, subcommand: WalletSubCommand::OnlineWalletSubCommand(Sync { max_addresses: Some(50) @@ -1023,17 +1066,17 @@ mod test { descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()), #[cfg(feature = "electrum")] - proxy: None, - #[cfg(feature = "electrum")] - retries: 5, - #[cfg(feature = "electrum")] - timeout: None, - #[cfg(feature = "electrum")] - electrum: "ssl://electrum.blockstream.info:60002".to_string(), - #[cfg(feature = "esplora")] - esplora: None, + electrum_opts: ElectrumOpts { + proxy: None, + retries: 5, + timeout: None, + electrum: "ssl://electrum.blockstream.info:60002".to_string(), + }, #[cfg(feature = "esplora")] - esplora_concurrency: 4, + esplora_opts: EsploraOpts { + esplora: None, + esplora_concurrency: 4, + } }, subcommand: WalletSubCommand::OfflineWalletSubCommand(CreateTx { recipients: vec![(script1, 123456), (script2, 78910)], @@ -1069,17 +1112,17 @@ mod test { descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(), change_descriptor: None, #[cfg(feature = "electrum")] - proxy: None, - #[cfg(feature = "electrum")] - retries: 5, - #[cfg(feature = "electrum")] - timeout: None, - #[cfg(feature = "electrum")] - electrum: "ssl://electrum.blockstream.info:60002".to_string(), - #[cfg(feature = "esplora")] - esplora: None, + electrum_opts: ElectrumOpts { + proxy: None, + retries: 5, + timeout: None, + electrum: "ssl://electrum.blockstream.info:60002".to_string(), + }, #[cfg(feature = "esplora")] - esplora_concurrency: 4, + esplora_opts: EsploraOpts { + esplora: None, + esplora_concurrency: 4, + } }, subcommand: WalletSubCommand::OnlineWalletSubCommand(Broadcast { psbt: Some("cHNidP8BAEICAAAAASWhGE1AhvtO+2GjJHopssFmgfbq+WweHd8zN/DeaqmDAAAAAAD/////AQAAAAAAAAAABmoEAAECAwAAAAAAAAA=".to_string()),