diff --git a/CHANGELOG.md b/CHANGELOG.md index de884f0..99914c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change rpc `--skip-blocks` option to `--start-time` which specifies time initial sync will start scanning from. - Add new `bdk-cli node []` to control the backend node deployed by `regtest-*` features. - Add an integration testing framework in `src/tests/integration.rs`. This framework uses the `regtest-*` feature to run automated testing with bdk-cli. +- Add possible values for `network` option to improve help message, and fix typo in doc. ## [0.5.0] diff --git a/README.md b/README.md index 9f1c7b1..191f0ab 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,66 @@ -# bdk-cli lib and example bin tool +
+

BDK-CLI

+ + + +

+ A Command-line Bitcoin Wallet App in pure rust using BDK +

+ +

+ Crate Info + MIT or Apache-2.0 Licensed + CI Status + + API Docs + Rustc Version 1.56+ + Chat on Discord +

+ +

+ Project Homepage + | + Documentation +

+
-![CI](https://github.com/bitcoindevkit/bdk-cli/workflows/CI/badge.svg) -![Code Coverage](https://github.com/bitcoindevkit/bdk-cli/workflows/Code%20Coverage/badge.svg) ## 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/latest/bdk/wallet/struct.Wallet.html). This might look tiny and innocent, but by harnessing the power of BDK it provides a powerful generic descriptor based command line wallet tool. +And yes, it can do Taproot!! + +This crate can be used for the following purposes: + - Instantly create a miniscript based wallet and connect to your backend of choice (Electrum, Esplora, Core RPC, CBF etc) and quickly play around with your own complex bitcoin scripting workflow. With one or many wallets, connected with one or many backends. + - The `tests/integration.rs` module is used to document high level complex workflows between BDK and different Bitcoin infrastructure systems, like Core, Electrum and Lightning(soon TM). + - (Planned) Expose the basic command handler via `wasm` to integrate `bdk-cli` functionality natively into the web platform. See also the [playground](https://bitcoindevkit.org/bdk-cli/playground/) page. + +If you are considering using 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 what's possible with descriptors, miniscript, and BDK APIs. For more information on BDK refer to the [website](https://bitcoindevkit.org/) and the [rust docs](https://docs.rs/bdk/latest/bdk/index.html) + +bdk-cli can be compiled with different features to suit your experimental needs. + - Database Options + - `key-value-db` : Sets the wallet database to a `sled` db. + - `sqlite-db` : Sets the wallet database to a `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 the bitcoin p2p network. + - `esplora-ureq` or `esplora-reqwest` : Connects the wallet to an esplora server synchronously or asynchronously. + - Extra Utility Tools + - `repl` : use bdk-cli as a [REPL](https://codewith.mu/en/tutorials/1.0/repl) shell (useful for quick manual testing of 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/bitcoindevkit/bdk-reserves). (requires the `electrum` feature) + - Automated Node Backend + - `regtest-bitcoin` : Auto deploys a regtest `bitcoind` node, connects the wallet, and exposes core rpc commands via `bdk-cli node` subcommands. + - `regtest-electrum` : Auto deploys `electrsd` and connected `bitcoind` nodes, exposes core rpc commands via `bdk-cli node` and provides a wallet connected to the local `electrsd`. + +The `default` feature set is `repl` and `sqlite-db`. With the `default` features, `bdk-cli` can be used as an **air-gapped** wallet, and can do everything that doesn't require a network connection. + ## Install bdk-cli ### From source -To install dev version of `bdk-cli` from local git repo with the `electrum` blockchain client enabled: +To install a dev version of `bdk-cli` from a local git repo with the `electrum` blockchain client enabled: ```shell cd @@ -19,8 +69,8 @@ bdk-cli help # to verify it worked ``` 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 +disabled. To enable these commands a blockchain client feature such as `electrum` or another +blockchain client feature must be enabled. Below is an example of how to run the `bdk-cli` binary with the `esplora-ureq` blockchain client feature. ```shell @@ -31,7 +81,7 @@ At most one blockchain feature can be enabled, available blockchain client featu `electrum`, `esplora-ureq` (blocking), `esplora-reqwest` (async), `compact_filters` and `rpc`. ### From crates.io -You can the install the binaries for the latest tag of `bdk-cli` with online wallet features +You can install the binary for the latest tag of `bdk-cli` with online wallet features directly from [crates.io](https://crates.io/crates/bdk-cli) with a command as below: ```sh cargo install bdk-cli --features electrum @@ -39,7 +89,7 @@ cargo install bdk-cli --features electrum ### bdk-cli bin usage examples -To get usage information for the `bdk-cli` bin use the below command which returns a list of +To get usage information for the `bdk-cli` binary use the below command which returns a list of available wallet options and commands: ```shell @@ -52,13 +102,13 @@ To sync a wallet to the default electrum server: cargo run --features electrum -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync ``` -To sync a wallet to Bitcoin Core node (assuming a regtest node at 127.0.0.1:18443) using the core rpc: +To sync a wallet to a Bitcoin Core node (assuming a regtest node at 127.0.0.1:18443) using the core rpc: ```shell cargo run --features rpc -- --network regtest wallet --node 127.0.0.1:18443 --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" sync ``` -To sync a wallet to Bitcoin Core node (assuming a regtest node at 127.0.0.1:18444) serving compact filters: +To sync a wallet to a Bitcoin Core node (assuming a regtest node at 127.0.0.1:18444) serving compact filters: Note: - This will increase build time by few minutes for the binaries because of `librocksdb`. - Bitcoin Core v0.21.0 or higher is required to serve compact filters. @@ -73,7 +123,7 @@ To get a wallet balance with customized logging: RUST_LOG=debug,sled=info,rustls=info cargo run -- wallet --descriptor "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)" get_balance ``` -To generate a new extended master key, suitable for using in a descriptor: +To generate a new extended master key, suitable for use in a descriptor: ```shell cargo run -- key generate diff --git a/src/commands.rs b/src/commands.rs index fafa5ad..4764d62 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -8,7 +8,9 @@ //! bdk-cli Command structure //! -//! This module defines all the bdk-cli commands using [structopt] +//! This module defines all the bdk-cli commands structure. +//! All optional args are defined in the structs below. +//! All subcommands are defined in the below enums. #![allow(clippy::large_enum_variant)] use structopt::clap::AppSettings; @@ -38,38 +40,39 @@ use crate::utils::{parse_outpoint, parse_recipient}; /// 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 #[structopt(version = option_env ! ("CARGO_PKG_VERSION").unwrap_or("unknown"), author = option_env ! ("CARGO_PKG_AUTHORS").unwrap_or(""))] pub struct CliOpts { - /// Sets the network + /// Sets the network. #[structopt( name = "NETWORK", short = "n", long = "network", - default_value = "testnet" + default_value = "testnet", + possible_values = &["bitcoin", "testnet", "signet", "regtest"] )] pub network: Network, /// Sets the wallet data directory. /// Default value : "~/.bdk-bitcoin #[structopt(name = "DATADIR", short = "d", long = "datadir")] pub datadir: Option, - /// 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 { - /// Node operation subcommands + /// Node operation subcommands. /// /// These commands can be used to control the backend bitcoin-core node - /// launched automatically with the `regtest-*` feature sets. The commands issues - /// bitcoin-cli rpc calls on the demon, in the background. + /// launched automatically with the `regtest-*` feature sets. The commands issue + /// bitcoin-cli rpc calls on the daemon, in the background. /// - /// Feel free to open feature-request in https://github.com/bitcoindevkit/bdk-cli + /// 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")] @@ -77,7 +80,7 @@ pub enum CliSubCommand { #[structopt(subcommand)] subcommand: NodeSubCommand, }, - /// Wallet Operations + /// Wallet operations. /// /// bdk-cli wallet operations includes all the basic wallet level tasks. /// Most commands can be used without connecting to any backend. To use commands that @@ -89,30 +92,30 @@ pub enum CliSubCommand { #[structopt(subcommand)] subcommand: WalletSubCommand, }, - /// Key Management Operations + /// Key management operations. /// /// Provides basic key operations that are not related to a specific wallet such as generating a /// new random master extended key or restoring a master extended key from mnemonic words. - + /// /// 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)] subcommand: KeySubCommand, }, - /// Compile a miniscript policy to an output descriptor + /// Compile a miniscript policy to an output descriptor. #[cfg(feature = "compiler")] #[structopt(long_about = "Miniscript policy compiler")] Compile { - /// Sets the spending policy to compile + /// Sets the spending policy to compile. #[structopt(name = "POLICY", required = true, index = 1)] policy: String, - /// Sets the script type used to embed the compiled policy + /// 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"])] script_type: String, }, #[cfg(feature = "repl")] - /// REPL command loop mode + /// REPL command loop mode. /// /// 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. @@ -120,21 +123,21 @@ pub enum CliSubCommand { #[structopt(flatten)] wallet_opts: WalletOpts, }, - /// Proof of Reserves Operations + /// Proof of reserves operations. /// - /// This can be used to produce and verify Proof of Reserves (BIP 322) using bdk-cli + /// This can be used to produce and verify Proof of Reserves (similar to BIP 322) using bdk-cli. #[cfg(all(feature = "reserves", feature = "electrum"))] ExternalReserves { - /// Sets the challenge message with which the proof was produced + /// Sets the challenge message with which the proof was produced. #[structopt(name = "MESSAGE", required = true, index = 1)] message: String, - /// Sets the proof in form of a PSBT to verify + /// Sets the proof in form of a PSBT to verify. #[structopt(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)] confirmations: usize, - /// Sets the addresses for which the proof was produced + /// Sets the addresses for which the proof was produced. #[structopt(name = "ADDRESSES", required = true, index = 4)] addresses: Vec, #[structopt(flatten)] @@ -142,25 +145,27 @@ pub enum CliSubCommand { }, } +/// Backend Node operation subcommands. #[derive(Debug, StructOpt, Clone, PartialEq)] #[structopt(rename_all = "lower")] #[cfg(any(feature = "regtest-node"))] pub enum NodeSubCommand { - /// Get info + /// Get info. GetInfo, - /// Get new address from node's test wallet + /// Get new address from node's test wallet. GetNewAddress, /// Generate given number of blocks and fund the internal wallet with coinbases. Generate { block_num: u64 }, - /// Get Wallet balance + /// Get Wallet balance. GetBalance, - /// Send to an external wallet address + /// Send to an external wallet address. SendToAddress { address: String, amount: u64 }, - /// Execute any bitcoin-cli commands + /// Execute any bitcoin-cli commands. #[structopt(external_subcommand)] BitcoinCli(Vec), } +/// Wallet operation subcommands. #[derive(Debug, StructOpt, Clone, PartialEq)] pub enum WalletSubCommand { #[cfg(any( @@ -175,18 +180,19 @@ pub enum WalletSubCommand { OfflineWalletSubCommand(OfflineWalletSubCommand), } +/// Config options wallet operations can take. #[derive(Debug, StructOpt, Clone, PartialEq)] pub struct WalletOpts { - /// Selects the wallet to use + /// Selects the wallet to use. #[structopt(name = "WALLET_NAME", short = "w", long = "wallet")] pub wallet: Option, - /// Adds verbosity, returns PSBT in JSON format alongside serialized, displays expanded objects + /// Adds verbosity, returns PSBT in JSON format alongside serialized, displays expanded objects. #[structopt(name = "VERBOSE", short = "v", long = "verbose")] pub verbose: bool, - /// Sets the descriptor to use for the external addresses + /// Sets the descriptor to use for the external addresses. #[structopt(name = "DESCRIPTOR", short = "d", long = "descriptor", required = true)] pub descriptor: String, - /// Sets the descriptor to use for internal addresses + /// Sets the descriptor to use for internal addresses. #[structopt(name = "CHANGE_DESCRIPTOR", short = "c", long = "change_descriptor")] pub change_descriptor: Option, #[cfg(feature = "electrum")] @@ -206,18 +212,19 @@ pub struct WalletOpts { 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)] pub struct ProxyOpts { - /// Sets the SOCKS5 proxy for Blockchain backend + /// Sets the SOCKS5 proxy for a blockchain client. #[structopt(name = "PROXY_ADDRS:PORT", long = "proxy", short = "p")] pub proxy: Option, - /// Sets the SOCKS5 proxy credential + /// Sets the SOCKS5 proxy credential. #[structopt(name="PROXY_USER:PASSWD", long="proxy_auth", short="a", parse(try_from_str = parse_proxy_auth))] pub proxy_auth: Option<(String, String)>, - /// Sets the SOCKS5 proxy retries for the Electrum client + /// Sets the SOCKS5 proxy retries for the blockchain client. #[structopt( name = "PROXY_RETRIES", short = "r", @@ -227,10 +234,11 @@ pub struct ProxyOpts { pub retries: u8, } +/// Options to configure a BIP157 Compact Filter backend. #[cfg(feature = "compact_filters")] #[derive(Debug, StructOpt, Clone, PartialEq)] pub struct CompactFilterOpts { - /// Sets the full node network address + /// Sets the full node network address. #[structopt( name = "ADDRESS:PORT", short = "n", @@ -239,11 +247,11 @@ pub struct CompactFilterOpts { )] pub address: Vec, - /// Sets the number of parallel node connections + /// Sets the number of parallel node connections. #[structopt(name = "CONNECTIONS", long = "conn_count", default_value = "4")] pub conn_count: usize, - /// Optionally skip initial `skip_blocks` blocks + /// Optionally skip initial `skip_blocks` blocks. #[structopt( name = "SKIP_BLOCKS", short = "k", @@ -253,10 +261,11 @@ pub struct CompactFilterOpts { pub skip_blocks: usize, } +/// Options to configure a bitcoin core rpc backend. #[cfg(feature = "rpc")] #[derive(Debug, StructOpt, Clone, PartialEq)] pub struct RpcOpts { - /// Sets the full node address for rpc connection + /// Sets the full node address for rpc connection. #[structopt( name = "ADDRESS:PORT", short = "n", @@ -265,7 +274,7 @@ pub struct RpcOpts { )] pub address: String, - /// Sets the rpc basic authentication + /// Sets the rpc basic authentication. #[structopt( name = "USER:PASSWD", short = "a", @@ -275,7 +284,7 @@ pub struct RpcOpts { )] pub basic_auth: (String, String), - /// Sets an optional cookie authentication + /// Sets an optional cookie authentication. #[structopt(name = "COOKIE", long = "cookie")] pub cookie: Option, @@ -289,13 +298,14 @@ pub struct RpcOpts { pub start_time: u64, } +/// Options to configure electrum backend. #[cfg(feature = "electrum")] #[derive(Debug, StructOpt, Clone, PartialEq)] pub struct ElectrumOpts { - /// Sets the SOCKS5 proxy timeout for the Electrum client + /// Sets the SOCKS5 proxy timeout for the Electrum client. #[structopt(name = "PROXY_TIMEOUT", short = "t", long = "timeout")] pub timeout: Option, - /// Sets the Electrum server to use + /// Sets the Electrum server to use. #[structopt( name = "ELECTRUM_URL", short = "s", @@ -314,10 +324,11 @@ pub struct ElectrumOpts { pub stop_gap: usize, } +/// Options to configure Esplora backend. #[cfg(feature = "esplora")] #[derive(Debug, StructOpt, Clone, PartialEq)] pub struct EsploraOpts { - /// Use the esplora server if given as parameter + /// Use the esplora server if given as parameter. #[structopt( name = "ESPLORA_URL", short = "s", @@ -326,7 +337,7 @@ pub struct EsploraOpts { )] pub server: String, - /// Socket timeout + /// Socket timeout. #[structopt(name = "TIMEOUT", long = "timeout", default_value = "5")] pub timeout: u64, @@ -339,55 +350,56 @@ pub struct EsploraOpts { )] pub stop_gap: usize, - /// Number of parallel requests sent to the esplora service (default: 4) + /// Number of parallel requests sent to the esplora service. #[structopt(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")] pub enum OfflineWalletSubCommand { - /// Generates a new external address + /// Generates a new external address. GetNewAddress, - /// Lists the available spendable UTXOs + /// Lists the available spendable UTXOs. ListUnspent, - /// Lists all the incoming and outgoing transactions of the wallet + /// Lists all the incoming and outgoing transactions of the wallet. ListTransactions, - /// Returns the current wallet balance + /// Returns the current wallet balance. GetBalance, - /// Creates a new unsigned transaction + /// Creates a new unsigned transaction. CreateTx { - /// Adds a recipient to the transaction + /// Adds a recipient to the transaction. #[structopt(name = "ADDRESS:SAT", long = "to", required = true, parse(try_from_str = parse_recipient))] recipients: Vec<(Script, u64)>, - /// Sends all the funds (or all the selected utxos). Requires only one recipients of value 0 + /// Sends all the funds (or all the selected utxos). Requires only one recipient with value 0. #[structopt(short = "all", long = "send_all")] send_all: bool, - /// Enables Replace-By-Fee (BIP125) + /// Enables Replace-By-Fee (BIP125). #[structopt(short = "rbf", long = "enable_rbf")] 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")] offline_signer: bool, - /// Selects which utxos *must* be spent + /// Selects which utxos *must* be spent. #[structopt(name = "MUST_SPEND_TXID:VOUT", long = "utxos", parse(try_from_str = parse_outpoint))] utxos: Option>, - /// Marks a utxo as unspendable + /// Marks a utxo as unspendable. #[structopt(name = "CANT_SPEND_TXID:VOUT", long = "unspendable", parse(try_from_str = parse_outpoint))] unspendable: Option>, - /// Fee rate to use in sat/vbyte + /// Fee rate to use in sat/vbyte. #[structopt(name = "SATS_VBYTE", short = "fee", long = "fee_rate")] fee_rate: Option, - /// Selects which policy should be used to satisfy the external descriptor + /// Selects which policy should be used to satisfy the external descriptor. #[structopt(name = "EXT_POLICY", long = "external_policy")] external_policy: Option, - /// Selects which policy should be used to satisfy the internal descriptor + /// Selects which policy should be used to satisfy the internal descriptor. #[structopt(name = "INT_POLICY", long = "internal_policy")] internal_policy: Option, }, - /// Bumps the fees of an RBF transaction + /// Bumps the fees of an RBF transaction. BumpFee { - /// TXID of the transaction to update + /// TXID of the transaction to update. #[structopt(name = "TXID", short = "txid", long = "txid")] txid: String, /// Allows the wallet to reduce the amount to the specified address in order to increase fees. @@ -396,58 +408,59 @@ pub enum OfflineWalletSubCommand { /// 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")] offline_signer: bool, - /// Selects which utxos *must* be added to the tx. Unconfirmed utxos cannot be used + /// 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))] utxos: Option>, - /// Marks an utxo as unspendable, in case more inputs are needed to cover the extra fees + /// 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))] unspendable: Option>, - /// The new targeted fee rate in sat/vbyte + /// The new targeted fee rate in sat/vbyte. #[structopt(name = "SATS_VBYTE", short = "fee", long = "fee_rate")] fee_rate: f32, }, - /// Returns the available spending policies for the descriptor + /// Returns the available spending policies for the descriptor. Policies, - /// Returns the public version of the wallet's descriptor(s) + /// Returns the public version of the wallet's descriptor(s). PublicDescriptor, - /// Signs and tries to finalize a PSBT + /// Signs and tries to finalize a PSBT. Sign { - /// Sets the PSBT to sign + /// Sets the PSBT to sign. #[structopt(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 + /// 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")] assume_height: Option, - /// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided + /// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided. #[structopt(name = "WITNESS", long = "trust_witness_utxo")] trust_witness_utxo: Option, }, - /// Extracts a raw transaction from a PSBT + /// Extracts a raw transaction from a PSBT. ExtractPsbt { /// Sets the PSBT to extract #[structopt(name = "BASE64_PSBT", long = "psbt")] psbt: String, }, - /// Finalizes a PSBT + /// Finalizes a PSBT. FinalizePsbt { - /// Sets the PSBT to finalize + /// Sets the PSBT to finalize. #[structopt(name = "BASE64_PSBT", long = "psbt")] psbt: String, - /// Assume the blockchain has reached a specific height + /// Assume the blockchain has reached a specific height. #[structopt(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 + /// Whether the signer should trust the witness_utxo, if the non_witness_utxo hasn’t been provided. #[structopt(name = "WITNESS", long = "trust_witness_utxo")] trust_witness_utxo: Option, }, - /// Combines multiple PSBTs into one + /// Combines multiple PSBTs into one. CombinePsbt { - /// Add one PSBT to combine. This option can be repeated multiple times, one for each PSBT + /// Add one PSBT to combine. This option can be repeated multiple times, one for each PSBT. #[structopt(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")] #[cfg(any( @@ -457,11 +470,11 @@ pub enum OfflineWalletSubCommand { feature = "rpc" ))] pub enum OnlineWalletSubCommand { - /// Syncs with the chosen blockchain server + /// Syncs with the chosen blockchain server. Sync, - /// Broadcasts a transaction to the network. Takes either a raw transaction or a PSBT to extract + /// Broadcasts a transaction to the network. Takes either a raw transaction or a PSBT to extract. Broadcast { - /// Sets the PSBT to sign + /// Sets the PSBT to sign. #[structopt( name = "BASE64_PSBT", long = "psbt", @@ -469,7 +482,7 @@ pub enum OnlineWalletSubCommand { conflicts_with = "RAWTX" )] psbt: Option, - /// Sets the raw transaction to broadcast + /// Sets the raw transaction to broadcast. #[structopt( name = "RAWTX", long = "tx", @@ -478,33 +491,34 @@ pub enum OnlineWalletSubCommand { )] tx: Option, }, - /// Produce a proof of reserves + /// Produce a proof of reserves. #[cfg(feature = "reserves")] ProduceProof { - /// Sets the message + /// Sets the message. #[structopt(name = "MESSAGE", long = "message")] msg: String, }, - /// Verify a proof of reserves for our wallet + /// Verify a proof of reserves for our wallet. #[cfg(feature = "reserves")] VerifyProof { - /// Sets the PSBT to verify + /// Sets the PSBT to verify. #[structopt(name = "BASE64_PSBT", long = "psbt")] psbt: String, - /// Sets the message to verify + /// Sets the message to verify. #[structopt(name = "MESSAGE", long = "message")] msg: String, - /// Sets the number of block confirmations for UTXOs to be considered. If nothing is specified, 6 is used. + /// Sets the number of block confirmations for UTXOs to be considered. #[structopt(name = "CONFIRMATIONS", long = "confirmations", default_value = "6")] confirmations: u32, }, } +/// Subcommands for Key operations. #[derive(Debug, StructOpt, Clone, PartialEq)] pub enum KeySubCommand { - /// Generates new random seed mnemonic phrase and corresponding master extended key + /// Generates new random seed mnemonic phrase and corresponding master extended key. Generate { - /// Entropy level based on number of random seed mnemonic words + /// Entropy level based on number of random seed mnemonic words. #[structopt( name = "WORD_COUNT", short = "e", @@ -513,51 +527,52 @@ pub enum KeySubCommand { possible_values = &["12","24"], )] word_count: usize, - /// Seed password + /// Seed password. #[structopt(name = "PASSWORD", short = "p", long = "password")] password: Option, }, - /// Restore a master extended key from seed backup mnemonic words + /// Restore a master extended key from seed backup mnemonic words. Restore { - /// Seed mnemonic words, must be quoted (eg. "word1 word2 ...") + /// Seed mnemonic words, must be quoted (eg. "word1 word2 ..."). #[structopt(name = "MNEMONIC", short = "m", long = "mnemonic")] mnemonic: String, - /// Seed password + /// Seed password. #[structopt(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 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 + /// Extended private key to derive from. #[structopt(name = "XPRV", short = "x", long = "xprv")] xprv: ExtendedPrivKey, - /// Path to use to derive extended public key from extended private key + /// Path to use to derive extended public key from extended private key. #[structopt(name = "PATH", short = "p", long = "path")] path: DerivationPath, }, } +/// Subcommands available in REPL mode. #[cfg(feature = "repl")] #[derive(Debug, StructOpt, Clone, PartialEq)] #[structopt(global_settings =&[AppSettings::NoBinaryName], rename_all = "lower")] pub enum ReplSubCommand { - /// Execute wallet Commands + /// Execute wallet commands. Wallet { #[structopt(subcommand)] subcommand: WalletSubCommand, }, - /// Execute Key Commands + /// Execute key commands. Key { #[structopt(subcommand)] subcommand: KeySubCommand, }, - /// Execute Node Commands + /// Execute node commands. #[cfg(feature = "regtest-node")] Node { #[structopt(subcommand)] subcommand: NodeSubCommand, }, - /// Exit REPL loop + /// Exit REPL loop. Exit, } diff --git a/src/handlers.rs b/src/handlers.rs index 536ce7d..8845f00 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -8,7 +8,7 @@ //! Command Handlers //! -//! This module describes all the command handling logic used by bdk-cli +//! This module describes all the command handling logic used by bdk-cli. use std::collections::BTreeMap; @@ -380,7 +380,7 @@ where } } -/// Execute a key sub-command +/// Handle a key sub-command /// /// Key sub-commands are described in [`KeySubCommand`]. pub(crate) fn handle_key_subcommand( @@ -450,7 +450,7 @@ 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")] @@ -478,7 +478,7 @@ pub(crate) fn handle_compile_subcommand( 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"))] @@ -519,6 +519,7 @@ pub(crate) fn handle_ext_reserves_subcommand( Ok(json!({ "spendable": spendable })) } +/// The global top level handler. #[maybe_async] pub(crate) fn handle_command(cli_opts: CliOpts) -> Result { let network = cli_opts.network; diff --git a/src/main.rs b/src/main.rs index 66e53f9..2c02d49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ // 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; diff --git a/src/nodes.rs b/src/nodes.rs index fd2686f..c075334 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -32,19 +32,19 @@ use { 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, }, #[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: Box, }, - // An Esplora backend with underlying bitcoin core. - // Wallet connected to it, via the esplora url + /// An Esplora backend with an underlying bitcoin core + /// Wallet connected to it, via the esplora url. #[cfg(any(feature = "regtest-esplora-ureq", feature = "regtest-esplora-reqwest"))] Esplora { bitcoind: Box, @@ -110,7 +110,7 @@ impl Nodes { } } - // Expose the underlying RPC client + // Expose the underlying RPC client. pub fn get_client(&self) -> Result<&Client, Error> { match self { Self::None => Err(Error::Generic( diff --git a/src/utils.rs b/src/utils.rs index 8333ce5..566db9f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -68,7 +68,7 @@ pub(crate) fn maybe_descriptor_wallet_name( Ok(wallet_opts) } -/// Parse the recipient (Address,Amount) argument from cli input +/// Parse the recipient (Address,Amount) argument from cli input. pub(crate) fn parse_recipient(s: &str) -> Result<(Script, u64), String> { let parts: Vec<_> = s.split(':').collect(); if parts.len() != 2 { @@ -86,7 +86,7 @@ pub(crate) fn parse_recipient(s: &str) -> Result<(Script, u64), String> { feature = "esplora", feature = "rpc" ))] -/// Parse the proxy (Socket:Port) argument from the cli input +/// Parse the proxy (Socket:Port) argument from the cli input. pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), String> { let parts: Vec<_> = s.split(':').collect(); if parts.len() != 2 { @@ -99,6 +99,7 @@ pub(crate) fn parse_proxy_auth(s: &str) -> Result<(String, String), String> { 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, @@ -133,15 +134,15 @@ pub fn get_outpoints_for_address( .collect() } -/// Parse a outpoint (Txid:Vout) argument from cli input +/// Parse a outpoint (Txid:Vout) argument from cli input. pub(crate) fn parse_outpoint(s: &str) -> Result { 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 +/// If not the default home directory is created at `~/.bdk-bitcoin`. pub(crate) fn prepare_home_dir(home_path: Option) -> Result { let dir = home_path.unwrap_or_else(|| { let mut dir = PathBuf::new(); @@ -162,7 +163,7 @@ pub(crate) fn prepare_home_dir(home_path: Option) -> Result Result { let mut dir = home_path.to_owned(); @@ -176,7 +177,7 @@ fn prepare_wallet_dir(wallet_name: &str, home_path: &Path) -> Result Result { let mut db_dir = prepare_wallet_dir(wallet_name, home_path)?; @@ -195,7 +196,7 @@ fn prepare_wallet_db_dir(wallet_name: &str, home_path: &Path) -> Result Result { let mut bc_dir = prepare_wallet_dir(wallet_name, home_path)?; @@ -213,9 +214,9 @@ fn prepare_bc_dir(wallet_name: &str, home_path: &Path) -> Result 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 `/bitcoind/regtest/wallets`. +/// Create the global bitcoind directory. +/// multiple wallets can access the same node datadir, and they will have separate +/// wallet names in `/bitcoind/regtest/wallets`. #[cfg(feature = "regtest-node")] pub(crate) fn prepare_bitcoind_datadir(home_path: &Path) -> Result { let mut dir = home_path.to_owned(); @@ -230,9 +231,9 @@ pub(crate) fn prepare_bitcoind_datadir(home_path: &Path) -> Result/electrsd/regtest/wallets`. +/// Create the global electrsd directory. +/// multiple wallets can access the same node datadir, and they will have separate +/// wallet names in `/bitcoind/regtest/wallets`. #[cfg(feature = "regtest-electrum")] pub(crate) fn prepare_electrum_datadir(home_path: &Path) -> Result { let mut dir = home_path.to_owned(); @@ -247,7 +248,7 @@ pub(crate) fn prepare_electrum_datadir(home_path: &Path) -> Result Result { #[cfg(feature = "regtest-node")] @@ -359,7 +361,7 @@ pub(crate) fn new_backend(_datadir: &Path) -> Result { feature = "rpc" ))] /// Create a new blockchain for a given [Nodes] if available -/// Or else create one from the wallet configuration options +/// or else create one from the wallet configuration options. pub(crate) fn new_blockchain( _network: Network, wallet_opts: &WalletOpts, @@ -474,7 +476,7 @@ pub(crate) fn new_blockchain( AnyBlockchain::from_config(&config) } -/// Create a new wallet from given wallet configuration options +/// Create a new wallet from given wallet configuration options. pub(crate) fn new_wallet( network: Network, wallet_opts: &WalletOpts,