2020//!
2121
2222use bitcoin:: blockdata:: witness:: Witness ;
23- use bitcoin:: util:: sighash;
23+ use bitcoin:: util:: { sighash, taproot } ;
2424use std:: fmt;
2525use std:: str:: FromStr ;
2626
@@ -206,6 +206,92 @@ impl<'txin> Interpreter<'txin> {
206206 }
207207 }
208208
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+
209295 /// Iterate over all satisfied constraints while checking signatures
210296 /// Not all fields are used by legacy/segwitv0 descriptors; if you are sure this is a legacy
211297 /// 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> {
216302 pub fn iter_check_sigs < ' iter , C : secp256k1:: Verification > (
217303 & ' iter self ,
218304 secp : & ' iter secp256k1:: Secp256k1 < C > ,
219- tx : & ' iter bitcoin:: Transaction , // actually a 'txin, but 'txin : 'iter
305+ tx : & ' txin bitcoin:: Transaction ,
220306 input_idx : usize ,
221307 prevouts : & ' iter sighash:: Prevouts , // actually a 'prevouts, but 'prevouts: 'iter
222308 ) -> 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 ;
233309 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)
235311 } ) )
236312 }
237313
@@ -310,8 +386,8 @@ impl<'txin> Interpreter<'txin> {
310386 }
311387 }
312388
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 {
315391 match self . inner {
316392 inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Pk ) => false ,
317393 inner:: Inner :: PublicKey ( _, inner:: PubkeyType :: Pkh ) => false ,
@@ -322,6 +398,22 @@ impl<'txin> Interpreter<'txin> {
322398 inner:: Inner :: Script ( _, inner:: ScriptType :: Sh ) => false ,
323399 inner:: Inner :: Script ( _, inner:: ScriptType :: Wsh ) => false ,
324400 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 ,
325417 inner:: Inner :: Script ( _, inner:: ScriptType :: Tr ) => true ,
326418 }
327419 }
0 commit comments