@@ -242,9 +242,9 @@ pub enum PsbtError {
242
242
#[ error( "{0}" ) ]
243
243
#[ cfg_attr( feature = "wasm" , assoc( js_code = "sign-error" ) ) ]
244
244
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 ,
248
248
#[ error( "Could not find our key in an input." ) ]
249
249
#[ cfg_attr( feature = "wasm" , assoc( js_code = "key-not-found" ) ) ]
250
250
KeyNotFound ,
@@ -256,13 +256,19 @@ pub enum PsbtError {
256
256
enum OurKey {
257
257
Segwit ( bitcoin:: secp256k1:: PublicKey , Keypath ) ,
258
258
TaprootInternal ( Keypath ) ,
259
+ TaprootScript (
260
+ bitcoin:: secp256k1:: XOnlyPublicKey ,
261
+ bitcoin:: taproot:: TapLeafHash ,
262
+ Keypath ,
263
+ ) ,
259
264
}
260
265
261
266
impl OurKey {
262
267
fn keypath ( & self ) -> Keypath {
263
268
match self {
264
269
OurKey :: Segwit ( _, kp) => kp. clone ( ) ,
265
270
OurKey :: TaprootInternal ( kp) => kp. clone ( ) ,
271
+ OurKey :: TaprootScript ( _, _, kp) => kp. clone ( ) ,
266
272
}
267
273
}
268
274
}
@@ -336,19 +342,34 @@ fn find_our_key<T: PsbtOutputInfo>(
336
342
our_root_fingerprint : & [ u8 ] ,
337
343
output_info : T ,
338
344
) -> 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
+ {
347
348
if & fingerprint[ ..] == our_root_fingerprint {
348
349
// 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
+ ) ) ;
350
372
}
351
- return Err ( PsbtError :: KeyNotFound ) ;
352
373
}
353
374
for ( pubkey, ( fingerprint, derivation_path) ) in output_info. get_bip32_derivation ( ) . iter ( ) {
354
375
if & fingerprint[ ..] == our_root_fingerprint {
@@ -576,15 +597,26 @@ pub fn make_script_config_policy(policy: &str, keys: &[KeyOriginInfo]) -> pb::Bt
576
597
}
577
598
}
578
599
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)
588
620
}
589
621
590
622
impl < R : Runtime > PairedBitBox < R > {
@@ -681,7 +713,7 @@ impl<R: Runtime> PairedBitBox<R> {
681
713
format_unit : pb:: btc_sign_init_request:: FormatUnit ,
682
714
) -> Result < Vec < Vec < u8 > > , Error > {
683
715
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 ) {
685
717
self . validate_version ( ">=9.10.0" ) ?; // taproot since 9.10.0
686
718
}
687
719
@@ -709,7 +741,7 @@ impl<R: Runtime> PairedBitBox<R> {
709
741
let input_index: usize = next_response. index as _ ;
710
742
let tx_input: & TxInput = & transaction. inputs [ input_index] ;
711
743
712
- let input_is_schnorr = is_taproot (
744
+ let input_is_schnorr = is_schnorr (
713
745
& transaction. script_configs [ tx_input. script_config_index as usize ] ,
714
746
) ;
715
747
let perform_antiklepto = is_inputs_pass2 && !input_is_schnorr;
@@ -897,7 +929,12 @@ impl<R: Runtime> PairedBitBox<R> {
897
929
psbt_input. tap_key_sig = Some (
898
930
bitcoin:: taproot:: Signature :: from_slice ( signature)
899
931
. 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) ;
901
938
}
902
939
}
903
940
}
0 commit comments