@@ -518,13 +518,75 @@ pub struct ScriptId {
518518 pub index : u32 ,
519519}
520520
521+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
522+ pub enum InputScriptType {
523+ P2shP2pk ,
524+ P2sh ,
525+ P2shP2wsh ,
526+ P2wsh ,
527+ P2trLegacy ,
528+ P2trMusig2ScriptPath ,
529+ P2trMusig2KeyPath ,
530+ }
531+
532+ impl InputScriptType {
533+ pub fn from_script_id ( script_id : ScriptId , psbt_input : & Input ) -> Result < Self , String > {
534+ let chain = Chain :: try_from ( script_id. chain ) . map_err ( |e| e. to_string ( ) ) ?;
535+ match chain {
536+ Chain :: P2shExternal | Chain :: P2shInternal => Ok ( InputScriptType :: P2sh ) ,
537+ Chain :: P2shP2wshExternal | Chain :: P2shP2wshInternal => Ok ( InputScriptType :: P2shP2wsh ) ,
538+ Chain :: P2wshExternal | Chain :: P2wshInternal => Ok ( InputScriptType :: P2wsh ) ,
539+ Chain :: P2trInternal | Chain :: P2trExternal => Ok ( InputScriptType :: P2trLegacy ) ,
540+ Chain :: P2trMusig2Internal | Chain :: P2trMusig2External => {
541+ // check if tap_script_sigs or tap_scripts are set
542+ if !psbt_input. tap_script_sigs . is_empty ( ) || !psbt_input. tap_scripts . is_empty ( ) {
543+ Ok ( InputScriptType :: P2trMusig2ScriptPath )
544+ } else {
545+ Ok ( InputScriptType :: P2trMusig2KeyPath )
546+ }
547+ }
548+ }
549+ }
550+
551+ /// Detects the script type from a script_id chain and PSBT input metadata
552+ ///
553+ /// # Arguments
554+ /// - `script_id`: Optional script ID containing chain information (None for replay protection inputs)
555+ /// - `psbt_input`: The PSBT input containing signature metadata
556+ /// - `output_script`: The output script being spent
557+ /// - `replay_protection`: Replay protection configuration
558+ ///
559+ /// # Returns
560+ /// - `Ok(InputScriptType)` with the detected script type
561+ /// - `Err(String)` if the script type cannot be determined
562+ pub fn detect (
563+ script_id : Option < ScriptId > ,
564+ psbt_input : & Input ,
565+ output_script : & ScriptBuf ,
566+ replay_protection : & ReplayProtection ,
567+ ) -> Result < Self , String > {
568+ // For replay protection inputs (no script_id), detect from output script
569+ match script_id {
570+ Some ( id) => Self :: from_script_id ( id, psbt_input) ,
571+ None => {
572+ if replay_protection. is_replay_protection_input ( output_script) {
573+ Ok ( InputScriptType :: P2shP2pk )
574+ } else {
575+ Err ( "Input without script_id is not a replay protection input" . to_string ( ) )
576+ }
577+ }
578+ }
579+ }
580+ }
581+
521582/// Parsed input from a PSBT transaction
522583#[ derive( Debug , Clone ) ]
523584pub struct ParsedInput {
524585 pub address : String ,
525586 pub script : Vec < u8 > ,
526587 pub value : u64 ,
527588 pub script_id : Option < ScriptId > ,
589+ pub script_type : InputScriptType ,
528590}
529591
530592impl ParsedInput {
@@ -576,11 +638,17 @@ impl ParsedInput {
576638 )
577639 . map_err ( ParseInputError :: Address ) ?;
578640
641+ // Detect the script type using script_id chain information
642+ let script_type =
643+ InputScriptType :: detect ( script_id, psbt_input, output_script, replay_protection)
644+ . map_err ( ParseInputError :: ScriptTypeDetection ) ?;
645+
579646 Ok ( Self {
580647 address,
581648 script : output_script. to_bytes ( ) ,
582649 value : value. to_sat ( ) ,
583650 script_id,
651+ script_type,
584652 } )
585653 }
586654}
@@ -598,6 +666,8 @@ pub enum ParseInputError {
598666 WalletValidation ( String ) ,
599667 /// Failed to generate address for input
600668 Address ( crate :: address:: AddressError ) ,
669+ /// Failed to detect script type for input
670+ ScriptTypeDetection ( String ) ,
601671}
602672
603673impl std:: fmt:: Display for ParseInputError {
@@ -618,6 +688,9 @@ impl std::fmt::Display for ParseInputError {
618688 ParseInputError :: Address ( error) => {
619689 write ! ( f, "failed to generate address: {}" , error)
620690 }
691+ ParseInputError :: ScriptTypeDetection ( error) => {
692+ write ! ( f, "failed to detect script type: {}" , error)
693+ }
621694 }
622695 }
623696}
0 commit comments