@@ -656,6 +656,87 @@ type ScriptInfo struct {
656656 SigOps int
657657}
658658
659+ // calcP2SHScriptInfo returns ScriptInfo fields for spends whose pkScript class
660+ // is pay-to-script-hash. It handles both cases hidden behind P2SH:
661+ // - ordinary legacy P2SH, where sigScript reveals a redeem script that executes
662+ // directly.
663+ // - nested witness spends, where sigScript reveals a redeem script that is
664+ // itself a witness program.
665+ //
666+ // The caller has already initialized si.ExpectedInputs from the outer pkScript,
667+ // so this helper is responsible for folding in any additional inputs required
668+ // by the redeem script layer and, for nested P2WSH, the inner witness script
669+ // layer as well.
670+ func calcP2SHScriptInfo (scriptVersion uint16 , sigScript , pkScript []byte ,
671+ witness wire.TxWitness , numInputs , expectedInputCount int ,
672+ segwit bool ) (int , int , int ) {
673+
674+ // For P2SH, the consensus-visible redeem script is the final data push in
675+ // sigScript. This is the script that will ultimately execute for legacy
676+ // P2SH, and it is also the script we must inspect to decide whether the
677+ // spend is actually nested witness.
678+ redeemScript := finalOpcodeData (scriptVersion , sigScript )
679+
680+ // Classify the revealed redeem script so we can account for both its script
681+ // shape and the number of stack elements it expects from the spending
682+ // input.
683+ redeemClass := typeOfScript (scriptVersion , redeemScript )
684+
685+ // Fold the redeem-script layer into ExpectedInputs. A return value of -1
686+ // means the helper cannot determine the count for this script shape, so the
687+ // entire overall ExpectedInputs value becomes unknown as well.
688+ rsInputs := expectedInputs (redeemScript , redeemClass )
689+ if rsInputs == - 1 {
690+ expectedInputCount = - 1
691+ } else {
692+ expectedInputCount += rsInputs
693+ }
694+
695+ // If segwit is active and the actual redeem script is itself a witness
696+ // program, then this is a nested witness spend such as P2SH-P2WPKH or
697+ // P2SH-P2WSH. The key point is that this decision is based on the actual
698+ // redeem script revealed by sigScript.
699+ if segwit && IsWitnessProgram (redeemScript ) {
700+ // Nested P2WSH adds one more layer of indirection: the witness program
701+ // commits to a witness script, and the final witness element carries
702+ // the serialized witness script itself. That inner script can require
703+ // its own stack arguments, so ExpectedInputs must include them too.
704+ if redeemClass == WitnessV0ScriptHashTy && len (witness ) > 0 {
705+ // In P2WSH, the last witness item is the witness script being
706+ // executed.
707+ witnessScript := witness [len (witness )- 1 ]
708+
709+ // Classify the inner witness script and ask how many stack elements
710+ // it expects from the preceding witness items.
711+ witnessScriptClass := typeOfScript (scriptVersion , witnessScript )
712+ wsInputs := expectedInputs (witnessScript , witnessScriptClass )
713+
714+ // Preserve the same unknown-count behavior as above: once any inner
715+ // layer reports an unknown expected-input count, the overall answer
716+ // is unknown. Otherwise, add the inner witness-script contribution
717+ // on top of the already-counted outer P2SH and redeem-script
718+ // layers.
719+ if wsInputs == - 1 {
720+ expectedInputCount = - 1
721+ } else if expectedInputCount != - 1 {
722+ expectedInputCount += wsInputs
723+ }
724+ }
725+
726+ // Once the spend is known to be nested witness, sigops must be counted
727+ // with witness-aware rules, and the provided input items are the total
728+ // of the pushed sigScript items plus the witness stack items.
729+ sigOps := GetWitnessSigOpCount (sigScript , pkScript , witness )
730+ return expectedInputCount , sigOps , len (witness ) + numInputs
731+ }
732+
733+ // Otherwise this is ordinary legacy P2SH. The revealed redeem script
734+ // executes directly, so sigops are counted from that script using legacy
735+ // rules, and the only provided inputs are the pushed items from sigScript.
736+ sigOps := countSigOpsV0 (redeemScript , true )
737+ return expectedInputCount , sigOps , numInputs
738+ }
739+
659740// CalcScriptInfo returns a structure providing data about the provided script
660741// pair. It will error if the pair is in someway invalid such that they can not
661742// be analysed, i.e. if they do not parse or the pkScript is not a push-only
@@ -700,21 +781,11 @@ func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
700781
701782 switch {
702783 // Count sigops taking into account pay-to-script-hash.
703- case si .PkScriptClass == ScriptHashTy && bip16 && ! segwit :
704- // The redeem script is the final data push of the signature script.
705- redeemScript := finalOpcodeData (scriptVersion , sigScript )
706- reedeemClass := typeOfScript (scriptVersion , redeemScript )
707- rsInputs := expectedInputs (redeemScript , reedeemClass )
708- if rsInputs == - 1 {
709- si .ExpectedInputs = - 1
710- } else {
711- si .ExpectedInputs += rsInputs
712- }
713- si .SigOps = countSigOpsV0 (redeemScript , true )
714-
715- // All entries pushed to stack (or are OP_RESERVED and exec
716- // will fail).
717- si .NumInputs = numInputs
784+ case si .PkScriptClass == ScriptHashTy && bip16 :
785+ si .ExpectedInputs , si .SigOps , si .NumInputs = calcP2SHScriptInfo (
786+ scriptVersion , sigScript , pkScript , witness , numInputs ,
787+ si .ExpectedInputs , segwit ,
788+ )
718789
719790 // If segwit is active, and this is a regular p2wkh output, then we'll
720791 // treat the script as a p2pkh output in essence.
@@ -723,26 +794,6 @@ func CalcScriptInfo(sigScript, pkScript []byte, witness wire.TxWitness,
723794 si .SigOps = GetWitnessSigOpCount (sigScript , pkScript , witness )
724795 si .NumInputs = len (witness )
725796
726- // We'll attempt to detect the nested p2sh case so we can accurately
727- // count the signature operations involved.
728- case si .PkScriptClass == ScriptHashTy &&
729- IsWitnessProgram (sigScript [1 :]) && bip16 && segwit :
730-
731- // Extract the pushed witness program from the sigScript so we
732- // can determine the number of expected inputs.
733- redeemClass := typeOfScript (scriptVersion , sigScript [1 :])
734- shInputs := expectedInputs (sigScript [1 :], redeemClass )
735- if shInputs == - 1 {
736- si .ExpectedInputs = - 1
737- } else {
738- si .ExpectedInputs += shInputs
739- }
740-
741- si .SigOps = GetWitnessSigOpCount (sigScript , pkScript , witness )
742-
743- si .NumInputs = len (witness )
744- si .NumInputs += numInputs
745-
746797 // If segwit is active, and this is a p2wsh output, then we'll need to
747798 // examine the witness script to generate accurate script info.
748799 case si .PkScriptClass == WitnessV0ScriptHashTy && segwit :
0 commit comments