From: Steve Myers Date: Tue, 5 Jan 2021 20:54:21 +0000 (-0800) Subject: Prep for publishing to crates.io, rename bin to bdk-cli X-Git-Tag: v0.1.0~4 X-Git-Url: http://internal-gitweb-vhost/script/%22https:/database/struct.SegwitCodeLengthError.html?a=commitdiff_plain;h=88f57258724def486aa973273804ecc0c02d9609;p=bdk-cli Prep for publishing to crates.io, rename bin to bdk-cli --- diff --git a/Cargo.toml b/Cargo.toml index 110f73e..a49e43e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,14 @@ name = "bdk-cli" version = "0.1.0" edition = "2018" -authors = ["Alekos Filini ", "Riccardo Casatta "] +authors = ["Alekos Filini ", "Riccardo Casatta ", "Steve Myers +// +// Copyright (c) 2020 Magical Bitcoin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +use std::fs; +use std::path::PathBuf; +use std::sync::Arc; + +use bitcoin::Network; +use clap::AppSettings; +use log::{debug, info, warn}; +use rustyline::error::ReadlineError; +use rustyline::Editor; +use structopt::StructOpt; + +use bdk::bitcoin; +#[cfg(feature = "esplora")] +use bdk::blockchain::esplora::EsploraBlockchainConfig; +use bdk::blockchain::{ + AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig, +}; +use bdk::sled; +use bdk::Wallet; +use bdk_cli::{self, WalletOpt, WalletSubCommand}; + +#[derive(Debug, StructOpt, Clone, PartialEq)] +#[structopt(name = "BDK CLI", setting = AppSettings::NoBinaryName, +version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), +author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))] +struct ReplOpt { + /// Wallet sub-command + #[structopt(subcommand)] + pub subcommand: WalletSubCommand, +} + +fn prepare_home_dir() -> PathBuf { + let mut dir = PathBuf::new(); + dir.push(&dirs_next::home_dir().unwrap()); + dir.push(".bdk-bitcoin"); + + if !dir.exists() { + info!("Creating home directory {}", dir.as_path().display()); + fs::create_dir(&dir).unwrap(); + } + + dir.push("database.sled"); + dir +} + +fn main() { + env_logger::init(); + + let cli_opt: WalletOpt = WalletOpt::from_args(); + + let network = cli_opt.network; + debug!("network: {:?}", network); + if network == Network::Bitcoin { + warn!("This is experimental software and not currently recommended for use on Bitcoin mainnet, proceed with caution.") + } + + let descriptor = cli_opt.descriptor.as_str(); + let change_descriptor = cli_opt.change_descriptor.as_deref(); + debug!("descriptors: {:?} {:?}", descriptor, change_descriptor); + + let database = sled::open(prepare_home_dir().to_str().unwrap()).unwrap(); + let tree = database.open_tree(cli_opt.wallet).unwrap(); + debug!("database opened successfully"); + + // Try to use Esplora config if "esplora" feature is enabled + #[cfg(feature = "esplora")] + let config_esplora: Option = { + let esplora_concurrency = cli_opt.esplora_concurrency; + cli_opt.esplora.map(|base_url| { + AnyBlockchainConfig::Esplora(EsploraBlockchainConfig { + base_url, + concurrency: Some(esplora_concurrency), + }) + }) + }; + #[cfg(not(feature = "esplora"))] + let config_esplora = None; + + // Fall back to Electrum config if Esplora config isn't provided + let config = + config_esplora.unwrap_or(AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { + url: cli_opt.electrum, + socks5: cli_opt.proxy, + retry: 10, + timeout: 10, + })); + + let wallet = Wallet::new( + descriptor, + change_descriptor, + network, + tree, + AnyBlockchain::from_config(&config).unwrap(), + ) + .unwrap(); + + let wallet = Arc::new(wallet); + + match cli_opt.subcommand { + WalletSubCommand::Repl => { + let mut rl = Editor::<()>::new(); + + // if rl.load_history("history.txt").is_err() { + // println!("No previous history."); + // } + + loop { + let readline = rl.readline(">> "); + match readline { + Ok(line) => { + if line.trim() == "" { + continue; + } + rl.add_history_entry(line.as_str()); + let split_line: Vec<&str> = line.split(' ').collect(); + let repl_subcommand: Result = + ReplOpt::from_iter_safe(split_line); + debug!("repl_subcommand = {:?}", repl_subcommand); + + if let Err(err) = repl_subcommand { + println!("{}", err.message); + continue; + } + + let result = bdk_cli::handle_wallet_subcommand( + &Arc::clone(&wallet), + repl_subcommand.unwrap().subcommand, + ) + .unwrap(); + println!("{}", serde_json::to_string_pretty(&result).unwrap()); + } + Err(ReadlineError::Interrupted) => continue, + Err(ReadlineError::Eof) => break, + Err(err) => { + println!("{:?}", err); + break; + } + } + } + + // rl.save_history("history.txt").unwrap(); + } + _ => { + let result = bdk_cli::handle_wallet_subcommand(&wallet, cli_opt.subcommand).unwrap(); + println!("{}", serde_json::to_string_pretty(&result).unwrap()); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index d5ec993..d105176 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! parse global wallet options and wallet subcommand options needed for a wallet command line //! interface. //! -//! See the `repl.rs` example for how to use this module to create a simple command line REPL +//! See the `bdk-cli` example bin for how to use this module to create a simple command line //! wallet application. //! //! See [`WalletOpt`] for global wallet options and [`WalletSubCommand`] for supported sub-commands. @@ -50,7 +50,7 @@ //! // to get args from cli use: //! // let cli_opt = WalletOpt::from_args(); //! -//! let cli_args = vec!["repl", "--network", "testnet", "--descriptor", +//! let cli_args = vec!["bdk-cli", "--network", "testnet", "--descriptor", //! "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)", //! "sync", "--max_addresses", "50"]; //! let cli_opt = WalletOpt::from_iter(&cli_args); @@ -124,7 +124,7 @@ use bdk::{FeeRate, KeychainKind, TxBuilder, Wallet}; /// # use structopt::StructOpt; /// # use bdk_cli::{WalletSubCommand, WalletOpt}; /// -/// let cli_args = vec!["repl", "--network", "testnet", +/// let cli_args = vec!["bdk-cli", "--network", "testnet", /// "--descriptor", "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/44'/1'/0'/0/*)", /// "sync", "--max_addresses", "50"]; /// @@ -153,7 +153,7 @@ use bdk::{FeeRate, KeychainKind, TxBuilder, Wallet}; /// ``` #[derive(Debug, StructOpt, Clone, PartialEq)] -#[structopt(name = "BDK Wallet", +#[structopt(name = "BDK CLI", version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))] pub struct WalletOpt { @@ -212,8 +212,8 @@ pub struct WalletOpt { /// Wallet sub-command /// /// A [structopt](https://docs.rs/crate/structopt) enum that parses wallet sub-command arguments from -/// the command line or from a `String` vector, such as in the [`repl`](https://github.com/bitcoindevkit/bdk/blob/master/examples/repl.rs) -/// example app. +/// the command line or from a `String` vector, such as in the [`bdk-cli`](https://github.com/bitcoindevkit/bdk-cli/blob/master/src/bdkcli.rs) +/// example cli wallet. /// /// # Example /// @@ -221,7 +221,7 @@ pub struct WalletOpt { /// # use bdk_cli::WalletSubCommand; /// # use structopt::StructOpt; /// -/// let sync_sub_command = WalletSubCommand::from_iter(&["repl", "sync", "--max_addresses", "50"]); +/// let sync_sub_command = WalletSubCommand::from_iter(&["bdk-cli", "sync", "--max_addresses", "50"]); /// assert!(matches!( /// sync_sub_command, /// WalletSubCommand::Sync { @@ -232,7 +232,7 @@ pub struct WalletOpt { /// /// To capture wallet sub-commands from a string vector without a preceeding binary name you can /// create a custom struct the includes the `NoBinaryName` clap setting and wraps the WalletSubCommand -/// enum. See also the [`repl`](https://github.com/bitcoindevkit/bdk/blob/master/examples/repl.rs) +/// enum. See also the [`bdk-cli`](https://github.com/bitcoindevkit/bdk-cli/blob/master/src/bdkcli.rs) /// example app. /// /// # Example @@ -242,7 +242,7 @@ pub struct WalletOpt { /// # use clap::AppSettings; /// /// #[derive(Debug, StructOpt, Clone, PartialEq)] -/// #[structopt(name = "BDK Wallet", setting = AppSettings::NoBinaryName, +/// #[structopt(name = "BDK CLI", setting = AppSettings::NoBinaryName, /// version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), /// author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))] /// struct ReplOpt { @@ -597,7 +597,7 @@ mod test { #[test] fn test_get_new_address() { - let cli_args = vec!["repl", "--network", "bitcoin", + let cli_args = vec!["bdk-cli", "--network", "bitcoin", "--descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", "--change_descriptor", "wpkh(xpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", "--esplora", "https://blockstream.info/api/", @@ -625,7 +625,7 @@ mod test { #[test] fn test_sync() { - let cli_args = vec!["repl", "--network", "testnet", + let cli_args = vec!["bdk-cli", "--network", "testnet", "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", "sync", "--max_addresses", "50"]; @@ -652,7 +652,7 @@ mod test { #[test] fn test_create_tx() { - let cli_args = vec!["repl", "--network", "testnet", "--proxy", "127.0.0.1:9150", + let cli_args = vec!["bdk-cli", "--network", "testnet", "--proxy", "127.0.0.1:9150", "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", "--change_descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/1/*)", "--server","ssl://electrum.blockstream.info:50002", @@ -706,7 +706,7 @@ mod test { #[test] fn test_broadcast() { - let cli_args = vec!["repl", "--network", "testnet", + let cli_args = vec!["bdk-cli", "--network", "testnet", "--descriptor", "wpkh(tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)", "broadcast", "--psbt", "cHNidP8BAEICAAAAASWhGE1AhvtO+2GjJHopssFmgfbq+WweHd8zN/DeaqmDAAAAAAD/////AQAAAAAAAAAABmoEAAECAwAAAAAAAAA="]; diff --git a/src/repl.rs b/src/repl.rs deleted file mode 100644 index 17b6cad..0000000 --- a/src/repl.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Magical Bitcoin Library -// Written in 2020 by -// Alekos Filini -// -// Copyright (c) 2020 Magical Bitcoin -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -use std::fs; -use std::path::PathBuf; -use std::sync::Arc; - -use bitcoin::Network; -use clap::AppSettings; -use log::{debug, info, warn}; -use rustyline::error::ReadlineError; -use rustyline::Editor; -use structopt::StructOpt; - -use bdk::bitcoin; -#[cfg(feature = "esplora")] -use bdk::blockchain::esplora::EsploraBlockchainConfig; -use bdk::blockchain::{ - AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig, -}; -use bdk::sled; -use bdk::Wallet; -use bdk_cli::{self, WalletOpt, WalletSubCommand}; - -#[derive(Debug, StructOpt, Clone, PartialEq)] -#[structopt(name = "BDK Wallet", setting = AppSettings::NoBinaryName, -version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), -author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))] -struct ReplOpt { - /// Wallet sub-command - #[structopt(subcommand)] - pub subcommand: WalletSubCommand, -} - -fn prepare_home_dir() -> PathBuf { - let mut dir = PathBuf::new(); - dir.push(&dirs_next::home_dir().unwrap()); - dir.push(".bdk-bitcoin"); - - if !dir.exists() { - info!("Creating home directory {}", dir.as_path().display()); - fs::create_dir(&dir).unwrap(); - } - - dir.push("database.sled"); - dir -} - -fn main() { - env_logger::init(); - - let cli_opt: WalletOpt = WalletOpt::from_args(); - - let network = cli_opt.network; - debug!("network: {:?}", network); - if network == Network::Bitcoin { - warn!("This is experimental software and not currently recommended for use on Bitcoin mainnet, proceed with caution.") - } - - let descriptor = cli_opt.descriptor.as_str(); - let change_descriptor = cli_opt.change_descriptor.as_deref(); - debug!("descriptors: {:?} {:?}", descriptor, change_descriptor); - - let database = sled::open(prepare_home_dir().to_str().unwrap()).unwrap(); - let tree = database.open_tree(cli_opt.wallet).unwrap(); - debug!("database opened successfully"); - - // Try to use Esplora config if "esplora" feature is enabled - #[cfg(feature = "esplora")] - let config_esplora: Option = { - let esplora_concurrency = cli_opt.esplora_concurrency; - cli_opt.esplora.map(|base_url| { - AnyBlockchainConfig::Esplora(EsploraBlockchainConfig { - base_url, - concurrency: Some(esplora_concurrency), - }) - }) - }; - #[cfg(not(feature = "esplora"))] - let config_esplora = None; - - // Fall back to Electrum config if Esplora config isn't provided - let config = - config_esplora.unwrap_or(AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig { - url: cli_opt.electrum, - socks5: cli_opt.proxy, - retry: 10, - timeout: 10, - })); - - let wallet = Wallet::new( - descriptor, - change_descriptor, - network, - tree, - AnyBlockchain::from_config(&config).unwrap(), - ) - .unwrap(); - - let wallet = Arc::new(wallet); - - match cli_opt.subcommand { - WalletSubCommand::Repl => { - let mut rl = Editor::<()>::new(); - - // if rl.load_history("history.txt").is_err() { - // println!("No previous history."); - // } - - loop { - let readline = rl.readline(">> "); - match readline { - Ok(line) => { - if line.trim() == "" { - continue; - } - rl.add_history_entry(line.as_str()); - let split_line: Vec<&str> = line.split(' ').collect(); - let repl_subcommand: Result = - ReplOpt::from_iter_safe(split_line); - debug!("repl_subcommand = {:?}", repl_subcommand); - - if let Err(err) = repl_subcommand { - println!("{}", err.message); - continue; - } - - let result = bdk_cli::handle_wallet_subcommand( - &Arc::clone(&wallet), - repl_subcommand.unwrap().subcommand, - ) - .unwrap(); - println!("{}", serde_json::to_string_pretty(&result).unwrap()); - } - Err(ReadlineError::Interrupted) => continue, - Err(ReadlineError::Eof) => break, - Err(err) => { - println!("{:?}", err); - break; - } - } - } - - // rl.save_history("history.txt").unwrap(); - } - _ => { - let result = bdk_cli::handle_wallet_subcommand(&wallet, cli_opt.subcommand).unwrap(); - println!("{}", serde_json::to_string_pretty(&result).unwrap()); - } - } -}