// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
+use bitcoin::secp256k1::Secp256k1;
+use bitcoin::Network;
use std::fs;
use std::path::PathBuf;
-#[cfg(feature = "rpc")]
-use bitcoin::secp256k1::Secp256k1;
-use bitcoin::Network;
use clap::AppSettings;
use log::{debug, error, info, warn};
use bdk::blockchain::{AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain};
#[cfg(feature = "rpc")]
-use bdk::blockchain::rpc::{wallet_name_from_descriptor, Auth, RpcConfig};
+use bdk::blockchain::rpc::{Auth, RpcConfig};
use bdk::database::BatchDatabase;
use bdk::sled;
use bdk::sled::Tree;
+use bdk::wallet::wallet_name_from_descriptor;
use bdk::Wallet;
use bdk::{bitcoin, Error};
use bdk_cli::WalletSubCommand;
fn open_database(wallet_opts: &WalletOpts) -> Result<Tree, Error> {
let mut database_path = prepare_home_dir()?;
- database_path.push(wallet_opts.wallet.clone());
+ let wallet_name = wallet_opts
+ .wallet
+ .as_deref()
+ .expect("We should always have a wallet name at this point");
+ database_path.push(wallet_name);
let database = sled::open(database_path)?;
- let tree = database.open_tree(&wallet_opts.wallet)?;
+ let tree = database.open_tree(&wallet_name)?;
debug!("database opened successfully");
Ok(tree)
}
stop_gap: wallet_opts.electrum_opts.stop_gap,
});
- #[cfg(feature = "esplora-ureq")]
- let config = AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
- base_url: wallet_opts.esplora_opts.server.clone(),
- timeout_read: wallet_opts.esplora_opts.read_timeout,
- timeout_write: wallet_opts.esplora_opts.write_timeout,
- stop_gap: wallet_opts.esplora_opts.stop_gap,
- proxy: wallet_opts.proxy_opts.proxy.clone(),
- });
-
- #[cfg(feature = "esplora-reqwest")]
+ #[cfg(feature = "esplora")]
let config = AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: wallet_opts.esplora_opts.server.clone(),
+ timeout: Some(wallet_opts.esplora_opts.timeout),
concurrency: Some(wallet_opts.esplora_opts.conc),
stop_gap: wallet_opts.esplora_opts.stop_gap,
proxy: wallet_opts.proxy_opts.proxy.clone(),
Ok(result) => println!("{}", result),
Err(e) => {
match e {
- Error::ChecksumMismatch => error!("Descriptor checksum mismatch. Are you using a different descriptor for an already defined wallet name? (if you are not specifying the wallet name it defaults to 'main')"),
+ Error::ChecksumMismatch => error!("Descriptor checksum mismatch. Are you using a different descriptor for an already defined wallet name? (if you are not specifying the wallet name it is automatically named based on the descriptor)"),
e => error!("{}", e.to_string()),
}
},
}
}
+fn maybe_descriptor_wallet_name(
+ wallet_opts: WalletOpts,
+ network: Network,
+) -> Result<WalletOpts, Error> {
+ if wallet_opts.wallet.is_some() {
+ return Ok(wallet_opts);
+ }
+ // Use deterministic wallet name derived from descriptor
+ let wallet_name = wallet_name_from_descriptor(
+ &wallet_opts.descriptor[..],
+ wallet_opts.change_descriptor.as_deref(),
+ network,
+ &Secp256k1::new(),
+ )?;
+ let mut wallet_opts = wallet_opts;
+ wallet_opts.wallet = Some(wallet_name);
+
+ Ok(wallet_opts)
+}
+
fn handle_command(cli_opts: CliOpts, network: Network) -> Result<String, Error> {
let result = match cli_opts.subcommand {
#[cfg(any(
wallet_opts,
subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand),
} => {
+ let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, network)?;
let database = open_database(&wallet_opts)?;
let wallet = new_online_wallet(network, &wallet_opts, database)?;
let result = bdk_cli::handle_online_wallet_subcommand(&wallet, online_subcommand)?;
wallet_opts,
subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand),
} => {
+ let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, network)?;
let database = open_database(&wallet_opts)?;
let wallet = new_offline_wallet(network, &wallet_opts, database)?;
let result = bdk_cli::handle_offline_wallet_subcommand(
/// network: Network::Testnet,
/// subcommand: CliSubCommand::Wallet {
/// wallet_opts: WalletOpts {
-/// wallet: "main".to_string(),
+/// wallet: None,
/// verbose: false,
/// descriptor: "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/44'/1'/0'/0/*)".to_string(),
/// change_descriptor: None,
/// server: "ssl://electrum.blockstream.info:60002".to_string(),
/// stop_gap: 10
/// },
-/// #[cfg(feature = "esplora-ureq")]
+/// #[cfg(feature = "esplora")]
/// esplora_opts: EsploraOpts {
/// server: "https://blockstream.info/testnet/api/".to_string(),
-/// read_timeout: 5,
-/// write_timeout: 5,
-/// stop_gap: 10
-/// },
-/// #[cfg(feature = "esplora-reqwest")]
-/// esplora_opts: EsploraOpts {
-/// server: "https://blockstream.info/testnet/api/".to_string(),
-/// conc: 4,
-/// stop_gap: 10
+/// timeout: 5,
+/// stop_gap: 10,
+/// conc: 4
/// },
/// #[cfg(feature = "rpc")]
/// rpc_opts: RpcOpts{
/// let wallet_opts = WalletOpts::from_iter(&cli_args);
///
/// let expected_wallet_opts = WalletOpts {
-/// wallet: "main".to_string(),
+/// wallet: None,
/// verbose: false,
/// descriptor: "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/44'/1'/0'/0/*)".to_string(),
/// change_descriptor: None,
/// server: "ssl://electrum.blockstream.info:60002".to_string(),
/// stop_gap: 10
/// },
-/// #[cfg(feature = "esplora-ureq")]
+/// #[cfg(feature = "esplora")]
/// esplora_opts: EsploraOpts {
/// server: "https://blockstream.info/testnet/api/".to_string(),
-/// read_timeout: 5,
-/// write_timeout: 5,
-/// stop_gap: 10
-/// },
-/// #[cfg(feature = "esplora-reqwest")]
-/// esplora_opts: EsploraOpts {
-/// server: "https://blockstream.info/testnet/api/".to_string(),
-/// conc: 4,
-/// stop_gap: 10
+/// timeout: 5,
+/// stop_gap: 10,
+/// conc: 4
/// },
/// #[cfg(feature = "compact_filters")]
/// compactfilter_opts: CompactFilterOpts{
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct WalletOpts {
/// Selects the wallet to use
- #[structopt(
- name = "WALLET_NAME",
- short = "w",
- long = "wallet",
- default_value = "main"
- )]
- pub wallet: String,
+ #[structopt(name = "WALLET_NAME", short = "w", long = "wallet")]
+ pub wallet: Option<String>,
/// Adds verbosity, returns PSBT in JSON format alongside serialized, displays expanded objects
#[structopt(name = "VERBOSE", short = "v", long = "verbose")]
pub verbose: bool,
/// Esplora options
///
/// Esplora blockchain client information used by [`OnlineWalletSubCommand`]s.
-#[cfg(feature = "esplora-ureq")]
+#[cfg(feature = "esplora")]
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct EsploraOpts {
/// Use the esplora server if given as parameter
)]
pub server: String,
- /// Socket read timeout
- #[structopt(name = "READ_TIMEOUT", long = "read_timeout", default_value = "5")]
- pub read_timeout: u64,
-
- /// Socket write timeout
- #[structopt(name = "WRITE_TIMEOUT", long = "write_timeout", default_value = "5")]
- pub write_timeout: u64,
+ /// Socket timeout
+ #[structopt(name = "TIMEOUT", long = "timeout", default_value = "5")]
+ pub timeout: u64,
/// Stop searching addresses for transactions after finding an unused gap of this length.
#[structopt(
default_value = "10"
)]
pub stop_gap: usize,
-}
-
-#[cfg(feature = "esplora-reqwest")]
-#[derive(Debug, StructOpt, Clone, PartialEq)]
-pub struct EsploraOpts {
- /// Use the esplora server if given as parameter
- #[structopt(
- name = "ESPLORA_URL",
- short = "s",
- long = "server",
- default_value = "https://blockstream.info/testnet/api/"
- )]
- pub server: String,
/// Number of parallel requests sent to the esplora service (default: 4)
#[structopt(name = "CONCURRENCY", long = "conc", default_value = "4")]
pub conc: u8,
-
- /// Stop searching addresses for transactions after finding an unused gap of this length.
- #[structopt(
- name = "STOP_GAP",
- long = "stop_gap",
- short = "g",
- default_value = "10"
- )]
- pub stop_gap: usize,
}
// This is a workaround for `structopt` issue #333, #391, #418; see https://github.com/TeXitoi/structopt/issues/333#issuecomment-712265332
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
server: "ssl://electrum.blockstream.info:60002".to_string(),
stop_gap: 10,
},
- #[cfg(feature = "esplora-ureq")]
+ #[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
- },
- #[cfg(feature = "esplora-reqwest")]
- esplora_opts: EsploraOpts {
- server: "https://blockstream.info/testnet/api/".to_string(),
conc: 4,
- stop_gap: 10,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
network: Network::Testnet,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
"--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)",
"--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)",
"--server", "https://blockstream.info/api/",
- "--read_timeout", "10",
- "--write_timeout", "10",
+ "--timeout", "10",
"--stop_gap", "20",
"get_new_address"];
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
esplora_opts: EsploraOpts {
server: "https://blockstream.info/api/".to_string(),
- read_timeout: 10,
- write_timeout: 10,
- stop_gap: 20
+ timeout: 10,
+ stop_gap: 20,
+ conc: 4,
},
proxy_opts: ProxyOpts{
proxy: None,
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
esplora_opts: EsploraOpts {
server: "https://blockstream.info/api/".to_string(),
conc: 10,
- stop_gap: 20
+ stop_gap: 20,
+ timeout: 5,
},
proxy_opts: ProxyOpts{
proxy: None,
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
network: Network::Testnet,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: None,
server: "ssl://electrum.blockstream.info:60002".to_string(),
stop_gap: 10,
},
- #[cfg(feature = "esplora-ureq")]
+ #[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
- },
- #[cfg(feature = "esplora-reqwest")]
- esplora_opts: EsploraOpts {
- server: "https://blockstream.info/testnet/api/".to_string(),
conc: 4,
- stop_gap: 10,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
network: Network::Testnet,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
server: "ssl://electrum.blockstream.info:60002".to_string(),
stop_gap: 10,
},
- #[cfg(feature = "esplora-ureq")]
+ #[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
- },
- #[cfg(feature = "esplora-reqwest")]
- esplora_opts: EsploraOpts {
- server: "https://blockstream.info/testnet/api/".to_string(),
conc: 4,
- stop_gap: 10,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
network: Network::Testnet,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: Some("wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)".to_string()),
server: "ssl://electrum.blockstream.info:60002".to_string(),
stop_gap: 10,
},
- #[cfg(feature = "esplora-ureq")]
+ #[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
- },
- #[cfg(feature = "esplora-reqwest")]
- esplora_opts: EsploraOpts {
- server: "https://blockstream.info/testnet/api/".to_string(),
conc: 4,
- stop_gap: 10,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
network: Network::Testnet,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)".to_string(),
change_descriptor: None,
server: "ssl://electrum.blockstream.info:60002".to_string(),
stop_gap: 10,
},
- #[cfg(feature = "esplora-ureq")]
+ #[cfg(feature = "esplora")]
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
- },
- #[cfg(feature = "esplora-reqwest")]
- esplora_opts: EsploraOpts {
- server: "https://blockstream.info/testnet/api/".to_string(),
conc: 4,
- stop_gap: 10,
},
#[cfg(feature = "compact_filters")]
compactfilter_opts: CompactFilterOpts{
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
.to_string(),
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
.to_string(),
change_descriptor: None,
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
+ conc: 4,
},
proxy_opts: ProxyOpts {
proxy: None,
network: Network::Bitcoin,
subcommand: CliSubCommand::Wallet {
wallet_opts: WalletOpts {
- wallet: "main".to_string(),
+ wallet: None,
verbose: false,
descriptor: "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
.to_string(),
change_descriptor: None,
esplora_opts: EsploraOpts {
server: "https://blockstream.info/testnet/api/".to_string(),
- read_timeout: 5,
- write_timeout: 5,
+ timeout: 5,
stop_gap: 10,
+ conc: 4,
},
proxy_opts: ProxyOpts {
proxy: None,