-# bdk-cli lib and example bin tool
+<div align="center">
+ <h1>BDK-CLI</h1>
+
+ <img src="https://github.com/bitcoindevkit/bdk/raw/master/static/bdk.png" width="220" />
+
+ <p>
+ <strong>A Command-line Bitcoin Wallet App in pure rust using BDK</strong>
+ </p>
+
+ <p>
+ <a href="https://crates.io/crates/bdk-cli"><img alt="Crate Info" src="https://img.shields.io/crates/v/bdk-cli.svg"/></a>
+ <a href="https://github.com/bitcoindevkit/bdk-cli/blob/master/LICENSE"><img alt="MIT or Apache-2.0 Licensed" src="https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg"/></a>
+ <a href="https://github.com/bitcoindevkit/bdk-cli/actions?query=workflow%3ACI"><img alt="CI Status" src="https://github.com/bitcoindevkit/bdk-cli/workflows/CI/badge.svg"></a>
+ <a href="https://codecov.io/gh/bitcoindevkit/bdk-cli"><img src="https://codecov.io/gh/bitcoindevkit/bdk-cli/branch/master/graph/badge.svg"/></a>
+ <a href="https://docs.rs/bdk-cli"><img alt="API Docs" src="https://img.shields.io/badge/docs.rs-bdk_cli-green"/></a>
+ <a href="https://blog.rust-lang.org/2020/08/27/Rust-1.56.0.html"><img alt="Rustc Version 1.56+" src="https://img.shields.io/badge/rustc-1.56%2B-lightgrey.svg"/></a>
+ <a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
+ </p>
+
+ <h4>
+ <a href="https://bitcoindevkit.org">Project Homepage</a>
+ <span> | </span>
+ <a href="https://docs.rs/bdk-cli">Documentation</a>
+ </h4>
+</div>
-
-
## About
-This project provides a command line interface (cli) Bitcoin wallet library and [`REPL`](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)
-wallet tool based on the [bdk](https://github.com/bitcoindevkit/bdk) library.
+This project provides a command-line Bitcoin wallet application using the latest [BDK APIs](https://docs.rs/bdk/0.19.0/bdk/wallet/struct.Wallet.html). This is used mostly as an high level integration testing framework, by the BDK team, using the [tests](/tests/integration.rs) modules.
+
+But if you are planning to use BDK in your own wallet project, bdk-cli is a nice playground to get started with. It allows easy testnet and regtest wallet operations, to try out whats possible with descriptors, miniscripts, and BDK APIs. For more information on BDK refer the [website](https://bitcoindevkit.org/), and the [rust docs](https://docs.rs/bdk/latest/bdk/index.html)
+
+bdk-cli can be compiled in various different ways to suit the experimental needs.
+ - Database Options
+ - `key-value-db` : Sets the wallet database a `sled` db.
+ - `sqlite-db` : Sets the wallet database as `sqlite3` db.
+ - Blockchain Options
+ - `rpc` : Connects the wallet to bitcoin core via RPC.
+ - `electrum` : Connects the wallet to an electrum server.
+ - `compact_filters` : Deploy a BIP157 node to get blockchain data from bitcoin p2p network.
+ - `esplora-ureq/reqwest` : Connects the wallet to a esplora server sync/asynchronously.
+ - Extra Utility Tools
+ - `repl` : use bdk-cli as a [REPL](https://codewith.mu/en/tutorials/1.0/repl) shell (useful for quick hand testing wallet operations).
+ - `compiler` : opens up bdk-cli policy compiler commands.
+ - `verify` : uses `bitcoinconsensus` to verify transactions at every `sync` call of the wallet.
+ - `reserves` : opens up bdk-cli **Proof of Reserves** operation commands using the [bdk-reserves plugin](https://github.com/weareseba/bdk-reserves). (requires `electrum`)
+ - Automated Node Backend
+ - `regtest-bitcoin` : Auto deploys a regtest bitcoin node, connects the wallet, and exposes core rpc commands via `bdk-cli node` subsommands.
+ - `regtest-electrum` : Auto deploys a `electrsd` connected to a `bitcoind` and an wallet connectded to `electrsd`. `bdk-cli node` subcommand still calls the `bitcoind` RPC.
+
+The `deafult` feature sets are `repl` and `sqlite-db`. With `default` features, `bdk-cli` works as an **air-gapped** wallet, and can do everything that doesn't require a network connection.
+
## Install bdk-cli
### From source
If no blockchain client feature is enabled online wallet commands `sync` and `broadcast` will be
disabled. To enable these commands a blockchain client features such as `electrum` or another
-blockchain backend feature must be enabled. Below is an example of how run the `bdk-cli` bin with
+blockchain client feature must be enabled. Below is an example of how run the `bdk-cli` bin with
the `esplora-ureq` blockchain client feature.
```shell
//! bdk-cli Command structure
//!
-//! This module defines all the bdk-cli commands using [structopt]
+//! This module defines all the bdk-cli commands structure.
+//! All option args are defined in the structs below.
+//! All subcommands are defined in the below enums.
#![allow(clippy::large_enum_variant)]
use structopt::clap::AppSettings;
/// But this is not just any toy.
/// bdk-cli is also a fully functioning Bitcoin wallet with taproot support!
///
-/// For more information checkout https://bitcoindevkit.org/
+/// For more information checkout <https://bitcoindevkit.org/>
#[structopt(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"),
author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))]
pub struct CliOpts {
/// Default value : "~/.bdk-bitcoin
#[structopt(name = "DATADIR", short = "d", long = "datadir")]
pub datadir: Option<std::path::PathBuf>,
- /// Top level cli sub-command
+ /// Top level cli sub-commands
#[structopt(subcommand)]
pub subcommand: CliSubCommand,
}
-/// CLI sub-commands
+/// Top level cli sub-commands
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(rename_all = "snake")]
pub enum CliSubCommand {
/// launched automatically with the `regtest-*` feature sets. The commands issues
/// bitcoin-cli rpc calls on the demon, in the background.
///
- /// Feel free to open feature-request in https://github.com/bitcoindevkit/bdk-cli
+ /// Feel free to open feature-request in <https://github.com/bitcoindevkit/bdk-cli>
/// if you need extra rpc calls not covered in the command list.
#[cfg(feature = "regtest-node")]
#[structopt(long_about = "Regtest Node mode")]
},
}
+/// Backend Node operation subcommands
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(rename_all = "lower")]
#[cfg(any(feature = "regtest-node"))]
BitcoinCli(Vec<String>),
}
+/// Wallet operation subcommands
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub enum WalletSubCommand {
#[cfg(any(
OfflineWalletSubCommand(OfflineWalletSubCommand),
}
+/// Config options wallet operations can take
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct WalletOpts {
/// Selects the wallet to use
pub proxy_opts: ProxyOpts,
}
+/// Options to configure SOCKS5 Proxy connection to backend
#[cfg(any(feature = "compact_filters", feature = "electrum", feature = "esplora"))]
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct ProxyOpts {
pub retries: u8,
}
+/// Options to configure BIP157 Compact Filter backend
#[cfg(feature = "compact_filters")]
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct CompactFilterOpts {
pub skip_blocks: usize,
}
+/// Options to configure bitcoin core rpc backend
#[cfg(feature = "rpc")]
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct RpcOpts {
pub start_time: u64,
}
+/// Options to configure electrum backend
#[cfg(feature = "electrum")]
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct ElectrumOpts {
pub stop_gap: usize,
}
+/// Options to configure Esplora backend
#[cfg(feature = "esplora")]
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub struct EsploraOpts {
pub conc: u8,
}
+/// Wallet subcommands that can be issued without a blockchain backend
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(rename_all = "snake")]
pub enum OfflineWalletSubCommand {
},
}
+/// Wallet subcommands that needs a blockchain backend
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(rename_all = "snake")]
#[cfg(any(
},
}
+/// Subcommands for Key operations
#[derive(Debug, StructOpt, Clone, PartialEq)]
pub enum KeySubCommand {
/// Generates new random seed mnemonic phrase and corresponding master extended key
},
}
+/// Subcommands available in REPL mode
#[cfg(feature = "repl")]
#[derive(Debug, StructOpt, Clone, PartialEq)]
#[structopt(global_settings =&[AppSettings::NoBinaryName], rename_all = "lower")]
}
}
-/// Execute a key sub-command
+/// Handle a key sub-command
///
/// Key sub-commands are described in [`KeySubCommand`].
pub(crate) fn handle_key_subcommand(
}
}
-/// Execute the miniscript compiler sub-command
+/// Handle the miniscript compiler sub-command
///
/// Compiler options are described in [`CliSubCommand::Compile`].
#[cfg(feature = "compiler")]
Ok(json!({"descriptor": descriptor.to_string()}))
}
-/// Proof of reserves verification sub-command
+/// Handle Proof of Reserves commands
///
/// Proof of reserves options are described in [`CliSubCommand::ExternalReserves`].
#[cfg(all(feature = "reserves", feature = "electrum"))]
Ok(json!({ "spendable": spendable }))
}
+/// The global top level handler.
#[maybe_async]
pub(crate) fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
let network = cli_opts.network;
// You may not use this file except in accordance with one or both of these
// licenses.
-//! BDK CLI APP
-//!
-//! This module describes the app's main() function
+#![doc = include_str!("../README.md")]
+#![doc(html_logo_url = "https://github.com/bitcoindevkit/bdk/raw/master/static/bdk.png")]
+#![warn(missing_docs)]
mod commands;
mod handlers;
pub enum Nodes {
None,
#[cfg(feature = "regtest-bitcoin")]
- // A bitcoin core backend. Wallet connected to it via RPC.
+ /// A bitcoin core backend. Wallet connected to it via RPC.
Bitcoin {
bitcoind: Box<electrsd::bitcoind::BitcoinD>,
},
#[cfg(feature = "regtest-electrum")]
- // An Electrum backend, with an underlying bitcoin core
- // Wallet connected to it, via the electrum url
+ /// An Electrum backend, with an underlying bitcoin core
+ /// Wallet connected to it, via the electrum url
Electrum {
bitcoind: Box<electrsd::bitcoind::BitcoinD>,
electrsd: Box<electrsd::ElectrsD>,
},
- // An Esplora backend with underlying bitcoin core.
- // Wallet connected to it, via the esplora url
+ /// An Esplora backend with underlying bitcoin core.
+ /// Wallet connected to it, via the esplora url
#[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))]
Esplora {
bitcoind: Box<electrsd::bitcoind::BitcoinD>,
Ok((user, passwd))
}
+/// Fetch all the utxos, for a given address
#[cfg(all(feature = "reserves", feature = "electrum"))]
pub fn get_outpoints_for_address(
address: Address,
OutPoint::from_str(s).map_err(|e| e.to_string())
}
-/// prepare bdk-cli home directory
+/// Prepare bdk-cli home directory
///
/// This function is called to check if [`crate::CliOpts`] datadir is set.
/// If not the default home directory is created at `~/.bdk-bitcoin
Ok(dir)
}
-/// prepare bdk_cli wallet directory
+/// Prepare bdk_cli wallet directory
fn prepare_wallet_dir(wallet_name: &str, home_path: &Path) -> Result<PathBuf, Error> {
let mut dir = home_path.to_owned();
Ok(bc_dir)
}
-// We create only a global single node directory. Because multiple
-// wallets can access the same node datadir, and they will have separate
-// wallet names in `<home_path>/bitcoind/regtest/wallets`.
+/// Create the global bitcoind directory.
+/// multiple wallets can access the same node datadir, and they will have separate
+/// wallet names in `<home_path>/bitcoind/regtest/wallets`.
#[cfg(feature = "regtest-node")]
pub(crate) fn prepare_bitcoind_datadir(home_path: &Path) -> Result<PathBuf, Error> {
let mut dir = home_path.to_owned();
Ok(dir)
}
-// We create only a global single node directory. Because multiple
-// wallets can access the same node datadir, and they will have separate
-// wallet names in `<home_path>/electrsd/regtest/wallets`.
+/// Create the global electrsd directory.
+/// multiple wallets can access the same node datadir, and they will have separate
+/// wallet names in `<home_path>/bitcoind/regtest/wallets`.
#[cfg(feature = "regtest-electrum")]
pub(crate) fn prepare_electrum_datadir(home_path: &Path) -> Result<PathBuf, Error> {
let mut dir = home_path.to_owned();
Ok(database)
}
+/// Create a new backend node at given datadir
#[allow(dead_code)]
pub(crate) fn new_backend(_datadir: &Path) -> Result<Nodes, Error> {
#[cfg(feature = "regtest-node")]