@@ -13,8 +13,7 @@ use crate::error::BDKCliError as Error;
1313use std:: {
1414 fmt:: Display ,
1515 path:: { Path , PathBuf } ,
16- str:: FromStr ,
17- sync:: Arc ,
16+ str:: FromStr , sync:: Arc ,
1817} ;
1918
2019use crate :: commands:: WalletOpts ;
@@ -23,11 +22,7 @@ use bdk_kyoto::{
2322 BuilderExt , Info , LightClient , Receiver , ScanType :: Sync , UnboundedReceiver , Warning ,
2423 builder:: Builder ,
2524} ;
26- use bdk_wallet:: {
27- bitcoin:: secp256k1:: All ,
28- keys:: { IntoDescriptorKey , KeyMap } ,
29- miniscript:: { Legacy , Miniscript , Terminal } ,
30- } ;
25+ use bdk_wallet:: { bitcoin:: bip32:: { DerivationPath , Xpub } , keys:: DescriptorPublicKey , miniscript:: { descriptor:: { DescriptorXKey , Wildcard } , Descriptor , Miniscript , Terminal } , template:: DescriptorTemplate } ;
3126use cli_table:: { Cell , CellStruct , Style , Table } ;
3227
3328#[ cfg( any(
@@ -44,20 +39,10 @@ use bdk_wallet::{KeychainKind, PersistedWallet, WalletPersister};
4439
4540use bdk_wallet:: bip39:: { Language , Mnemonic } ;
4641use bdk_wallet:: bitcoin:: {
47- Address , Network , OutPoint , ScriptBuf ,
48- bip32:: { DerivationPath , Xpriv , Xpub } ,
49- secp256k1:: Secp256k1 ,
50- } ;
51- use bdk_wallet:: descriptor:: {
52- Segwitv0 , { Descriptor , DescriptorPublicKey } ,
53- } ;
54- use bdk_wallet:: keys:: {
55- DerivableKey , DescriptorSecretKey , ExtendedKey , GeneratableKey , GeneratedKey , bip39:: WordCount ,
56- } ;
57- use bdk_wallet:: miniscript:: {
58- Tap ,
59- descriptor:: { DescriptorXKey , Wildcard } ,
42+ Address , Network , OutPoint , ScriptBuf , bip32:: Xpriv , secp256k1:: Secp256k1 ,
6043} ;
44+ use bdk_wallet:: descriptor:: Segwitv0 ;
45+ use bdk_wallet:: keys:: { GeneratableKey , GeneratedKey , bip39:: WordCount } ;
6146use serde_json:: { Value , json} ;
6247
6348/// Parse the recipient (Address,Amount) argument from cli input.
@@ -395,111 +380,64 @@ pub fn is_mnemonic(s: &str) -> bool {
395380 ( 12 ..=24 ) . contains ( & word_count) && s. chars ( ) . all ( |c| c. is_alphanumeric ( ) || c. is_whitespace ( ) )
396381}
397382
398- pub fn extract_keymap (
399- desc_type : & str ,
400- desc_secret : DescriptorSecretKey ,
401- secp : & Secp256k1 < All > ,
402- ) -> Result < ( DescriptorPublicKey , KeyMap ) , Error > {
403- let ( desc_pub, keymap, _) = match desc_type. to_lowercase ( ) . as_str ( ) {
404- "pkh" => {
405- let descriptor_key = IntoDescriptorKey :: < Legacy > :: into_descriptor_key ( desc_secret) ?;
406- descriptor_key. extract ( secp) ?
407- }
408- "wpkh" | "sh" | "wsh" => {
409- let descriptor_key = IntoDescriptorKey :: < Segwitv0 > :: into_descriptor_key ( desc_secret) ?;
410- descriptor_key. extract ( secp) ?
411- }
412- "tr" => {
413- let descriptor_key = IntoDescriptorKey :: < Tap > :: into_descriptor_key ( desc_secret) ?;
414- descriptor_key. extract ( secp) ?
415- }
416- _ => {
417- return Err ( Error :: Generic ( format ! (
418- "Unsupported descriptor type: {desc_type}"
419- ) ) ) ;
420- }
421- } ;
422- Ok ( ( desc_pub, keymap) )
423- }
424-
425- pub fn build_public_descriptor (
383+ pub fn generate_descriptors (
426384 desc_type : & str ,
427- key : DescriptorPublicKey ,
428- ) -> Result < Descriptor < DescriptorPublicKey > , Error > {
429- match desc_type. to_lowercase ( ) . as_str ( ) {
430- "pkh" => Descriptor :: new_pkh ( key) . map_err ( Error :: from) ,
431- "wpkh" => Descriptor :: new_wpkh ( key) . map_err ( Error :: from) ,
432- "sh" => Descriptor :: new_sh_wpkh ( key) . map_err ( Error :: from) ,
433- "wsh" => {
434- let pk_k = Miniscript :: from_ast ( Terminal :: PkK ( key) ) . map_err ( Error :: from) ?;
435- let pk_ms: Miniscript < DescriptorPublicKey , Segwitv0 > =
436- Miniscript :: from_ast ( Terminal :: Check ( Arc :: new ( pk_k) ) ) . map_err ( Error :: from) ?;
437- Descriptor :: new_wsh ( pk_ms) . map_err ( Error :: from)
438- }
439- "tr" => Descriptor :: new_tr ( key, None ) . map_err ( Error :: from) ,
440- _ => Err ( Error :: Generic ( format ! (
441- "Unsupported descriptor type: {desc_type}"
442- ) ) ) ,
443- }
444- }
445-
446- pub fn generate_descriptors ( desc_type : & str , key : & str , network : Network ) -> Result < Value , Error > {
447- let secp = Secp256k1 :: new ( ) ;
448- let purpose = match desc_type. to_lowercase ( ) . as_str ( ) {
449- "pkh" => 44u32 ,
450- "sh" => 49u32 ,
451- "wpkh" | "wsh" => 84u32 ,
452- "tr" => 86u32 ,
453- _ => 84u32 ,
454- } ;
455- let coin_type = match network {
456- Network :: Bitcoin => 0u32 ,
457- _ => 1u32 ,
458- } ;
459- let derivation_base = format ! ( "/{purpose}h/{coin_type}h/0h" ) ;
460- let derivation_path = DerivationPath :: from_str ( & format ! ( "m{derivation_base}" ) ) ?;
461-
385+ key : & str ,
386+ network : Network ,
387+ ) -> Result < Value , Error > {
462388 let is_private = key. starts_with ( "xprv" ) || key. starts_with ( "tprv" ) ;
463389
464390 if is_private {
465- generate_private_descriptors ( desc_type, key, & derivation_path , & secp )
391+ generate_private_descriptors ( desc_type, key, network )
466392 } else {
393+ let purpose = match desc_type. to_lowercase ( ) . as_str ( ) {
394+ "pkh" => 44u32 ,
395+ "sh" => 49u32 ,
396+ "wpkh" | "wsh" => 84u32 ,
397+ "tr" => 86u32 ,
398+ _ => 84u32 ,
399+ } ;
400+ let coin_type = match network {
401+ Network :: Bitcoin => 0u32 ,
402+ _ => 1u32 ,
403+ } ;
404+ let derivation_path = DerivationPath :: from_str ( & format ! ( "m/{purpose}h/{coin_type}h/0h" ) ) ?;
467405 generate_public_descriptors ( desc_type, key, & derivation_path)
468406 }
469407}
470408
409+ /// Generate descriptors from private key using BIP templates
471410fn generate_private_descriptors (
472411 desc_type : & str ,
473412 key : & str ,
474- account_path : & DerivationPath ,
475- secp : & Secp256k1 < All > ,
413+ network : Network ,
476414) -> Result < Value , Error > {
415+ use bdk_wallet:: template:: { Bip44 , Bip49 , Bip84 , Bip86 } ;
416+
417+ let secp = Secp256k1 :: new ( ) ;
477418 let xprv: Xpriv = key. parse ( ) ?;
478- let fingerprint = xprv. fingerprint ( secp) ;
479-
480- let account_xprv = xprv. derive_priv ( secp, account_path) ?;
481-
482- let build_descriptor = |branch : & str | -> Result < ( String , String ) , Error > {
483- let branch_path = DerivationPath :: from_str ( branch) ?;
484-
485- let desc_xprv = DescriptorXKey {
486- origin : Some ( ( fingerprint, account_path. clone ( ) ) ) ,
487- xkey : account_xprv,
488- derivation_path : branch_path,
489- wildcard : Wildcard :: Unhardened ,
490- } ;
491- let desc_secret = DescriptorSecretKey :: XPrv ( desc_xprv) ;
492-
493- let ( desc_pub, keymap) = extract_keymap ( desc_type, desc_secret, secp) ?;
494- let descriptor = build_public_descriptor ( desc_type, desc_pub) ?;
495- let public_str = descriptor. to_string ( ) ;
496- let private_str = descriptor. to_string_with_secret ( & keymap) ;
419+ let fingerprint = xprv. fingerprint ( & secp) ;
420+
421+ let ( external_desc, external_keymap, _) = match desc_type. to_lowercase ( ) . as_str ( ) {
422+ "pkh" => Bip44 ( xprv, KeychainKind :: External ) . build ( network) ?,
423+ "sh" => Bip49 ( xprv, KeychainKind :: External ) . build ( network) ?,
424+ "wpkh" | "wsh" => Bip84 ( xprv, KeychainKind :: External ) . build ( network) ?,
425+ "tr" => Bip86 ( xprv, KeychainKind :: External ) . build ( network) ?,
426+ _ => return Err ( Error :: Generic ( format ! ( "Unsupported descriptor type: {desc_type}" ) ) ) ,
427+ } ;
497428
498- Ok ( ( public_str, private_str) )
429+ let ( internal_desc, internal_keymap, _) = match desc_type. to_lowercase ( ) . as_str ( ) {
430+ "pkh" => Bip44 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
431+ "sh" => Bip49 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
432+ "wpkh" | "wsh" => Bip84 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
433+ "tr" => Bip86 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
434+ _ => return Err ( Error :: Generic ( format ! ( "Unsupported descriptor type: {desc_type}" ) ) ) ,
499435 } ;
500436
501- let ( external_pub, external_priv) = build_descriptor ( "0" ) ?;
502- let ( internal_pub, internal_priv) = build_descriptor ( "1" ) ?;
437+ let external_priv = external_desc. to_string_with_secret ( & external_keymap) ;
438+ let external_pub = external_desc. to_string ( ) ;
439+ let internal_priv = internal_desc. to_string_with_secret ( & internal_keymap) ;
440+ let internal_pub = internal_desc. to_string ( ) ;
503441
504442 Ok ( json ! ( {
505443 "public_descriptors" : {
@@ -514,21 +452,7 @@ fn generate_private_descriptors(
514452 } ) )
515453}
516454
517- pub fn generate_descriptor_with_mnemonic (
518- network : Network ,
519- desc_type : & str ,
520- ) -> Result < serde_json:: Value , Error > {
521- let mnemonic: GeneratedKey < Mnemonic , Segwitv0 > =
522- Mnemonic :: generate ( ( WordCount :: Words12 , Language :: English ) ) . map_err ( Error :: BIP39Error ) ?;
523-
524- let seed = mnemonic. to_seed ( "" ) ;
525- let xprv = Xpriv :: new_master ( network, & seed) ?;
526-
527- let mut result = generate_descriptors ( desc_type, & xprv. to_string ( ) , network) ?;
528- result[ "mnemonic" ] = json ! ( mnemonic. to_string( ) ) ;
529- Ok ( result)
530- }
531-
455+ /// Generate descriptors from public key (xpub/tpub)
532456pub fn generate_public_descriptors (
533457 desc_type : & str ,
534458 key : & str ,
@@ -562,16 +486,54 @@ pub fn generate_public_descriptors(
562486 } ) )
563487}
564488
489+ /// Build a descriptor from a public key
490+ pub fn build_public_descriptor (
491+ desc_type : & str ,
492+ key : DescriptorPublicKey ,
493+ ) -> Result < Descriptor < DescriptorPublicKey > , Error > {
494+ match desc_type. to_lowercase ( ) . as_str ( ) {
495+ "pkh" => Descriptor :: new_pkh ( key) . map_err ( Error :: from) ,
496+ "wpkh" => Descriptor :: new_wpkh ( key) . map_err ( Error :: from) ,
497+ "sh" => Descriptor :: new_sh_wpkh ( key) . map_err ( Error :: from) ,
498+ "wsh" => {
499+ let pk_k = Miniscript :: from_ast ( Terminal :: PkK ( key) ) . map_err ( Error :: from) ?;
500+ let pk_ms: Miniscript < DescriptorPublicKey , Segwitv0 > =
501+ Miniscript :: from_ast ( Terminal :: Check ( Arc :: new ( pk_k) ) ) . map_err ( Error :: from) ?;
502+ Descriptor :: new_wsh ( pk_ms) . map_err ( Error :: from)
503+ }
504+ "tr" => Descriptor :: new_tr ( key, None ) . map_err ( Error :: from) ,
505+ _ => Err ( Error :: Generic ( format ! (
506+ "Unsupported descriptor type: {desc_type}"
507+ ) ) ) ,
508+ }
509+ }
510+
511+ /// Generate new mnemonic and descriptors
512+ pub fn generate_descriptor_with_mnemonic (
513+ network : Network ,
514+ desc_type : & str ,
515+ ) -> Result < serde_json:: Value , Error > {
516+ let mnemonic: GeneratedKey < Mnemonic , Segwitv0 > =
517+ Mnemonic :: generate ( ( WordCount :: Words12 , Language :: English ) )
518+ . map_err ( Error :: BIP39Error ) ?;
519+
520+ let seed = mnemonic. to_seed ( "" ) ;
521+ let xprv = Xpriv :: new_master ( network, & seed) ?;
522+
523+ let mut result = generate_descriptors ( desc_type, & xprv. to_string ( ) , network) ?;
524+ result[ "mnemonic" ] = json ! ( mnemonic. to_string( ) ) ;
525+ Ok ( result)
526+ }
527+
528+ /// Generate descriptors from existing mnemonic
565529pub fn generate_descriptor_from_mnemonic (
566530 mnemonic_str : & str ,
567531 network : Network ,
568532 desc_type : & str ,
569533) -> Result < serde_json:: Value , Error > {
570534 let mnemonic = Mnemonic :: parse_in ( Language :: English , mnemonic_str) ?;
571- let ext_key: ExtendedKey = mnemonic. into_extended_key ( ) ?;
572- let xprv = ext_key
573- . into_xprv ( network)
574- . ok_or_else ( || Error :: Generic ( "No xprv found" . to_string ( ) ) ) ?;
535+ let seed = mnemonic. to_seed ( "" ) ;
536+ let xprv = Xpriv :: new_master ( network, & seed) ?;
575537
576538 let mut result = generate_descriptors ( desc_type, & xprv. to_string ( ) , network) ?;
577539 result[ "mnemonic" ] = json ! ( mnemonic_str) ;
0 commit comments