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)