@@ -37,12 +37,18 @@ use bdk_wallet::miniscript::miniscript;
3737#[ cfg( feature = "sqlite" ) ]
3838use bdk_wallet:: rusqlite:: Connection ;
3939use bdk_wallet:: { KeychainKind , SignOptions , Wallet } ;
40+
4041#[ cfg( feature = "compiler" ) ]
4142use bdk_wallet:: {
42- bitcoin:: XOnlyPublicKey ,
43+ bitcoin:: {
44+ XOnlyPublicKey ,
45+ key:: { Parity , rand} ,
46+ secp256k1:: { PublicKey , Scalar , SecretKey } ,
47+ } ,
4348 descriptor:: { Descriptor , Legacy , Miniscript } ,
4449 miniscript:: { Tap , descriptor:: TapTree , policy:: Concrete } ,
4550} ;
51+
4652use cli_table:: { Cell , CellStruct , Style , Table , format:: Justify } ;
4753use serde_json:: json;
4854#[ cfg( feature = "cbf" ) ]
@@ -893,50 +899,70 @@ pub(crate) fn handle_compile_subcommand(
893899 pretty : bool ,
894900) -> Result < String , Error > {
895901 let policy = Concrete :: < String > :: from_str ( policy. as_str ( ) ) ?;
896- let legacy_policy: Miniscript < String , Legacy > = policy
897- . compile ( )
898- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
899- let segwit_policy: Miniscript < String , Segwitv0 > = policy
900- . compile ( )
901- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
902- let taproot_policy: Miniscript < String , Tap > = policy
903- . compile ( )
904- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
902+
903+ let legacy_policy: Miniscript < String , Legacy > = policy. compile ( ) ?;
904+ let segwit_policy: Miniscript < String , Segwitv0 > = policy. compile ( ) ?;
905+ let taproot_policy: Miniscript < String , Tap > = policy. compile ( ) ?;
906+
907+ let mut r = None ;
908+ let mut shorten_descriptor = None ;
905909
906910 let descriptor = match script_type. as_str ( ) {
907911 "sh" => Descriptor :: new_sh ( legacy_policy) ,
908912 "wsh" => Descriptor :: new_wsh ( segwit_policy) ,
909913 "sh-wsh" => Descriptor :: new_sh_wsh ( segwit_policy) ,
910914 "tr" => {
911- // For tr descriptors, we use a well-known unspendable key (NUMS point).
912- // This ensures the key path is effectively disabled and only script path can be used.
913- // See https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
915+ // For tr descriptors, we use a randomized unspendable key (H + rG).
916+ // This improves privacy by preventing observers from determining if key path spending is disabled.
917+ // See BIP-341: https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
918+
919+ let secp = Secp256k1 :: new ( ) ;
920+ let r_secret = SecretKey :: new ( & mut rand:: thread_rng ( ) ) ;
921+ r = Some ( r_secret. display_secret ( ) . to_string ( ) ) ;
914922
915- let xonly_public_key = XOnlyPublicKey :: from_str ( NUMS_UNSPENDABLE_KEY_HEX )
916- . map_err ( |e| Error :: Generic ( format ! ( "Invalid NUMS key: {e}" ) ) ) ?;
923+ let nums_key = XOnlyPublicKey :: from_str ( NUMS_UNSPENDABLE_KEY_HEX ) ?;
924+ let nums_point = PublicKey :: from_x_only_public_key ( nums_key, Parity :: Even ) ;
925+
926+ let internal_key_point = nums_point. add_exp_tweak ( & secp, & Scalar :: from ( r_secret) ) ?;
927+ let ( xonly_internal_key, _) = internal_key_point. x_only_public_key ( ) ;
917928
918929 let tree = TapTree :: Leaf ( Arc :: new ( taproot_policy) ) ;
919- Descriptor :: new_tr ( xonly_public_key. to_string ( ) , Some ( tree) )
930+
931+ shorten_descriptor = Some ( Descriptor :: new_tr (
932+ shorten ( xonly_internal_key, 4 , 4 ) ,
933+ Some ( tree. clone ( ) ) ,
934+ ) ?) ;
935+
936+ Descriptor :: new_tr ( xonly_internal_key. to_string ( ) , Some ( tree) )
920937 }
921938 _ => {
922939 return Err ( Error :: Generic (
923940 "Invalid script type. Supported types: sh, wsh, sh-wsh, tr" . to_string ( ) ,
924941 ) ) ;
925942 }
926943 } ?;
944+
927945 if pretty {
928- let table = vec ! [ vec![
929- "Descriptor" . cell( ) . bold( true ) ,
930- descriptor. to_string( ) . cell( ) ,
931- ] ]
932- . table ( )
933- . display ( )
934- . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
946+ let descriptor = shorten_descriptor. unwrap_or ( descriptor) ;
947+
948+ let mut rows = vec ! [ vec![ "Descriptor" . cell( ) . bold( true ) , descriptor. cell( ) ] ] ;
949+
950+ if let Some ( r_value) = & r {
951+ rows. push ( vec ! [ "r" . cell( ) . bold( true ) , shorten( r_value, 4 , 4 ) . cell( ) ] ) ;
952+ }
953+
954+ let table = rows
955+ . table ( )
956+ . display ( )
957+ . map_err ( |e| Error :: Generic ( e. to_string ( ) ) ) ?;
958+
935959 Ok ( format ! ( "{table}" ) )
936960 } else {
937- Ok ( serde_json:: to_string_pretty (
938- & json ! ( { "descriptor" : descriptor. to_string( ) } ) ,
939- ) ?)
961+ let mut output = json ! ( { "descriptor" : descriptor} ) ;
962+ if let Some ( r_value) = r {
963+ output[ "r" ] = json ! ( r_value) ;
964+ }
965+ Ok ( serde_json:: to_string_pretty ( & output) ?)
940966 }
941967}
942968
0 commit comments