@@ -717,8 +717,20 @@ async fn _process(
717
717
let mut hasher_amounts = Sha256 :: new ( ) ;
718
718
let mut hasher_scriptpubkeys = Sha256 :: new ( ) ;
719
719
720
- // Are all inputs taproot?
721
- let taproot_only = validated_script_configs. iter ( ) . all ( is_taproot) ;
720
+ let prev_txs = pb:: btc_sign_init_request:: PrevTxs :: try_from ( request. prev_txs ) ?;
721
+
722
+ // We will request to stream previous transactions if not all inputs are Taproot.
723
+ let prevtxs_required: bool = match prev_txs {
724
+ // For backwards compatibility for client's that don't provide the `prev_txs` field: handle
725
+ // it like before `prev_txs` was introduced by inspecting the script configs and seeing if
726
+ // all of them are taproot. This is not robust, as non-Taproot change outputs are included
727
+ // there and falsely leads to previous transactions being required.
728
+ pb:: btc_sign_init_request:: PrevTxs :: Default => {
729
+ !validated_script_configs. iter ( ) . all ( is_taproot)
730
+ }
731
+ pb:: btc_sign_init_request:: PrevTxs :: Required => true ,
732
+ pb:: btc_sign_init_request:: PrevTxs :: NotRequired => false ,
733
+ } ;
722
734
723
735
let mut silent_payment = if request. contains_silent_payment_outputs {
724
736
Some ( SilentPayment :: new ( SECP256K1 , coin. try_into ( ) ?) )
@@ -775,7 +787,7 @@ async fn _process(
775
787
hasher_scriptpubkeys. update ( serialize_varint ( pk_script. len ( ) as u64 ) . as_slice ( ) ) ;
776
788
hasher_scriptpubkeys. update ( pk_script. as_slice ( ) ) ;
777
789
778
- if !taproot_only {
790
+ if prevtxs_required {
779
791
handle_prevtx (
780
792
input_index,
781
793
& tx_input,
@@ -784,6 +796,8 @@ async fn _process(
784
796
& mut next_response,
785
797
)
786
798
. await ?;
799
+ } else if !is_taproot ( script_config_account) {
800
+ return Err ( Error :: InvalidInput ) ;
787
801
}
788
802
789
803
if let Some ( ref mut silent_payment) = silent_payment {
@@ -1572,6 +1586,7 @@ mod tests {
1572
1586
. outputs
1573
1587
. iter ( )
1574
1588
. any ( |output| output. silent_payment . is_some ( ) ) ,
1589
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
1575
1590
}
1576
1591
}
1577
1592
@@ -1595,6 +1610,7 @@ mod tests {
1595
1610
locktime : self . locktime ,
1596
1611
format_unit : FormatUnit :: Default as _ ,
1597
1612
contains_silent_payment_outputs : false ,
1613
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
1598
1614
}
1599
1615
}
1600
1616
@@ -1679,6 +1695,7 @@ mod tests {
1679
1695
locktime : 0 ,
1680
1696
format_unit : FormatUnit :: Default as _ ,
1681
1697
contains_silent_payment_outputs : false ,
1698
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
1682
1699
} ;
1683
1700
1684
1701
{
@@ -1871,6 +1888,7 @@ mod tests {
1871
1888
locktime: 0 ,
1872
1889
format_unit: FormatUnit :: Default as _,
1873
1890
contains_silent_payment_outputs: false ,
1891
+ prev_txs: pb:: btc_sign_init_request:: PrevTxs :: Default as _,
1874
1892
}
1875
1893
) ) ,
1876
1894
Err ( Error :: InvalidInput )
@@ -2147,6 +2165,57 @@ mod tests {
2147
2165
assert ! ( unsafe { !PREVTX_REQUESTED } ) ;
2148
2166
}
2149
2167
2168
+ /// Test signing if all inputs are of type P2TR, but change is not of type P2TR.
2169
+ /// Previous transactions are requested for backwards compatibility when
2170
+ #[ test]
2171
+ fn test_script_type_p2tr_different_change ( ) {
2172
+ let transaction =
2173
+ alloc:: rc:: Rc :: new ( core:: cell:: RefCell :: new ( Transaction :: new ( pb:: BtcCoin :: Btc ) ) ) ;
2174
+ for input in transaction. borrow_mut ( ) . inputs . iter_mut ( ) {
2175
+ input. input . keypath [ 0 ] = 86 + HARDENED ;
2176
+ input. input . script_config_index = 1 ;
2177
+ }
2178
+
2179
+ let tx = transaction. clone ( ) ;
2180
+ // Check that previous transactions are not streamed, as all inputs are taproot.
2181
+ static mut PREVTX_REQUESTED : bool = false ;
2182
+ * crate :: hww:: MOCK_NEXT_REQUEST . 0 . borrow_mut ( ) =
2183
+ Some ( Box :: new ( move |response : Response | {
2184
+ let next = extract_next ( & response) ;
2185
+ if NextType :: try_from ( next. r#type ) . unwrap ( ) == NextType :: PrevtxInit {
2186
+ unsafe { PREVTX_REQUESTED = true }
2187
+ }
2188
+ Ok ( tx. borrow ( ) . make_host_request ( response) )
2189
+ } ) ) ;
2190
+
2191
+ mock_unlocked ( ) ;
2192
+ bitbox02:: random:: fake_reset ( ) ;
2193
+ let mut init_request = transaction. borrow ( ) . init_request ( ) ;
2194
+ init_request
2195
+ . script_configs
2196
+ . push ( pb:: BtcScriptConfigWithKeypath {
2197
+ script_config : Some ( pb:: BtcScriptConfig {
2198
+ config : Some ( pb:: btc_script_config:: Config :: SimpleType (
2199
+ SimpleType :: P2tr as _ ,
2200
+ ) ) ,
2201
+ } ) ,
2202
+ keypath : vec ! [ 86 + HARDENED , 0 + HARDENED , 10 + HARDENED ] ,
2203
+ } ) ;
2204
+
2205
+ init_request. prev_txs = pb:: btc_sign_init_request:: PrevTxs :: NotRequired as _ ;
2206
+ assert ! ( block_on( process( & mut TestingHal :: new( ) , & init_request) ) . is_ok( ) ) ;
2207
+ assert ! ( unsafe { !PREVTX_REQUESTED } ) ;
2208
+
2209
+ // Also test compatibility mode - before the introduction of `prev_txs`, the previous
2210
+ // transactions were wrongly requested in this case.
2211
+ unsafe {
2212
+ PREVTX_REQUESTED = false ;
2213
+ }
2214
+ init_request. prev_txs = pb:: btc_sign_init_request:: PrevTxs :: Default as _ ;
2215
+ assert ! ( block_on( process( & mut TestingHal :: new( ) , & init_request) ) . is_ok( ) ) ;
2216
+ assert ! ( unsafe { PREVTX_REQUESTED } ) ;
2217
+ }
2218
+
2150
2219
/// Test signing if with mixed inputs, one of them being taproot. Previous transactions of all
2151
2220
/// inputs should be streamed in this case.
2152
2221
#[ test]
@@ -2180,11 +2249,31 @@ mod tests {
2180
2249
} ) ,
2181
2250
keypath : vec ! [ 86 + HARDENED , 0 + HARDENED , 10 + HARDENED ] ,
2182
2251
} ) ;
2252
+
2253
+ // In compatibility mode, prevtxs are correctly requested.
2254
+ init_request. prev_txs = pb:: btc_sign_init_request:: PrevTxs :: Default as _ ;
2255
+ unsafe { PREVTX_REQUESTED = 0 } ;
2183
2256
assert ! ( block_on( process( & mut TestingHal :: new( ) , & init_request) ) . is_ok( ) ) ;
2184
2257
assert_eq ! (
2185
2258
unsafe { PREVTX_REQUESTED } ,
2186
2259
transaction. borrow( ) . inputs. len( ) as _
2187
2260
) ;
2261
+
2262
+ // Same as when the client explicitly states it.
2263
+ init_request. prev_txs = pb:: btc_sign_init_request:: PrevTxs :: Required as _ ;
2264
+ unsafe { PREVTX_REQUESTED = 0 } ;
2265
+ assert ! ( block_on( process( & mut TestingHal :: new( ) , & init_request) ) . is_ok( ) ) ;
2266
+ assert_eq ! (
2267
+ unsafe { PREVTX_REQUESTED } ,
2268
+ transaction. borrow( ) . inputs. len( ) as _
2269
+ ) ;
2270
+
2271
+ // Client can't lie about it.
2272
+ init_request. prev_txs = pb:: btc_sign_init_request:: PrevTxs :: NotRequired as _ ;
2273
+ assert_eq ! (
2274
+ block_on( process( & mut TestingHal :: new( ) , & init_request) ) ,
2275
+ Err ( Error :: InvalidInput )
2276
+ ) ;
2188
2277
}
2189
2278
2190
2279
/// Test signing UTXOs with high keypath address indices. Even though we don't support verifying
@@ -2881,6 +2970,7 @@ mod tests {
2881
2970
locktime : tx. locktime ,
2882
2971
format_unit : FormatUnit :: Default as _ ,
2883
2972
contains_silent_payment_outputs : false ,
2973
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
2884
2974
}
2885
2975
} ;
2886
2976
@@ -2976,6 +3066,7 @@ mod tests {
2976
3066
locktime : tx. locktime ,
2977
3067
format_unit : FormatUnit :: Default as _ ,
2978
3068
contains_silent_payment_outputs : false ,
3069
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
2979
3070
}
2980
3071
} ;
2981
3072
assert_eq ! (
@@ -3047,6 +3138,7 @@ mod tests {
3047
3138
locktime : tx. locktime ,
3048
3139
format_unit : FormatUnit :: Default as _ ,
3049
3140
contains_silent_payment_outputs : false ,
3141
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
3050
3142
}
3051
3143
} ;
3052
3144
let result = block_on ( process ( & mut TestingHal :: new ( ) , & init_request) ) ;
@@ -3124,6 +3216,7 @@ mod tests {
3124
3216
locktime : tx. locktime ,
3125
3217
format_unit : FormatUnit :: Default as _ ,
3126
3218
contains_silent_payment_outputs : false ,
3219
+ prev_txs : pb:: btc_sign_init_request:: PrevTxs :: Default as _ ,
3127
3220
}
3128
3221
} ;
3129
3222
let result = block_on ( process ( & mut TestingHal :: new ( ) , & init_request) ) ;
0 commit comments