@@ -266,3 +266,352 @@ pub fn compute_tweak_data(
266266
267267 Ok ( A_sum . mul_tweak ( & secp, & input_hash) ?)
268268}
269+
270+ #[ cfg( test) ]
271+ #[ cfg_attr( coverage_nightly, coverage( off) ) ]
272+ mod tests {
273+ use super :: { extract_pubkey, ScriptBuf , TxIn } ;
274+
275+ use bitcoin:: {
276+ hex:: test_hex_unwrap as hex,
277+ secp256k1:: { self , PublicKey } ,
278+ OutPoint , Sequence , Witness ,
279+ } ;
280+
281+ use std:: str:: FromStr ;
282+
283+ use crate :: SpInputs ;
284+
285+ #[ test]
286+ fn test_extract_pubkey_wrapped_segwit_ok ( ) {
287+ let script_pubkey = ScriptBuf :: from_hex ( "a914809b71783f1b55eeadeb1678baef0c994adc425987" )
288+ . expect ( "should succeed" ) ;
289+ // third input from testnet tx 65eb5594eda20b3a2437c2e2c28ba7633f0492cbb33f62ee31469b913ce8a5ca
290+ let txin = TxIn {
291+ previous_output : OutPoint {
292+ txid : "04d984cdcf728975c173c45c49a242cedee2da5dc200b2f83ca6a98aecf11280"
293+ . parse ( )
294+ . unwrap ( ) ,
295+ vout : 1 ,
296+ } ,
297+ script_sig : ScriptBuf :: from_hex ( "1600146a721dcca372f3c17b2c649b2ba61aa0fda98a91" )
298+ . unwrap ( ) ,
299+ sequence : Sequence :: MAX ,
300+ witness : Witness :: from_slice ( & [ hex ! (
301+ "304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f87971101"
302+ ) , hex ! ( "02ae68d299cbb8ab99bf24c9af79a7b13d28ac8cd21f6f7f750300eda41a589a5d" ) ] ) ,
303+ } ;
304+
305+ let expected_pubkey = PublicKey :: from_str (
306+ "02ae68d299cbb8ab99bf24c9af79a7b13d28ac8cd21f6f7f750300eda41a589a5d" ,
307+ )
308+ . expect ( "should work" ) ;
309+ let maybe_pubkey = extract_pubkey ( txin, & script_pubkey) ;
310+
311+ assert ! ( maybe_pubkey. is_some( ) ) ;
312+
313+ let ( input_type, parsed_pubkey) = maybe_pubkey. expect ( "is some" ) ;
314+
315+ assert_eq ! ( SpInputs :: WrappedSegwit , input_type) ;
316+
317+ assert_eq ! ( expected_pubkey, parsed_pubkey) ;
318+ }
319+
320+ #[ test]
321+ fn test_extract_pubkey_p2wpkh_ok ( ) {
322+ let script_pubkey = ScriptBuf :: from_hex ( "001453d9c40342ee880e766522c3e2b854d37f2b3cbf" )
323+ . expect ( "should succeed" ) ;
324+ // only input from mainnet tx 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe
325+ let txin = TxIn {
326+ previous_output : OutPoint {
327+ txid : "a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec"
328+ . parse ( )
329+ . unwrap ( ) ,
330+ vout : 1 ,
331+ } ,
332+ script_sig : ScriptBuf :: new ( ) ,
333+ sequence : Sequence :: MAX ,
334+ witness : Witness :: from_slice ( & [
335+ hex ! ( "3044022025f63cdce46c749ff1953200b4fda615c7bbd6aa1717850c39297ce1087071ca0220675349fe9f9c6cf626f66abfd4ea1381b470a8315d1a8922d2573dc1410c661501" ) ,
336+ hex ! ( "03eb01a0190cb4d5da80878b20ff3823cc45b4fe55288393ee5d9f8a7f5eb65bbb" ) ,
337+ ] ) ,
338+ } ;
339+
340+ let expected_pubkey = PublicKey :: from_str (
341+ "03eb01a0190cb4d5da80878b20ff3823cc45b4fe55288393ee5d9f8a7f5eb65bbb" ,
342+ )
343+ . expect ( "should succeed" ) ;
344+ let maybe_pubkey = extract_pubkey ( txin, & script_pubkey) ;
345+
346+ assert ! ( maybe_pubkey. is_some( ) ) ;
347+
348+ let ( input_type, parsed_pubkey) = maybe_pubkey. expect ( "is some" ) ;
349+
350+ assert_eq ! ( SpInputs :: P2WPKH , input_type) ;
351+
352+ assert_eq ! ( expected_pubkey, parsed_pubkey) ;
353+ }
354+
355+ #[ test]
356+ fn test_extract_pubkey_p2tr_ok ( ) {
357+ let script_pubkey = ScriptBuf :: from_hex (
358+ "51200f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667" ,
359+ )
360+ . expect ( "should succeed" ) ;
361+ // only input from mainnet tx 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe
362+ let txin = TxIn {
363+ previous_output : OutPoint {
364+ txid : "a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec"
365+ . parse ( )
366+ . unwrap ( ) ,
367+ vout : 0 ,
368+ } ,
369+ script_sig : ScriptBuf :: new ( ) ,
370+ sequence : Sequence :: MAX ,
371+ witness : Witness :: from_slice ( & [ hex ! (
372+ "b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae01"
373+ ) ] ) ,
374+ } ;
375+
376+ let expected_pubkey = PublicKey :: from_str (
377+ "020f0c8db753acbd17343a39c2f3f4e35e4be6da749f9e35137ab220e7b238a667" ,
378+ )
379+ . expect ( "should succeed" ) ;
380+
381+ let maybe_pubkey = extract_pubkey ( txin, & script_pubkey) ;
382+
383+ assert ! ( maybe_pubkey. is_some( ) ) ;
384+
385+ let ( input_type, parsed_pubkey) = maybe_pubkey. expect ( "is some" ) ;
386+
387+ assert_eq ! ( SpInputs :: P2TR , input_type) ;
388+
389+ assert_eq ! ( expected_pubkey, parsed_pubkey) ;
390+ }
391+
392+ #[ test]
393+ fn test_extract_pubkey_p2pkh_ok ( ) {
394+ let script_pubkey =
395+ ScriptBuf :: from_hex ( "76a9140c443537e6e31f06e6edb2d4bb80f8481e2831ac88ac" )
396+ . expect ( "should succeed" ) ;
397+ // only input from mainnet tx 4316fe7be359937317f42ffaf05ab02554297fb83096a0beb985a25f9e338215
398+ let txin = TxIn {
399+ previous_output : OutPoint {
400+ txid : "40e331b67c0fe7750bb3b1943b378bf702dce86124dc12fa5980f975db7ec930"
401+ . parse ( )
402+ . unwrap ( ) ,
403+ vout : 0 ,
404+ } ,
405+ script_sig : ScriptBuf :: from_hex ( "473044022076baac422976af25b32479ccb81df8a2d7f4f73cfb2ff98cfe10241feefdb43702204c08a9fc646150a9aceb3ebc26344e1596ddd6b7bc8aa44cb116a3adca173e3701210360a953b3da3f5cc0ec246a99411c19916fab7e72b59e105955b6e3e9d3a44773" ) . expect ( "should succeed" ) ,
406+ sequence : Sequence :: MAX ,
407+ witness : Witness :: new ( ) ,
408+ } ;
409+
410+ let expected_pubkey = PublicKey :: from_str (
411+ "0360a953b3da3f5cc0ec246a99411c19916fab7e72b59e105955b6e3e9d3a44773" ,
412+ )
413+ . expect ( "should succeed" ) ;
414+ let maybe_pubkey = extract_pubkey ( txin, & script_pubkey) ;
415+
416+ assert ! ( maybe_pubkey. is_some( ) ) ;
417+
418+ let ( input_type, parsed_pubkey) = maybe_pubkey. expect ( "is some" ) ;
419+
420+ assert_eq ! ( SpInputs :: P2PKH , input_type) ;
421+
422+ assert_eq ! ( expected_pubkey, parsed_pubkey) ;
423+ }
424+
425+ #[ test]
426+ fn test_extract_pubkey_malleated_p2pkh_ok ( ) {
427+ let script_pubkey =
428+ ScriptBuf :: from_hex ( "76a9147cdd63cc408564188e8e472640e921c7c90e651d88ac" )
429+ . expect ( "should succeed" ) ;
430+ // only input from mainnet tx 4316fe7be359937317f42ffaf05ab02554297fb83096a0beb985a25f9e338215
431+ let txin = TxIn {
432+ previous_output : OutPoint {
433+ txid : "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"
434+ . parse ( )
435+ . unwrap ( ) ,
436+ vout : 1 ,
437+ } ,
438+ script_sig : ScriptBuf :: from_hex ( "0075473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" ) . expect ( "should succeed" ) ,
439+ sequence : Sequence :: MAX ,
440+ witness : Witness :: new ( ) ,
441+ } ;
442+
443+ let expected_pubkey = PublicKey :: from_str (
444+ "03782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" ,
445+ )
446+ . expect ( "should succeed" ) ;
447+
448+ let maybe_pubkey = extract_pubkey ( txin, & script_pubkey) ;
449+
450+ assert ! ( maybe_pubkey. is_some( ) ) ;
451+
452+ let ( input_type, parsed_pubkey) = maybe_pubkey. expect ( "is some" ) ;
453+
454+ assert_eq ! ( SpInputs :: P2PKH , input_type) ;
455+
456+ assert_eq ! ( expected_pubkey, parsed_pubkey) ;
457+ }
458+
459+ #[ test]
460+ fn test_extract_pubkey_wrapped_segwit_invalid_key ( ) {
461+ let script_pubkey = ScriptBuf :: from_hex ( "a914809b71783f1b55eeadeb1678baef0c994adc425987" )
462+ . expect ( "should succeed" ) ;
463+ // crafted using third input in testnet tx 65eb5594eda20b3a2437c2e2c28ba7633f0492cbb33f62ee31469b913ce8a5ca as template
464+ let txin = TxIn {
465+ previous_output : OutPoint {
466+ txid : "04d984cdcf728975c173c45c49a242cedee2da5dc200b2f83ca6a98aecf11280"
467+ . parse ( )
468+ . unwrap ( ) ,
469+ vout : 1 ,
470+ } ,
471+ script_sig : ScriptBuf :: from_hex ( "1600146a721dcca372f3c17b2c649b2ba61aa0fda98a91" )
472+ . unwrap ( ) ,
473+ sequence : Sequence :: MAX ,
474+ witness : Witness :: from_slice ( & [ hex ! (
475+ "304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f87971101"
476+ ) , secp256k1:: constants:: ZERO . to_vec ( ) ] ) ,
477+ } ;
478+
479+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
480+ }
481+
482+ #[ test]
483+ fn test_extract_pubkey_p2wpkh_invalid_key ( ) {
484+ let script_pubkey = ScriptBuf :: from_hex ( "001453d9c40342ee880e766522c3e2b854d37f2b3cbf" )
485+ . expect ( "should succeed" ) ;
486+ // crafted using the only input from mainnet tx 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe as template
487+ let txin = TxIn {
488+ previous_output : OutPoint {
489+ txid : "a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec"
490+ . parse ( )
491+ . unwrap ( ) ,
492+ vout : 1 ,
493+ } ,
494+ script_sig : ScriptBuf :: new ( ) ,
495+ sequence : Sequence :: MAX ,
496+ witness : Witness :: from_slice ( & [
497+ hex ! ( "3044022025f63cdce46c749ff1953200b4fda615c7bbd6aa1717850c39297ce1087071ca0220675349fe9f9c6cf626f66abfd4ea1381b470a8315d1a8922d2573dc1410c661501" ) ,
498+ secp256k1:: constants:: ZERO . to_vec ( )
499+ ] ) ,
500+ } ;
501+
502+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
503+ }
504+
505+ #[ test]
506+ fn test_extract_pubkey_p2tr_invalid_key ( ) {
507+ let script_pubkey = ScriptBuf :: from_hex (
508+ "51200000000000000000000000000000000000000000000000000000000000000000" ,
509+ )
510+ . expect ( "should succeed" ) ;
511+ // crafted using only input from mainnet tx 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe as template
512+ let txin = TxIn {
513+ previous_output : OutPoint {
514+ txid : "a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec"
515+ . parse ( )
516+ . unwrap ( ) ,
517+ vout : 0 ,
518+ } ,
519+ script_sig : ScriptBuf :: new ( ) ,
520+ sequence : Sequence :: MAX ,
521+ witness : Witness :: from_slice ( & [ hex ! (
522+ "b693a0797b24bae12ed0516a2f5ba765618dca89b75e498ba5b745b71644362298a45ca39230d10a02ee6290a91cebf9839600f7e35158a447ea182ea0e022ae01"
523+ ) ] ) ,
524+ } ;
525+
526+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
527+ }
528+
529+ #[ test]
530+ fn test_extract_pubkey_p2pkh_invalid_key ( ) {
531+ let script_pubkey =
532+ ScriptBuf :: from_hex ( "76a9140c443537e6e31f06e6edb2d4bb80f8481e2831ac88ac" )
533+ . expect ( "should succeed" ) ;
534+ // only input from mainnet tx 4316fe7be359937317f42ffaf05ab02554297fb83096a0beb985a25f9e338215
535+ let txin = TxIn {
536+ previous_output : OutPoint {
537+ txid : "40e331b67c0fe7750bb3b1943b378bf702dce86124dc12fa5980f975db7ec930"
538+ . parse ( )
539+ . unwrap ( ) ,
540+ vout : 0 ,
541+ } ,
542+ script_sig : ScriptBuf :: from_hex ( "473044022076baac422976af25b32479ccb81df8a2d7f4f73cfb2ff98cfe10241feefdb43702204c08a9fc646150a9aceb3ebc26344e1596ddd6b7bc8aa44cb116a3adca173e370121000000000000000000000000000000000000000000000000000000000000000000" ) . expect ( "should succeed" ) ,
543+ sequence : Sequence :: MAX ,
544+ witness : Witness :: new ( ) ,
545+ } ;
546+
547+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
548+ }
549+
550+ #[ test]
551+ fn test_extract_pubkey_wrapped_segwit_uncompressed ( ) {
552+ let script_pubkey = ScriptBuf :: from_hex ( "a914809b71783f1b55eeadeb1678baef0c994adc425987" )
553+ . expect ( "should succeed" ) ;
554+ // crafted using third input from testnet tx 65eb5594eda20b3a2437c2e2c28ba7633f0492cbb33f62ee31469b913ce8a5ca as template
555+ let txin = TxIn {
556+ previous_output : OutPoint {
557+ txid : "04d984cdcf728975c173c45c49a242cedee2da5dc200b2f83ca6a98aecf11280"
558+ . parse ( )
559+ . unwrap ( ) ,
560+ vout : 1 ,
561+ } ,
562+ script_sig : ScriptBuf :: from_hex ( "1600146a721dcca372f3c17b2c649b2ba61aa0fda98a91" )
563+ . unwrap ( ) ,
564+ sequence : Sequence :: MAX ,
565+ witness : Witness :: from_slice ( & [ hex ! (
566+ "304402204ebf033caf3a1a210623e98b49acb41db2220c531843106d5c50736b144b15aa02201a006be1ebc2ffef0927d4458e3bb5e41e5abc7e44fc5ceb920049b46f87971101"
567+ // notice uncompressed public key here, at the end of the witness
568+ ) , hex ! ( "04ae68d299cbb8ab99bf24c9af79a7b13d28ac8cd21f6f7f750300eda41a589a5d6a7210f279e3be22089aef2e29cf359a7eb0067d8caebae4298c5bec56ca41c2" ) ] ) ,
569+ } ;
570+
571+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
572+ }
573+
574+ #[ test]
575+ fn test_extract_pubkey_p2wpkh_uncompressed ( ) {
576+ let script_pubkey = ScriptBuf :: from_hex ( "001453d9c40342ee880e766522c3e2b854d37f2b3cbf" )
577+ . expect ( "should succeed" ) ;
578+ // crafted using the only input from mainnet tx 091d2aaadc409298fd8353a4cd94c319481a0b4623fb00872fe240448e93fcbe as template
579+ let txin = TxIn {
580+ previous_output : OutPoint {
581+ txid : "a7115c7267dbb4aab62b37818d431b784fe731f4d2f9fa0939a9980d581690ec"
582+ . parse ( )
583+ . unwrap ( ) ,
584+ vout : 1 ,
585+ } ,
586+ script_sig : ScriptBuf :: new ( ) ,
587+ sequence : Sequence :: MAX ,
588+ witness : Witness :: from_slice ( & [
589+ hex ! ( "3044022025f63cdce46c749ff1953200b4fda615c7bbd6aa1717850c39297ce1087071ca0220675349fe9f9c6cf626f66abfd4ea1381b470a8315d1a8922d2573dc1410c661501" ) ,
590+ hex ! ( "04eb01a0190cb4d5da80878b20ff3823cc45b4fe55288393ee5d9f8a7f5eb65bbb8ac7e2a9044b74ce6284ebb08a92ebe489c27b43e542355e783b661b62ed76fd" ) ,
591+ ] ) ,
592+ } ;
593+
594+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
595+ }
596+ #[ test]
597+ fn test_extract_pubkey_malleated_p2pkh_wrong_pubkey_hash ( ) {
598+ // Use a not matching script pubkey to make the pubkey hash differ
599+ let script_pubkey =
600+ ScriptBuf :: from_hex ( "76a914b675771222403e064d9fb4d676fcfef47585b07f88ac" )
601+ . expect ( "should succeed" ) ;
602+ // only input from mainnet tx 4316fe7be359937317f42ffaf05ab02554297fb83096a0beb985a25f9e338215
603+ let txin = TxIn {
604+ previous_output : OutPoint {
605+ txid : "f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16"
606+ . parse ( )
607+ . unwrap ( ) ,
608+ vout : 1 ,
609+ } ,
610+ script_sig : ScriptBuf :: from_hex ( "0075473045022100a8c61b2d470e393279d1ba54f254b7c237de299580b7fa01ffcc940442ecec4502201afba952f4e4661c40acde7acc0341589031ba103a307b886eb867b23b850b972103782eeb913431ca6e9b8c2fd80a5f72ed2024ef72a3c6fb10263c379937323338" ) . expect ( "should succeed" ) ,
611+ sequence : Sequence :: MAX ,
612+ witness : Witness :: new ( ) ,
613+ } ;
614+
615+ assert ! ( extract_pubkey( txin, & script_pubkey) . is_none( ) ) ;
616+ }
617+ }
0 commit comments