Skip to content

Commit 7df419a

Browse files
committed
Merge #180: Adding descriptor generator
7d3720e feat(desc): remove generate subcommand (Vihiga Tyonum) 32a2c88 feat(desc):update descriptors gen to use templates (Vihiga Tyonum) c8f23ba feat(descriptors): fix descriptor generation (Vihiga Tyonum) 550e816 feat(descriptors): add multipath descs and pretty (Vihiga Tyonum) 1f77842 fix descriptors generation (Vihiga Tyonum) 6c9c3a1 feat: add descriptor generation (AmosOO7) Pull request description: # Pull Request: Add `descriptor` Subcommand ## Description This PR introduces a new `descriptor` subcommand to the BDK CLI that enable users to generate and inspect **Bitcoin wallet descriptors** for internal and external paths, supporting common types (pkh, sh, wpkh, wsh, tr). It includes support for both **single-path** descriptors from extended keys (`xprv`/`xpub` or `tprv`/`tpub`). This resolves issue #175 ## Features Implemented - Adds new `descriptor` subcommand with: - `generate` – generate new descriptors - Supports BIP44, BIP49, BIP84, and BIP86 script types - Generates **internal and external descriptors** - Reasonable defaults: - `--type` defaults to `wsh` and a short `t` - `--network` default `testnet` --- ## Usage Examples ### 1. Generate Descriptors from an Extended Private Key ```bash cargo run -- --network testnet descriptor generate --type wsh <XPRV> ``` ### 2. Generate Descriptors from a Mnemonic (no key provided) ```bash cargo run -- --network testnet descriptor generate --type tr ``` * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk-cli/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [ ] I've added tests for the new feature * [ ] I've added docs for the new feature * [ ] I've updated `CHANGELOG.md` ACKs for top commit: notmandatory: tACK 7d3720e tvpeter: ACK 7d3720e Tree-SHA512: 38182e657ffeaec89c84c601f8c41482eee9962a1847eaf6a3ad8b55d01858e0d2a056248d5bd4090734787caedf1c11232bb56337c90d5451c2664ff3571b50
2 parents bdb5777 + 7d3720e commit 7df419a

File tree

4 files changed

+350
-22
lines changed

4 files changed

+350
-22
lines changed

src/commands.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
//! All subcommands are defined in the below enums.
1414
1515
#![allow(clippy::large_enum_variant)]
16-
1716
use bdk_wallet::bitcoin::{
1817
Address, Network, OutPoint, ScriptBuf,
1918
bip32::{DerivationPath, Xpriv},
@@ -107,8 +106,23 @@ pub enum CliSubCommand {
107106
#[command(flatten)]
108107
wallet_opts: WalletOpts,
109108
},
109+
/// Output Descriptors operations.
110+
///
111+
/// Generate output descriptors from either extended key (Xprv/Xpub) or mnemonic phrase.
112+
/// This feature is intended for development and testing purposes only.
113+
Descriptor {
114+
/// Descriptor type (script type)
115+
#[arg(
116+
long = "type",
117+
short = 't',
118+
value_parser = ["pkh", "wpkh", "sh", "wsh", "tr"],
119+
default_value = "wsh"
120+
)]
121+
desc_type: String,
122+
/// Optional key: xprv, xpub, or mnemonic phrase
123+
key: Option<String>,
124+
},
110125
}
111-
112126
/// Wallet operation subcommands.
113127
#[derive(Debug, Subcommand, Clone, PartialEq)]
114128
pub enum WalletSubCommand {
@@ -470,6 +484,19 @@ pub enum ReplSubCommand {
470484
#[command(subcommand)]
471485
subcommand: KeySubCommand,
472486
},
487+
/// Generate descriptors
488+
Descriptor {
489+
/// Descriptor type (script type).
490+
#[arg(
491+
long = "type",
492+
short = 't',
493+
value_parser = ["pkh", "wpkh", "sh", "wsh", "tr"],
494+
default_value = "wsh"
495+
)]
496+
desc_type: String,
497+
/// Optional key: xprv, xpub, or mnemonic phrase
498+
key: Option<String>,
499+
},
473500
/// Exit REPL loop.
474501
Exit,
475502
}

src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use thiserror::Error;
55

66
#[derive(Debug, Error)]
77
pub enum BDKCliError {
8-
#[error("BIP39 error: {0}")]
9-
BIP39Error(#[from] bdk_wallet::bip39::Error),
8+
#[error("BIP39 error: {0:?}")]
9+
BIP39Error(#[from] Option<bdk_wallet::bip39::Error>),
1010

1111
#[error("BIP32 error: {0}")]
1212
BIP32Error(#[from] bdk_wallet::bitcoin::bip32::Error),

src/handlers.rs

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ use crate::commands::*;
1515
use crate::error::BDKCliError as Error;
1616
#[cfg(any(feature = "sqlite", feature = "redb"))]
1717
use crate::persister::Persister;
18-
#[cfg(feature = "cbf")]
19-
use crate::utils::BlockchainClient::KyotoClient;
2018
use crate::utils::*;
2119
#[cfg(feature = "redb")]
2220
use bdk_redb::Store as RedbStore;
2321
use bdk_wallet::bip39::{Language, Mnemonic};
22+
use bdk_wallet::bitcoin::base64::Engine;
23+
use bdk_wallet::bitcoin::base64::prelude::BASE64_STANDARD;
2424
use bdk_wallet::bitcoin::{
2525
Address, Amount, FeeRate, Network, Psbt, Sequence, Txid,
2626
bip32::{DerivationPath, KeySource},
@@ -40,11 +40,17 @@ use bdk_wallet::rusqlite::Connection;
4040
use bdk_wallet::{KeychainKind, SignOptions, Wallet};
4141
#[cfg(feature = "compiler")]
4242
use bdk_wallet::{
43+
bitcoin::XOnlyPublicKey,
4344
descriptor::{Descriptor, Legacy, Miniscript},
4445
miniscript::{Tap, descriptor::TapTree, policy::Concrete},
4546
};
4647
use cli_table::{Cell, CellStruct, Style, Table, format::Justify};
4748
use serde_json::json;
49+
#[cfg(feature = "cbf")]
50+
use {crate::utils::BlockchainClient::KyotoClient, bdk_kyoto::LightClient, tokio::select};
51+
52+
#[cfg(feature = "electrum")]
53+
use crate::utils::BlockchainClient::Electrum;
4854
use std::collections::BTreeMap;
4955
#[cfg(any(feature = "electrum", feature = "esplora"))]
5056
use std::collections::HashSet;
@@ -54,16 +60,6 @@ use std::io::Write;
5460
use std::str::FromStr;
5561
#[cfg(any(feature = "redb", feature = "compiler"))]
5662
use std::sync::Arc;
57-
58-
#[cfg(feature = "electrum")]
59-
use crate::utils::BlockchainClient::Electrum;
60-
#[cfg(feature = "cbf")]
61-
use bdk_kyoto::LightClient;
62-
#[cfg(feature = "compiler")]
63-
use bdk_wallet::bitcoin::XOnlyPublicKey;
64-
use bdk_wallet::bitcoin::base64::prelude::*;
65-
#[cfg(feature = "cbf")]
66-
use tokio::select;
6763
#[cfg(any(
6864
feature = "electrum",
6965
feature = "esplora",
@@ -1260,6 +1256,10 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
12601256
}
12611257
Ok("".to_string())
12621258
}
1259+
CliSubCommand::Descriptor { desc_type, key } => {
1260+
let descriptor = handle_descriptor_command(cli_opts.network, desc_type, key, pretty)?;
1261+
Ok(descriptor)
1262+
}
12631263
};
12641264
result
12651265
}
@@ -1307,6 +1307,11 @@ async fn respond(
13071307
.map_err(|e| e.to_string())?;
13081308
Some(value)
13091309
}
1310+
ReplSubCommand::Descriptor { desc_type, key } => {
1311+
let value = handle_descriptor_command(network, desc_type, key, cli_opts.pretty)
1312+
.map_err(|e| e.to_string())?;
1313+
Some(value)
1314+
}
13101315
ReplSubCommand::Exit => None,
13111316
};
13121317
if let Some(value) = response {
@@ -1333,6 +1338,35 @@ fn readline() -> Result<String, Error> {
13331338
Ok(buffer)
13341339
}
13351340

1341+
/// Handle the descriptor command
1342+
pub fn handle_descriptor_command(
1343+
network: Network,
1344+
desc_type: String,
1345+
key: Option<String>,
1346+
pretty: bool,
1347+
) -> Result<String, Error> {
1348+
let result = match key {
1349+
Some(key) => {
1350+
if is_mnemonic(&key) {
1351+
// User provided mnemonic
1352+
generate_descriptor_from_mnemonic(&key, network, &desc_type)
1353+
} else {
1354+
// User provided xprv/xpub
1355+
generate_descriptors(&desc_type, &key, network)
1356+
}
1357+
}
1358+
// Generate new mnemonic and descriptors
1359+
None => generate_descriptor_with_mnemonic(network, &desc_type),
1360+
}?;
1361+
format_descriptor_output(&result, pretty)
1362+
}
1363+
1364+
#[cfg(any(
1365+
feature = "electrum",
1366+
feature = "esplora",
1367+
feature = "cbf",
1368+
feature = "rpc"
1369+
))]
13361370
#[cfg(test)]
13371371
mod test {
13381372
#[cfg(any(

0 commit comments

Comments
 (0)