@@ -40,12 +40,18 @@ use bdk_wallet::miniscript::miniscript;
4040#[ cfg( feature = "sqlite" ) ]
4141use bdk_wallet:: rusqlite:: Connection ;
4242use bdk_wallet:: { KeychainKind , SignOptions , Wallet } ;
43+
4344#[ cfg( feature = "compiler" ) ]
4445use bdk_wallet:: {
45- bitcoin:: XOnlyPublicKey ,
46+ bitcoin:: {
47+ XOnlyPublicKey ,
48+ key:: { Parity , rand} ,
49+ secp256k1:: { PublicKey , Scalar , SecretKey } ,
50+ } ,
4651 descriptor:: { Descriptor , Legacy , Miniscript } ,
4752 miniscript:: { Tap , descriptor:: TapTree , policy:: Concrete } ,
4853} ;
54+
4955use clap:: CommandFactory ;
5056use cli_table:: { Cell , CellStruct , Style , Table , format:: Justify } ;
5157use serde_json:: json;
@@ -1014,50 +1020,65 @@ pub(crate) fn handle_compile_subcommand(
10141020 pretty : bool ,
10151021) -> Result < String , Error > {
10161022 let policy = Concrete :: < String > :: from_str ( policy. as_str ( ) ) ?;
1017- let legacy_policy: Miniscript < String , Legacy > = policy
1018- . compile ( )
1019- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
1020- let segwit_policy: Miniscript < String , Segwitv0 > = policy
1021- . compile ( )
1022- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
1023- let taproot_policy: Miniscript < String , Tap > = policy
1024- . compile ( )
1025- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
1023+
1024+ let legacy_policy: Miniscript < String , Legacy > = policy. compile ( ) ?;
1025+ let segwit_policy: Miniscript < String , Segwitv0 > = policy. compile ( ) ?;
1026+ let taproot_policy: Miniscript < String , Tap > = policy. compile ( ) ?;
1027+
1028+ let mut r = None ;
10261029
10271030 let descriptor = match script_type. as_str ( ) {
10281031 "sh" => Descriptor :: new_sh ( legacy_policy) ,
10291032 "wsh" => Descriptor :: new_wsh ( segwit_policy) ,
10301033 "sh-wsh" => Descriptor :: new_sh_wsh ( segwit_policy) ,
10311034 "tr" => {
1032- // For tr descriptors, we use a well-known unspendable key (NUMS point).
1033- // This ensures the key path is effectively disabled and only script path can be used.
1034- // See https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
1035+ // For tr descriptors, we use a randomized unspendable key (H + rG).
1036+ // This improves privacy by preventing observers from determining if key path spending is disabled.
1037+ // See BIP-341: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
1038+
1039+ let secp = Secp256k1 :: new ( ) ;
1040+ let r_secret = SecretKey :: new ( & mut rand:: thread_rng ( ) ) ;
1041+ r = Some ( r_secret. display_secret ( ) . to_string ( ) ) ;
1042+
1043+ let nums_key = XOnlyPublicKey :: from_str ( NUMS_UNSPENDABLE_KEY_HEX ) ?;
1044+ let nums_point = PublicKey :: from_x_only_public_key ( nums_key, Parity :: Even ) ;
10351045
1036- let xonly_public_key = XOnlyPublicKey :: from_str ( NUMS_UNSPENDABLE_KEY_HEX )
1037- . map_err ( |e| Error :: Generic ( format ! ( "Invalid NUMS key: {e}" ) ) ) ? ;
1046+ let internal_key_point = nums_point . add_exp_tweak ( & secp , & Scalar :: from ( r_secret ) ) ? ;
1047+ let ( xonly_internal_key , _ ) = internal_key_point . x_only_public_key ( ) ;
10381048
10391049 let tree = TapTree :: Leaf ( Arc :: new ( taproot_policy) ) ;
1040- Descriptor :: new_tr ( xonly_public_key. to_string ( ) , Some ( tree) )
1050+
1051+ Descriptor :: new_tr ( xonly_internal_key. to_string ( ) , Some ( tree) )
10411052 }
10421053 _ => {
10431054 return Err ( Error :: Generic (
10441055 "Invalid script type. Supported types: sh, wsh, sh-wsh, tr" . to_string ( ) ,
10451056 ) ) ;
10461057 }
10471058 } ?;
1059+
10481060 if pretty {
1049- let table = vec ! [ vec![
1061+ let mut rows = vec ! [ vec![
10501062 "Descriptor" . cell( ) . bold( true ) ,
1051- descriptor. to_string( ) . cell( ) ,
1052- ] ]
1053- . table ( )
1054- . display ( )
1055- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
1063+ shorten( & descriptor, 32 , 29 ) . cell( ) ,
1064+ ] ] ;
1065+
1066+ if let Some ( r_value) = & r {
1067+ rows. push ( vec ! [ "r" . cell( ) . bold( true ) , shorten( r_value, 4 , 4 ) . cell( ) ] ) ;
1068+ }
1069+
1070+ let table = rows
1071+ . table ( )
1072+ . display ( )
1073+ . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
1074+
10561075 Ok ( format ! ( "{table}" ) )
10571076 } else {
1058- Ok ( serde_json:: to_string_pretty (
1059- & json ! ( { "descriptor" : descriptor. to_string( ) } ) ,
1060- ) ?)
1077+ let mut output = json ! ( { "descriptor" : descriptor} ) ;
1078+ if let Some ( r_value) = r {
1079+ output[ "r" ] = json ! ( r_value) ;
1080+ }
1081+ Ok ( serde_json:: to_string_pretty ( & output) ?)
10611082 }
10621083}
10631084
0 commit comments