Skip to content

Conversation

AmosOO7
Copy link

@AmosOO7 AmosOO7 commented May 2, 2025

Pull Request: Add descriptor Subcommand (Generate & Info)

Description

This PR introduces a new descriptor subcommand to the BDK CLI that enables users to generate and inspect Bitcoin wallet descriptors for internal and external paths, supporting common BIP standards (44, 49, 84, 86). It includes support for both single-path and multipath descriptors from extended keys (xprv/xpub or tprv/tpub).

This resolves issue #175

Note: The --weak option for string-based key generation is intentionally excluded in this PR, pending further instruction.


Features Implemented

  • Adds new descriptor subcommand with:
    • generate – generate new descriptors
    • info – inspect and display descriptor metadata
  • Supports BIP44, BIP49, BIP84, and BIP86 script types
  • Generates internal and external descriptors
  • Supports --multipath for wildcard derivation paths
  • Rejects mismatched key-network pairs (e.g., xprv on testnet)
  • Reasonable defaults:
    • --type defaults to 84 and a short t
    • --network default testnet
    • --multipath defaults to false and a short m

Usage Examples

1. Generate Descriptors from an Extended Private Key

cargo run -- --network testnet descriptor generate --type 84 <XPRV>

2. Generate Descriptors from a Mnemonic (no key provided)

cargo run -- --network testnet descriptor generate --type 86

3. Generate Multipath Descriptors from an Extended Public Key or Private Key

cargo run -- --network testnet descriptor generate --type 44 --multipath <XPUB/XPRIV>

4. Show Info about an Existing Descriptor

cargo run -- --network testnet descriptor info "wpkh([fingerprint/84h/1h/0h]xpub/0/*)"
  • I've signed all my commits
  • I followed the contribution guidelines
  • 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

@AmosOO7 AmosOO7 marked this pull request as ready for review May 7, 2025 14:17
@AmosOO7 AmosOO7 force-pushed the adding_descriptor_generator branch from 09b4f4a to ebf6be3 Compare May 7, 2025 16:23
@AmosOO7 AmosOO7 requested a review from tvpeter May 19, 2025 16:21
Copy link
Collaborator

@tvpeter tvpeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work Amos.

Most of the fns in utils should be in the handlers file, while most of those in handlers should be placed in the utils.

@AmosOO7 AmosOO7 requested a review from tvpeter May 22, 2025 12:31
@notmandatory notmandatory moved this to In Progress in BDK-CLI May 28, 2025
@notmandatory notmandatory added the enhancement New feature or request label May 28, 2025
@notmandatory notmandatory added this to the CLI 1.1.0 milestone May 28, 2025
Copy link
Collaborator

@tvpeter tvpeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your work on this, @AmosOO7.

If possible, please revert the imports you removed, as this will help in tracking your imports. Also, kindly remove any unnecessary comments and squash your commits into logical groupings. I have also left comments on other specific areas for your review.

Thank you!

@AmosOO7 AmosOO7 force-pushed the adding_descriptor_generator branch 7 times, most recently from 6bff257 to 31f256a Compare June 12, 2025 11:45
Copy link
Collaborator

@tvpeter tvpeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for continuing to work on this.

I have left a couple of comments.

@AmosOO7 AmosOO7 force-pushed the adding_descriptor_generator branch from 15495c4 to 6c8e714 Compare June 21, 2025 16:55
@AmosOO7 AmosOO7 requested a review from tvpeter June 22, 2025 08:33
@coveralls
Copy link

coveralls commented Jun 24, 2025

Pull Request Test Coverage Report for Build 17490529141

Details

  • 0 of 328 (0.0%) changed or added relevant lines in 3 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.5%) to 1.68%

Changes Missing Coverage Covered Lines Changed/Added Lines %
src/commands.rs 0 1 0.0%
src/handlers.rs 0 72 0.0%
src/utils.rs 0 255 0.0%
Totals Coverage Status
Change from base Build 17324807959: -0.5%
Covered Lines: 25
Relevant Lines: 1488

💛 - Coveralls

Copy link
Collaborator

@tvpeter tvpeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AmosOO7 for continuing to work on this.

The checks for path and how a user can use it is a little bit confusing. I think we might need to split it into subcommands, similar to how key has generate, derive and restore. If you have any suggestions for the best subcategories, please feel free to share them in the conversation before continuing with the implementation. Otherwise, I will propose the subcategories this coming week to make it clearer. I've also left some additional comments for you.

Additionally, it would be a good idea to include documentation comments for each command option and parameter to help users better understand their functionalities.

Thank you.

@AmosOO7
Copy link
Author

AmosOO7 commented Jul 16, 2025

After having a discussion with @tvpeter and following his suggestions.

Breaking the descriptor command into subcommands improves clarity, usability, and future extensibility of the BDK CLI.


Suggested Subcommands for descriptor

Subcommand Description
from-key Generate descriptors from a provided Xprv or Xpub
from-mnemonic Generate descriptors from a newly generated or provided BIP39 mnemonic
multipath Generate multipath (wildcard derivation) descriptors from a key
info (optional/future) Inspect a descriptor and show parsed structure (e.g., key origin, type, script)

Detailed Breakdown

1. descriptor from-key

  • Input: --type, <XPRV | XPUB>

  • Optional: --path m/84h/1h/0h (for custom derivation base)

  • Output: external/internal descriptor strings

  • Optionally support both single and multipath modes via --multipath

2. descriptor from-mnemonic

  • Input: --type,

  • Optional: --mnemonic <WORDS> (if not, generate randomly)

  • Output: same as from-key, plus return mnemonic used

3. descriptor multipath

  • Input: --type, <XPRV | XPUB>

  • Output: wildcard descriptors (e.g., /0/* and /1/*)

4. (Optional) descriptor info

  • Input: descriptor string

  • Output: parsed structure, origin, derivation, checksum, script type, etc.


Example CLI Usage


From key

bdk-cli descriptor from-key --type 84 <XPRV | <XPUB>;

From mnemonic

bdk-cli descriptor from-mnemonic --type 86 (optional)<WORDS>

From key with multipath

bdk-cli descriptor multipath --type 44 <XPUB | <XPUB>;

Descriptor info (optional)

bdk-cli descriptor info "wpkh([abcd1234/84'/1'/0']xpub.../0/*)"

What do you think about the suggestions @tvpeter and @notmandatory ?

@tvpeter
Copy link
Collaborator

tvpeter commented Jul 17, 2025

I think the following is more descriptive:

From key

bdk-cli descriptor from-key --type 84 <XPRV | ;

bdk-cli --network [network] descriptor generate --type [type] --key <xprv | xpub>

From mnemonic

bdk-cli descriptor from-mnemonic --type 86 (optional)

bdk-cli --network [network] descriptor generate --type [type] --mnemonic [mnemonic]

From key with multipath

bdk-cli descriptor multipath --type 44 <XPUB | ;

bdk-cli --network [network] descriptor generate --type [type] --multipath <xprv|xpub>

Descriptor info (optional)

bdk-cli descriptor info "wpkh([abcd1234/84'/1'/0']xpub.../0/*)"

bdk-cli --network [network] descriptor --info <descriptor>

note:

  • <param> - required
  • [param] - optional as defaults are provided

Also, wait for confirmation from Notmandatory.

@notmandatory
Copy link
Member

notmandatory commented Jul 17, 2025

@tvpeter I agree that having two descriptor sub-commands generate and info makes sense. Also good for generate sub-command to have optional --type with default being 84. One other suggestion is that I think you can remove the --key and --mnemonic options and just require a key in either xprv OR mnemonic format, we should be able to parse either format into a key. But if that turns out to be too hard it's OK to be explicit and use --key and --mnemonic mutually exclusive but required options.

Having the descriptor info <descriptor> is nice to have but not sure it's that useful since you can see this info by just looking at the descriptor. But could still be useful as a training tool to show people what each part is for new people.

@tvpeter
Copy link
Collaborator

tvpeter commented Jul 17, 2025

One other suggestion is that I think you can remove the --key and --mnemonic options and just require a key in either xprv OR mnemonic format, we should be able to parse either format into a key. But if that turns out to be too hard it's OK to be explicit and use --key and --mnemonic mutually exclusive but required options.

Having the descriptor info <descriptor> is nice to have but not sure it's that useful since you can see this info by just looking at the descriptor. But could still be useful as a training tool to show people what each part is for new people.

@AmosOO7, you should be able to update according to this. Thank you

@AmosOO7 AmosOO7 requested a review from tvpeter July 27, 2025 18:44
Copy link
Collaborator

@tvpeter tvpeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @AmosOO7,

Thanks for continuing to push this PR. I have left comments, when you address them, I will continue with testing.
Thank you

src/error.rs Outdated
Comment on lines 122 to 123
#[error("Invalid arguments: {0}")]
InvalidArguments(String),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variant seems to be duplicating the Generic Variant. See output below:

Screenshot 2025-08-19 at 6 21 45 PM

@tvpeter
Copy link
Collaborator

tvpeter commented Sep 4, 2025

@AmosOO7, can you please make time to update this PR? I was hoping it could make it into the next release. It would be great if you could push it forward.
Thank you.

@AmosOO7 AmosOO7 requested a review from tvpeter September 5, 2025 10:49
Copy link
Collaborator

@tvpeter tvpeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your efforts on this PR @AmosOO7

I have left comments but don't worry, I will push changes so that it is easier for us to close the PR.
Thank you.

src/utils.rs Outdated
Comment on lines 500 to 506
let descriptor_type = match script_type {
44 => Bip44,
49 => Bip49,
84 => Bip84,
86 => Bip86,
_ => return Err(Error::UnsupportedScriptType(script_type)),
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were already validated in handle_descriptor_subcommand fn in handlers.rs

src/utils.rs Outdated

let desc_secret = DescriptorSecretKey::XPrv(desc_xprv.clone());
let (desc_key, keymap, _) = match descriptor_type {
DescriptorType::Bip84 | DescriptorType::Bip49 | DescriptorType::Bip44 => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BIP44 is legacy and not Segwitv0

src/handlers.rs Outdated
Comment on lines 1412 to 1418
let descriptor_type = match script_type {
44 => DescriptorType::Bip44,
49 => DescriptorType::Bip49,
84 => DescriptorType::Bip84,
86 => DescriptorType::Bip86,
_ => return Err(Error::UnsupportedScriptType(script_type)),
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant validation as this was already handled by the handle_descriptor_subcommand fn

src/handlers.rs Outdated
Comment on lines 1407 to 1411
pub fn generate_standard_descriptor(
network: &Network,
script_type: u8,
key: &str,
) -> Result<Value, Error> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for this fn as generate_descriptor_from_key_by_type fn can be called directly in handle_descriptor_subcommand passing the network, key, descriptor_type (descriptor_type having already been validated)

src/utils.rs Outdated
Comment on lines 446 to 453
DescriptorType::Bip49 => format!(
"{}([{}{}]{}{}))",
external_fmt, origin, derivation_base, xprv_str, "/0"
),
_ => format!(
"{}([{}{}]{}{})",
external_fmt, origin, derivation_base, xprv_str, "/0"
),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These descriptors will not have wildcard.

src/utils.rs Outdated
Comment on lines 396 to 409
pub fn generate_descriptor_from_key_by_type(
network: &Network,
key: &str,
descriptor_type: DescriptorType,
) -> Result<serde_json::Value, Error> {
let derivation_path = match descriptor_type {
DescriptorType::Bip44 => "m/44h/1h/0h",
DescriptorType::Bip49 => "m/49h/1h/0h",
DescriptorType::Bip84 => "m/84h/1h/0h",
DescriptorType::Bip86 => "m/86h/1h/0h",
};

generate_bip_descriptor_from_key(network, key, derivation_path, descriptor_type)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fn is redundant

AmosOO7 and others added 8 commits September 24, 2025 17:39
This allow users to generate wallet descriptors
from either extended private keys (xprv), or fresh
BIP39 mnemonics.
It supports standard script types (BIP44, BIP49,
 BIP84, BIP86) and outputs both public and private
 descriptors for external and internal branches.
The design improves developer and user experience
by enabling easier wallet creation and script
descriptor introspection.

Closes bitcoindevkit#175
- Ensure you can generate different type of descriptor with mnemonic
- Changed the Json output format
- Added path subcommand for generating descriptors from keys
- Set multipath to accept different types for descriptors
- Set multipath default value to false and gave it a short of m
- Set type default to 84 and gave it a short to t
- Created Subcommnds for the descriptor command; generate and info
- Created function to get descriptors from mnemonics
- Ensured consistent fingerprint when generating descriptors from both mnemonic and key
- Removed the previously inserted path variable
and commands, and also deleted redundant errors
that were created.
- Updated the comment on the descriptor command,
also added comments to the inputs for the subcommands.
- Added possiblevalues for the descriptor types
in the subcommand
- Made the descriptor type format consistent
- Fixed duplicated on invalidArguments call
- Updated the comment on the descriptor command, a>
- Added possiblevalues for the descriptor types in>
- Made the descriptor type format consistent
- Fixed duplicated on invalidArguments call
@tvpeter tvpeter force-pushed the adding_descriptor_generator branch from 37ce428 to 3ba1dc0 Compare September 24, 2025 17:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

4 participants