From 8dd7412a8469b2ebf768d85033b99bf339260336 Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Mon, 20 Apr 2015 15:28:00 -0500 Subject: [PATCH] txscript: Rename Script to Engine. This commit renames the Script type to Engine to better reflect its purpose. It also renames the NewScript function to NewEngine to match. This is being done because name Script for the engine is confusing since it implies it is an actual script rather than the execution environment for the script. It also paves the way for eventually supplying a ParsedScript type which will be less likely to be confused with the execution environment. While moving the code, some additional variable names and comments have been updated to better match the style used throughout the rest of the code base. In addition, an attempt has been made to use consistent naming of the engine as 'vm' instead of using different variables names as it was previously. Finally, the relevant engine code has been moved into a new file named engine.go and related tests moved to engine_test.go. --- blockchain/scriptval.go | 6 +- txscript/engine.go | 550 +++++++++++++++++++++++++++++++++++++ txscript/engine_test.go | 173 ++++++++++++ txscript/example_test.go | 4 +- txscript/internal_test.go | 18 +- txscript/opcode.go | 469 ++++++++++++++++--------------- txscript/opcode_test.go | 26 +- txscript/reference_test.go | 16 +- txscript/script.go | 540 ------------------------------------ txscript/script_test.go | 175 +----------- 10 files changed, 997 insertions(+), 980 deletions(-) create mode 100644 txscript/engine.go create mode 100644 txscript/engine_test.go diff --git a/blockchain/scriptval.go b/blockchain/scriptval.go index 3f56c07e..ef9115e9 100644 --- a/blockchain/scriptval.go +++ b/blockchain/scriptval.go @@ -83,8 +83,8 @@ out: // Create a new script engine for the script pair. sigScript := txIn.SignatureScript pkScript := originMsgTx.TxOut[originTxIndex].PkScript - engine, err := txscript.NewScript(pkScript, - txVI.tx.MsgTx(), txVI.txInIndex, v.flags) + vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), + txVI.txInIndex, v.flags) if err != nil { str := fmt.Sprintf("failed to parse input "+ "%s:%d which references output %s:%d - "+ @@ -98,7 +98,7 @@ out: } // Execute the script pair. - if err := engine.Execute(); err != nil { + if err := vm.Execute(); err != nil { str := fmt.Sprintf("failed to validate input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ diff --git a/txscript/engine.go b/txscript/engine.go new file mode 100644 index 00000000..5440eaba --- /dev/null +++ b/txscript/engine.go @@ -0,0 +1,550 @@ +// Copyright (c) 2013-2015 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "fmt" + "math/big" + + "github.com/btcsuite/btcd/wire" +) + +// ScriptFlags is a bitmask defining additional operations or +// tests that will be done when executing a Script. +type ScriptFlags uint32 + +const ( + // ScriptBip16 defines whether the bip16 threshhold has passed and thus + // pay-to-script hash transactions will be fully validated. + ScriptBip16 ScriptFlags = 1 << iota + + // ScriptStrictMultiSig defines whether to verify the stack item + // used by CHECKMULTISIG is zero length. + ScriptStrictMultiSig + + // ScriptDiscourageUpgradableNops defines whether to verify that + // NOP1 through NOP10 are reserved for future soft-fork upgrades. This + // flag must not be used for consensus critical code nor applied to + // blocks as this flag is only for stricter standard transaction + // checks. This flag is only applied when the above opcodes are + // 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 + + // ScriptVerifyLowS defines that signtures are required to comply with + // the DER format and whose S value is <= order / 2. This is rule 5 + // of BIP0062. + ScriptVerifyLowS + + // ScriptVerifyMinimalData defines that signatures must use the smallest + // push operator. This is both rules 3 and 4 of BIP0062. + ScriptVerifyMinimalData + + // ScriptVerifySigPushOnly defines that signature scripts must contain + // only pushed data. This is rule 2 of BIP0062. + ScriptVerifySigPushOnly + + // ScriptVerifyStrictEncoding defines that signature scripts and + // public keys must follow the strict encoding requirements. + ScriptVerifyStrictEncoding + + // StandardVerifyFlags are the script flags which are used when + // executing transaction scripts to enforce additional checks which + // are required for the script to be considered standard. These checks + // help reduce issues related to transaction malleability as well as + // allow pay-to-script hash transactions. Note these flags are + // different than what is required for the consensus rules in that they + // are more strict. + // + // TODO: This definition does not belong here. It belongs in a policy + // package. + StandardVerifyFlags = ScriptBip16 | + ScriptVerifyDERSignatures | + ScriptVerifyStrictEncoding | + ScriptVerifyMinimalData | + ScriptStrictMultiSig | + ScriptDiscourageUpgradableNops | + ScriptVerifyCleanStack +) + +// Engine is the virtual machine that executes scripts. +type Engine struct { + scripts [][]parsedOpcode + scriptIdx int + scriptOff int + lastcodesep int + dstack Stack // data stack + astack Stack // alt stack + tx wire.MsgTx + txIdx int + condStack []int + numOps int + bip16 bool // treat execution as pay-to-script-hash + 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 comply with the DER format + verifyLowS bool // verify signatures comply with the DER format and have an S value <= halforder + savedFirstStack [][]byte // stack from first script for bip16 scripts +} + +// Execute will execute all script in the script engine and return either nil +// for successful validation or an error if one occurred. +func (vm *Engine) Execute() (err error) { + done := false + for done != true { + log.Tracef("%v", newLogClosure(func() string { + dis, err := vm.DisasmPC() + if err != nil { + return fmt.Sprintf("stepping (%v)", err) + } + return fmt.Sprintf("stepping %v", dis) + })) + + done, err = vm.Step() + if err != nil { + return err + } + log.Tracef("%v", newLogClosure(func() string { + var dstr, astr string + + // if we're tracing, dump the stacks. + if vm.dstack.Depth() != 0 { + dstr = "Stack:\n" + vm.dstack.String() + } + if vm.astack.Depth() != 0 { + astr = "AltStack:\n" + vm.astack.String() + } + + return dstr + astr + })) + } + + return vm.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 (vm *Engine) 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 vm.scriptIdx < len(vm.scripts) { + return ErrStackScriptUnfinished + } + if finalScript && vm.verifyCleanStack && vm.dstack.Depth() != 1 { + return ErrStackCleanStack + } else if vm.dstack.Depth() < 1 { + return ErrStackEmptyStack + } + + v, err := vm.dstack.PopBool() + if err != nil { + return err + } + if v == false { + // log interesting data. + log.Tracef("%v", newLogClosure(func() string { + dis0, _ := vm.DisasmScript(0) + dis1, _ := vm.DisasmScript(1) + return fmt.Sprintf("scripts failed: script0: %s\n"+ + "script1: %s", dis0, dis1) + })) + return ErrStackScriptFailed + } + return nil +} + +// Step will execute the next instruction and move the program counter to the +// next opcode in the script, or the next script if the curent has ended. Step +// will return true in the case that the last opcode was successfully executed. +// if an error is returned then the result of calling Step or any other method +// is undefined. +func (vm *Engine) Step() (done bool, err error) { + // verify that it is pointing to a valid script address + err = vm.validPC() + if err != nil { + return true, err + } + opcode := vm.scripts[vm.scriptIdx][vm.scriptOff] + + err = opcode.exec(vm) + if err != nil { + return true, err + } + + if vm.dstack.Depth()+vm.astack.Depth() > maxStackSize { + return false, ErrStackOverflow + } + + // prepare for next instruction + vm.scriptOff++ + if vm.scriptOff >= len(vm.scripts[vm.scriptIdx]) { + // Illegal to have an `if' that straddles two scripts. + if err == nil && len(vm.condStack) != 1 { + return false, ErrStackMissingEndif + } + + // alt stack doesn't persist. + _ = vm.astack.DropN(vm.astack.Depth()) + + vm.numOps = 0 // number of ops is per script. + vm.scriptOff = 0 + if vm.scriptIdx == 0 && vm.bip16 { + vm.scriptIdx++ + vm.savedFirstStack = vm.GetStack() + } else if vm.scriptIdx == 1 && vm.bip16 { + // Put us past the end for CheckErrorCondition() + vm.scriptIdx++ + // We check script ran ok, if so then we pull + // the script out of the first stack and executre that. + err := vm.CheckErrorCondition(false) + if err != nil { + return false, err + } + + script := vm.savedFirstStack[len(vm.savedFirstStack)-1] + pops, err := parseScript(script) + if err != nil { + return false, err + } + vm.scripts = append(vm.scripts, pops) + // Set stack to be the stack from first script + // minus the script itself + vm.SetStack(vm.savedFirstStack[:len(vm.savedFirstStack)-1]) + } else { + vm.scriptIdx++ + } + // there are zero length scripts in the wild + if vm.scriptIdx < len(vm.scripts) && vm.scriptOff >= len(vm.scripts[vm.scriptIdx]) { + vm.scriptIdx++ + } + vm.lastcodesep = 0 + if vm.scriptIdx >= len(vm.scripts) { + return true, nil + } + } + return false, nil +} + +// curPC returns either the current script and offset, or an error if the +// position isn't valid. +func (vm *Engine) curPC() (script int, off int, err error) { + err = vm.validPC() + if err != nil { + return 0, 0, err + } + return vm.scriptIdx, vm.scriptOff, nil +} + +// validPC returns an error if the current script position is valid for +// execution, nil otherwise. +func (vm *Engine) validPC() error { + if vm.scriptIdx >= len(vm.scripts) { + return fmt.Errorf("Past input scripts %v:%v %v:xxxx", vm.scriptIdx, vm.scriptOff, len(vm.scripts)) + } + if vm.scriptOff >= len(vm.scripts[vm.scriptIdx]) { + return fmt.Errorf("Past input scripts %v:%v %v:%04d", vm.scriptIdx, vm.scriptOff, vm.scriptIdx, len(vm.scripts[vm.scriptIdx])) + } + return nil +} + +// DisasmScript returns the disassembly string for the script at offset +// ``idx''. Where 0 is the scriptSig and 1 is the scriptPubKey. +func (vm *Engine) DisasmScript(idx int) (string, error) { + if idx >= len(vm.scripts) { + return "", ErrStackInvalidIndex + } + + var disstr string + for i := range vm.scripts[idx] { + disstr = disstr + vm.disasm(idx, i) + "\n" + } + return disstr, nil +} + +// DisasmPC returns the string for the disassembly of the opcode that will be +// next to execute when Step() is called. +func (vm *Engine) DisasmPC() (string, error) { + scriptIdx, scriptOff, err := vm.curPC() + if err != nil { + return "", err + } + return vm.disasm(scriptIdx, scriptOff), nil +} + +// disasm is a helper member to produce the output for DisasmPC and +// DisasmScript. It produces the opcode prefixed by the program counter at the +// provided position in the script. it does no error checking and leaves that +// to the caller to provide a valid offse. +func (vm *Engine) disasm(scriptIdx int, scriptOff int) string { + return fmt.Sprintf("%02x:%04x: %s", scriptIdx, scriptOff, + vm.scripts[scriptIdx][scriptOff].print(false)) +} + +// subScript will return the script since the last OP_CODESEPARATOR +func (vm *Engine) subScript() []parsedOpcode { + return vm.scripts[vm.scriptIdx][vm.lastcodesep:] +} + +// checkHashTypeEncoding returns whether or not the passed hashtype adheres to +// the strict encoding requirements if enabled. +func (vm *Engine) checkHashTypeEncoding(hashType SigHashType) error { + if !vm.verifyStrictEncoding { + return nil + } + + sigHashType := hashType & ^SigHashAnyOneCanPay + if sigHashType < SigHashAll || sigHashType > SigHashSingle { + return fmt.Errorf("invalid hashtype: 0x%x\n", hashType) + } + return nil +} + +// checkPubKeyEncoding returns whether or not the passed public key adheres to +// the strict encoding requirements if enabled. +func (vm *Engine) checkPubKeyEncoding(pubKey []byte) error { + if !vm.verifyStrictEncoding { + return nil + } + + if len(pubKey) == 33 && (pubKey[0] == 0x02 || pubKey[0] == 0x03) { + // Compressed + return nil + } + if len(pubKey) == 65 && pubKey[0] == 0x04 { + // Uncompressed + return nil + } + return ErrStackInvalidPubKey +} + +// checkSignatureEncoding returns whether or not the passed signature adheres to +// the strict encoding requirements if enabled. +func (vm *Engine) checkSignatureEncoding(sig []byte) error { + if !vm.verifyDERSignatures && !vm.verifyLowS && !vm.verifyStrictEncoding { + return nil + } + + if len(sig) < 8 { + // Too short + return fmt.Errorf("malformed signature: too short: %d < 8", + len(sig)) + } + if len(sig) > 72 { + // Too long + return fmt.Errorf("malformed signature: too long: %d > 72", + len(sig)) + } + if sig[0] != 0x30 { + // Wrong type + return fmt.Errorf("malformed signature: format has wrong type: 0x%x", + sig[0]) + } + if int(sig[1]) != len(sig)-2 { + // Invalid length + return fmt.Errorf("malformed signature: bad length: %d != %d", + sig[1], len(sig)-2) + } + + rLen := int(sig[3]) + + // Make sure S is inside the signature + if rLen+5 > len(sig) { + return fmt.Errorf("malformed signature: S out of bounds") + } + + sLen := int(sig[rLen+5]) + + // The length of the elements does not match + // the length of the signature + if rLen+sLen+6 != len(sig) { + return fmt.Errorf("malformed signature: invalid R length") + } + + // R elements must be integers + if sig[2] != 0x02 { + return fmt.Errorf("malformed signature: missing first integer marker") + } + + // Zero-length integers are not allowed for R + if rLen == 0 { + return fmt.Errorf("malformed signature: R length is zero") + } + + // R must not be negative + if sig[4]&0x80 != 0 { + return fmt.Errorf("malformed signature: R value is negative") + } + + // Null bytes at the start of R are not allowed, unless R would + // otherwise be interpreted as a negative number. + if rLen > 1 && sig[4] == 0x00 && sig[5]&0x80 == 0 { + return fmt.Errorf("malformed signature: invalid R value") + } + + // S elements must be integers + if sig[rLen+4] != 0x02 { + return fmt.Errorf("malformed signature: missing second integer marker") + } + + // Zero-length integers are not allowed for S + if sLen == 0 { + return fmt.Errorf("malformed signature: S length is zero") + } + + // S must not be negative + if sig[rLen+6]&0x80 != 0 { + return fmt.Errorf("malformed signature: S value is negative") + } + + // Null bytes at the start of S are not allowed, unless S would + // otherwise be interpreted as a negative number. + if sLen > 1 && sig[rLen+6] == 0x00 && sig[rLen+7]&0x80 == 0 { + return fmt.Errorf("malformed signature: invalid S value") + } + + // Verify the S value is <= halforder. + if vm.verifyLowS { + sValue := new(big.Int).SetBytes(sig[rLen+6 : rLen+6+sLen]) + if sValue.Cmp(halfOrder) > 0 { + return ErrStackInvalidLowSSignature + } + } + + return nil +} + +// getStack returns the contents of stack as a byte array bottom up +func getStack(stack *Stack) [][]byte { + array := make([][]byte, stack.Depth()) + for i := range array { + // PeekByteArry can't fail due to overflow, already checked + array[len(array)-i-1], _ = stack.PeekByteArray(i) + } + return array +} + +// setStack sets the stack to the contents of the array where the last item in +// the array is the top item in the stack. +func setStack(stack *Stack, data [][]byte) { + // This can not error. Only errors are for invalid arguments. + _ = stack.DropN(stack.Depth()) + + for i := range data { + stack.PushByteArray(data[i]) + } +} + +// GetStack returns the contents of the primary stack as an array. where the +// last item in the array is the top of the stack. +func (vm *Engine) GetStack() [][]byte { + return getStack(&vm.dstack) +} + +// SetStack sets the contents of the primary stack to the contents of the +// provided array where the last item in the array will be the top of the stack. +func (vm *Engine) SetStack(data [][]byte) { + setStack(&vm.dstack, data) +} + +// GetAltStack returns the contents of the primary stack as an array. where the +// last item in the array is the top of the stack. +func (vm *Engine) GetAltStack() [][]byte { + return getStack(&vm.astack) +} + +// SetAltStack sets the contents of the primary stack to the contents of the +// provided array where the last item in the array will be the top of the stack. +func (vm *Engine) SetAltStack(data [][]byte) { + setStack(&vm.astack, data) +} + +// NewEngine returns a new script engine for the provided public key script, +// transaction, and input index. The flags modify the behavior of the script +// engine according to the description provided by each flag. +func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags) (*Engine, error) { + if txIdx < 0 || txIdx >= len(tx.TxIn) { + return nil, ErrInvalidIndex + } + scriptSig := tx.TxIn[txIdx].SignatureScript + + var vm Engine + if flags&ScriptVerifySigPushOnly != 0 && !IsPushOnlyScript(scriptSig) { + return nil, ErrStackNonPushOnly + } + + scripts := [][]byte{scriptSig, scriptPubKey} + vm.scripts = make([][]parsedOpcode, len(scripts)) + for i, scr := range scripts { + if len(scr) > maxScriptSize { + return nil, ErrStackLongScript + } + var err error + vm.scripts[i], err = parseScript(scr) + if err != nil { + return nil, err + } + + // If the first scripts(s) are empty, must start on later ones. + if i == 0 && len(scr) == 0 { + // This could end up seeing an invalid initial pc if + // all scripts were empty. However, that is an invalid + // case and should fail. + vm.scriptIdx = i + 1 + } + } + + // Parse flags. + if flags&ScriptBip16 == ScriptBip16 && isScriptHash(vm.scripts[1]) { + // if we are pay to scripthash then we only accept input + // scripts that push data + if !isPushOnly(vm.scripts[0]) { + return nil, ErrStackP2SHNonPushOnly + } + vm.bip16 = true + } + if flags&ScriptStrictMultiSig == ScriptStrictMultiSig { + vm.strictMultiSig = true + } + if flags&ScriptDiscourageUpgradableNops == ScriptDiscourageUpgradableNops { + vm.discourageUpgradableNops = true + } + if flags&ScriptVerifyStrictEncoding == ScriptVerifyStrictEncoding { + vm.verifyStrictEncoding = true + } + if flags&ScriptVerifyDERSignatures == ScriptVerifyDERSignatures { + vm.verifyDERSignatures = true + } + if flags&ScriptVerifyMinimalData == ScriptVerifyMinimalData { + vm.dstack.verifyMinimalData = true + vm.astack.verifyMinimalData = true + } + if flags&ScriptVerifyCleanStack == ScriptVerifyCleanStack { + if flags&ScriptBip16 != ScriptBip16 { + return nil, ErrInvalidFlags + } + vm.verifyCleanStack = true + } + if flags&ScriptVerifyLowS == ScriptVerifyLowS { + vm.verifyLowS = true + } + + vm.tx = *tx + vm.txIdx = txIdx + vm.condStack = []int{OpCondTrue} + + return &vm, nil +} diff --git a/txscript/engine_test.go b/txscript/engine_test.go new file mode 100644 index 00000000..ea782a8e --- /dev/null +++ b/txscript/engine_test.go @@ -0,0 +1,173 @@ +// Copyright (c) 2013-2015 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript_test + +import ( + "testing" + + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" +) + +// TestBadPC sets the pc to a deliberately bad result then confirms that Step() +// and Disasm fail correctly. +func TestBadPC(t *testing.T) { + t.Parallel() + + type pcTest struct { + script, off int + } + pcTests := []pcTest{ + { + script: 2, + off: 0, + }, + { + script: 0, + off: 2, + }, + } + // 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 _, test := range pcTests { + vm, err := txscript.NewEngine(pkScript, tx, 0, 0) + if err != nil { + t.Errorf("Failed to create script: %v", err) + } + + // set to after all scripts + vm.TstSetPC(test.script, test.off) + + _, err = vm.Step() + if err == nil { + t.Errorf("Step with invalid pc (%v) succeeds!", test) + continue + } + + _, err = vm.DisasmPC() + if err == nil { + t.Errorf("DisasmPC with invalid pc (%v) succeeds!", + test) + } + } +} + +// TestCheckErrorCondition tests the execute early test in CheckErrorCondition() +// since most code paths are tested elsewhere. +func TestCheckErrorCondition(t *testing.T) { + t.Parallel() + + // 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{}, + Sequence: 4294967295, + }, + }, + TxOut: []*wire.TxOut{ + { + Value: 1000000000, + PkScript: []byte{}, + }, + }, + LockTime: 0, + } + pkScript := []byte{ + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_NOP, + txscript.OP_TRUE, + } + + vm, err := txscript.NewEngine(pkScript, tx, 0, 0) + if err != nil { + t.Errorf("failed to create script: %v", err) + } + + for i := 0; i < len(pkScript)-1; i++ { + done, err := vm.Step() + if err != nil { + t.Errorf("failed to step %dth time: %v", i, err) + return + } + if done { + t.Errorf("finshed early on %dth time", i) + return + } + + err = vm.CheckErrorCondition(false) + if err != txscript.ErrStackScriptUnfinished { + t.Errorf("got unexepected error %v on %dth iteration", + err, i) + return + } + } + done, err := vm.Step() + if err != nil { + t.Errorf("final step failed %v", err) + return + } + if !done { + t.Errorf("final step isn't done!") + return + } + + err = vm.CheckErrorCondition(false) + if err != nil { + t.Errorf("unexpected error %v on final check", err) + } +} diff --git a/txscript/example_test.go b/txscript/example_test.go index 3f86a036..7234d816 100644 --- a/txscript/example_test.go +++ b/txscript/example_test.go @@ -164,13 +164,13 @@ func ExampleSignTxOutput() { flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops - s, err := txscript.NewScript(originTx.TxOut[0].PkScript, redeemTx, 0, + vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags) if err != nil { fmt.Println(err) return } - if err := s.Execute(); err != nil { + if err := vm.Execute(); err != nil { fmt.Println(err) return } diff --git a/txscript/internal_test.go b/txscript/internal_test.go index 6b0c7e0b..a556f72b 100644 --- a/txscript/internal_test.go +++ b/txscript/internal_test.go @@ -79,11 +79,9 @@ func TestCheckPubKeyEncoding(t *testing.T) { isValid: false, }, } - s := Script{ - verifyStrictEncoding: true, - } + vm := Engine{verifyStrictEncoding: true} for _, test := range tests { - err := s.checkPubKeyEncoding(test.key) + err := vm.checkPubKeyEncoding(test.key) if err != nil && test.isValid { t.Errorf("checkSignatureEncoding test '%s' failed "+ "when it should have succeeded: %v", test.name, @@ -339,11 +337,9 @@ func TestCheckSignatureEncoding(t *testing.T) { }, } - s := Script{ - verifyStrictEncoding: true, - } + vm := Engine{verifyStrictEncoding: true} for _, test := range tests { - err := s.checkSignatureEncoding(test.sig) + err := vm.checkSignatureEncoding(test.sig) if err != nil && test.isValid { t.Errorf("checkSignatureEncoding test '%s' failed "+ "when it should have succeeded: %v", test.name, @@ -375,9 +371,9 @@ func TstRemoveOpcodeByData(pkscript []byte, data []byte) ([]byte, error) { // TestSetPC allows the test modules to set the program counter to whatever they // want. -func (s *Script) TstSetPC(script, off int) { - s.scriptidx = script - s.scriptoff = off +func (vm *Engine) TstSetPC(script, off int) { + vm.scriptIdx = script + vm.scriptOff = off } // Internal tests for opcodde parsing with bad data templates. diff --git a/txscript/opcode.go b/txscript/opcode.go index 2016a040..285158b9 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -27,8 +27,8 @@ type opcode struct { value byte name string length int - opfunc func(*parsedOpcode, *Script) error - parsefunc func(*opcode, *Script, []byte) error + opfunc func(*parsedOpcode, *Engine) error + parsefunc func(*opcode, *Engine, []byte) error } // These constants are the values of the official opcode used on the btc wiki, @@ -859,7 +859,6 @@ var opcodeOnelineRepls = map[string]string{ type parsedOpcode struct { opcode *opcode data []byte - opfunc func(op parsedOpcode, s Script) error } // The following opcodes are disabled and are thus always bad to see in the @@ -970,7 +969,7 @@ func (pop *parsedOpcode) checkMinimalDataPush() error { // exec peforms execution on the opcode. It takes into account whether or not // it is hidden by conditionals, but some rules still must be tested in this // case. -func (pop *parsedOpcode) exec(s *Script) error { +func (pop *parsedOpcode) exec(vm *Engine) error { // Disabled opcodes are ``fail on program counter''. if pop.disabled() { return ErrStackOpDisabled @@ -983,8 +982,8 @@ func (pop *parsedOpcode) exec(s *Script) error { // Note that this includes OP_RESERVED which counts as a push operation. if pop.opcode.value > OP_16 { - s.numOps++ - if s.numOps > MaxOpsPerScript { + vm.numOps++ + if vm.numOps > MaxOpsPerScript { return ErrStackTooManyOperations } @@ -994,20 +993,20 @@ func (pop *parsedOpcode) exec(s *Script) error { // If we are not a conditional opcode and we aren't executing, then // we are done now. - if s.condStack[0] != OpCondTrue && !pop.conditional() { + if vm.condStack[0] != OpCondTrue && !pop.conditional() { return nil } // Ensure all executed data push opcodes use the minimal encoding when // the minimal data verification is set. - if s.dstack.verifyMinimalData && s.condStack[0] == OpCondTrue && + if vm.dstack.verifyMinimalData && vm.condStack[0] == OpCondTrue && pop.opcode.value >= 0 && pop.opcode.value <= OP_PUSHDATA4 { if err := pop.checkMinimalDataPush(); err != nil { return err } } - return pop.opcode.opfunc(pop, s) + return pop.opcode.opfunc(pop, vm) } func (pop *parsedOpcode) print(oneline bool) string { @@ -1093,46 +1092,46 @@ func (pop *parsedOpcode) bytes() ([]byte, error) { // opcode implementation functions from here -func opcodeDisabled(op *parsedOpcode, s *Script) error { +func opcodeDisabled(op *parsedOpcode, vm *Engine) error { return ErrStackOpDisabled } -func opcodeReserved(op *parsedOpcode, s *Script) error { +func opcodeReserved(op *parsedOpcode, vm *Engine) error { return ErrStackReservedOpcode } // Recognised opcode, but for bitcoind internal use only. -func opcodeInvalid(op *parsedOpcode, s *Script) error { +func opcodeInvalid(op *parsedOpcode, vm *Engine) error { return ErrStackInvalidOpcode } -func opcodeFalse(op *parsedOpcode, s *Script) error { - s.dstack.PushByteArray([]byte("")) +func opcodeFalse(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushByteArray([]byte("")) return nil } -func opcodePushData(op *parsedOpcode, s *Script) error { - s.dstack.PushByteArray(op.data) +func opcodePushData(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushByteArray(op.data) return nil } -func opcode1Negate(op *parsedOpcode, s *Script) error { - s.dstack.PushInt(big.NewInt(-1)) +func opcode1Negate(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushInt(big.NewInt(-1)) return nil } -func opcodeN(op *parsedOpcode, s *Script) error { +func opcodeN(op *parsedOpcode, vm *Engine) error { // 16 consecutive opcodes add increasing numbers to the stack. - s.dstack.PushInt(big.NewInt(int64(op.opcode.value - (OP_1 - 1)))) + vm.dstack.PushInt(big.NewInt(int64(op.opcode.value - (OP_1 - 1)))) return nil } -func opcodeNop(op *parsedOpcode, s *Script) error { +func opcodeNop(op *parsedOpcode, vm *Engine) error { switch op.opcode.value { case OP_NOP1, OP_NOP2, OP_NOP3, OP_NOP4, OP_NOP5, OP_NOP6, OP_NOP7, OP_NOP8, OP_NOP9, OP_NOP10: - if s.discourageUpgradableNops { + if vm.discourageUpgradableNops { return fmt.Errorf("%s reserved for soft-fork upgrades", opcodemap[op.opcode.value].name) } } @@ -1141,12 +1140,12 @@ func opcodeNop(op *parsedOpcode, s *Script) error { // opcodeIf computes true/false based on the value on the stack and pushes // the condition on the condStack (conditional execution stack) -func opcodeIf(op *parsedOpcode, s *Script) error { +func opcodeIf(op *parsedOpcode, vm *Engine) error { // opcodeIf will be executed even if it is on the non-execute side // of the conditional, this is so proper nesting is maintained var condval int - if s.condStack[0] == OpCondTrue { - ok, err := s.dstack.PopBool() + if vm.condStack[0] == OpCondTrue { + ok, err := vm.dstack.PopBool() if err != nil { return err } @@ -1158,19 +1157,19 @@ func opcodeIf(op *parsedOpcode, s *Script) error { } cond := []int{condval} // push condition to the 'head' of the slice - s.condStack = append(cond, s.condStack...) + vm.condStack = append(cond, vm.condStack...) // TODO(drahn) check if a maximum condtitional stack limit exists return nil } // opcodeNotIf computes true/false based on the value on the stack and pushes // the (inverted) condition on the condStack (conditional execution stack) -func opcodeNotIf(op *parsedOpcode, s *Script) error { +func opcodeNotIf(op *parsedOpcode, vm *Engine) error { // opcodeIf will be executed even if it is on the non-execute side // of the conditional, this is so proper nesting is maintained var condval int - if s.condStack[0] == OpCondTrue { - ok, err := s.dstack.PopBool() + if vm.condStack[0] == OpCondTrue { + ok, err := vm.dstack.PopBool() if err != nil { return err } @@ -1182,23 +1181,23 @@ func opcodeNotIf(op *parsedOpcode, s *Script) error { } cond := []int{condval} // push condition to the 'head' of the slice - s.condStack = append(cond, s.condStack...) + vm.condStack = append(cond, vm.condStack...) // TODO(drahn) check if a maximum condtitional stack limit exists return nil } // opcodeElse inverts conditional execution for other half of if/else/endif -func opcodeElse(op *parsedOpcode, s *Script) error { - if len(s.condStack) < 2 { +func opcodeElse(op *parsedOpcode, vm *Engine) error { + if len(vm.condStack) < 2 { // intial true cannot be toggled, only pushed conditionals return ErrStackNoIf } - switch s.condStack[0] { + switch vm.condStack[0] { case OpCondTrue: - s.condStack[0] = OpCondFalse + vm.condStack[0] = OpCondFalse case OpCondFalse: - s.condStack[0] = OpCondTrue + vm.condStack[0] = OpCondTrue case OpCondSkip: // value doesn't change in skip } @@ -1207,20 +1206,20 @@ func opcodeElse(op *parsedOpcode, s *Script) error { // opcodeEndif terminates a conditional block, removing the value from the // conditional execution stack. -func opcodeEndif(op *parsedOpcode, s *Script) error { - if len(s.condStack) < 2 { +func opcodeEndif(op *parsedOpcode, vm *Engine) error { + if len(vm.condStack) < 2 { // intial true cannot be popped, only pushed conditionals return ErrStackNoIf } - stk := make([]int, len(s.condStack)-1, len(s.condStack)-1) - copy(stk, s.condStack[1:]) - s.condStack = stk + stk := make([]int, len(vm.condStack)-1, len(vm.condStack)-1) + copy(stk, vm.condStack[1:]) + vm.condStack = stk return nil } -func opcodeVerify(op *parsedOpcode, s *Script) error { - verified, err := s.dstack.PopBool() +func opcodeVerify(op *parsedOpcode, vm *Engine) error { + verified, err := vm.dstack.PopBool() if err != nil { return err } @@ -1231,93 +1230,93 @@ func opcodeVerify(op *parsedOpcode, s *Script) error { return nil } -func opcodeReturn(op *parsedOpcode, s *Script) error { +func opcodeReturn(op *parsedOpcode, vm *Engine) error { return ErrStackEarlyReturn } -func opcodeToAltStack(op *parsedOpcode, s *Script) error { - so, err := s.dstack.PopByteArray() +func opcodeToAltStack(op *parsedOpcode, vm *Engine) error { + so, err := vm.dstack.PopByteArray() if err != nil { return err } - s.astack.PushByteArray(so) + vm.astack.PushByteArray(so) return nil } -func opcodeFromAltStack(op *parsedOpcode, s *Script) error { - so, err := s.astack.PopByteArray() +func opcodeFromAltStack(op *parsedOpcode, vm *Engine) error { + so, err := vm.astack.PopByteArray() if err != nil { return err } - s.dstack.PushByteArray(so) + vm.dstack.PushByteArray(so) return nil } -func opcode2Drop(op *parsedOpcode, s *Script) error { - return s.dstack.DropN(2) +func opcode2Drop(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DropN(2) } -func opcode2Dup(op *parsedOpcode, s *Script) error { - return s.dstack.DupN(2) +func opcode2Dup(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DupN(2) } -func opcode3Dup(op *parsedOpcode, s *Script) error { - return s.dstack.DupN(3) +func opcode3Dup(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DupN(3) } -func opcode2Over(op *parsedOpcode, s *Script) error { - return s.dstack.OverN(2) +func opcode2Over(op *parsedOpcode, vm *Engine) error { + return vm.dstack.OverN(2) } -func opcode2Rot(op *parsedOpcode, s *Script) error { - return s.dstack.RotN(2) +func opcode2Rot(op *parsedOpcode, vm *Engine) error { + return vm.dstack.RotN(2) } -func opcode2Swap(op *parsedOpcode, s *Script) error { - return s.dstack.SwapN(2) +func opcode2Swap(op *parsedOpcode, vm *Engine) error { + return vm.dstack.SwapN(2) } -func opcodeIfDup(op *parsedOpcode, s *Script) error { - val, err := s.dstack.PeekInt(0) +func opcodeIfDup(op *parsedOpcode, vm *Engine) error { + val, err := vm.dstack.PeekInt(0) if err != nil { return err } // Push copy of data iff it isn't zero if val.Sign() != 0 { - s.dstack.PushInt(val) + vm.dstack.PushInt(val) } return nil } -func opcodeDepth(op *parsedOpcode, s *Script) error { - s.dstack.PushInt(big.NewInt(int64(s.dstack.Depth()))) +func opcodeDepth(op *parsedOpcode, vm *Engine) error { + vm.dstack.PushInt(big.NewInt(int64(vm.dstack.Depth()))) return nil } -func opcodeDrop(op *parsedOpcode, s *Script) error { - return s.dstack.DropN(1) +func opcodeDrop(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DropN(1) } -func opcodeDup(op *parsedOpcode, s *Script) error { - return s.dstack.DupN(1) +func opcodeDup(op *parsedOpcode, vm *Engine) error { + return vm.dstack.DupN(1) } -func opcodeNip(op *parsedOpcode, s *Script) error { - return s.dstack.NipN(1) +func opcodeNip(op *parsedOpcode, vm *Engine) error { + return vm.dstack.NipN(1) } -func opcodeOver(op *parsedOpcode, s *Script) error { - return s.dstack.OverN(1) +func opcodeOver(op *parsedOpcode, vm *Engine) error { + return vm.dstack.OverN(1) } // Copy object N items back in the stack to the top. Where N is the value in // the top of the stack. -func opcodePick(op *parsedOpcode, s *Script) error { - pidx, err := s.dstack.PopInt() +func opcodePick(op *parsedOpcode, vm *Engine) error { + pidx, err := vm.dstack.PopInt() if err != nil { return err } @@ -1325,13 +1324,13 @@ func opcodePick(op *parsedOpcode, s *Script) error { // PopInt promises that the int returned is 32 bit. val := int(pidx.Int64()) - return s.dstack.PickN(val) + return vm.dstack.PickN(val) } // Move object N items back in the stack to the top. Where N is the value in // the top of the stack. -func opcodeRoll(op *parsedOpcode, s *Script) error { - ridx, err := s.dstack.PopInt() +func opcodeRoll(op *parsedOpcode, vm *Engine) error { + ridx, err := vm.dstack.PopInt() if err != nil { return err } @@ -1339,181 +1338,181 @@ func opcodeRoll(op *parsedOpcode, s *Script) error { // PopInt promises that the int returned is 32 bit. val := int(ridx.Int64()) - return s.dstack.RollN(val) + return vm.dstack.RollN(val) } // Rotate top three items on the stack to the left. // e.g. 1,2,3 -> 2,3,1 -func opcodeRot(op *parsedOpcode, s *Script) error { - return s.dstack.RotN(1) +func opcodeRot(op *parsedOpcode, vm *Engine) error { + return vm.dstack.RotN(1) } // Swap the top two items on the stack: 1,2 -> 2,1 -func opcodeSwap(op *parsedOpcode, s *Script) error { - return s.dstack.SwapN(1) +func opcodeSwap(op *parsedOpcode, vm *Engine) error { + return vm.dstack.SwapN(1) } // The item at the top of the stack is copied and inserted before the // second-to-top item. e.g.: 2,1, -> 2,1,2 -func opcodeTuck(op *parsedOpcode, s *Script) error { - return s.dstack.Tuck() +func opcodeTuck(op *parsedOpcode, vm *Engine) error { + return vm.dstack.Tuck() } // Push the size of the item on top of the stack onto the stack. -func opcodeSize(op *parsedOpcode, s *Script) error { - i, err := s.dstack.PeekByteArray(0) +func opcodeSize(op *parsedOpcode, vm *Engine) error { + i, err := vm.dstack.PeekByteArray(0) if err != nil { return err } - s.dstack.PushInt(big.NewInt(int64(len(i)))) + vm.dstack.PushInt(big.NewInt(int64(len(i)))) return nil } -func opcodeEqual(op *parsedOpcode, s *Script) error { - a, err := s.dstack.PopByteArray() +func opcodeEqual(op *parsedOpcode, vm *Engine) error { + a, err := vm.dstack.PopByteArray() if err != nil { return err } - b, err := s.dstack.PopByteArray() + b, err := vm.dstack.PopByteArray() if err != nil { return err } - s.dstack.PushBool(bytes.Equal(a, b)) + vm.dstack.PushBool(bytes.Equal(a, b)) return nil } -func opcodeEqualVerify(op *parsedOpcode, s *Script) error { - err := opcodeEqual(op, s) +func opcodeEqualVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeEqual(op, vm) if err == nil { - err = opcodeVerify(op, s) + err = opcodeVerify(op, vm) } return err } -func opcode1Add(op *parsedOpcode, s *Script) error { - m, err := s.dstack.PopInt() +func opcode1Add(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() if err != nil { return err } - s.dstack.PushInt(new(big.Int).Add(m, big.NewInt(1))) + vm.dstack.PushInt(new(big.Int).Add(m, big.NewInt(1))) return nil } -func opcode1Sub(op *parsedOpcode, s *Script) error { - m, err := s.dstack.PopInt() +func opcode1Sub(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() if err != nil { return err } - s.dstack.PushInt(new(big.Int).Sub(m, big.NewInt(1))) + vm.dstack.PushInt(new(big.Int).Sub(m, big.NewInt(1))) return nil } -func opcodeNegate(op *parsedOpcode, s *Script) error { +func opcodeNegate(op *parsedOpcode, vm *Engine) error { // XXX when we remove types just flip the 0x80 bit of msb - m, err := s.dstack.PopInt() + m, err := vm.dstack.PopInt() if err != nil { return err } - s.dstack.PushInt(new(big.Int).Neg(m)) + vm.dstack.PushInt(new(big.Int).Neg(m)) return nil } -func opcodeAbs(op *parsedOpcode, s *Script) error { +func opcodeAbs(op *parsedOpcode, vm *Engine) error { // XXX when we remove types just &= ~0x80 on msb - m, err := s.dstack.PopInt() + m, err := vm.dstack.PopInt() if err != nil { return err } - s.dstack.PushInt(new(big.Int).Abs(m)) + vm.dstack.PushInt(new(big.Int).Abs(m)) return nil } // If then input is 0 or 1, it is flipped. Otherwise the output will be 0. // (n.b. official client just has 1 is 0, else 0) -func opcodeNot(op *parsedOpcode, s *Script) error { - m, err := s.dstack.PopInt() +func opcodeNot(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() if err != nil { return err } if m.Sign() == 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } // opcode returns 0 if the input is 0, 1 otherwise. -func opcode0NotEqual(op *parsedOpcode, s *Script) error { - m, err := s.dstack.PopInt() +func opcode0NotEqual(op *parsedOpcode, vm *Engine) error { + m, err := vm.dstack.PopInt() if err != nil { return err } if m.Sign() != 0 { m.SetInt64(1) } - s.dstack.PushInt(m) + vm.dstack.PushInt(m) return nil } // Push result of adding top two entries on stack -func opcodeAdd(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeAdd(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } - s.dstack.PushInt(new(big.Int).Add(v0, v1)) + vm.dstack.PushInt(new(big.Int).Add(v0, v1)) return nil } // Push result of subtracting 2nd entry on stack from first. -func opcodeSub(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeSub(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } - s.dstack.PushInt(new(big.Int).Sub(v1, v0)) + vm.dstack.PushInt(new(big.Int).Sub(v1, v0)) return nil } // If both of the top two entries on the stack are not zero output is 1. // Otherwise, 0. -func opcodeBoolAnd(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeBoolAnd(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v0.Sign() != 0 && v1.Sign() != 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil @@ -1521,187 +1520,187 @@ func opcodeBoolAnd(op *parsedOpcode, s *Script) error { // If either of the top two entries on the stack are not zero output is 1. // Otherwise, 0. -func opcodeBoolOr(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeBoolOr(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v0.Sign() != 0 || v1.Sign() != 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeNumEqual(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeNumEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v0.Cmp(v1) == 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeNumEqualVerify(op *parsedOpcode, s *Script) error { - err := opcodeNumEqual(op, s) +func opcodeNumEqualVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeNumEqual(op, vm) if err == nil { - err = opcodeVerify(op, s) + err = opcodeVerify(op, vm) } return err } -func opcodeNumNotEqual(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeNumNotEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v0.Cmp(v1) != 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeLessThan(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeLessThan(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v1.Cmp(v0) == -1 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeGreaterThan(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeGreaterThan(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v1.Cmp(v0) == 1 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeLessThanOrEqual(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeLessThanOrEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v1.Cmp(v0) <= 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeGreaterThanOrEqual(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeGreaterThanOrEqual(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v1.Cmp(v0) >= 0 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } -func opcodeMin(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeMin(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v1.Cmp(v0) == -1 { - s.dstack.PushInt(new(big.Int).Set(v1)) + vm.dstack.PushInt(new(big.Int).Set(v1)) } else { - s.dstack.PushInt(new(big.Int).Set(v0)) + vm.dstack.PushInt(new(big.Int).Set(v0)) } return nil } -func opcodeMax(op *parsedOpcode, s *Script) error { - v0, err := s.dstack.PopInt() +func opcodeMax(op *parsedOpcode, vm *Engine) error { + v0, err := vm.dstack.PopInt() if err != nil { return err } - v1, err := s.dstack.PopInt() + v1, err := vm.dstack.PopInt() if err != nil { return err } if v1.Cmp(v0) == 1 { - s.dstack.PushInt(new(big.Int).Set(v1)) + vm.dstack.PushInt(new(big.Int).Set(v1)) } else { - s.dstack.PushInt(new(big.Int).Set(v0)) + vm.dstack.PushInt(new(big.Int).Set(v0)) } return nil @@ -1709,26 +1708,26 @@ func opcodeMax(op *parsedOpcode, s *Script) error { // stack input: x, min, max. Returns 1 if x is within specified range // (left inclusive), 0 otherwise -func opcodeWithin(op *parsedOpcode, s *Script) error { - maxVal, err := s.dstack.PopInt() +func opcodeWithin(op *parsedOpcode, vm *Engine) error { + maxVal, err := vm.dstack.PopInt() if err != nil { return err } - minVal, err := s.dstack.PopInt() + minVal, err := vm.dstack.PopInt() if err != nil { return err } - x, err := s.dstack.PopInt() + x, err := vm.dstack.PopInt() if err != nil { return err } if x.Cmp(minVal) >= 0 && x.Cmp(maxVal) == -1 { - s.dstack.PushInt(big.NewInt(1)) + vm.dstack.PushInt(big.NewInt(1)) } else { - s.dstack.PushInt(big.NewInt(0)) + vm.dstack.PushInt(big.NewInt(0)) } return nil } @@ -1744,70 +1743,70 @@ func calcHash160(buf []byte) []byte { return calcHash(calcHash(buf, fastsha256.New()), ripemd160.New()) } -func opcodeRipemd160(op *parsedOpcode, s *Script) error { - buf, err := s.dstack.PopByteArray() +func opcodeRipemd160(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() if err != nil { return err } - s.dstack.PushByteArray(calcHash(buf, ripemd160.New())) + vm.dstack.PushByteArray(calcHash(buf, ripemd160.New())) return nil } -func opcodeSha1(op *parsedOpcode, s *Script) error { - buf, err := s.dstack.PopByteArray() +func opcodeSha1(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() if err != nil { return err } - s.dstack.PushByteArray(calcHash(buf, sha1.New())) + vm.dstack.PushByteArray(calcHash(buf, sha1.New())) return nil } -func opcodeSha256(op *parsedOpcode, s *Script) error { - buf, err := s.dstack.PopByteArray() +func opcodeSha256(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() if err != nil { return err } - s.dstack.PushByteArray(calcHash(buf, fastsha256.New())) + vm.dstack.PushByteArray(calcHash(buf, fastsha256.New())) return nil } -func opcodeHash160(op *parsedOpcode, s *Script) error { - buf, err := s.dstack.PopByteArray() +func opcodeHash160(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() if err != nil { return err } - s.dstack.PushByteArray(calcHash160(buf)) + vm.dstack.PushByteArray(calcHash160(buf)) return nil } -func opcodeHash256(op *parsedOpcode, s *Script) error { - buf, err := s.dstack.PopByteArray() +func opcodeHash256(op *parsedOpcode, vm *Engine) error { + buf, err := vm.dstack.PopByteArray() if err != nil { return err } - s.dstack.PushByteArray(wire.DoubleSha256(buf)) + vm.dstack.PushByteArray(wire.DoubleSha256(buf)) return nil } -func opcodeCodeSeparator(op *parsedOpcode, s *Script) error { - s.lastcodesep = s.scriptoff +func opcodeCodeSeparator(op *parsedOpcode, vm *Engine) error { + vm.lastcodesep = vm.scriptOff return nil } -func opcodeCheckSig(op *parsedOpcode, s *Script) error { +func opcodeCheckSig(op *parsedOpcode, vm *Engine) error { - pkStr, err := s.dstack.PopByteArray() + pkStr, err := vm.dstack.PopByteArray() if err != nil { return err } - sigStr, err := s.dstack.PopByteArray() + sigStr, err := vm.dstack.PopByteArray() if err != nil { return err } @@ -1816,7 +1815,7 @@ func opcodeCheckSig(op *parsedOpcode, s *Script) error { // at least 1 byte for the below. btcec will check full length upon // parsing the signature. if len(sigStr) < 1 { - s.dstack.PushBool(false) + vm.dstack.PushBool(false) return nil } @@ -1824,40 +1823,40 @@ func opcodeCheckSig(op *parsedOpcode, s *Script) error { hashType := SigHashType(sigStr[len(sigStr)-1]) sigStr = sigStr[:len(sigStr)-1] - if err := s.checkHashTypeEncoding(hashType); err != nil { + if err := vm.checkHashTypeEncoding(hashType); err != nil { return err } - if err := s.checkSignatureEncoding(sigStr); err != nil { + if err := vm.checkSignatureEncoding(sigStr); err != nil { return err } - if err := s.checkPubKeyEncoding(pkStr); err != nil { + if err := vm.checkPubKeyEncoding(pkStr); err != nil { return err } // Get script from the last OP_CODESEPARATOR and without any subsequent // OP_CODESEPARATORs - subScript := s.subScript() + subScript := vm.subScript() // Unlikely to hit any cases here, but remove the signature from // the script if present. subScript = removeOpcodeByData(subScript, sigStr) - hash := calcScriptHash(subScript, hashType, &s.tx, s.txidx) + hash := calcScriptHash(subScript, hashType, &vm.tx, vm.txIdx) pubKey, err := btcec.ParsePubKey(pkStr, btcec.S256()) if err != nil { - s.dstack.PushBool(false) + vm.dstack.PushBool(false) return nil } var signature *btcec.Signature - if s.verifyStrictEncoding || s.verifyDERSignatures { + if vm.verifyStrictEncoding || vm.verifyDERSignatures { signature, err = btcec.ParseDERSignature(sigStr, btcec.S256()) } else { signature, err = btcec.ParseSignature(sigStr, btcec.S256()) } if err != nil { - s.dstack.PushBool(false) + vm.dstack.PushBool(false) return nil } @@ -1873,21 +1872,21 @@ func opcodeCheckSig(op *parsedOpcode, s *Script) error { signature.R, signature.S, hex.Dump(hash)) })) ok := signature.Verify(hash, pubKey) - s.dstack.PushBool(ok) + vm.dstack.PushBool(ok) return nil } -func opcodeCheckSigVerify(op *parsedOpcode, s *Script) error { - err := opcodeCheckSig(op, s) +func opcodeCheckSigVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeCheckSig(op, vm) if err == nil { - err = opcodeVerify(op, s) + err = opcodeVerify(op, vm) } return err } // parsedSigInfo houses a raw signature along with its parsed form and a flag // for whether or not it has already been parsed. It is used to prevent parsing -// the same signature multiple times when verify an multisig. +// the same signature multiple times when verify a multisig. type parsedSigInfo struct { signature []byte parsedSignature *btcec.Signature @@ -1895,8 +1894,8 @@ type parsedSigInfo struct { } // stack; sigs pubkeys -func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { - numKeys, err := s.dstack.PopInt() +func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error { + numKeys, err := vm.dstack.PopInt() if err != nil { return err } @@ -1906,21 +1905,21 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { if numPubKeys < 0 || numPubKeys > MaxPubKeysPerMultiSig { return ErrStackTooManyPubkeys } - s.numOps += numPubKeys - if s.numOps > MaxOpsPerScript { + vm.numOps += numPubKeys + if vm.numOps > MaxOpsPerScript { return ErrStackTooManyOperations } pubKeys := make([][]byte, 0, numPubKeys) for i := 0; i < numPubKeys; i++ { - pubKey, err := s.dstack.PopByteArray() + pubKey, err := vm.dstack.PopByteArray() if err != nil { return err } pubKeys = append(pubKeys, pubKey) } - numSigs, err := s.dstack.PopInt() + numSigs, err := vm.dstack.PopInt() if err != nil { return err } @@ -1937,7 +1936,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { signatures := make([]*parsedSigInfo, 0, numSignatures) for i := 0; i < numSignatures; i++ { - signature, err := s.dstack.PopByteArray() + signature, err := vm.dstack.PopByteArray() if err != nil { return err } @@ -1947,18 +1946,18 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { // bug in bitcoind means we pop one more stack value than should be // used. - dummy, err := s.dstack.PopByteArray() + dummy, err := vm.dstack.PopByteArray() if err != nil { return err } - if s.strictMultiSig && len(dummy) != 0 { + if vm.strictMultiSig && len(dummy) != 0 { return fmt.Errorf("multisig dummy argument is not zero length: %d", len(dummy)) } // Trim OP_CODESEPARATORs - script := s.subScript() + script := vm.subScript() // Remove any of the signatures that happen to be in the script. // can't sign somthing containing the signature you're making, after @@ -2002,16 +2001,16 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { // Only parse and check the signature encoding once. var parsedSig *btcec.Signature if !sigInfo.parsed { - if err := s.checkHashTypeEncoding(hashType); err != nil { + if err := vm.checkHashTypeEncoding(hashType); err != nil { return err } - if err := s.checkSignatureEncoding(signature); err != nil { + if err := vm.checkSignatureEncoding(signature); err != nil { return err } // Parse the signature. var err error - if s.verifyStrictEncoding || s.verifyDERSignatures { + if vm.verifyStrictEncoding || vm.verifyDERSignatures { parsedSig, err = btcec.ParseDERSignature(signature, btcec.S256()) } else { @@ -2033,7 +2032,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { parsedSig = sigInfo.parsedSignature } - if err := s.checkPubKeyEncoding(pubKey); err != nil { + if err := vm.checkPubKeyEncoding(pubKey); err != nil { return err } @@ -2043,7 +2042,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { continue } - hash := calcScriptHash(script, hashType, &s.tx, s.txidx) + hash := calcScriptHash(script, hashType, &vm.tx, vm.txIdx) if parsedSig.Verify(hash, parsedPubKey) { // PubKey verified, move on to the next signature. @@ -2052,14 +2051,14 @@ func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { } } - s.dstack.PushBool(success) + vm.dstack.PushBool(success) return nil } -func opcodeCheckMultiSigVerify(op *parsedOpcode, s *Script) error { - err := opcodeCheckMultiSig(op, s) +func opcodeCheckMultiSigVerify(op *parsedOpcode, vm *Engine) error { + err := opcodeCheckMultiSig(op, vm) if err == nil { - err = opcodeVerify(op, s) + err = opcodeVerify(op, vm) } return err } diff --git a/txscript/opcode_test.go b/txscript/opcode_test.go index 4b18b6a6..2efb225e 100644 --- a/txscript/opcode_test.go +++ b/txscript/opcode_test.go @@ -507,9 +507,9 @@ func TestScripts(t *testing.T) { flags = txscript.ScriptVerifyDERSignatures } mockTx.TxOut[0].PkScript = test.script - engine, err := txscript.NewScript(test.script, mockTx, 0, flags) + vm, err := txscript.NewEngine(test.script, mockTx, 0, flags) if err == nil { - err = engine.Execute() + err = vm.Execute() } if test.shouldFail != nil { @@ -4283,7 +4283,7 @@ func testOpcode(t *testing.T, test *detailedTest) { tx.TxOut[0].PkScript = test.script - engine, err := txscript.NewScript(tx.TxOut[0].PkScript, tx, 0, 0) + vm, err := txscript.NewEngine(tx.TxOut[0].PkScript, tx, 0, 0) if err != nil { if err != test.expectedReturn { t.Errorf("Error return not expected %s: %v %v", @@ -4292,8 +4292,8 @@ func testOpcode(t *testing.T, test *detailedTest) { } return } - engine.SetStack(test.before) - engine.SetAltStack(test.altbefore) + vm.SetStack(test.before) + vm.SetAltStack(test.altbefore) // test disassembly engine. // pc is at start of script 1, so check that DisasmScript matches @@ -4302,7 +4302,7 @@ func testOpcode(t *testing.T, test *detailedTest) { // disassemble. var disScript, disPC string if test.disassembly != "" { - dis0, err := engine.DisasmScript(0) + dis0, err := vm.DisasmScript(0) if err != nil { t.Errorf("%s: failed to disassemble script0: %v", test.name, err) @@ -4311,12 +4311,12 @@ func testOpcode(t *testing.T, test *detailedTest) { t.Errorf("%s: disassembly of empty script gave \"%s\"", test.name, dis0) } - disScript, err = engine.DisasmScript(1) + disScript, err = vm.DisasmScript(1) if err != nil { t.Errorf("%s: failed to disassemble script: %v", test.name, err) } - _, err = engine.DisasmScript(2) + _, err = vm.DisasmScript(2) if err != txscript.ErrStackInvalidIndex { t.Errorf("%s: got unexpected error for invalid "+ "disassembly index: \"%v\"", test.name, err) @@ -4326,7 +4326,7 @@ func testOpcode(t *testing.T, test *detailedTest) { done := false for !done { if test.disassembly != "" { - disCurPC, err := engine.DisasmPC() + disCurPC, err := vm.DisasmPC() if err != nil { t.Errorf("failed to disassemble pc for %s: %v", test.name, err) @@ -4334,7 +4334,7 @@ func testOpcode(t *testing.T, test *detailedTest) { disPC += disCurPC + "\n" } - done, err = engine.Step() + done, err = vm.Step() if err != nil { if err != test.expectedReturn { t.Errorf("Error return not expected %s: %v %v", @@ -4357,12 +4357,12 @@ func testOpcode(t *testing.T, test *detailedTest) { } } - after := engine.GetStack() + after := vm.GetStack() if !stacksEqual(after, test.after) { t.Errorf("Stacks not equal after %s:\ngot:\n%vexp:\n%v", test.name, after, test.after) } - altafter := engine.GetAltStack() + altafter := vm.GetAltStack() if !stacksEqual(altafter, test.altafter) { t.Errorf("AltStacks not equal after %s:\n got:\n%vexp:\n%v", test.name, altafter, test.altafter) @@ -4428,7 +4428,7 @@ func TestSigOps(t *testing.T) { // A basic test of GetPreciseSigOpCount for most opcodes, we do this by // running the same test for every one of the detailed tests with a fake -// sigscript. Sicne disassembly errors are always parse errors, and so are +// sigscript. Since disassembly errors are always parse errors, and so are // sigops count errors we use the same error code. // While this isn't as precise as using full transaction scripts, this gives // us coverage over a wider range of opcodes. See script_test.go for tests diff --git a/txscript/reference_test.go b/txscript/reference_test.go index c49bdffc..e158406c 100644 --- a/txscript/reference_test.go +++ b/txscript/reference_test.go @@ -201,9 +201,9 @@ func TestScriptInvalidTests(t *testing.T) { continue } tx := createSpendingTx(scriptSig, scriptPubKey) - s, err := NewScript(scriptPubKey, tx, 0, flags) + vm, err := NewEngine(scriptPubKey, tx, 0, flags) if err == nil { - if err := s.Execute(); err == nil { + if err := vm.Execute(); err == nil { t.Errorf("%s test succeeded when it "+ "should have failed\n", name) } @@ -255,12 +255,12 @@ func TestScriptValidTests(t *testing.T) { continue } tx := createSpendingTx(scriptSig, scriptPubKey) - s, err := NewScript(scriptPubKey, tx, 0, flags) + vm, err := NewEngine(scriptPubKey, tx, 0, flags) if err != nil { t.Errorf("%s failed to create script: %v", name, err) continue } - err = s.Execute() + err = vm.Execute() if err != nil { t.Errorf("%s failed to execute: %v", name, err) continue @@ -398,12 +398,12 @@ testloop: // These are meant to fail, so as soon as the first // input fails the transaction has failed. (some of the // test txns have good inputs, too.. - s, err := NewScript(pkScript, tx.MsgTx(), k, flags) + vm, err := NewEngine(pkScript, tx.MsgTx(), k, flags) if err != nil { continue testloop } - err = s.Execute() + err = vm.Execute() if err != nil { continue testloop } @@ -539,14 +539,14 @@ testloop: k, i, test) continue testloop } - s, err := NewScript(pkScript, tx.MsgTx(), k, flags) + vm, err := NewEngine(pkScript, tx.MsgTx(), k, flags) if err != nil { t.Errorf("test (%d:%v:%d) failed to create "+ "script: %v", i, test, k, err) continue } - err = s.Execute() + err = vm.Execute() if err != nil { t.Errorf("test (%d:%v:%d) failed to execute: "+ "%v", i, test, k, err) diff --git a/txscript/script.go b/txscript/script.go index 5946c067..22ec65de 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -228,28 +228,6 @@ func (t ScriptClass) String() string { return scriptClassToName[t] } -// Script is the virtual machine that executes scripts. -type Script struct { - scripts [][]parsedOpcode - scriptidx int - scriptoff int - lastcodesep int - dstack Stack // data stack - astack Stack // alt stack - tx wire.MsgTx - txidx int - condStack []int - numOps int - bip16 bool // treat execution as pay-to-script-hash - 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 comply with the DER format - verifyLowS bool // verify signatures comply with the DER format and have an S value <= halforder - savedFirstStack [][]byte // stack from first script for bip16 scripts -} - // isSmallInt returns whether or not the opcode is considered a small integer, // which is an OP_0, or OP_1 through OP_16. func isSmallInt(op *opcode) bool { @@ -367,134 +345,6 @@ func IsPushOnlyScript(script []byte) bool { return isPushOnly(pops) } -// checkHashTypeEncoding returns whether or not the passed hashtype adheres to -// the strict encoding requirements if enabled. -func (s *Script) checkHashTypeEncoding(hashType SigHashType) error { - if !s.verifyStrictEncoding { - return nil - } - - sigHashType := hashType & ^SigHashAnyOneCanPay - if sigHashType < SigHashAll || sigHashType > SigHashSingle { - return fmt.Errorf("invalid hashtype: 0x%x\n", hashType) - } - return nil -} - -// checkPubKeyEncoding returns whether or not the passed public key adheres to -// the strict encoding requirements if enabled. -func (s *Script) checkPubKeyEncoding(pubKey []byte) error { - if !s.verifyStrictEncoding { - return nil - } - - if len(pubKey) == 33 && (pubKey[0] == 0x02 || pubKey[0] == 0x03) { - // Compressed - return nil - } - if len(pubKey) == 65 && pubKey[0] == 0x04 { - // Uncompressed - return nil - } - return ErrStackInvalidPubKey -} - -// checkSignatureEncoding returns whether or not the passed signature adheres to -// the strict encoding requirements if enabled. -func (s *Script) checkSignatureEncoding(sig []byte) error { - if !s.verifyDERSignatures && !s.verifyLowS && !s.verifyStrictEncoding { - return nil - } - - if len(sig) < 8 { - // Too short - return fmt.Errorf("malformed signature: too short: %d < 8", - len(sig)) - } - if len(sig) > 72 { - // Too long - return fmt.Errorf("malformed signature: too long: %d > 72", - len(sig)) - } - if sig[0] != 0x30 { - // Wrong type - return fmt.Errorf("malformed signature: format has wrong type: 0x%x", - sig[0]) - } - if int(sig[1]) != len(sig)-2 { - // Invalid length - return fmt.Errorf("malformed signature: bad length: %d != %d", - sig[1], len(sig)-2) - } - - rLen := int(sig[3]) - - // Make sure S is inside the signature - if rLen+5 > len(sig) { - return fmt.Errorf("malformed signature: S out of bounds") - } - - sLen := int(sig[rLen+5]) - - // The length of the elements does not match - // the length of the signature - if rLen+sLen+6 != len(sig) { - return fmt.Errorf("malformed signature: invalid R length") - } - - // R elements must be integers - if sig[2] != 0x02 { - return fmt.Errorf("malformed signature: missing first integer marker") - } - - // Zero-length integers are not allowed for R - if rLen == 0 { - return fmt.Errorf("malformed signature: R length is zero") - } - - // R must not be negative - if sig[4]&0x80 != 0 { - return fmt.Errorf("malformed signature: R value is negative") - } - - // Null bytes at the start of R are not allowed, unless R would - // otherwise be interpreted as a negative number. - if rLen > 1 && sig[4] == 0x00 && sig[5]&0x80 == 0 { - return fmt.Errorf("malformed signature: invalid R value") - } - - // S elements must be integers - if sig[rLen+4] != 0x02 { - return fmt.Errorf("malformed signature: missing second integer marker") - } - - // Zero-length integers are not allowed for S - if sLen == 0 { - return fmt.Errorf("malformed signature: S length is zero") - } - - // S must not be negative - if sig[rLen+6]&0x80 != 0 { - return fmt.Errorf("malformed signature: S value is negative") - } - - // Null bytes at the start of S are not allowed, unless S would - // otherwise be interpreted as a negative number. - if sLen > 1 && sig[rLen+6] == 0x00 && sig[rLen+7]&0x80 == 0 { - return fmt.Errorf("malformed signature: invalid S value") - } - - // Verify the S value is <= halforder. - if s.verifyLowS { - sValue := new(big.Int).SetBytes(sig[rLen+6 : rLen+6+sLen]) - if sValue.Cmp(halfOrder) > 0 { - return ErrStackInvalidLowSSignature - } - } - - return nil -} - // canonicalPush returns true if the object is either not a push instruction // or the push instruction contained wherein is matches the canonical form // or using the smallest instruction to do the job. False otherwise. @@ -635,350 +485,6 @@ func unparseScript(pops []parsedOpcode) ([]byte, error) { return script, nil } -// ScriptFlags is a bitmask defining additional operations or -// tests that will be done when executing a Script. -type ScriptFlags uint32 - -const ( - // ScriptBip16 defines whether the bip16 threshhold has passed and thus - // pay-to-script hash transactions will be fully validated. - ScriptBip16 ScriptFlags = 1 << iota - - // ScriptStrictMultiSig defines whether to verify the stack item - // used by CHECKMULTISIG is zero length. - ScriptStrictMultiSig - - // ScriptDiscourageUpgradableNops defines whether to verify that - // NOP1 through NOP10 are reserved for future soft-fork upgrades. This - // flag must not be used for consensus critical code nor applied to - // blocks as this flag is only for stricter standard transaction - // checks. This flag is only applied when the above opcodes are - // 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 - - // ScriptVerifyLowS defines that signtures are required to comply with - // the DER format and whose S value is <= order / 2. This is rule 5 - // of BIP0062. - ScriptVerifyLowS - - // ScriptVerifyMinimalData defines that signatures must use the smallest - // push operator. This is both rules 3 and 4 of BIP0062. - ScriptVerifyMinimalData - - // ScriptVerifySigPushOnly defines that signature scripts must contain - // only pushed data. This is rule 2 of BIP0062. - ScriptVerifySigPushOnly - - // ScriptVerifyStrictEncoding defines that signature scripts and - // public keys must follow the strict encoding requirements. - ScriptVerifyStrictEncoding - - // StandardVerifyFlags are the script flags which are used when - // executing transaction scripts to enforce additional checks which - // are required for the script to be considered standard. These checks - // help reduce issues related to transaction malleability as well as - // allow pay-to-script hash transactions. Note these flags are - // different than what is required for the consensus rules in that they - // are more strict. - // - // TODO: These flags do not belong here. These flags belong in a - // policy package. - StandardVerifyFlags = ScriptBip16 | - ScriptVerifyDERSignatures | - ScriptVerifyStrictEncoding | - ScriptVerifyMinimalData | - ScriptStrictMultiSig | - ScriptDiscourageUpgradableNops | - ScriptVerifyCleanStack -) - -// NewScript returns a new script engine for the provided tx and input idx with -// with a pubkeyscript scriptPubKey. If bip16 is true then it will be treated as -// if the bip16 threshhold has passed and thus pay-to-script hash transactions -// will be fully validated. -func NewScript(scriptPubKey []byte, tx *wire.MsgTx, txidx int, flags ScriptFlags) (*Script, error) { - if txidx < 0 || txidx >= len(tx.TxIn) { - return nil, ErrInvalidIndex - } - scriptSig := tx.TxIn[txidx].SignatureScript - - var m Script - if flags&ScriptVerifySigPushOnly == ScriptVerifySigPushOnly && !IsPushOnlyScript(scriptSig) { - return nil, ErrStackNonPushOnly - } - - scripts := [][]byte{scriptSig, scriptPubKey} - m.scripts = make([][]parsedOpcode, len(scripts)) - for i, scr := range scripts { - if len(scr) > maxScriptSize { - return nil, ErrStackLongScript - } - var err error - m.scripts[i], err = parseScript(scr) - if err != nil { - return nil, err - } - - // If the first scripts(s) are empty, must start on later ones. - if i == 0 && len(scr) == 0 { - // This could end up seeing an invalid initial pc if - // all scripts were empty. However, that is an invalid - // case and should fail. - m.scriptidx = i + 1 - } - } - - // Parse flags. - 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]) { - return nil, ErrStackP2SHNonPushOnly - } - m.bip16 = true - } - if flags&ScriptStrictMultiSig == ScriptStrictMultiSig { - m.strictMultiSig = true - } - if flags&ScriptDiscourageUpgradableNops == ScriptDiscourageUpgradableNops { - m.discourageUpgradableNops = true - } - if flags&ScriptVerifyStrictEncoding == ScriptVerifyStrictEncoding { - m.verifyStrictEncoding = true - } - if flags&ScriptVerifyDERSignatures == ScriptVerifyDERSignatures { - m.verifyDERSignatures = true - } - if flags&ScriptVerifyMinimalData == ScriptVerifyMinimalData { - m.dstack.verifyMinimalData = true - m.astack.verifyMinimalData = true - } - if flags&ScriptVerifyCleanStack == ScriptVerifyCleanStack { - if flags&ScriptBip16 != ScriptBip16 { - return nil, ErrInvalidFlags - } - m.verifyCleanStack = true - } - if flags&ScriptVerifyLowS == ScriptVerifyLowS { - m.verifyLowS = true - } - - m.tx = *tx - m.txidx = txidx - m.condStack = []int{OpCondTrue} - - return &m, nil -} - -// Execute will execute all script in the script engine and return either nil -// for successful validation or an error if one occurred. -func (s *Script) Execute() (err error) { - done := false - for done != true { - log.Tracef("%v", newLogClosure(func() string { - dis, err := s.DisasmPC() - if err != nil { - return fmt.Sprintf("stepping (%v)", err) - } - return fmt.Sprintf("stepping %v", dis) - })) - - done, err = s.Step() - if err != nil { - return err - } - log.Tracef("%v", newLogClosure(func() string { - var dstr, astr string - - // if we're tracing, dump the stacks. - if s.dstack.Depth() != 0 { - dstr = "Stack:\n" + s.dstack.String() - } - if s.astack.Depth() != 0 { - astr = "AltStack:\n" + s.astack.String() - } - - return dstr + astr - })) - } - - 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(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 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 { - return err - } - if v == false { - // log interesting data. - log.Tracef("%v", newLogClosure(func() string { - dis0, _ := s.DisasmScript(0) - dis1, _ := s.DisasmScript(1) - return fmt.Sprintf("scripts failed: script0: %s\n"+ - "script1: %s", dis0, dis1) - })) - return ErrStackScriptFailed - } - return nil -} - -// Step will execute the next instruction and move the program counter to the -// next opcode in the script, or the next script if the curent has ended. Step -// will return true in the case that the last opcode was successfully executed. -// if an error is returned then the result of calling Step or any other method -// is undefined. -func (s *Script) Step() (done bool, err error) { - // verify that it is pointing to a valid script address - err = s.validPC() - if err != nil { - return true, err - } - opcode := s.scripts[s.scriptidx][s.scriptoff] - - err = opcode.exec(s) - if err != nil { - return true, err - } - - if s.dstack.Depth()+s.astack.Depth() > maxStackSize { - return false, ErrStackOverflow - } - - // prepare for next instruction - s.scriptoff++ - if s.scriptoff >= len(s.scripts[s.scriptidx]) { - // Illegal to have an `if' that straddles two scripts. - if err == nil && len(s.condStack) != 1 { - return false, ErrStackMissingEndif - } - - // alt stack doesn't persist. - _ = s.astack.DropN(s.astack.Depth()) - - s.numOps = 0 // number of ops is per script. - s.scriptoff = 0 - if s.scriptidx == 0 && s.bip16 { - s.scriptidx++ - s.savedFirstStack = s.GetStack() - } else if s.scriptidx == 1 && s.bip16 { - // Put us past the end for CheckErrorCondition() - 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(false) - if err != nil { - return false, err - } - - script := s.savedFirstStack[len(s.savedFirstStack)-1] - pops, err := parseScript(script) - if err != nil { - return false, err - } - s.scripts = append(s.scripts, pops) - // Set stack to be the stack from first script - // minus the script itself - s.SetStack(s.savedFirstStack[:len(s.savedFirstStack)-1]) - } else { - s.scriptidx++ - } - // there are zero length scripts in the wild - if s.scriptidx < len(s.scripts) && s.scriptoff >= len(s.scripts[s.scriptidx]) { - s.scriptidx++ - } - s.lastcodesep = 0 - if s.scriptidx >= len(s.scripts) { - return true, nil - } - } - return false, nil -} - -// curPC returns either the current script and offset, or an error if the -// position isn't valid. -func (s *Script) curPC() (script int, off int, err error) { - err = s.validPC() - if err != nil { - return 0, 0, err - } - return s.scriptidx, s.scriptoff, nil -} - -// validPC returns an error if the current script position is valid for -// execution, nil otherwise. -func (s *Script) validPC() error { - if s.scriptidx >= len(s.scripts) { - return fmt.Errorf("Past input scripts %v:%v %v:xxxx", s.scriptidx, s.scriptoff, len(s.scripts)) - } - if s.scriptoff >= len(s.scripts[s.scriptidx]) { - return fmt.Errorf("Past input scripts %v:%v %v:%04d", s.scriptidx, s.scriptoff, s.scriptidx, len(s.scripts[s.scriptidx])) - } - return nil -} - -// 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) (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" - } - return disstr, nil -} - -// DisasmPC returns the string for the disassembly of the opcode that will be -// next to execute when Step() is called. -func (s *Script) DisasmPC() (string, error) { - scriptidx, scriptoff, err := s.curPC() - if err != nil { - return "", err - } - return s.disasm(scriptidx, scriptoff), nil -} - -// disasm is a helper member to produce the output for DisasmPC and -// DisasmScript. It produces the opcode prefixed by the program counter at the -// provided position in the script. it does no error checking and leaves that -// to the caller to provide a valid offse. -func (s *Script) disasm(scriptidx int, scriptoff int) string { - return fmt.Sprintf("%02x:%04x: %s", scriptidx, scriptoff, - s.scripts[scriptidx][scriptoff].print(false)) -} - -// subScript will return the script since the last OP_CODESEPARATOR -func (s *Script) subScript() []parsedOpcode { - return s.scripts[s.scriptidx][s.lastcodesep:] -} - // removeOpcode will remove any opcode matching ``opcode'' from the opcode // stream in pkscript func removeOpcode(pkscript []parsedOpcode, opcode byte) []parsedOpcode { @@ -1110,52 +616,6 @@ func calcScriptHash(script []parsedOpcode, hashType SigHashType, tx *wire.MsgTx, return wire.DoubleSha256(wbuf.Bytes()) } -// getStack returns the contents of stack as a byte array bottom up -func getStack(stack *Stack) [][]byte { - array := make([][]byte, stack.Depth()) - for i := range array { - // PeekByteArry can't fail due to overflow, already checked - array[len(array)-i-1], _ = - stack.PeekByteArray(i) - } - return array -} - -// setStack sets the stack to the contents of the array where the last item in -// the array is the top item in the stack. -func setStack(stack *Stack, data [][]byte) { - // This can not error. Only errors are for invalid arguments. - _ = stack.DropN(stack.Depth()) - - for i := range data { - stack.PushByteArray(data[i]) - } -} - -// GetStack returns the contents of the primary stack as an array. where the -// last item in the array is the top of the stack. -func (s *Script) GetStack() [][]byte { - return getStack(&s.dstack) -} - -// SetStack sets the contents of the primary stack to the contents of the -// provided array where the last item in the array will be the top of the stack. -func (s *Script) SetStack(data [][]byte) { - setStack(&s.dstack, data) -} - -// GetAltStack returns the contents of the primary stack as an array. where the -// last item in the array is the top of the stack. -func (s *Script) GetAltStack() [][]byte { - return getStack(&s.astack) -} - -// SetAltStack sets the contents of the primary stack to the contents of the -// provided array where the last item in the array will be the top of the stack. -func (s *Script) SetAltStack(data [][]byte) { - setStack(&s.astack, data) -} - // GetSigOpCount provides a quick count of the number of signature operations // in a script. a CHECKSIG operations counts for 1, and a CHECK_MULTISIG for 20. // If the script fails to parse, then the count up to the point of failure is diff --git a/txscript/script_test.go b/txscript/script_test.go index 2a332f42..9bc60ad1 100644 --- a/txscript/script_test.go +++ b/txscript/script_test.go @@ -1636,7 +1636,7 @@ func testTx(t *testing.T, test txTest) { if test.strictSigs { flags |= txscript.ScriptVerifyDERSignatures } - engine, err := txscript.NewScript(test.pkScript, test.tx, test.idx, + vm, err := txscript.NewEngine(test.pkScript, test.tx, test.idx, flags) if err != nil { if err != test.parseErr { @@ -1650,7 +1650,7 @@ func testTx(t *testing.T, test txTest) { test.parseErr) } - err = engine.Execute() + err = vm.Execute() if err != nil { // failed means no specified error if test.shouldFail == true { @@ -2418,167 +2418,6 @@ func TestIsPayToScriptHash(t *testing.T) { } } -// This test sets the pc to a deliberately bad result then confirms that Step() -// and Disasm fail correctly. -func TestBadPC(t *testing.T) { - t.Parallel() - - type pcTest struct { - script, off int - } - pcTests := []pcTest{ - { - script: 2, - off: 0, - }, - { - script: 0, - off: 2, - }, - } - // 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 _, test := range pcTests { - engine, err := txscript.NewScript(pkScript, tx, 0, 0) - if err != nil { - t.Errorf("Failed to create script: %v", err) - } - - // set to after all scripts - engine.TstSetPC(test.script, test.off) - - _, err = engine.Step() - if err == nil { - t.Errorf("Step with invalid pc (%v) succeeds!", test) - continue - } - - _, err = engine.DisasmPC() - if err == nil { - t.Errorf("DisasmPC with invalid pc (%v) succeeds!", - test) - } - } -} - -// Most codepaths in CheckErrorCondition() are testd elsewhere, this tests -// the execute early test. -func TestCheckErrorCondition(t *testing.T) { - t.Parallel() - - // 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{}, - Sequence: 4294967295, - }, - }, - TxOut: []*wire.TxOut{ - { - Value: 1000000000, - PkScript: []byte{}, - }, - }, - LockTime: 0, - } - pkScript := []byte{ - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_NOP, - txscript.OP_TRUE, - } - - engine, err := txscript.NewScript(pkScript, tx, 0, 0) - if err != nil { - t.Errorf("failed to create script: %v", err) - } - - for i := 0; i < len(pkScript)-1; i++ { - done, err := engine.Step() - if err != nil { - t.Errorf("failed to step %dth time: %v", i, err) - return - } - if done { - t.Errorf("finshed early on %dth time", i) - return - } - - err = engine.CheckErrorCondition(false) - if err != txscript.ErrStackScriptUnfinished { - t.Errorf("got unexepected error %v on %dth iteration", - err, i) - return - } - } - done, err := engine.Step() - if err != nil { - t.Errorf("final step failed %v", err) - return - } - if !done { - t.Errorf("final step isn't done!") - return - } - - err = engine.CheckErrorCondition(false) - if err != nil { - t.Errorf("unexpected error %v on final check", err) - } -} - type TstSigScript struct { name string inputs []TstInput @@ -2890,14 +2729,14 @@ nexttest: // Validate tx input scripts scriptFlags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures for j := range tx.TxIn { - engine, err := txscript.NewScript(SigScriptTests[i]. + vm, err := txscript.NewEngine(SigScriptTests[i]. inputs[j].txout.PkScript, tx, j, scriptFlags) if err != nil { t.Errorf("cannot create script vm for test %v: %v", SigScriptTests[i].name, err) continue nexttest } - err = engine.Execute() + err = vm.Execute() if (err == nil) != SigScriptTests[i].inputs[j].inputValidates { if err == nil { t.Errorf("passed test '%v' validation incorrectly: %v", @@ -3314,14 +3153,14 @@ func signAndCheck(msg string, tx *wire.MsgTx, idx int, pkScript []byte, func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error { tx.TxIn[idx].SignatureScript = sigScript - engine, err := txscript.NewScript(pkScript, tx, idx, + vm, err := txscript.NewEngine(pkScript, tx, idx, txscript.ScriptBip16|txscript.ScriptVerifyDERSignatures) if err != nil { return fmt.Errorf("failed to make script engine for %s: %v", msg, err) } - err = engine.Execute() + err = vm.Execute() if err != nil { return fmt.Errorf("invalid script signature for %s: %v", msg, err) @@ -4851,7 +4690,7 @@ func TestInvalidFlagCombinations(t *testing.T) { pkScript := []byte{txscript.OP_NOP} for i, test := range tests { - _, err := txscript.NewScript(pkScript, tx, 0, test) + _, err := txscript.NewEngine(pkScript, tx, 0, test) if err != txscript.ErrInvalidFlags { t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+ "error: %v", i, err)