20
20
//!
21
21
22
22
use bitcoin:: blockdata:: witness:: Witness ;
23
- use bitcoin:: util:: sighash;
23
+ use bitcoin:: util:: { sighash, taproot } ;
24
24
use std:: fmt;
25
25
use std:: str:: FromStr ;
26
26
@@ -206,6 +206,92 @@ impl<'txin> Interpreter<'txin> {
206
206
}
207
207
}
208
208
209
+ /// Verify a signature for a given transaction and prevout information
210
+ /// This is a low level API, [`Interpreter::iter`] or [`Interpreter::iter_assume_sig`]
211
+ /// should satisfy most use-cases.
212
+ /// Returns false if
213
+ /// - the signature verification fails
214
+ /// - the input index is out of range
215
+ /// - Insufficient sighash information is present
216
+ /// - sighash single without corresponding output
217
+ // TODO: Create a good first isse to change this to error
218
+ pub fn verify_sig < C : secp256k1:: Verification > (
219
+ & self ,
220
+ secp : & secp256k1:: Secp256k1 < C > ,
221
+ tx : & bitcoin:: Transaction ,
222
+ input_idx : usize ,
223
+ prevouts : & sighash:: Prevouts ,
224
+ sig : & KeySigPair ,
225
+ ) -> bool {
226
+ fn get_prevout < ' u > (
227
+ prevouts : & sighash:: Prevouts < ' u > ,
228
+ input_index : usize ,
229
+ ) -> Option < & ' u bitcoin:: TxOut > {
230
+ match prevouts {
231
+ sighash:: Prevouts :: One ( index, prevout) => {
232
+ if input_index == * index {
233
+ Some ( prevout)
234
+ } else {
235
+ None
236
+ }
237
+ }
238
+ sighash:: Prevouts :: All ( prevouts) => prevouts. get ( input_index) ,
239
+ }
240
+ }
241
+ let mut cache = bitcoin:: util:: sighash:: SigHashCache :: new ( tx) ;
242
+ match sig {
243
+ KeySigPair :: Ecdsa ( key, ecdsa_sig) => {
244
+ let script_pubkey = self . script_code . as_ref ( ) . expect ( "Legacy have script code" ) ;
245
+ let sighash = if self . is_legacy ( ) {
246
+ let sighash_u32 = ecdsa_sig. hash_ty . as_u32 ( ) ;
247
+ cache. legacy_signature_hash ( input_idx, & script_pubkey, sighash_u32)
248
+ } else if self . is_segwit_v0 ( ) {
249
+ let amt = match get_prevout ( prevouts, input_idx) {
250
+ Some ( txout) => txout. value ,
251
+ None => return false ,
252
+ } ;
253
+ cache. segwit_signature_hash ( input_idx, & script_pubkey, amt, ecdsa_sig. hash_ty )
254
+ } else {
255
+ // taproot(or future) signatures in segwitv0 context
256
+ return false ;
257
+ } ;
258
+ let msg =
259
+ sighash. map ( |hash| secp256k1:: Message :: from_slice ( & hash) . expect ( "32 byte" ) ) ;
260
+ let success =
261
+ msg. map ( |msg| secp. verify_ecdsa ( & msg, & ecdsa_sig. sig , & key. inner ) . is_ok ( ) ) ;
262
+ success. unwrap_or ( false ) // unwrap_or checks for errors, while success would have checksig results
263
+ }
264
+ KeySigPair :: Schnorr ( xpk, schnorr_sig) => {
265
+ let sighash_msg = if self . is_taproot_v1_key_spend ( ) {
266
+ cache. taproot_key_spend_signature_hash ( input_idx, prevouts, schnorr_sig. hash_ty )
267
+ } else if self . is_taproot_v1_script_spend ( ) {
268
+ let tap_script = self . script_code . as_ref ( ) . expect (
269
+ "Internal Hack: Saving leaf script instead\
270
+ of script code for script spend",
271
+ ) ;
272
+ let leaf_hash = taproot:: TapLeafHash :: from_script (
273
+ & tap_script,
274
+ taproot:: LeafVersion :: TapScript ,
275
+ ) ;
276
+ cache. taproot_script_spend_signature_hash (
277
+ input_idx,
278
+ prevouts,
279
+ leaf_hash,
280
+ schnorr_sig. hash_ty ,
281
+ )
282
+ } else {
283
+ // schnorr sigs in ecdsa descriptors
284
+ return false ;
285
+ } ;
286
+ let msg =
287
+ sighash_msg. map ( |hash| secp256k1:: Message :: from_slice ( & hash) . expect ( "32 byte" ) ) ;
288
+ let success =
289
+ msg. map ( |msg| secp. verify_schnorr ( & schnorr_sig. sig , & msg, & xpk) . is_ok ( ) ) ;
290
+ success. unwrap_or ( false ) // unwrap_or_default checks for errors, while success would have checksig results
291
+ }
292
+ }
293
+ }
294
+
209
295
/// Iterate over all satisfied constraints while checking signatures
210
296
/// Not all fields are used by legacy/segwitv0 descriptors; if you are sure this is a legacy
211
297
/// spend (you can check with the `is_legacy\is_segwitv0` method) you can provide dummy data for
@@ -216,22 +302,12 @@ impl<'txin> Interpreter<'txin> {
216
302
pub fn iter_check_sigs < ' iter , C : secp256k1:: Verification > (
217
303
& ' iter self ,
218
304
secp : & ' iter secp256k1:: Secp256k1 < C > ,
219
- tx : & ' iter bitcoin:: Transaction , // actually a 'txin, but 'txin : 'iter
305
+ tx : & ' txin bitcoin:: Transaction ,
220
306
input_idx : usize ,
221
307
prevouts : & ' iter sighash:: Prevouts , // actually a 'prevouts, but 'prevouts: 'iter
222
308
) -> Iter < ' txin , ' iter > {
223
- fn verify_sig < C : secp256k1:: Verification > (
224
- _secp : & secp256k1:: Secp256k1 < C > ,
225
- _tx : & bitcoin:: Transaction ,
226
- _input_idx : usize ,
227
- _prevouts : & sighash:: Prevouts ,
228
- _sig : & KeySigPair ,
229
- ) -> bool {
230
- panic ! ( "TODO" ) ;
231
- }
232
- let _warn_error = & self . script_code ;
233
309
self . iter ( Box :: new ( move |sig| {
234
- verify_sig ( secp, tx, input_idx, prevouts, sig)
310
+ self . verify_sig ( secp, tx, input_idx, prevouts, sig)
235
311
} ) )
236
312
}
237
313
@@ -310,8 +386,8 @@ impl<'txin> Interpreter<'txin> {
310
386
}
311
387
}
312
388
313
- /// Whether this is a taproot spend
314
- pub fn is_taproot_v1 ( & self ) -> bool {
389
+ /// Whether this is a taproot key spend
390
+ pub fn is_taproot_v1_key_spend ( & self ) -> bool {
315
391
match self . inner {
316
392
inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Pk ) => false ,
317
393
inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Pkh ) => false ,
@@ -322,6 +398,22 @@ impl<'txin> Interpreter<'txin> {
322
398
inner:: Inner :: Script ( _, inner:: ScriptType :: Sh ) => false ,
323
399
inner:: Inner :: Script ( _, inner:: ScriptType :: Wsh ) => false ,
324
400
inner:: Inner :: Script ( _, inner:: ScriptType :: ShWsh ) => false ,
401
+ inner:: Inner :: Script ( _, inner:: ScriptType :: Tr ) => false ,
402
+ }
403
+ }
404
+
405
+ /// Whether this is a taproot script spend
406
+ pub fn is_taproot_v1_script_spend ( & self ) -> bool {
407
+ match self . inner {
408
+ inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Pk ) => false ,
409
+ inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Pkh ) => false ,
410
+ inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Wpkh ) => false ,
411
+ inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: ShWpkh ) => false ,
412
+ inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Tr ) => false ,
413
+ inner:: Inner :: Script ( _, inner:: ScriptType :: Bare ) => false ,
414
+ inner:: Inner :: Script ( _, inner:: ScriptType :: Sh ) => false ,
415
+ inner:: Inner :: Script ( _, inner:: ScriptType :: Wsh ) => false ,
416
+ inner:: Inner :: Script ( _, inner:: ScriptType :: ShWsh ) => false ,
325
417
inner:: Inner :: Script ( _, inner:: ScriptType :: Tr ) => true ,
326
418
}
327
419
}
0 commit comments