@@ -542,6 +542,21 @@ func (vm *Engine) isWitnessVersionActive(version uint) bool {
542542 return vm .witnessProgram != nil && uint (vm .witnessVersion ) == version
543543}
544544
545+ // witnessProgramAcceptStack collapses the data stack down to a single element
546+ // for witness programs that succeed without inner script execution. The
547+ // running pkScript must have left a truthy top item; the stack is then reduced
548+ // to one element so the cleanstack accounting is consistent regardless of how
549+ // many items the outer pkScript template pushed.
550+ func (vm * Engine ) witnessProgramAcceptStack () error {
551+ topIdx := len (vm .dstack .stk ) - 1
552+ if topIdx < 0 || ! asBool (vm .dstack .stk [topIdx ]) {
553+ return scriptError (ErrEvalFalse ,
554+ "witness program produced false result" )
555+ }
556+ vm .dstack .stk = vm .dstack .stk [:1 ]
557+ return nil
558+ }
559+
545560// verifyWitnessProgram validates the stored witness program using the passed
546561// witness as input.
547562func (vm * Engine ) verifyWitnessProgram (witness wire.TxWitness ) error {
@@ -786,11 +801,9 @@ func (vm *Engine) verifyWitnessProgram(witness wire.TxWitness) error {
786801
787802 return scriptError (ErrDiscourageUpgradableWitnessProgram , errStr )
788803 default :
789- // If we encounter an unknown witness program version and we
790- // aren't discouraging future unknown witness based soft-forks,
791- // then we de-activate the segwit behavior within the VM for
792- // the remainder of execution.
793- vm .witnessProgram = nil
804+ if err := vm .witnessProgramAcceptStack (); err != nil {
805+ return err
806+ }
794807 }
795808
796809 // TODO(roasbeef): other sanity checks here
@@ -1479,6 +1492,71 @@ func (vm *Engine) SetAltStack(data [][]byte) {
14791492 setStack (& vm .astack , data )
14801493}
14811494
1495+ // buildWitnessProgram detects native and nested P2SH witness programs for the
1496+ // current input, records the extracted witness version/program on the engine,
1497+ // and rejects stray witness data on non-witness spends.
1498+ func (vm * Engine ) buildWitnessProgram (scriptSig , scriptPubKey []byte ,
1499+ hasWitness bool ) error {
1500+
1501+ var witProgram []byte
1502+
1503+ switch {
1504+ case IsWitnessProgram (scriptPubKey ):
1505+ // The scriptSig must be *empty* for all native witness programs,
1506+ // otherwise we introduce malleability.
1507+ if len (scriptSig ) != 0 {
1508+ errStr := "native witness program cannot also have a signature " +
1509+ "script"
1510+ return scriptError (ErrWitnessMalleated , errStr )
1511+ }
1512+
1513+ witProgram = scriptPubKey
1514+
1515+ case vm .bip16 :
1516+ // Mirror Bitcoin Core's nested-P2SH-witness detection: the candidate
1517+ // witness program is the actual redeem script, which is the final
1518+ // pushed element of the push-only scriptSig. Detection must be
1519+ // independent of whether the witness stack is empty.
1520+ if len (scriptSig ) == 0 || ! IsPushOnlyScript (scriptSig ) {
1521+ break
1522+ }
1523+ redeem := finalOpcodeData (0 , scriptSig )
1524+ if len (redeem ) == 0 || ! IsWitnessProgram (redeem ) {
1525+ break
1526+ }
1527+
1528+ // scriptSig must be exactly one canonical push of the redeem script;
1529+ // otherwise we reintroduce malleability.
1530+ canonical , err := NewScriptBuilder ().AddData (redeem ).Script ()
1531+ if err != nil || ! bytes .Equal (scriptSig , canonical ) {
1532+ errStr := "signature script for witness nested p2sh is not " +
1533+ "canonical"
1534+ return scriptError (ErrWitnessMalleatedP2SH , errStr )
1535+ }
1536+
1537+ witProgram = redeem
1538+ }
1539+
1540+ if witProgram == nil {
1541+ // If we didn't find a witness program in either the pkScript or as a
1542+ // datapush within the sigScript, then there MUST NOT be any witness
1543+ // data associated with the input being validated.
1544+ if hasWitness {
1545+ errStr := "non-witness inputs cannot have a witness"
1546+ return scriptError (ErrWitnessUnexpected , errStr )
1547+ }
1548+
1549+ return nil
1550+ }
1551+
1552+ var err error
1553+ vm .witnessVersion , vm .witnessProgram , err = ExtractWitnessProgramInfo (
1554+ witProgram ,
1555+ )
1556+
1557+ return err
1558+ }
1559+
14821560// NewEngine returns a new script engine for the provided public key script,
14831561// transaction, and input index. The flags modify the behavior of the script
14841562// engine according to the description provided by each flag.
@@ -1592,55 +1670,11 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags
15921670 return nil , scriptError (ErrInvalidFlags , errStr )
15931671 }
15941672
1595- var witProgram []byte
1596-
1597- switch {
1598- case IsWitnessProgram (vm .scripts [1 ]):
1599- // The scriptSig must be *empty* for all native witness
1600- // programs, otherwise we introduce malleability.
1601- if len (scriptSig ) != 0 {
1602- errStr := "native witness program cannot " +
1603- "also have a signature script"
1604- return nil , scriptError (ErrWitnessMalleated , errStr )
1605- }
1606-
1607- witProgram = scriptPubKey
1608- case len (tx .TxIn [txIdx ].Witness ) != 0 && vm .bip16 :
1609- // The sigScript MUST be *exactly* a single canonical
1610- // data push of the witness program, otherwise we
1611- // reintroduce malleability.
1612- sigPops := vm .scripts [0 ]
1613- if len (sigPops ) > 2 &&
1614- isCanonicalPush (sigPops [0 ], sigPops [1 :]) &&
1615- IsWitnessProgram (sigPops [1 :]) {
1616-
1617- witProgram = sigPops [1 :]
1618- } else {
1619- errStr := "signature script for witness " +
1620- "nested p2sh is not canonical"
1621- return nil , scriptError (ErrWitnessMalleatedP2SH , errStr )
1622- }
1623- }
1624-
1625- if witProgram != nil {
1626- var err error
1627- vm .witnessVersion , vm .witnessProgram , err = ExtractWitnessProgramInfo (
1628- witProgram ,
1629- )
1630- if err != nil {
1631- return nil , err
1632- }
1633- } else {
1634- // If we didn't find a witness program in either the
1635- // pkScript or as a datapush within the sigScript, then
1636- // there MUST NOT be any witness data associated with
1637- // the input being validated.
1638- if vm .witnessProgram == nil && len (tx .TxIn [txIdx ].Witness ) != 0 {
1639- errStr := "non-witness inputs cannot have a witness"
1640- return nil , scriptError (ErrWitnessUnexpected , errStr )
1641- }
1673+ hasWitness := len (tx .TxIn [txIdx ].Witness ) != 0
1674+ err := vm .buildWitnessProgram (scriptSig , scriptPubKey , hasWitness )
1675+ if err != nil {
1676+ return nil , err
16421677 }
1643-
16441678 }
16451679
16461680 // Setup the current tokenizer used to parse through the script one opcode
0 commit comments