@@ -242,9 +242,9 @@ pub enum PsbtError {
242242 #[ error( "{0}" ) ]
243243 #[ cfg_attr( feature = "wasm" , assoc( js_code = "sign-error" ) ) ]
244244 SignError ( #[ from] bitcoin:: psbt:: SignError ) ,
245- #[ error( "The BitBox does not support Taproot script path spending ." ) ]
246- #[ cfg_attr( feature = "wasm" , assoc( js_code = "unsupported-tap-script " ) ) ]
247- UnsupportedTapScript ,
245+ #[ error( "Taproot pubkeys must be unique across the internal key and all leaf scripts ." ) ]
246+ #[ cfg_attr( feature = "wasm" , assoc( js_code = "key-not-unique " ) ) ]
247+ KeyNotUnique ,
248248 #[ error( "Could not find our key in an input." ) ]
249249 #[ cfg_attr( feature = "wasm" , assoc( js_code = "key-not-found" ) ) ]
250250 KeyNotFound ,
@@ -256,13 +256,19 @@ pub enum PsbtError {
256256enum OurKey {
257257 Segwit ( bitcoin:: secp256k1:: PublicKey , Keypath ) ,
258258 TaprootInternal ( Keypath ) ,
259+ TaprootScript (
260+ bitcoin:: secp256k1:: XOnlyPublicKey ,
261+ bitcoin:: taproot:: TapLeafHash ,
262+ Keypath ,
263+ ) ,
259264}
260265
261266impl OurKey {
262267 fn keypath ( & self ) -> Keypath {
263268 match self {
264269 OurKey :: Segwit ( _, kp) => kp. clone ( ) ,
265270 OurKey :: TaprootInternal ( kp) => kp. clone ( ) ,
271+ OurKey :: TaprootScript ( _, _, kp) => kp. clone ( ) ,
266272 }
267273 }
268274}
@@ -336,19 +342,34 @@ fn find_our_key<T: PsbtOutputInfo>(
336342 our_root_fingerprint : & [ u8 ] ,
337343 output_info : T ,
338344) -> Result < OurKey , PsbtError > {
339- if let Some ( tap_internal_key) = output_info. get_tap_internal_key ( ) {
340- let ( leaf_hashes, ( fingerprint, derivation_path) ) = output_info
341- . get_tap_key_origins ( )
342- . get ( tap_internal_key)
343- . ok_or ( PsbtError :: KeyNotFound ) ?;
344- if !leaf_hashes. is_empty ( ) {
345- return Err ( PsbtError :: UnsupportedTapScript ) ;
346- }
345+ for ( xonly, ( leaf_hashes, ( fingerprint, derivation_path) ) ) in
346+ output_info. get_tap_key_origins ( ) . iter ( )
347+ {
347348 if & fingerprint[ ..] == our_root_fingerprint {
348349 // TODO: check for fingerprint collision
349- return Ok ( OurKey :: TaprootInternal ( derivation_path. into ( ) ) ) ;
350+
351+ if let Some ( tap_internal_key) = output_info. get_tap_internal_key ( ) {
352+ if tap_internal_key == xonly {
353+ if !leaf_hashes. is_empty ( ) {
354+ // TODO change err msg, we don't support the
355+ // same key as internal key and also in a leaf
356+ // script.
357+ return Err ( PsbtError :: KeyNotUnique ) ;
358+ }
359+ return Ok ( OurKey :: TaprootInternal ( derivation_path. into ( ) ) ) ;
360+ }
361+ }
362+ if leaf_hashes. len ( ) != 1 {
363+ // TODO change err msg, per BIP-388 all pubkeys are
364+ // unique, so it can't be in multiple leafs.
365+ return Err ( PsbtError :: KeyNotUnique ) ;
366+ }
367+ return Ok ( OurKey :: TaprootScript (
368+ * xonly,
369+ leaf_hashes[ 0 ] ,
370+ derivation_path. into ( ) ,
371+ ) ) ;
350372 }
351- return Err ( PsbtError :: KeyNotFound ) ;
352373 }
353374 for ( pubkey, ( fingerprint, derivation_path) ) in output_info. get_bip32_derivation ( ) . iter ( ) {
354375 if & fingerprint[ ..] == our_root_fingerprint {
@@ -576,15 +597,26 @@ pub fn make_script_config_policy(policy: &str, keys: &[KeyOriginInfo]) -> pb::Bt
576597 }
577598}
578599
579- fn is_taproot ( script_config : & pb:: BtcScriptConfigWithKeypath ) -> bool {
580- matches ! ( script_config,
581- pb:: BtcScriptConfigWithKeypath {
582- script_config:
583- Some ( pb:: BtcScriptConfig {
584- config: Some ( pb:: btc_script_config:: Config :: SimpleType ( simple_type) ) ,
585- } ) ,
586- ..
587- } if * simple_type == pb:: btc_script_config:: SimpleType :: P2tr as i32 )
600+ fn is_taproot_simple ( script_config : & pb:: BtcScriptConfigWithKeypath ) -> bool {
601+ matches ! (
602+ script_config. script_config. as_ref( ) ,
603+ Some ( pb:: BtcScriptConfig {
604+ config: Some ( pb:: btc_script_config:: Config :: SimpleType ( simple_type) ) ,
605+ } ) if * simple_type == pb:: btc_script_config:: SimpleType :: P2tr as i32
606+ )
607+ }
608+
609+ fn is_taproot_policy ( script_config : & pb:: BtcScriptConfigWithKeypath ) -> bool {
610+ matches ! (
611+ script_config. script_config. as_ref( ) ,
612+ Some ( pb:: BtcScriptConfig {
613+ config: Some ( pb:: btc_script_config:: Config :: Policy ( policy) ) ,
614+ } ) if policy. policy. as_str( ) . starts_with( "tr(" ) ,
615+ )
616+ }
617+
618+ fn is_schnorr ( script_config : & pb:: BtcScriptConfigWithKeypath ) -> bool {
619+ is_taproot_simple ( script_config) | is_taproot_policy ( script_config)
588620}
589621
590622impl < R : Runtime > PairedBitBox < R > {
@@ -681,7 +713,7 @@ impl<R: Runtime> PairedBitBox<R> {
681713 format_unit : pb:: btc_sign_init_request:: FormatUnit ,
682714 ) -> Result < Vec < Vec < u8 > > , Error > {
683715 self . validate_version ( ">=9.4.0" ) ?; // anti-klepto since 9.4.0
684- if transaction. script_configs . iter ( ) . any ( is_taproot ) {
716+ if transaction. script_configs . iter ( ) . any ( is_taproot_simple ) {
685717 self . validate_version ( ">=9.10.0" ) ?; // taproot since 9.10.0
686718 }
687719
@@ -709,7 +741,7 @@ impl<R: Runtime> PairedBitBox<R> {
709741 let input_index: usize = next_response. index as _ ;
710742 let tx_input: & TxInput = & transaction. inputs [ input_index] ;
711743
712- let input_is_schnorr = is_taproot (
744+ let input_is_schnorr = is_schnorr (
713745 & transaction. script_configs [ tx_input. script_config_index as usize ] ,
714746 ) ;
715747 let perform_antiklepto = is_inputs_pass2 && !input_is_schnorr;
@@ -897,7 +929,12 @@ impl<R: Runtime> PairedBitBox<R> {
897929 psbt_input. tap_key_sig = Some (
898930 bitcoin:: taproot:: Signature :: from_slice ( signature)
899931 . map_err ( |_| Error :: InvalidSignature ) ?,
900- )
932+ ) ;
933+ }
934+ OurKey :: TaprootScript ( xonly, leaf_hash, _) => {
935+ let sig = bitcoin:: taproot:: Signature :: from_slice ( signature)
936+ . map_err ( |_| Error :: InvalidSignature ) ?;
937+ psbt_input. tap_script_sigs . insert ( ( xonly, leaf_hash) , sig) ;
901938 }
902939 }
903940 }
0 commit comments