@@ -24,9 +24,13 @@ use bdk_kyoto::{
2424 builder:: Builder ,
2525} ;
2626use bdk_wallet:: {
27- bitcoin:: secp256k1:: All ,
28- keys:: { IntoDescriptorKey , KeyMap } ,
29- miniscript:: { Legacy , Miniscript , Terminal } ,
27+ bitcoin:: bip32:: { DerivationPath , Xpub } ,
28+ keys:: DescriptorPublicKey ,
29+ miniscript:: {
30+ Descriptor , Miniscript , Terminal ,
31+ descriptor:: { DescriptorXKey , Wildcard } ,
32+ } ,
33+ template:: DescriptorTemplate ,
3034} ;
3135use cli_table:: { Cell , CellStruct , Style , Table } ;
3236
@@ -44,20 +48,10 @@ use bdk_wallet::{KeychainKind, PersistedWallet, WalletPersister};
4448
4549use bdk_wallet:: bip39:: { Language , Mnemonic } ;
4650use 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 } ,
51+ Address , Network , OutPoint , ScriptBuf , bip32:: Xpriv , secp256k1:: Secp256k1 ,
6052} ;
53+ use bdk_wallet:: descriptor:: Segwitv0 ;
54+ use bdk_wallet:: keys:: { GeneratableKey , GeneratedKey , bip39:: WordCount } ;
6155use serde_json:: { Value , json} ;
6256
6357/// Parse the recipient (Address,Amount) argument from cli input.
@@ -395,111 +389,68 @@ pub fn is_mnemonic(s: &str) -> bool {
395389 ( 12 ..=24 ) . contains ( & word_count) && s. chars ( ) . all ( |c| c. is_alphanumeric ( ) || c. is_whitespace ( ) )
396390}
397391
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 (
426- 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-
446392pub 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-
462393 let is_private = key. starts_with ( "xprv" ) || key. starts_with ( "tprv" ) ;
463394
464395 if is_private {
465- generate_private_descriptors ( desc_type, key, & derivation_path , & secp )
396+ generate_private_descriptors ( desc_type, key, network )
466397 } else {
398+ let purpose = match desc_type. to_lowercase ( ) . as_str ( ) {
399+ "pkh" => 44u32 ,
400+ "sh" => 49u32 ,
401+ "wpkh" | "wsh" => 84u32 ,
402+ "tr" => 86u32 ,
403+ _ => 84u32 ,
404+ } ;
405+ let coin_type = match network {
406+ Network :: Bitcoin => 0u32 ,
407+ _ => 1u32 ,
408+ } ;
409+ let derivation_path = DerivationPath :: from_str ( & format ! ( "m/{purpose}h/{coin_type}h/0h" ) ) ?;
467410 generate_public_descriptors ( desc_type, key, & derivation_path)
468411 }
469412}
470413
414+ /// Generate descriptors from private key using BIP templates
471415fn generate_private_descriptors (
472416 desc_type : & str ,
473417 key : & str ,
474- account_path : & DerivationPath ,
475- secp : & Secp256k1 < All > ,
418+ network : Network ,
476419) -> Result < Value , Error > {
477- let xprv: Xpriv = key. parse ( ) ?;
478- let fingerprint = xprv. fingerprint ( secp) ;
420+ use bdk_wallet:: template:: { Bip44 , Bip49 , Bip84 , Bip86 } ;
479421
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) ;
422+ let secp = Secp256k1 :: new ( ) ;
423+ let xprv: Xpriv = key. parse ( ) ?;
424+ let fingerprint = xprv. fingerprint ( & secp) ;
492425
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) ;
426+ let ( external_desc, external_keymap, _) = match desc_type. to_lowercase ( ) . as_str ( ) {
427+ "pkh" => Bip44 ( xprv, KeychainKind :: External ) . build ( network) ?,
428+ "sh" => Bip49 ( xprv, KeychainKind :: External ) . build ( network) ?,
429+ "wpkh" | "wsh" => Bip84 ( xprv, KeychainKind :: External ) . build ( network) ?,
430+ "tr" => Bip86 ( xprv, KeychainKind :: External ) . build ( network) ?,
431+ _ => {
432+ return Err ( Error :: Generic ( format ! (
433+ "Unsupported descriptor type: {desc_type}"
434+ ) ) ) ;
435+ }
436+ } ;
497437
498- Ok ( ( public_str, private_str) )
438+ let ( internal_desc, internal_keymap, _) = match desc_type. to_lowercase ( ) . as_str ( ) {
439+ "pkh" => Bip44 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
440+ "sh" => Bip49 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
441+ "wpkh" | "wsh" => Bip84 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
442+ "tr" => Bip86 ( xprv, KeychainKind :: Internal ) . build ( network) ?,
443+ _ => {
444+ return Err ( Error :: Generic ( format ! (
445+ "Unsupported descriptor type: {desc_type}"
446+ ) ) ) ;
447+ }
499448 } ;
500449
501- let ( external_pub, external_priv) = build_descriptor ( "0" ) ?;
502- let ( internal_pub, internal_priv) = build_descriptor ( "1" ) ?;
450+ let external_priv = external_desc. to_string_with_secret ( & external_keymap) ;
451+ let external_pub = external_desc. to_string ( ) ;
452+ let internal_priv = internal_desc. to_string_with_secret ( & internal_keymap) ;
453+ let internal_pub = internal_desc. to_string ( ) ;
503454
504455 Ok ( json ! ( {
505456 "public_descriptors" : {
@@ -514,21 +465,7 @@ fn generate_private_descriptors(
514465 } ) )
515466}
516467
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-
468+ /// Generate descriptors from public key (xpub/tpub)
532469pub fn generate_public_descriptors (
533470 desc_type : & str ,
534471 key : & str ,
@@ -562,16 +499,53 @@ pub fn generate_public_descriptors(
562499 } ) )
563500}
564501
502+ /// Build a descriptor from a public key
503+ pub fn build_public_descriptor (
504+ desc_type : & str ,
505+ key : DescriptorPublicKey ,
506+ ) -> Result < Descriptor < DescriptorPublicKey > , Error > {
507+ match desc_type. to_lowercase ( ) . as_str ( ) {
508+ "pkh" => Descriptor :: new_pkh ( key) . map_err ( Error :: from) ,
509+ "wpkh" => Descriptor :: new_wpkh ( key) . map_err ( Error :: from) ,
510+ "sh" => Descriptor :: new_sh_wpkh ( key) . map_err ( Error :: from) ,
511+ "wsh" => {
512+ let pk_k = Miniscript :: from_ast ( Terminal :: PkK ( key) ) . map_err ( Error :: from) ?;
513+ let pk_ms: Miniscript < DescriptorPublicKey , Segwitv0 > =
514+ Miniscript :: from_ast ( Terminal :: Check ( Arc :: new ( pk_k) ) ) . map_err ( Error :: from) ?;
515+ Descriptor :: new_wsh ( pk_ms) . map_err ( Error :: from)
516+ }
517+ "tr" => Descriptor :: new_tr ( key, None ) . map_err ( Error :: from) ,
518+ _ => Err ( Error :: Generic ( format ! (
519+ "Unsupported descriptor type: {desc_type}"
520+ ) ) ) ,
521+ }
522+ }
523+
524+ /// Generate new mnemonic and descriptors
525+ pub fn generate_descriptor_with_mnemonic (
526+ network : Network ,
527+ desc_type : & str ,
528+ ) -> Result < serde_json:: Value , Error > {
529+ let mnemonic: GeneratedKey < Mnemonic , Segwitv0 > =
530+ Mnemonic :: generate ( ( WordCount :: Words12 , Language :: English ) ) . map_err ( Error :: BIP39Error ) ?;
531+
532+ let seed = mnemonic. to_seed ( "" ) ;
533+ let xprv = Xpriv :: new_master ( network, & seed) ?;
534+
535+ let mut result = generate_descriptors ( desc_type, & xprv. to_string ( ) , network) ?;
536+ result[ "mnemonic" ] = json ! ( mnemonic. to_string( ) ) ;
537+ Ok ( result)
538+ }
539+
540+ /// Generate descriptors from existing mnemonic
565541pub fn generate_descriptor_from_mnemonic (
566542 mnemonic_str : & str ,
567543 network : Network ,
568544 desc_type : & str ,
569545) -> Result < serde_json:: Value , Error > {
570546 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 ( ) ) ) ?;
547+ let seed = mnemonic. to_seed ( "" ) ;
548+ let xprv = Xpriv :: new_master ( network, & seed) ?;
575549
576550 let mut result = generate_descriptors ( desc_type, & xprv. to_string ( ) , network) ?;
577551 result[ "mnemonic" ] = json ! ( mnemonic_str) ;
0 commit comments