From 9523345814f510f237e91e845c52ff68d613c4ba Mon Sep 17 00:00:00 2001
From: David Hill <dhill@mindcry.org>
Date: Wed, 25 Feb 2015 15:04:37 -0500
Subject: [PATCH] txscript:  Add new flag ScriptVerifyCleanStack

The ScriptVerifyCleanStack flag requires that only a single
stack element remains after evaluation and that when interpreted
as a bool, it must be true.  This is BIP0062, rule 6.

This mimics Bitcoin Core commit b6e03cc59208305681745ad06f2056ffe6690597
---
 txscript/data/script_invalid.json | 12 +++++++
 txscript/data/script_valid.json   |  6 ++++
 txscript/internal_test.go         |  2 ++
 txscript/script.go                | 54 ++++++++++++++++++++++++-------
 txscript/script_test.go           | 54 +++++++++++++++++++++++++++++--
 5 files changed, 114 insertions(+), 14 deletions(-)

diff --git a/txscript/data/script_invalid.json b/txscript/data/script_invalid.json
index 91e4696c..99d65551 100644
--- a/txscript/data/script_invalid.json
+++ b/txscript/data/script_invalid.json
@@ -779,6 +779,18 @@
     "P2SH,STRICTENC",
     "2-of-3 with one valid and one invalid signature due to parse error, nSigs > validSigs"
 ],
+[
+    "11 0x47 0x3044022053205076a7bb13d2db3162a2d97d8197631f829b065948b7019b15482af819a902204328dcc02c994ca086b1226d0d5f1674d23cfae0d846143df812b81cab3391e801",
+    "0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG",
+    "CLEANSTACK,P2SH",
+    "P2PK with unnecessary input"
+],
+[
+    "11 0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac",
+    "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL",
+    "CLEANSTACK,P2SH",
+    "P2SH with unnecessary input"
+],
 
 ["The End"]
 ]
diff --git a/txscript/data/script_valid.json b/txscript/data/script_valid.json
index 9b3d2f33..34e2c8d6 100644
--- a/txscript/data/script_valid.json
+++ b/txscript/data/script_valid.json
@@ -897,6 +897,12 @@
     "P2SH",
     "P2SH with unnecessary input but no CLEANSTACK"
 ],
+[
+    "0x47 0x304402202f7505132be14872581f35d74b759212d9da40482653f1ffa3116c3294a4a51702206adbf347a2240ca41c66522b1a22a41693610b76a8e7770645dc721d1635854f01 0x43 0x410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8ac",
+    "HASH160 0x14 0x31edc23bdafda4639e669f89ad6b2318dd79d032 EQUAL",
+    "CLEANSTACK,P2SH",
+    "P2SH with CLEANSTACK"
+],
 
 ["The End"]
 ]
diff --git a/txscript/internal_test.go b/txscript/internal_test.go
index 8f47cbe1..e307313f 100644
--- a/txscript/internal_test.go
+++ b/txscript/internal_test.go
@@ -4573,6 +4573,8 @@ func parseScriptFlags(flagStr string) (ScriptFlags, error) {
 		switch flag {
 		case "":
 			// Nothing.
+		case "CLEANSTACK":
+			flags |= ScriptVerifyCleanStack
 		case "DERSIG":
 			flags |= ScriptVerifyDERSignatures
 		case "DISCOURAGE_UPGRADABLE_NOPS":
diff --git a/txscript/script.go b/txscript/script.go
index 530a6ef6..ba270372 100644
--- a/txscript/script.go
+++ b/txscript/script.go
@@ -127,10 +127,19 @@ var (
 	// flag is set and the script contains invalid pubkeys.
 	ErrStackInvalidPubKey = errors.New("invalid strict pubkey")
 
+	// ErrStackCleanStack is returned when the ScriptVerifyCleanStack flag
+	// is set and after evalution the stack does not contain only one element,
+	// which also must be true if interpreted as a boolean.
+	ErrStackCleanStack = errors.New("stack is not clean")
+
 	// ErrStackMinimalData is returned when the ScriptVerifyMinimalData flag
 	// is set and the script contains push operations that do not use
 	// the minimal opcode required.
 	ErrStackMinimalData = errors.New("non-minimally encoded script number")
+
+	// ErrInvalidFlags is returned when the passed flags to NewScript contain
+	// an invalid combination.
+	ErrInvalidFlags = errors.New("invalid flags combination")
 )
 
 const (
@@ -222,6 +231,7 @@ type Script struct {
 	strictMultiSig           bool     // verify multisig stack item is zero length
 	discourageUpgradableNops bool     // NOP1 to NOP10 are reserved for future soft-fork upgrades
 	verifyStrictEncoding     bool     // verify strict encoding of signatures
+	verifyCleanStack         bool     // verify stack is clean after script evaluation
 	verifyDERSignatures      bool     // verify signatures compily with the DER
 	savedFirstStack          [][]byte // stack from first script for bip16 scripts
 }
@@ -624,6 +634,12 @@ const (
 	// executed.
 	ScriptDiscourageUpgradableNops
 
+	// ScriptVerifyCleanStack defines that the stack must contain only
+	// one stack element after evaluation and that the element must be
+	// true if interpreted as a boolean.  This is rule 6 of BIP0062.
+	// This flag should never be used without the ScriptBip16 flag.
+	ScriptVerifyCleanStack
+
 	// ScriptVerifyDERSignatures defines that signatures are required
 	// to compily with the DER format.
 	ScriptVerifyDERSignatures
@@ -655,7 +671,8 @@ const (
 		ScriptVerifyStrictEncoding |
 		ScriptVerifyMinimalData |
 		ScriptStrictMultiSig |
-		ScriptDiscourageUpgradableNops
+		ScriptDiscourageUpgradableNops |
+		ScriptVerifyCleanStack
 )
 
 // NewScript returns a new script engine for the provided tx and input idx with
@@ -690,8 +707,7 @@ func NewScript(scriptSig []byte, scriptPubKey []byte, txidx int, tx *wire.MsgTx,
 	}
 
 	// Parse flags.
-	bip16 := flags&ScriptBip16 == ScriptBip16
-	if bip16 && isScriptHash(m.scripts[1]) {
+	if flags&ScriptBip16 == ScriptBip16 && isScriptHash(m.scripts[1]) {
 		// if we are pay to scripthash then we only accept input
 		// scripts that push data
 		if !isPushOnly(m.scripts[0]) {
@@ -715,6 +731,12 @@ func NewScript(scriptSig []byte, scriptPubKey []byte, txidx int, tx *wire.MsgTx,
 		m.dstack.verifyMinimalData = true
 		m.astack.verifyMinimalData = true
 	}
+	if flags&ScriptVerifyCleanStack == ScriptVerifyCleanStack {
+		if flags&ScriptBip16 != ScriptBip16 {
+			return nil, ErrInvalidFlags
+		}
+		m.verifyCleanStack = true
+	}
 
 	m.tx = *tx
 	m.txidx = txidx
@@ -755,23 +777,29 @@ func (s *Script) Execute() (err error) {
 		}))
 	}
 
-	return s.CheckErrorCondition()
+	return s.CheckErrorCondition(true)
 }
 
 // CheckErrorCondition returns nil if the running script has ended and was
 // successful, leaving a a true boolean on the stack. An error otherwise,
 // including if the script has not finished.
-func (s *Script) CheckErrorCondition() (err error) {
+func (s *Script) CheckErrorCondition(finalScript bool) error {
 	// Check we are actually done. if pc is past the end of script array
 	// then we have run out of scripts to run.
 	if s.scriptidx < len(s.scripts) {
 		return ErrStackScriptUnfinished
 	}
-	if s.dstack.Depth() < 1 {
+	if finalScript && s.verifyCleanStack && s.dstack.Depth() != 1 {
+		return ErrStackCleanStack
+	} else if s.dstack.Depth() < 1 {
 		return ErrStackEmptyStack
 	}
+
 	v, err := s.dstack.PopBool()
-	if err == nil && v == false {
+	if err != nil {
+		return err
+	}
+	if v == false {
 		// log interesting data.
 		log.Tracef("%v", newLogClosure(func() string {
 			dis0, _ := s.DisasmScript(0)
@@ -779,9 +807,9 @@ func (s *Script) CheckErrorCondition() (err error) {
 			return fmt.Sprintf("scripts failed: script0: %s\n"+
 				"script1: %s", dis0, dis1)
 		}))
-		err = ErrStackScriptFailed
+		return ErrStackScriptFailed
 	}
-	return err
+	return nil
 }
 
 // Step will execute the next instruction and move the program counter to the
@@ -827,7 +855,7 @@ func (s *Script) Step() (done bool, err error) {
 			s.scriptidx++
 			// We check script ran ok, if so then we pull
 			// the script out of the first stack and executre that.
-			err := s.CheckErrorCondition()
+			err := s.CheckErrorCondition(false)
 			if err != nil {
 				return false, err
 			}
@@ -880,10 +908,12 @@ func (s *Script) validPC() error {
 
 // DisasmScript returns the disassembly string for the script at offset
 // ``idx''.  Where 0 is the scriptSig and 1 is the scriptPubKey.
-func (s *Script) DisasmScript(idx int) (disstr string, err error) {
+func (s *Script) DisasmScript(idx int) (string, error) {
 	if idx >= len(s.scripts) {
 		return "", ErrStackInvalidIndex
 	}
+
+	var disstr string
 	for i := range s.scripts[idx] {
 		disstr = disstr + s.disasm(idx, i) + "\n"
 	}
@@ -892,7 +922,7 @@ func (s *Script) DisasmScript(idx int) (disstr string, err error) {
 
 // DisasmPC returns the string for the disassembly of the opcode that will be
 // next to execute when Step() is called.
-func (s *Script) DisasmPC() (disstr string, err error) {
+func (s *Script) DisasmPC() (string, error) {
 	scriptidx, scriptoff, err := s.curPC()
 	if err != nil {
 		return "", err
diff --git a/txscript/script_test.go b/txscript/script_test.go
index 904fa793..0777879b 100644
--- a/txscript/script_test.go
+++ b/txscript/script_test.go
@@ -2559,7 +2559,7 @@ func TestCheckErrorCondition(t *testing.T) {
 			return
 		}
 
-		err = engine.CheckErrorCondition()
+		err = engine.CheckErrorCondition(false)
 		if err != txscript.ErrStackScriptUnfinished {
 			t.Errorf("got unexepected error %v on %dth iteration",
 				err, i)
@@ -2576,7 +2576,7 @@ func TestCheckErrorCondition(t *testing.T) {
 		return
 	}
 
-	err = engine.CheckErrorCondition()
+	err = engine.CheckErrorCondition(false)
 	if err != nil {
 		t.Errorf("unexpected error %v on final check", err)
 	}
@@ -4816,3 +4816,53 @@ func TestIsPushOnlyScript(t *testing.T) {
 			test.expected)
 	}
 }
+
+func TestInvalidFlagCombinations(t *testing.T) {
+	t.Parallel()
+
+	tests := []txscript.ScriptFlags{
+		txscript.ScriptVerifyCleanStack,
+	}
+
+	// tx with almost empty scripts.
+	tx := &wire.MsgTx{
+		Version: 1,
+		TxIn: []*wire.TxIn{
+			{
+				PreviousOutPoint: wire.OutPoint{
+					Hash: wire.ShaHash([32]byte{
+						0xc9, 0x97, 0xa5, 0xe5,
+						0x6e, 0x10, 0x41, 0x02,
+						0xfa, 0x20, 0x9c, 0x6a,
+						0x85, 0x2d, 0xd9, 0x06,
+						0x60, 0xa2, 0x0b, 0x2d,
+						0x9c, 0x35, 0x24, 0x23,
+						0xed, 0xce, 0x25, 0x85,
+						0x7f, 0xcd, 0x37, 0x04,
+					}),
+					Index: 0,
+				},
+				SignatureScript: []uint8{txscript.OP_NOP},
+				Sequence:        4294967295,
+			},
+		},
+		TxOut: []*wire.TxOut{
+			{
+				Value:    1000000000,
+				PkScript: []byte{},
+			},
+		},
+		LockTime: 0,
+	}
+	pkScript := []byte{txscript.OP_NOP}
+
+	for i, test := range tests {
+		_, err := txscript.NewScript(tx.TxIn[0].SignatureScript,
+			pkScript, 0, tx, test)
+
+		if err != txscript.ErrInvalidFlags {
+			t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+
+				"error: %v", i, err)
+		}
+	}
+}