diff --git a/app/appmessage/domainconverters.go b/app/appmessage/domainconverters.go index 6d91961dc..460dc9dcb 100644 --- a/app/appmessage/domainconverters.go +++ b/app/appmessage/domainconverters.go @@ -77,7 +77,7 @@ func DomainTransactionToMsgTx(domainTransaction *externalapi.DomainTransaction) LockTime: domainTransaction.LockTime, SubnetworkID: domainTransaction.SubnetworkID, Gas: domainTransaction.Gas, - PayloadHash: &domainTransaction.PayloadHash, + PayloadHash: domainTransaction.PayloadHash, Payload: domainTransaction.Payload, } } @@ -121,7 +121,7 @@ func MsgTxToDomainTransaction(msgTx *MsgTx) *externalapi.DomainTransaction { LockTime: msgTx.LockTime, SubnetworkID: msgTx.SubnetworkID, Gas: msgTx.Gas, - PayloadHash: *msgTx.PayloadHash, + PayloadHash: msgTx.PayloadHash, Payload: msgTx.Payload, } } diff --git a/app/appmessage/p2p_msgtx.go b/app/appmessage/p2p_msgtx.go index f8691c66b..343e5cf58 100644 --- a/app/appmessage/p2p_msgtx.go +++ b/app/appmessage/p2p_msgtx.go @@ -6,9 +6,10 @@ package appmessage import ( "encoding/binary" - "math" "strconv" + "github.com/kaspanet/kaspad/domain/consensus/utils/constants" + "github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization" "github.com/kaspanet/kaspad/domain/consensus/utils/hashes" @@ -19,38 +20,10 @@ import ( ) const ( - // TxVersion is the current latest supported transaction version. - TxVersion = 1 - - // MaxTxInSequenceNum is the maximum sequence number the sequence field - // of a transaction input can be. - MaxTxInSequenceNum uint64 = math.MaxUint64 - // MaxPrevOutIndex is the maximum index the index field of a previous // outpoint can be. MaxPrevOutIndex uint32 = 0xffffffff - // SequenceLockTimeDisabled is a flag that if set on a transaction - // input's sequence number, the sequence number will not be interpreted - // as a relative locktime. - SequenceLockTimeDisabled = 1 << 31 - - // SequenceLockTimeIsSeconds is a flag that if set on a transaction - // input's sequence number, the relative locktime has units of 512 - // seconds. - SequenceLockTimeIsSeconds = 1 << 22 - - // SequenceLockTimeMask is a mask that extracts the relative locktime - // when masked against the transaction input sequence number. - SequenceLockTimeMask = 0x0000ffff - - // SequenceLockTimeGranularity is the defined time based granularity - // for milliseconds-based relative time locks. When converting from milliseconds - // to a sequence number, the value is right shifted by this amount, - // therefore the granularity of relative time locks in 524288 or 2^19 - // seconds. Enforced relative lock times are multiples of 524288 milliseconds. - SequenceLockTimeGranularity = 19 - // defaultTxInOutAlloc is the default size used for the backing array for // transaction inputs and outputs. The array will dynamically grow as needed, // but this figure is intended to provide enough space for the number of @@ -130,7 +103,7 @@ func NewTxIn(prevOut *Outpoint, signatureScript []byte) *TxIn { return &TxIn{ PreviousOutpoint: *prevOut, SignatureScript: signatureScript, - Sequence: MaxTxInSequenceNum, + Sequence: constants.MaxTxInSequenceNum, } } @@ -163,7 +136,7 @@ type MsgTx struct { LockTime uint64 SubnetworkID externalapi.DomainSubnetworkID Gas uint64 - PayloadHash *externalapi.DomainHash + PayloadHash externalapi.DomainHash Payload []byte } @@ -310,9 +283,9 @@ func newMsgTx(version int32, txIn []*TxIn, txOut []*TxOut, subnetworkID *externa txOut = make([]*TxOut, 0, defaultTxInOutAlloc) } - var payloadHash *externalapi.DomainHash + var payloadHash externalapi.DomainHash if *subnetworkID != subnetworks.SubnetworkIDNative { - payloadHash = hashes.HashData(payload) + payloadHash = *hashes.HashData(payload) } return &MsgTx{ diff --git a/domain/consensus/processes/transactionvalidator/transaction_in_context.go b/domain/consensus/processes/transactionvalidator/transaction_in_context.go index 8121ce600..3f7f9a5bb 100644 --- a/domain/consensus/processes/transactionvalidator/transaction_in_context.go +++ b/domain/consensus/processes/transactionvalidator/transaction_in_context.go @@ -1,7 +1,6 @@ package transactionvalidator import ( - "github.com/kaspanet/kaspad/app/appmessage" "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/ruleerrors" "github.com/kaspanet/kaspad/domain/consensus/utils/constants" @@ -248,14 +247,14 @@ func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries( // mask in order to obtain the time lock delta required before // this input can be spent. sequenceNum := input.Sequence - relativeLock := int64(sequenceNum & appmessage.SequenceLockTimeMask) + relativeLock := int64(sequenceNum & constants.SequenceLockTimeMask) switch { // Relative time locks are disabled for this input, so we can // skip any further calculation. - case sequenceNum&appmessage.SequenceLockTimeDisabled == appmessage.SequenceLockTimeDisabled: + case sequenceNum&constants.SequenceLockTimeDisabled == constants.SequenceLockTimeDisabled: continue - case sequenceNum&appmessage.SequenceLockTimeIsSeconds == appmessage.SequenceLockTimeIsSeconds: + case sequenceNum&constants.SequenceLockTimeIsSeconds == constants.SequenceLockTimeIsSeconds: // This input requires a relative time lock expressed // in seconds before it can be spent. Therefore, we // need to query for the block prior to the one in @@ -290,11 +289,11 @@ func (v *transactionValidator) calcTxSequenceLockFromReferencedUTXOEntries( } // Time based relative time-locks have a time granularity of - // appmessage.SequenceLockTimeGranularity, so we shift left by this + // constants.SequenceLockTimeGranularity, so we shift left by this // amount to convert to the proper relative time-lock. We also // subtract one from the relative lock to maintain the original // lockTime semantics. - timeLockMilliseconds := (relativeLock << appmessage.SequenceLockTimeGranularity) - 1 + timeLockMilliseconds := (relativeLock << constants.SequenceLockTimeGranularity) - 1 timeLock := medianTime + timeLockMilliseconds if timeLock > sequenceLock.Milliseconds { sequenceLock.Milliseconds = timeLock diff --git a/domain/consensus/utils/constants/constants.go b/domain/consensus/utils/constants/constants.go index 02e83dc45..87cf247ee 100644 --- a/domain/consensus/utils/constants/constants.go +++ b/domain/consensus/utils/constants/constants.go @@ -1,5 +1,7 @@ package constants +import "math" + const ( // BlockVersion represents the current version of blocks mined and the maximum block version // this node is able to validate @@ -46,4 +48,29 @@ const ( // CoinbasePayloadScriptPublicKeyMaxLength is the maximum allowed script public key in the coinbase's payload CoinbasePayloadScriptPublicKeyMaxLength = 150 + + // MaxTxInSequenceNum is the maximum sequence number the sequence field + // of a transaction input can be. + MaxTxInSequenceNum uint64 = math.MaxUint64 + + // SequenceLockTimeDisabled is a flag that if set on a transaction + // input's sequence number, the sequence number will not be interpreted + // as a relative locktime. + SequenceLockTimeDisabled = 1 << 31 + + // SequenceLockTimeIsSeconds is a flag that if set on a transaction + // input's sequence number, the relative locktime has units of 512 + // seconds. + SequenceLockTimeIsSeconds = 1 << 22 + + // SequenceLockTimeMask is a mask that extracts the relative locktime + // when masked against the transaction input sequence number. + SequenceLockTimeMask = 0x0000ffff + + // SequenceLockTimeGranularity is the defined time based granularity + // for milliseconds-based relative time locks. When converting from milliseconds + // to a sequence number, the value is right shifted by this amount, + // therefore the granularity of relative time locks in 524288 or 2^19 + // seconds. Enforced relative lock times are multiples of 524288 milliseconds. + SequenceLockTimeGranularity = 19 ) diff --git a/domain/consensus/utils/txscript/engine_test.go b/domain/consensus/utils/txscript/engine_test.go new file mode 100644 index 000000000..46f1dafc6 --- /dev/null +++ b/domain/consensus/utils/txscript/engine_test.go @@ -0,0 +1,338 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "testing" + + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" +) + +// TestBadPC sets the pc to a deliberately bad result then confirms that Step() +// and Disasm fail correctly. +func TestBadPC(t *testing.T) { + t.Parallel() + + tests := []struct { + script, off int + }{ + {script: 2, off: 0}, + {script: 0, off: 2}, + } + + // tx with almost empty scripts. + inputs := []*externalapi.DomainTransactionInput{ + { + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID([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: mustParseShortForm(""), + Sequence: 4294967295, + }, + } + outputs := []*externalapi.DomainTransactionOutput{{ + Value: 1000000000, + ScriptPublicKey: nil, + }} + tx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: inputs, + Outputs: outputs, + } + scriptPubKey := mustParseShortForm("NOP") + + for _, test := range tests { + vm, err := NewEngine(scriptPubKey, tx, 0, 0, nil) + if err != nil { + t.Errorf("Failed to create script: %v", err) + } + + // set to after all scripts + vm.scriptIdx = test.script + vm.scriptOff = 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) + } + } +} + +func TestCheckErrorCondition(t *testing.T) { + tests := []struct { + script string + finalScript bool + stepCount int + expectedErr error + }{ + {"OP_1", true, 1, nil}, + {"NOP", true, 0, scriptError(ErrScriptUnfinished, "")}, + {"NOP", true, 1, scriptError(ErrEmptyStack, "")}, + {"OP_1 OP_1", true, 2, scriptError(ErrCleanStack, "")}, + {"OP_0", true, 1, scriptError(ErrEvalFalse, "")}, + } + + for i, test := range tests { + func() { + inputs := []*externalapi.DomainTransactionInput{{ + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID([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: nil, + Sequence: 4294967295, + }} + outputs := []*externalapi.DomainTransactionOutput{{ + Value: 1000000000, + ScriptPublicKey: nil, + }} + tx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: inputs, + Outputs: outputs, + } + + scriptPubKey := mustParseShortForm(test.script) + + vm, err := NewEngine(scriptPubKey, tx, 0, 0, nil) + if err != nil { + t.Errorf("TestCheckErrorCondition: %d: failed to create script: %v", i, err) + } + + for j := 0; j < test.stepCount; j++ { + _, err = vm.Step() + if err != nil { + t.Errorf("TestCheckErrorCondition: %d: failed to execute step No. %d: %v", i, j+1, err) + return + } + + if j != test.stepCount-1 { + err = vm.CheckErrorCondition(false) + if !IsErrorCode(err, ErrScriptUnfinished) { + t.Fatalf("TestCheckErrorCondition: %d: got unexepected error %v on %dth iteration", + i, err, j) + return + } + } + } + + err = vm.CheckErrorCondition(test.finalScript) + if e := checkScriptError(err, test.expectedErr); e != nil { + t.Errorf("TestCheckErrorCondition: %d: %s", i, e) + } + }() + } +} + +// TestCheckPubKeyEncoding ensures the internal checkPubKeyEncoding function +// works as expected. +func TestCheckPubKeyEncoding(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + key []byte + isValid bool + }{ + { + name: "uncompressed ok", + key: hexToBytes("0411db93e1dcdb8a016b49840f8c53bc1eb68" + + "a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf" + + "9744464f82e160bfa9b8b64f9d4c03f999b8643f656b" + + "412a3"), + isValid: true, + }, + { + name: "compressed ok", + key: hexToBytes("02ce0b14fb842b1ba549fdd675c98075f12e9" + + "c510f8ef52bd021a9a1f4809d3b4d"), + isValid: true, + }, + { + name: "compressed ok", + key: hexToBytes("032689c7c2dab13309fb143e0e8fe39634252" + + "1887e976690b6b47f5b2a4b7d448e"), + isValid: true, + }, + { + name: "hybrid", + key: hexToBytes("0679be667ef9dcbbac55a06295ce870b07029" + + "bfcdb2dce28d959f2815b16f81798483ada7726a3c46" + + "55da4fbfc0e1108a8fd17b448a68554199c47d08ffb1" + + "0d4b8"), + isValid: false, + }, + { + name: "empty", + key: nil, + isValid: false, + }, + } + + vm := Engine{} + for _, test := range tests { + err := vm.checkPubKeyEncoding(test.key) + if err != nil && test.isValid { + t.Errorf("checkSignatureLength test '%s' failed "+ + "when it should have succeeded: %v", test.name, + err) + } else if err == nil && !test.isValid { + t.Errorf("checkSignatureEncooding test '%s' succeeded "+ + "when it should have failed", test.name) + } + } + +} + +func TestDisasmPC(t *testing.T) { + t.Parallel() + + // tx with almost empty scripts. + inputs := []*externalapi.DomainTransactionInput{{ + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID([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: mustParseShortForm("OP_2"), + Sequence: 4294967295, + }} + outputs := []*externalapi.DomainTransactionOutput{{ + Value: 1000000000, + ScriptPublicKey: nil, + }} + tx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: inputs, + Outputs: outputs, + } + + scriptPubKey := mustParseShortForm("OP_DROP NOP TRUE") + + vm, err := NewEngine(scriptPubKey, tx, 0, 0, nil) + if err != nil { + t.Fatalf("failed to create script: %v", err) + } + + tests := []struct { + expected string + expectedErr error + }{ + {"00:0000: OP_2", nil}, + {"01:0000: OP_DROP", nil}, + {"01:0001: OP_NOP", nil}, + {"01:0002: OP_1", nil}, + {"", scriptError(ErrInvalidProgramCounter, "")}, + } + + for i, test := range tests { + actual, err := vm.DisasmPC() + if e := checkScriptError(err, test.expectedErr); e != nil { + t.Errorf("TestDisasmPC: %d: %s", i, e) + } + + if actual != test.expected { + t.Errorf("TestDisasmPC: %d: expected: '%s'. Got: '%s'", i, test.expected, actual) + } + + // ignore results from vm.Step() to keep going even when no opcodes left, to hit error case + _, _ = vm.Step() + } +} + +func TestDisasmScript(t *testing.T) { + t.Parallel() + + // tx with almost empty scripts. + inputs := []*externalapi.DomainTransactionInput{{ + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID([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: mustParseShortForm("OP_2"), + Sequence: 4294967295, + }} + outputs := []*externalapi.DomainTransactionOutput{{ + Value: 1000000000, + ScriptPublicKey: nil, + }} + tx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: inputs, + Outputs: outputs, + } + + scriptPubKey := mustParseShortForm("OP_DROP NOP TRUE") + + vm, err := NewEngine(scriptPubKey, tx, 0, 0, nil) + if err != nil { + t.Fatalf("failed to create script: %v", err) + } + + tests := []struct { + index int + expected string + expectedErr error + }{ + {-1, "", scriptError(ErrInvalidIndex, "")}, + {0, "00:0000: OP_2\n", nil}, + {1, "01:0000: OP_DROP\n01:0001: OP_NOP\n01:0002: OP_1\n", nil}, + {2, "", scriptError(ErrInvalidIndex, "")}, + } + + for _, test := range tests { + actual, err := vm.DisasmScript(test.index) + if e := checkScriptError(err, test.expectedErr); e != nil { + t.Errorf("TestDisasmScript: %d: %s", test.index, e) + } + + if actual != test.expected { + t.Errorf("TestDisasmScript: %d: expected: '%s'. Got: '%s'", test.index, test.expected, actual) + } + } +} diff --git a/domain/consensus/utils/txscript/opcode.go b/domain/consensus/utils/txscript/opcode.go index 784bf13d1..f87bb87b0 100644 --- a/domain/consensus/utils/txscript/opcode.go +++ b/domain/consensus/utils/txscript/opcode.go @@ -12,12 +12,12 @@ import ( "fmt" "hash" + "github.com/kaspanet/kaspad/domain/consensus/utils/constants" + "github.com/kaspanet/go-secp256k1" "github.com/kaspanet/kaspad/domain/consensus/utils/hashes" "golang.org/x/crypto/ripemd160" - - "github.com/kaspanet/kaspad/app/appmessage" ) // An opcode defines the information related to a txscript opcode. opfunc, if @@ -1149,7 +1149,7 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { // The lock time feature can also be disabled, thereby bypassing // OP_CHECKLOCKTIMEVERIFY, if every transaction input has been finalized by - // setting its sequence to the maximum value (appmessage.MaxTxInSequenceNum). This + // setting its sequence to the maximum value (constants.MaxTxInSequenceNum). This // condition would result in the transaction being allowed into the blockDAG // making the opcode ineffective. // @@ -1161,7 +1161,7 @@ func opcodeCheckLockTimeVerify(op *parsedOpcode, vm *Engine) error { // NOTE: This implies that even if the transaction is not finalized due to // another input being unlocked, the opcode execution will still fail when the // input being used by the opcode is locked. - if vm.tx.Inputs[vm.txIdx].Sequence == appmessage.MaxTxInSequenceNum { + if vm.tx.Inputs[vm.txIdx].Sequence == constants.MaxTxInSequenceNum { return scriptError(ErrUnsatisfiedLockTime, "transaction input is finalized") } @@ -1205,7 +1205,7 @@ func opcodeCheckSequenceVerify(op *parsedOpcode, vm *Engine) error { // To provide for future soft-fork extensibility, if the // operand has the disabled lock-time flag set, // CHECKSEQUENCEVERIFY behaves as a NOP. - if sequence&uint64(appmessage.SequenceLockTimeDisabled) != 0 { + if sequence&uint64(constants.SequenceLockTimeDisabled) != 0 { return nil } @@ -1214,17 +1214,17 @@ func opcodeCheckSequenceVerify(op *parsedOpcode, vm *Engine) error { // number does not have this bit set prevents using this property // to get around a CHECKSEQUENCEVERIFY check. txSequence := vm.tx.Inputs[vm.txIdx].Sequence - if txSequence&appmessage.SequenceLockTimeDisabled != 0 { + if txSequence&constants.SequenceLockTimeDisabled != 0 { str := fmt.Sprintf("transaction sequence has sequence "+ "locktime disabled bit set: 0x%x", txSequence) return scriptError(ErrUnsatisfiedLockTime, str) } // Mask off non-consensus bits before doing comparisons. - lockTimeMask := uint64(appmessage.SequenceLockTimeIsSeconds | - appmessage.SequenceLockTimeMask) + lockTimeMask := uint64(constants.SequenceLockTimeIsSeconds | + constants.SequenceLockTimeMask) return verifyLockTime(txSequence&lockTimeMask, - appmessage.SequenceLockTimeIsSeconds, sequence&lockTimeMask) + constants.SequenceLockTimeIsSeconds, sequence&lockTimeMask) } // opcodeToAltStack removes the top item from the main data stack and pushes it diff --git a/domain/consensus/utils/txscript/reference_test.go b/domain/consensus/utils/txscript/reference_test.go new file mode 100644 index 000000000..1ff595b32 --- /dev/null +++ b/domain/consensus/utils/txscript/reference_test.go @@ -0,0 +1,398 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io/ioutil" + "strconv" + "strings" + "testing" + + "github.com/kaspanet/kaspad/domain/consensus/utils/constants" + + "github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization" + + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/kaspanet/kaspad/infrastructure/logger" + "github.com/pkg/errors" +) + +// scriptTestName returns a descriptive test name for the given reference script +// test data. +func scriptTestName(test []interface{}) (string, error) { + // The test must consist of a signature script, public key script, flags, + // and expected error. Finally, it may optionally contain a comment. + if len(test) < 4 || len(test) > 5 { + return "", errors.Errorf("invalid test length %d", len(test)) + } + + // Use the comment for the test name if one is specified, otherwise, + // construct the name based on the signature script, public key script, + // and flags. + var name string + if len(test) == 5 { + name = fmt.Sprintf("test (%s)", test[4]) + } else { + name = fmt.Sprintf("test ([%s, %s, %s])", test[0], + test[1], test[2]) + } + return name, nil +} + +// parse hex string into a []byte. +func parseHex(tok string) ([]byte, error) { + if !strings.HasPrefix(tok, "0x") { + return nil, errors.New("not a hex number") + } + return hex.DecodeString(tok[2:]) +} + +// shortFormOps holds a map of opcode names to values for use in short form +// parsing. It is declared here so it only needs to be created once. +var shortFormOps map[string]byte + +// parseShortForm parses a string into a script as follows: +// - Opcodes other than the push opcodes and unknown are present as +// either OP_NAME or just NAME +// - Plain numbers are made into push operations +// - Numbers beginning with 0x are inserted into the []byte as-is (so +// 0x14 is OP_DATA_20) +// - Single quoted strings are pushed as data +// - Anything else is an error +func parseShortForm(script string) ([]byte, error) { + // Only create the short form opcode map once. + if shortFormOps == nil { + ops := make(map[string]byte) + for opcodeName, opcodeValue := range OpcodeByName { + if strings.Contains(opcodeName, "OP_UNKNOWN") { + continue + } + ops[opcodeName] = opcodeValue + + // The opcodes named OP_# can't have the OP_ prefix + // stripped or they would conflict with the plain + // numbers. Also, since OP_FALSE and OP_TRUE are + // aliases for the OP_0, and OP_1, respectively, they + // have the same value, so detect those by name and + // allow them. + if (opcodeName == "OP_FALSE" || opcodeName == "OP_TRUE") || + (opcodeValue != Op0 && (opcodeValue < Op1 || + opcodeValue > Op16)) { + + ops[strings.TrimPrefix(opcodeName, "OP_")] = opcodeValue + } + } + shortFormOps = ops + } + + // Split only does one separator so convert all \n and tab into space. + script = strings.Replace(script, "\n", " ", -1) + script = strings.Replace(script, "\t", " ", -1) + tokens := strings.Split(script, " ") + builder := NewScriptBuilder() + + for _, tok := range tokens { + if len(tok) == 0 { + continue + } + // if parses as a plain number + if num, err := strconv.ParseInt(tok, 10, 64); err == nil { + builder.AddInt64(num) + continue + } else if bts, err := parseHex(tok); err == nil { + // Concatenate the bytes manually since the test code + // intentionally creates scripts that are too large and + // would cause the builder to error otherwise. + if builder.err == nil { + builder.script = append(builder.script, bts...) + } + } else if len(tok) >= 2 && + tok[0] == '\'' && tok[len(tok)-1] == '\'' { + builder.AddFullData([]byte(tok[1 : len(tok)-1])) + } else if opcode, ok := shortFormOps[tok]; ok { + builder.AddOp(opcode) + } else { + return nil, errors.Errorf("bad token %q", tok) + } + + } + return builder.Script() +} + +// parseScriptFlags parses the provided flags string from the format used in the +// reference tests into ScriptFlags suitable for use in the script engine. +func parseScriptFlags(flagStr string) (ScriptFlags, error) { + var flags ScriptFlags + + sFlags := strings.Split(flagStr, ",") + for _, flag := range sFlags { + switch flag { + case "": + // Nothing. + case "DISCOURAGE_UPGRADABLE_NOPS": + flags |= ScriptDiscourageUpgradableNops + default: + return flags, errors.Errorf("invalid flag: %s", flag) + } + } + return flags, nil +} + +// parseExpectedResult parses the provided expected result string into allowed +// script error codes. An error is returned if the expected result string is +// not supported. +func parseExpectedResult(expected string) ([]ErrorCode, error) { + switch expected { + case "OK": + return nil, nil + case "UNKNOWN_ERROR": + return []ErrorCode{ErrNumberTooBig, ErrMinimalData}, nil + case "PUBKEYFORMAT": + return []ErrorCode{ErrPubKeyFormat}, nil + case "EVAL_FALSE": + return []ErrorCode{ErrEvalFalse, ErrEmptyStack}, nil + case "EMPTY_STACK": + return []ErrorCode{ErrEmptyStack}, nil + case "EQUALVERIFY": + return []ErrorCode{ErrEqualVerify}, nil + case "NULLFAIL": + return []ErrorCode{ErrNullFail}, nil + case "SIG_HIGH_S": + return []ErrorCode{ErrSigHighS}, nil + case "SIG_HASHTYPE": + return []ErrorCode{ErrInvalidSigHashType}, nil + case "SIG_PUSHONLY": + return []ErrorCode{ErrNotPushOnly}, nil + case "CLEANSTACK": + return []ErrorCode{ErrCleanStack}, nil + case "BAD_OPCODE": + return []ErrorCode{ErrReservedOpcode, ErrMalformedPush}, nil + case "UNBALANCED_CONDITIONAL": + return []ErrorCode{ErrUnbalancedConditional, + ErrInvalidStackOperation}, nil + case "OP_RETURN": + return []ErrorCode{ErrEarlyReturn}, nil + case "VERIFY": + return []ErrorCode{ErrVerify}, nil + case "INVALID_STACK_OPERATION", "INVALID_ALTSTACK_OPERATION": + return []ErrorCode{ErrInvalidStackOperation}, nil + case "DISABLED_OPCODE": + return []ErrorCode{ErrDisabledOpcode}, nil + case "DISCOURAGE_UPGRADABLE_NOPS": + return []ErrorCode{ErrDiscourageUpgradableNOPs}, nil + case "PUSH_SIZE": + return []ErrorCode{ErrElementTooBig}, nil + case "OP_COUNT": + return []ErrorCode{ErrTooManyOperations}, nil + case "STACK_SIZE": + return []ErrorCode{ErrStackOverflow}, nil + case "SCRIPT_SIZE": + return []ErrorCode{ErrScriptTooBig}, nil + case "PUBKEY_COUNT": + return []ErrorCode{ErrInvalidPubKeyCount}, nil + case "SIG_COUNT": + return []ErrorCode{ErrInvalidSignatureCount}, nil + case "MINIMALDATA": + return []ErrorCode{ErrMinimalData}, nil + case "NEGATIVE_LOCKTIME": + return []ErrorCode{ErrNegativeLockTime}, nil + case "UNSATISFIED_LOCKTIME": + return []ErrorCode{ErrUnsatisfiedLockTime}, nil + case "MINIMALIF": + return []ErrorCode{ErrMinimalIf}, nil + } + + return nil, errors.Errorf("unrecognized expected result in test data: %v", + expected) +} + +// createSpendTx generates a basic spending transaction given the passed +// signature and public key scripts. +func createSpendingTx(sigScript, scriptPubKey []byte) *externalapi.DomainTransaction { + outpoint := externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID{}, + Index: ^uint32(0), + } + input := &externalapi.DomainTransactionInput{ + PreviousOutpoint: outpoint, + SignatureScript: []byte{Op0, Op0}, + Sequence: constants.MaxTxInSequenceNum, + } + output := &externalapi.DomainTransactionOutput{Value: 0, ScriptPublicKey: scriptPubKey} + coinbaseTx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{input}, + Outputs: []*externalapi.DomainTransactionOutput{output}, + } + + outpoint = externalapi.DomainOutpoint{ + TransactionID: *consensusserialization.TransactionID(coinbaseTx), + Index: 0, + } + input = &externalapi.DomainTransactionInput{ + PreviousOutpoint: outpoint, + SignatureScript: sigScript, + Sequence: constants.MaxTxInSequenceNum, + } + output = &externalapi.DomainTransactionOutput{Value: 0, ScriptPublicKey: nil} + spendingTx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: []*externalapi.DomainTransactionInput{input}, + Outputs: []*externalapi.DomainTransactionOutput{output}, + } + + return spendingTx +} + +// testScripts ensures all of the passed script tests execute with the expected +// results with or without using a signature cache, as specified by the +// parameter. +func testScripts(t *testing.T, tests [][]interface{}, useSigCache bool) { + // Create a signature cache to use only if requested. + var sigCache *SigCache + if useSigCache { + sigCache = NewSigCache(10) + } + + for i, test := range tests { + // "Format is: [[wit..., amount]?, scriptSig, scriptPubKey, + // flags, expected_scripterror, ... comments]" + + // Skip single line comments. + if len(test) == 1 { + continue + } + + // Construct a name for the test based on the comment and test + // data. + name, err := scriptTestName(test) + if err != nil { + t.Errorf("TestScripts: invalid test #%d: %v", i, err) + continue + } + + // Extract and parse the signature script from the test fields. + scriptSigStr, ok := test[0].(string) + if !ok { + t.Errorf("%s: signature script is not a string", name) + continue + } + scriptSig, err := parseShortForm(scriptSigStr) + if err != nil { + t.Errorf("%s: can't parse signature script: %v", name, + err) + continue + } + + // Extract and parse the public key script from the test fields. + scriptPubKeyStr, ok := test[1].(string) + if !ok { + t.Errorf("%s: public key script is not a string", name) + continue + } + scriptPubKey, err := parseShortForm(scriptPubKeyStr) + if err != nil { + t.Errorf("%s: can't parse public key script: %v", name, + err) + continue + } + + // Extract and parse the script flags from the test fields. + flagsStr, ok := test[2].(string) + if !ok { + t.Errorf("%s: flags field is not a string", name) + continue + } + flags, err := parseScriptFlags(flagsStr) + if err != nil { + t.Errorf("%s: %v", name, err) + continue + } + + // Extract and parse the expected result from the test fields. + // + // Convert the expected result string into the allowed script + // error codes. This is necessary because txscript is more + // fine grained with its errors than the reference test data, so + // some of the reference test data errors map to more than one + // possibility. + resultStr, ok := test[3].(string) + if !ok { + t.Errorf("%s: result field is not a string", name) + continue + } + allowedErrorCodes, err := parseExpectedResult(resultStr) + if err != nil { + t.Errorf("%s: %v", name, err) + continue + } + + // Generate a transaction pair such that one spends from the + // other and the provided signature and public key scripts are + // used, then create a new engine to execute the scripts. + tx := createSpendingTx(scriptSig, scriptPubKey) + + vm, err := NewEngine(scriptPubKey, tx, 0, flags, sigCache) + if err == nil { + err = vm.Execute() + } + + // Ensure there were no errors when the expected result is OK. + if resultStr == "OK" { + if err != nil { + t.Errorf("%s failed to execute: %v", name, err) + } + continue + } + + // At this point an error was expected so ensure the result of + // the execution matches it. + success := false + for _, code := range allowedErrorCodes { + if IsErrorCode(err, code) { + success = true + break + } + } + if !success { + var scriptErr Error + if ok := errors.As(err, &scriptErr); ok { + t.Errorf("%s: want error codes %v, got %v", name, + allowedErrorCodes, scriptErr.ErrorCode) + continue + } + t.Errorf("%s: want error codes %v, got err: %v (%T)", + name, allowedErrorCodes, err, err) + continue + } + } +} + +// TestScripts ensures all of the tests in script_tests.json execute with the +// expected results as defined in the test data. +func TestScripts(t *testing.T) { + file, err := ioutil.ReadFile("data/script_tests.json") + if err != nil { + t.Fatalf("TestScripts: %v\n", err) + } + + var tests [][]interface{} + err = json.Unmarshal(file, &tests) + if err != nil { + t.Fatalf("TestScripts couldn't Unmarshal: %v", err) + } + + // Disable non-test logs + logLevel := log.Level() + log.SetLevel(logger.LevelOff) + defer log.SetLevel(logLevel) + + // Run all script tests with and without the signature cache. + testScripts(t, tests, true) + testScripts(t, tests, false) +} diff --git a/domain/consensus/utils/txscript/script.go b/domain/consensus/utils/txscript/script.go index 17f6a1360..7615c2544 100644 --- a/domain/consensus/utils/txscript/script.go +++ b/domain/consensus/utils/txscript/script.go @@ -7,6 +7,7 @@ package txscript import ( "bytes" "fmt" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/consensusserialization" "github.com/pkg/errors" @@ -255,8 +256,6 @@ func shallowCopyTx(tx *externalapi.DomainTransaction) externalapi.DomainTransact // for the copied inputs and outputs and point the final slice of // pointers into the contiguous arrays. This avoids a lot of small // allocations. - // Specifically avoid using appmessage.NewMsgTx() to prevent correcting errors by - // auto-generating various fields. txCopy := externalapi.DomainTransaction{ Version: tx.Version, Inputs: make([]*externalapi.DomainTransactionInput, len(tx.Inputs)), diff --git a/domain/consensus/utils/txscript/script_test.go b/domain/consensus/utils/txscript/script_test.go new file mode 100644 index 000000000..70a08e21e --- /dev/null +++ b/domain/consensus/utils/txscript/script_test.go @@ -0,0 +1,3973 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "reflect" + "testing" +) + +// TestParseOpcode tests for opcode parsing with bad data templates. +func TestParseOpcode(t *testing.T) { + // Deep copy the array and make one of the opcodes invalid by setting it + // to the wrong length. + fakeArray := opcodeArray + fakeArray[OpPushData4] = opcode{value: OpPushData4, + name: "OP_PUSHDATA4", length: -8, opfunc: opcodePushData} + + // This script would be fine if -8 was a valid length. + _, err := parseScriptTemplate([]byte{OpPushData4, 0x1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00}, &fakeArray) + if err == nil { + t.Errorf("no error with dodgy opcode array!") + } +} + +// TestUnparsingInvalidOpcodes tests for errors when unparsing invalid parsed +// opcodes. +func TestUnparsingInvalidOpcodes(t *testing.T) { + tests := []struct { + name string + pop *parsedOpcode + expectedErr error + }{ + { + name: "OP_FALSE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpFalse], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_FALSE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpFalse], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_1 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData1], + data: nil, + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData1], + data: make([]byte, 1), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData1], + data: make([]byte, 2), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_2 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData2], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData2], + data: make([]byte, 2), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData2], + data: make([]byte, 3), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_3 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData3], + data: make([]byte, 2), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_3", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData3], + data: make([]byte, 3), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_3 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData3], + data: make([]byte, 4), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_4 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData4], + data: make([]byte, 3), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData4], + data: make([]byte, 4), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_4 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData4], + data: make([]byte, 5), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_5 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData5], + data: make([]byte, 4), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_5", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData5], + data: make([]byte, 5), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_5 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData5], + data: make([]byte, 6), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_6 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData6], + data: make([]byte, 5), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_6", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData6], + data: make([]byte, 6), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_6 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData6], + data: make([]byte, 7), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_7 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData7], + data: make([]byte, 6), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_7", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData7], + data: make([]byte, 7), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_7 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData7], + data: make([]byte, 8), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_8 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData8], + data: make([]byte, 7), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_8", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData8], + data: make([]byte, 8), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_8 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData8], + data: make([]byte, 9), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_9 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData9], + data: make([]byte, 8), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_9", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData9], + data: make([]byte, 9), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_9 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData9], + data: make([]byte, 10), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_10 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData10], + data: make([]byte, 9), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_10", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData10], + data: make([]byte, 10), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_10 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData10], + data: make([]byte, 11), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_11 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData11], + data: make([]byte, 10), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_11", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData11], + data: make([]byte, 11), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_11 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData11], + data: make([]byte, 12), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_12 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData12], + data: make([]byte, 11), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_12", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData12], + data: make([]byte, 12), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_12 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData12], + data: make([]byte, 13), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_13 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData13], + data: make([]byte, 12), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_13", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData13], + data: make([]byte, 13), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_13 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData13], + data: make([]byte, 14), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_14 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData14], + data: make([]byte, 13), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_14", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData14], + data: make([]byte, 14), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_14 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData14], + data: make([]byte, 15), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_15 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData15], + data: make([]byte, 14), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_15", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData15], + data: make([]byte, 15), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_15 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData15], + data: make([]byte, 16), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_16 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData16], + data: make([]byte, 15), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_16", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData16], + data: make([]byte, 16), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_16 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData16], + data: make([]byte, 17), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_17 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData17], + data: make([]byte, 16), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_17", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData17], + data: make([]byte, 17), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_17 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData17], + data: make([]byte, 18), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_18 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData18], + data: make([]byte, 17), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_18", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData18], + data: make([]byte, 18), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_18 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData18], + data: make([]byte, 19), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_19 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData19], + data: make([]byte, 18), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_19", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData19], + data: make([]byte, 19), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_19 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData19], + data: make([]byte, 20), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_20 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData20], + data: make([]byte, 19), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_20", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData20], + data: make([]byte, 20), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_20 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData20], + data: make([]byte, 21), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_21 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData21], + data: make([]byte, 20), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_21", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData21], + data: make([]byte, 21), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_21 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData21], + data: make([]byte, 22), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_22 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData22], + data: make([]byte, 21), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_22", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData22], + data: make([]byte, 22), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_22 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData22], + data: make([]byte, 23), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_23 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData23], + data: make([]byte, 22), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_23", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData23], + data: make([]byte, 23), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_23 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData23], + data: make([]byte, 24), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_24 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData24], + data: make([]byte, 23), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_24", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData24], + data: make([]byte, 24), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_24 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData24], + data: make([]byte, 25), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_25 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData25], + data: make([]byte, 24), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_25", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData25], + data: make([]byte, 25), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_25 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData25], + data: make([]byte, 26), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_26 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData26], + data: make([]byte, 25), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_26", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData26], + data: make([]byte, 26), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_26 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData26], + data: make([]byte, 27), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_27 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData27], + data: make([]byte, 26), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_27", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData27], + data: make([]byte, 27), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_27 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData27], + data: make([]byte, 28), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_28 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData28], + data: make([]byte, 27), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_28", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData28], + data: make([]byte, 28), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_28 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData28], + data: make([]byte, 29), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_29 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData29], + data: make([]byte, 28), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_29", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData29], + data: make([]byte, 29), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_29 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData29], + data: make([]byte, 30), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_30 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData30], + data: make([]byte, 29), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_30", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData30], + data: make([]byte, 30), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_30 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData30], + data: make([]byte, 31), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_31 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData31], + data: make([]byte, 30), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_31", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData31], + data: make([]byte, 31), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_31 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData31], + data: make([]byte, 32), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_32 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData32], + data: make([]byte, 31), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_32", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData32], + data: make([]byte, 32), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_32 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData32], + data: make([]byte, 33), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_33 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData33], + data: make([]byte, 32), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_33", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData33], + data: make([]byte, 33), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_33 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData33], + data: make([]byte, 34), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_34 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData34], + data: make([]byte, 33), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_34", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData34], + data: make([]byte, 34), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_34 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData34], + data: make([]byte, 35), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_35 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData35], + data: make([]byte, 34), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_35", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData35], + data: make([]byte, 35), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_35 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData35], + data: make([]byte, 36), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_36 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData36], + data: make([]byte, 35), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_36", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData36], + data: make([]byte, 36), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_36 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData36], + data: make([]byte, 37), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_37 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData37], + data: make([]byte, 36), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_37", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData37], + data: make([]byte, 37), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_37 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData37], + data: make([]byte, 38), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_38 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData38], + data: make([]byte, 37), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_38", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData38], + data: make([]byte, 38), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_38 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData38], + data: make([]byte, 39), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_39 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData39], + data: make([]byte, 38), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_39", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData39], + data: make([]byte, 39), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_39 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData39], + data: make([]byte, 40), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_40 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData40], + data: make([]byte, 39), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_40", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData40], + data: make([]byte, 40), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_40 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData40], + data: make([]byte, 41), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_41 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData41], + data: make([]byte, 40), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_41", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData41], + data: make([]byte, 41), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_41 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData41], + data: make([]byte, 42), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_42 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData42], + data: make([]byte, 41), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_42", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData42], + data: make([]byte, 42), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_42 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData42], + data: make([]byte, 43), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_43 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData43], + data: make([]byte, 42), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_43", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData43], + data: make([]byte, 43), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_43 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData43], + data: make([]byte, 44), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_44 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData44], + data: make([]byte, 43), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_44", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData44], + data: make([]byte, 44), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_44 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData44], + data: make([]byte, 45), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_45 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData45], + data: make([]byte, 44), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_45", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData45], + data: make([]byte, 45), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_45 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData45], + data: make([]byte, 46), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_46 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData46], + data: make([]byte, 45), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_46", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData46], + data: make([]byte, 46), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_46 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData46], + data: make([]byte, 47), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_47 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData47], + data: make([]byte, 46), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_47", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData47], + data: make([]byte, 47), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_47 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData47], + data: make([]byte, 48), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_48 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData48], + data: make([]byte, 47), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_48", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData48], + data: make([]byte, 48), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_48 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData48], + data: make([]byte, 49), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_49 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData49], + data: make([]byte, 48), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_49", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData49], + data: make([]byte, 49), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_49 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData49], + data: make([]byte, 50), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_50 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData50], + data: make([]byte, 49), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_50", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData50], + data: make([]byte, 50), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_50 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData50], + data: make([]byte, 51), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_51 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData51], + data: make([]byte, 50), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_51", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData51], + data: make([]byte, 51), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_51 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData51], + data: make([]byte, 52), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_52 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData52], + data: make([]byte, 51), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_52", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData52], + data: make([]byte, 52), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_52 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData52], + data: make([]byte, 53), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_53 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData53], + data: make([]byte, 52), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_53", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData53], + data: make([]byte, 53), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_53 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData53], + data: make([]byte, 54), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_54 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData54], + data: make([]byte, 53), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_54", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData54], + data: make([]byte, 54), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_54 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData54], + data: make([]byte, 55), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_55 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData55], + data: make([]byte, 54), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_55", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData55], + data: make([]byte, 55), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_55 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData55], + data: make([]byte, 56), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_56 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData56], + data: make([]byte, 55), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_56", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData56], + data: make([]byte, 56), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_56 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData56], + data: make([]byte, 57), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_57 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData57], + data: make([]byte, 56), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_57", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData57], + data: make([]byte, 57), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_57 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData57], + data: make([]byte, 58), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_58 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData58], + data: make([]byte, 57), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_58", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData58], + data: make([]byte, 58), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_58 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData58], + data: make([]byte, 59), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_59 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData59], + data: make([]byte, 58), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_59", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData59], + data: make([]byte, 59), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_59 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData59], + data: make([]byte, 60), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_60 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData60], + data: make([]byte, 59), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_60", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData60], + data: make([]byte, 60), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_60 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData60], + data: make([]byte, 61), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_61 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData61], + data: make([]byte, 60), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_61", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData61], + data: make([]byte, 61), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_61 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData61], + data: make([]byte, 62), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_62 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData62], + data: make([]byte, 61), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_62", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData62], + data: make([]byte, 62), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_62 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData62], + data: make([]byte, 63), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_63 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData63], + data: make([]byte, 62), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_63", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData63], + data: make([]byte, 63), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_63 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData63], + data: make([]byte, 64), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_64 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData64], + data: make([]byte, 63), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_64", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData64], + data: make([]byte, 64), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_64 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData64], + data: make([]byte, 65), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_65 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData65], + data: make([]byte, 64), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_65", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData65], + data: make([]byte, 65), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_65 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData65], + data: make([]byte, 66), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_66 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData66], + data: make([]byte, 65), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_66", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData66], + data: make([]byte, 66), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_66 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData66], + data: make([]byte, 67), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_67 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData67], + data: make([]byte, 66), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_67", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData67], + data: make([]byte, 67), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_67 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData67], + data: make([]byte, 68), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_68 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData68], + data: make([]byte, 67), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_68", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData68], + data: make([]byte, 68), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_68 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData68], + data: make([]byte, 69), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_69 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData69], + data: make([]byte, 68), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_69", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData69], + data: make([]byte, 69), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_69 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData69], + data: make([]byte, 70), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_70 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData70], + data: make([]byte, 69), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_70", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData70], + data: make([]byte, 70), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_70 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData70], + data: make([]byte, 71), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_71 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData71], + data: make([]byte, 70), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_71", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData71], + data: make([]byte, 71), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_71 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData71], + data: make([]byte, 72), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_72 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData72], + data: make([]byte, 71), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_72", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData72], + data: make([]byte, 72), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_72 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData72], + data: make([]byte, 73), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_73 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData73], + data: make([]byte, 72), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_73", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData73], + data: make([]byte, 73), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_73 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData73], + data: make([]byte, 74), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_74 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData74], + data: make([]byte, 73), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_74", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData74], + data: make([]byte, 74), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_74 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData74], + data: make([]byte, 75), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_75 short", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData75], + data: make([]byte, 74), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DATA_75", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData75], + data: make([]byte, 75), + }, + expectedErr: nil, + }, + { + name: "OP_DATA_75 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpData75], + data: make([]byte, 76), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_PUSHDATA1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPushData1], + data: []byte{0, 1, 2, 3, 4}, + }, + expectedErr: nil, + }, + { + name: "OP_PUSHDATA2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPushData2], + data: []byte{0, 1, 2, 3, 4}, + }, + expectedErr: nil, + }, + { + name: "OP_PUSHDATA4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPushData1], + data: []byte{0, 1, 2, 3, 4}, + }, + expectedErr: nil, + }, + { + name: "OP_1NEGATE", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op1Negate], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_1NEGATE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op1Negate], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RESERVED", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReserved], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RESERVED long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReserved], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_TRUE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpTrue], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_TRUE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpTrue], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_3", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op3], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_3 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op3], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_4", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op4], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_4 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op4], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_5", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op5], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_5 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op5], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_6", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op6], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_6 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op6], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_7", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op7], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_7 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op7], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_8", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op8], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_8 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op8], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_9", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op9], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_9 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op9], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_10", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op10], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_10 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op10], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_11", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op11], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_11 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op11], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_12", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op12], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_12 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op12], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_13", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op13], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_13 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op13], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_14", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op14], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_14 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op14], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_15", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op15], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_15 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op15], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_16", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op16], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_16 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op16], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_VER", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVer], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VER long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVer], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_IF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpIf], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_IF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpIf], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOTIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNotIf], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOTIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNotIf], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_VERIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVerIf], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VERIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVerIf], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_VERNOTIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVerNotIf], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VERNOTIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVerNotIf], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_ELSE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpElse], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ELSE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpElse], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_ENDIF", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpEndIf], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ENDIF long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpEndIf], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_VERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVerify], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_VERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpVerify], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RETURN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReturn], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RETURN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReturn], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_TOALTSTACK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpToAltStack], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_TOALTSTACK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpToAltStack], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_FROMALTSTACK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpFromAltStack], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_FROMALTSTACK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpFromAltStack], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2DROP", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Drop], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2DROP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Drop], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2DUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Dup], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2DUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Dup], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_3DUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op3Dup], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_3DUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op3Dup], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2OVER", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Over], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2OVER long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Over], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2ROT", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Rot], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2ROT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Rot], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2SWAP", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Swap], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2SWAP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Swap], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_IFDUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpIfDup], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_IFDUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpIfDup], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DEPTH", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDepth], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DEPTH long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDepth], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DROP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDrop], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DROP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDrop], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DUP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDup], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DUP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDup], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NIP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNip], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NIP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNip], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_OVER", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpOver], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_OVER long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpOver], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_PICK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPick], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_PICK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPick], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_ROLL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRoll], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ROLL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRoll], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_ROT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRot], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ROT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRot], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_SWAP", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSwap], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SWAP long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSwap], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_TUCK", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpTuck], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_TUCK long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpTuck], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_CAT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCat], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CAT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCat], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_SUBSTR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSubStr], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SUBSTR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSubStr], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_LEFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLeft], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LEFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLeft], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_LEFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLeft], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LEFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLeft], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RIGHT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRight], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RIGHT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRight], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_SIZE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSize], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SIZE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSize], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_INVERT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpInvert], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_INVERT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpInvert], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_AND", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpAnd], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_AND long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpAnd], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_OR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpOr], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_OR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpOr], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_XOR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpXor], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_XOR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpXor], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_EQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpEqual], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_EQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpEqual], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_EQUALVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpEqualVerify], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_EQUALVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpEqualVerify], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RESERVED1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReserved1], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RESERVED1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReserved1], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RESERVED2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReserved2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RESERVED2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpReserved2], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_1ADD", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op1Add], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_1ADD long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op1Add], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_1SUB", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op1Sub], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_1SUB long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op1Sub], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2MUL", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Mul], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2MUL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Mul], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_2DIV", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Div], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_2DIV long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op2Div], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NEGATE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNegate], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NEGATE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNegate], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_ABS", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpAbs], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ABS long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpAbs], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNot], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNot], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_0NOTEQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op0NotEqual], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_0NOTEQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[Op0NotEqual], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_ADD", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpAdd], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_ADD long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpAdd], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_SUB", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSub], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SUB long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSub], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_MUL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMul], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MUL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMul], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_DIV", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDiv], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_DIV long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpDiv], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_MOD", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMod], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MOD long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMod], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_LSHIFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLShift], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LSHIFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLShift], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RSHIFT", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRShift], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RSHIFT long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRShift], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_BOOLAND", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpBoolAnd], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_BOOLAND long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpBoolAnd], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_BOOLOR", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpBoolOr], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_BOOLOR long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpBoolOr], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NUMEQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNumEqual], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NUMEQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNumEqual], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NUMEQUALVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNumEqualVerify], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NUMEQUALVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNumEqualVerify], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NUMNOTEQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNumNotEqual], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NUMNOTEQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNumNotEqual], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_LESSTHAN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLessThan], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LESSTHAN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLessThan], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_GREATERTHAN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpGreaterThan], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_GREATERTHAN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpGreaterThan], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_LESSTHANOREQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLessThanOrEqual], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_LESSTHANOREQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpLessThanOrEqual], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_GREATERTHANOREQUAL", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpGreaterThanOrEqual], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_GREATERTHANOREQUAL long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpGreaterThanOrEqual], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_MIN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMin], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MIN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMin], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_MAX", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMax], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_MAX long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpMax], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_WITHIN", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpWithin], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_WITHIN long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpWithin], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_RIPEMD160", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRipeMD160], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_RIPEMD160 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpRipeMD160], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_SHA1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSHA1], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SHA1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSHA1], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_SHA256", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSHA256], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_SHA256 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpSHA256], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_HASH160", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpHash160], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_HASH160 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpHash160], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_HASH256", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpHash256], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_HASH256 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpHash256], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_CHECKSIG", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckSig], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKSIG long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckSig], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_CHECKSIGVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckSigVerify], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKSIGVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckSigVerify], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_CHECKMULTISIG", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckMultiSig], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKMULTISIG long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckMultiSig], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_CHECKMULTISIGVERIFY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckMultiSigVerify], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_CHECKMULTISIGVERIFY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpCheckMultiSigVerify], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP1", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop1], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP1 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop1], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP2", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop2], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP2 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop2], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP3", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop3], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP3 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop3], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP4", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop4], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP4 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop4], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP5", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop5], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP5 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop5], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP6", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop6], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP6 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop6], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP7", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop7], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP7 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop7], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP8", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop8], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP8 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop8], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP9", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop9], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP9 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop9], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_NOP10", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop10], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_NOP10 long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpNop10], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_PUBKEYHASH", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPubKeyHash], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_PUBKEYHASH long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPubKeyHash], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_PUBKEY", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPubKey], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_PUBKEY long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpPubKey], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + { + name: "OP_INVALIDOPCODE", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpInvalidOpCode], + data: nil, + }, + expectedErr: nil, + }, + { + name: "OP_INVALIDOPCODE long", + pop: &parsedOpcode{ + opcode: &opcodeArray[OpInvalidOpCode], + data: make([]byte, 1), + }, + expectedErr: scriptError(ErrInternal, ""), + }, + } + + for _, test := range tests { + _, err := test.pop.bytes() + if e := checkScriptError(err, test.expectedErr); e != nil { + t.Errorf("Parsed opcode test '%s': %v", test.name, e) + continue + } + } +} + +// TestPushedData ensured the PushedData function extracts the expected data out +// of various scripts. +func TestPushedData(t *testing.T) { + t.Parallel() + + var tests = []struct { + script string + out [][]byte + valid bool + }{ + { + "0 IF 0 ELSE 2 ENDIF", + [][]byte{nil, nil}, + true, + }, + { + "16777216 10000000", + [][]byte{ + {0x00, 0x00, 0x00, 0x01}, // 16777216 + {0x80, 0x96, 0x98, 0x00}, // 10000000 + }, + true, + }, + { + "DUP HASH160 '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem' EQUALVERIFY CHECKSIG", + [][]byte{ + // 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem + { + 0x31, 0x37, 0x56, 0x5a, 0x4e, 0x58, 0x31, 0x53, 0x4e, 0x35, + 0x4e, 0x74, 0x4b, 0x61, 0x38, 0x55, 0x51, 0x46, 0x78, 0x77, + 0x51, 0x62, 0x46, 0x65, 0x46, 0x63, 0x33, 0x69, 0x71, 0x52, + 0x59, 0x68, 0x65, 0x6d, + }, + }, + true, + }, + { + "PUSHDATA4 1000 EQUAL", + nil, + false, + }, + } + + for i, test := range tests { + script := mustParseShortForm(test.script) + data, err := PushedData(script) + if test.valid && err != nil { + t.Errorf("TestPushedData failed test #%d: %v\n", i, err) + continue + } else if !test.valid && err == nil { + t.Errorf("TestPushedData failed test #%d: test should "+ + "be invalid\n", i) + continue + } + if !reflect.DeepEqual(data, test.out) { + t.Errorf("TestPushedData failed test #%d: want: %x "+ + "got: %x\n", i, test.out, data) + } + } +} + +// TestHasCanonicalPush ensures the canonicalPush function works as expected. +func TestHasCanonicalPush(t *testing.T) { + t.Parallel() + + for i := 0; i < 65535; i++ { + script, err := NewScriptBuilder().AddInt64(int64(i)).Script() + if err != nil { + t.Errorf("Script: test #%d unexpected error: %v\n", i, + err) + continue + } + if result, _ := IsPushOnlyScript(script); !result { + t.Errorf("IsPushOnlyScript: test #%d failed: %x\n", i, + script) + continue + } + pops, err := parseScript(script) + if err != nil { + t.Errorf("parseScript: #%d failed: %v", i, err) + continue + } + for _, pop := range pops { + if result := canonicalPush(pop); !result { + t.Errorf("canonicalPush: test #%d failed: %x\n", + i, script) + break + } + } + } + for i := 0; i <= MaxScriptElementSize; i++ { + builder := NewScriptBuilder() + builder.AddData(bytes.Repeat([]byte{0x49}, i)) + script, err := builder.Script() + if err != nil { + t.Errorf("StandardPushesTests test #%d unexpected error: %v\n", i, err) + continue + } + if result, _ := IsPushOnlyScript(script); !result { + t.Errorf("StandardPushesTests IsPushOnlyScript test #%d failed: %x\n", i, script) + continue + } + pops, err := parseScript(script) + if err != nil { + t.Errorf("StandardPushesTests #%d failed to TstParseScript: %v", i, err) + continue + } + for _, pop := range pops { + if result := canonicalPush(pop); !result { + t.Errorf("StandardPushesTests TstHasCanonicalPushes test #%d failed: %x\n", i, script) + break + } + } + } +} + +// TestGetPreciseSigOps ensures the more precise signature operation counting +// mechanism which includes signatures in P2SH scripts works as expected. +func TestGetPreciseSigOps(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + scriptSig []byte + nSigOps int + }{ + { + name: "scriptSig doesn't parse", + scriptSig: mustParseShortForm("PUSHDATA1 0x02"), + }, + { + name: "scriptSig isn't push only", + scriptSig: mustParseShortForm("1 DUP"), + nSigOps: 0, + }, + { + name: "scriptSig length 0", + scriptSig: nil, + nSigOps: 0, + }, + { + name: "No script at the end", + // No script at end but still push only. + scriptSig: mustParseShortForm("1 1"), + nSigOps: 0, + }, + { + name: "pushed script doesn't parse", + scriptSig: mustParseShortForm("DATA_2 PUSHDATA1 0x02"), + }, + } + + // The signature in the p2sh script is nonsensical for the tests since + // this script will never be executed. What matters is that it matches + // the right pattern. + scriptPubKey := mustParseShortForm("HASH160 DATA_20 0x433ec2ac1ffa1b7b7d0" + + "27f564529c57197f9ae88 EQUAL") + for _, test := range tests { + count := GetPreciseSigOpCount(test.scriptSig, scriptPubKey, true) + if count != test.nSigOps { + t.Errorf("%s: expected count of %d, got %d", test.name, + test.nSigOps, count) + + } + } +} + +// TestIsPayToScriptHash ensures the IsPayToScriptHash function returns the +// expected results for all the scripts in scriptClassTests. +func TestIsPayToScriptHash(t *testing.T) { + t.Parallel() + + for _, test := range scriptClassTests { + script := mustParseShortForm(test.script) + shouldBe := (test.class == ScriptHashTy) + p2sh := IsPayToScriptHash(script) + if p2sh != shouldBe { + t.Errorf("%s: expected p2sh %v, got %v", test.name, + shouldBe, p2sh) + } + } +} + +// TestHasCanonicalPushes ensures the canonicalPush function properly determines +// what is considered a canonical push. +func TestHasCanonicalPushes(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + script string + expected bool + }{ + { + name: "does not parse", + script: "0x046708afdb0fe5548271967f1a67130b7105cd6a82" + + "8e03909a67962e0ea1f61d", + expected: false, + }, + { + name: "non-canonical push", + script: "PUSHDATA1 0x04 0x01020304", + expected: false, + }, + } + + for i, test := range tests { + script := mustParseShortForm(test.script) + pops, err := parseScript(script) + if err != nil { + if test.expected { + t.Errorf("TstParseScript #%d failed: %v", i, err) + } + continue + } + for _, pop := range pops { + if canonicalPush(pop) != test.expected { + t.Errorf("canonicalPush: #%d (%s) wrong result"+ + "\ngot: %v\nwant: %v", i, test.name, + true, test.expected) + break + } + } + } +} + +// TestIsPushOnlyScript ensures the IsPushOnlyScript function returns the +// expected results. +func TestIsPushOnlyScript(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + script []byte + expectedResult bool + shouldFail bool + }{ + { + name: "does not parse", + script: mustParseShortForm("0x046708afdb0fe5548271967f1a67130" + + "b7105cd6a828e03909a67962e0ea1f61d"), + expectedResult: false, + shouldFail: true, + }, + { + name: "non push only script", + script: mustParseShortForm("0x515293"), //OP_1 OP_2 OP_ADD + expectedResult: false, + shouldFail: false, + }, + { + name: "push only script", + script: mustParseShortForm("0x5152"), //OP_1 OP_2 + expectedResult: true, + shouldFail: false, + }, + } + + for _, test := range tests { + isPushOnly, err := IsPushOnlyScript(test.script) + + if isPushOnly != test.expectedResult { + t.Errorf("IsPushOnlyScript (%s) wrong result\ngot: %v\nwant: "+ + "%v", test.name, isPushOnly, test.expectedResult) + } + + if test.shouldFail && err == nil { + t.Errorf("IsPushOnlyScript (%s) expected an error but got ", test.name) + } + + if !test.shouldFail && err != nil { + t.Errorf("IsPushOnlyScript (%s) expected no error but got: %v", test.name, err) + } + } +} + +// TestIsUnspendable ensures the IsUnspendable function returns the expected +// results. +func TestIsUnspendable(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + scriptPubKey []byte + expected bool + }{ + { + // Unspendable + scriptPubKey: []byte{0x6a, 0x04, 0x74, 0x65, 0x73, 0x74}, + expected: true, + }, + { + // Spendable + scriptPubKey: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0, + 0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45, + 0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6, + 0xfa, 0x0b, 0x5c, 0x88, 0xac}, + expected: false, + }, + } + + for i, test := range tests { + res := IsUnspendable(test.scriptPubKey) + if res != test.expected { + t.Errorf("TestIsUnspendable #%d failed: got %v want %v", + i, res, test.expected) + continue + } + } +} diff --git a/domain/consensus/utils/txscript/sign_test.go b/domain/consensus/utils/txscript/sign_test.go new file mode 100644 index 000000000..32c438320 --- /dev/null +++ b/domain/consensus/utils/txscript/sign_test.go @@ -0,0 +1,1101 @@ +// Copyright (c) 2013-2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "fmt" + "testing" + + "github.com/kaspanet/go-secp256k1" + "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" + "github.com/pkg/errors" + + "github.com/kaspanet/kaspad/domain/dagconfig" + "github.com/kaspanet/kaspad/util" +) + +type addressToKey struct { + key *secp256k1.PrivateKey + compressed bool +} + +func mkGetKey(keys map[string]addressToKey) KeyDB { + if keys == nil { + return KeyClosure(func(addr util.Address) (*secp256k1.PrivateKey, + bool, error) { + return nil, false, errors.New("nope") + }) + } + return KeyClosure(func(addr util.Address) (*secp256k1.PrivateKey, + bool, error) { + a2k, ok := keys[addr.EncodeAddress()] + if !ok { + return nil, false, errors.New("nope") + } + return a2k.key, a2k.compressed, nil + }) +} + +func mkGetScript(scripts map[string][]byte) ScriptDB { + if scripts == nil { + return ScriptClosure(func(addr util.Address) ([]byte, error) { + return nil, errors.New("nope") + }) + } + return ScriptClosure(func(addr util.Address) ([]byte, error) { + script, ok := scripts[addr.EncodeAddress()] + if !ok { + return nil, errors.New("nope") + } + return script, nil + }) +} + +func checkScripts(msg string, tx *externalapi.DomainTransaction, idx int, sigScript, scriptPubKey []byte) error { + tx.Inputs[idx].SignatureScript = sigScript + var flags ScriptFlags + vm, err := NewEngine(scriptPubKey, tx, idx, + flags, nil) + if err != nil { + return errors.Errorf("failed to make script engine for %s: %v", + msg, err) + } + + err = vm.Execute() + if err != nil { + return errors.Errorf("invalid script signature for %s: %v", msg, + err) + } + + return nil +} + +func signAndCheck(msg string, tx *externalapi.DomainTransaction, idx int, scriptPubKey []byte, + hashType SigHashType, kdb KeyDB, sdb ScriptDB, + previousScript []byte) error { + + sigScript, err := SignTxOutput(&dagconfig.TestnetParams, tx, idx, + scriptPubKey, hashType, kdb, sdb, nil) + if err != nil { + return errors.Errorf("failed to sign output %s: %v", msg, err) + } + + return checkScripts(msg, tx, idx, sigScript, scriptPubKey) +} + +func TestSignTxOutput(t *testing.T) { + t.Parallel() + + // make key + // make script based on key. + // sign with magic pixie dust. + hashTypes := []SigHashType{ + SigHashAll, + SigHashNone, + SigHashSingle, + SigHashAll | SigHashAnyOneCanPay, + SigHashNone | SigHashAnyOneCanPay, + SigHashSingle | SigHashAnyOneCanPay, + } + inputs := []*externalapi.DomainTransactionInput{ + { + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID{}, + Index: 0, + }, + Sequence: 4294967295, + }, + { + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID{}, + Index: 1, + }, + Sequence: 4294967295, + }, + { + PreviousOutpoint: externalapi.DomainOutpoint{ + TransactionID: externalapi.DomainTransactionID{}, + Index: 2, + }, + Sequence: 4294967295, + }, + } + outputs := []*externalapi.DomainTransactionOutput{ + { + Value: 1, + }, + { + Value: 2, + }, + { + Value: 3, + }, + } + tx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: inputs, + Outputs: outputs, + } + + // Pay to Pubkey Hash (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + uncompressedPubKey, err := pubKey.SerializeUncompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(uncompressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + } + + if err := signAndCheck(msg, tx, i, scriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (uncompressed) (merging with correct) + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + uncompressedPubKey, err := pubKey.SerializeUncompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + address, err := util.NewAddressPubKeyHash( + util.Hash160(uncompressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + } + + sigScript, err := SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), nil) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), sigScript) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptPubKey) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + compressedPubKey, err := pubKey.SerializeCompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(compressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + } + + if err := signAndCheck(msg, tx, i, scriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + compressedPubKey, err := pubKey.SerializeCompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(compressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + } + + sigScript, err := SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), nil) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), sigScript) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptPubKey) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // As before, but with p2sh now. + // Pay to Pubkey Hash (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + uncompressedPubKey, err := pubKey.SerializeUncompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(uncompressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + break + } + + scriptAddr, err := util.NewAddressScriptHash( + scriptPubKey, util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptScriptPubKey, err := PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script scriptPubKey for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptScriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): scriptPubKey, + }), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (uncompressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + uncompressedPubKey, err := pubKey.SerializeUncompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(uncompressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + break + } + + scriptAddr, err := util.NewAddressScriptHash( + scriptPubKey, util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptScriptPubKey, err := PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script scriptPubKey for "+ + "%s: %v", msg, err) + break + } + + _, err = SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptScriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): scriptPubKey, + }), nil) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err := SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptScriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): scriptPubKey, + }), nil) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptScriptPubKey) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + compressedPubKey, err := pubKey.SerializeCompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(compressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := util.NewAddressScriptHash( + scriptPubKey, util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptScriptPubKey, err := PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script scriptPubKey for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptScriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): scriptPubKey, + }), nil); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.Inputs { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := secp256k1.GeneratePrivateKey() + if err != nil { + t.Errorf("failed to make privKey for %s: %s", + msg, err) + break + } + + pubKey, err := key.SchnorrPublicKey() + if err != nil { + t.Errorf("failed to make a publickey for %s: %s", + key, err) + break + } + + compressedPubKey, err := pubKey.SerializeCompressed() + if err != nil { + t.Errorf("failed to make a pubkey for %s: %s", + key, err) + break + } + + address, err := util.NewAddressPubKeyHash( + util.Hash160(compressedPubKey), util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + scriptPubKey, err := PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make scriptPubKey "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := util.NewAddressScriptHash( + scriptPubKey, util.Bech32PrefixKaspaTest) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptScriptPubKey, err := PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script scriptPubKey for "+ + "%s: %v", msg, err) + break + } + + _, err = SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptScriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): scriptPubKey, + }), nil) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err := SignTxOutput(&dagconfig.TestnetParams, + tx, i, scriptScriptPubKey, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): scriptPubKey, + }), nil) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptScriptPubKey) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } +} + +type tstInput struct { + txout *externalapi.DomainTransactionOutput + sigscriptGenerates bool + inputValidates bool + indexOutOfRange bool +} + +type tstSigScript struct { + name string + inputs []tstInput + hashType SigHashType + compress bool + scriptAtWrongIndex bool +} + +var coinbaseOutpoint = &externalapi.DomainOutpoint{ + Index: (1 << 32) - 1, +} + +// Pregenerated private key, with associated public key and scriptPubKeys +// for the uncompressed and compressed hash160. +var ( + privKeyD = secp256k1.SerializedPrivateKey{0x6b, 0x0f, 0xd8, 0xda, 0x54, 0x22, 0xd0, 0xb7, + 0xb4, 0xfc, 0x4e, 0x55, 0xd4, 0x88, 0x42, 0xb3, 0xa1, 0x65, + 0xac, 0x70, 0x7f, 0x3d, 0xa4, 0x39, 0x5e, 0xcb, 0x3b, 0xb0, + 0xd6, 0x0e, 0x06, 0x92} + uncompressedScriptPubKey = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5, + 0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32, + 0x53, 0x90, 0x0e, 0x0a, 0x86, 0xc9, 0xfa, 0x88, 0xac} + compressedScriptPubKey = []byte{0x76, 0xa9, 0x14, 0x27, 0x4d, 0x9f, 0x7f, + 0x61, 0x7e, 0x7c, 0x7a, 0x1c, 0x1f, 0xb2, 0x75, 0x79, 0x10, + 0x43, 0x65, 0x68, 0x27, 0x9d, 0x86, 0x88, 0xac} + shortScriptPubKey = []byte{0x76, 0xa9, 0x14, 0xd1, 0x7c, 0xb5, + 0xeb, 0xa4, 0x02, 0xcb, 0x68, 0xe0, 0x69, 0x56, 0xbf, 0x32, + 0x53, 0x90, 0x0e, 0x0a, 0x88, 0xac} +) + +// Pretend output amounts. +const coinbaseVal = 2500000000 +const fee = 5000000 + +var sigScriptTests = []tstSigScript{ + { + name: "one input uncompressed", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "two inputs uncompressed", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal + fee, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "one input compressed", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: compressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: true, + scriptAtWrongIndex: false, + }, + { + name: "two inputs compressed", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: compressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal + fee, + ScriptPublicKey: compressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: true, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashNone", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashNone, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashSingle", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashSingle, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashAll | SigHashAnyoneCanPay", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll | SigHashAnyOneCanPay, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType SigHashAnyoneCanPay", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: false, + indexOutOfRange: false, + }, + }, + hashType: SigHashAnyOneCanPay, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "hashType non-exist", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: false, + indexOutOfRange: false, + }, + }, + hashType: 0x04, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "invalid compression", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: false, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: true, + scriptAtWrongIndex: false, + }, + { + name: "short ScriptPubKey", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: shortScriptPubKey, + }, + sigscriptGenerates: false, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: false, + scriptAtWrongIndex: false, + }, + { + name: "valid script at wrong index", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal + fee, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: false, + scriptAtWrongIndex: true, + }, + { + name: "index out of range", + inputs: []tstInput{ + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + { + txout: &externalapi.DomainTransactionOutput{ + Value: coinbaseVal + fee, + ScriptPublicKey: uncompressedScriptPubKey, + }, + sigscriptGenerates: true, + inputValidates: true, + indexOutOfRange: false, + }, + }, + hashType: SigHashAll, + compress: false, + scriptAtWrongIndex: true, + }, +} + +// Test the sigscript generation for valid and invalid inputs, all +// hashTypes, and with and without compression. This test creates +// sigscripts to spend fake coinbase inputs, as sigscripts cannot be +// created for the DomainTransactions in txTests, since they come from the blockDAG +// and we don't have the private keys. +func TestSignatureScript(t *testing.T) { + t.Parallel() + + privKey, _ := secp256k1.DeserializePrivateKey(&privKeyD) + +nexttest: + for i := range sigScriptTests { + outputs := []*externalapi.DomainTransactionOutput{ + {Value: 500, ScriptPublicKey: []byte{OpReturn}}, + } + + inputs := []*externalapi.DomainTransactionInput{} + for range sigScriptTests[i].inputs { + inputs = append(inputs, &externalapi.DomainTransactionInput{ + PreviousOutpoint: *coinbaseOutpoint, + }) + } + tx := &externalapi.DomainTransaction{ + Version: 1, + Inputs: inputs, + Outputs: outputs, + } + + var script []byte + var err error + for j := range tx.Inputs { + var idx int + if sigScriptTests[i].inputs[j].indexOutOfRange { + t.Errorf("at test %v", sigScriptTests[i].name) + idx = len(sigScriptTests[i].inputs) + } else { + idx = j + } + script, err = SignatureScript(tx, idx, + sigScriptTests[i].inputs[j].txout.ScriptPublicKey, + sigScriptTests[i].hashType, privKey, + sigScriptTests[i].compress) + + if (err == nil) != sigScriptTests[i].inputs[j].sigscriptGenerates { + if err == nil { + t.Errorf("passed test '%v' incorrectly", + sigScriptTests[i].name) + } else { + t.Errorf("failed test '%v': %v", + sigScriptTests[i].name, err) + } + continue nexttest + } + if !sigScriptTests[i].inputs[j].sigscriptGenerates { + // done with this test + continue nexttest + } + + tx.Inputs[j].SignatureScript = script + } + + // If testing using a correct sigscript but for an incorrect + // index, use last input script for first input. Requires > 0 + // inputs for test. + if sigScriptTests[i].scriptAtWrongIndex { + tx.Inputs[0].SignatureScript = script + sigScriptTests[i].inputs[0].inputValidates = false + } + + // Validate tx input scripts + var scriptFlags ScriptFlags + for j := range tx.Inputs { + vm, err := NewEngine(sigScriptTests[i]. + inputs[j].txout.ScriptPublicKey, tx, j, scriptFlags, nil) + if err != nil { + t.Errorf("cannot create script vm for test %v: %v", + sigScriptTests[i].name, err) + continue nexttest + } + err = vm.Execute() + if (err == nil) != sigScriptTests[i].inputs[j].inputValidates { + if err == nil { + t.Errorf("passed test '%v' validation incorrectly: %v", + sigScriptTests[i].name, err) + } else { + t.Errorf("failed test '%v' validation: %v", + sigScriptTests[i].name, err) + } + continue nexttest + } + } + } +} diff --git a/domain/consensus/utils/txscript/standard_test.go b/domain/consensus/utils/txscript/standard_test.go new file mode 100644 index 000000000..8e19a3e91 --- /dev/null +++ b/domain/consensus/utils/txscript/standard_test.go @@ -0,0 +1,506 @@ +// Copyright (c) 2013-2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "bytes" + "reflect" + "testing" + + "github.com/kaspanet/kaspad/domain/dagconfig" + "github.com/kaspanet/kaspad/util" +) + +// mustParseShortForm parses the passed short form script and returns the +// resulting bytes. It panics if an error occurs. This is only used in the +// tests as a helper since the only way it can fail is if there is an error in +// the test source code. +func mustParseShortForm(script string) []byte { + s, err := parseShortForm(script) + if err != nil { + panic("invalid short form script in test source: err " + + err.Error() + ", script: " + script) + } + + return s +} + +// newAddressPubKeyHash returns a new util.AddressPubKeyHash from the +// provided hash. It panics if an error occurs. This is only used in the tests +// as a helper since the only way it can fail is if there is an error in the +// test source code. +func newAddressPubKeyHash(pkHash []byte) util.Address { + addr, err := util.NewAddressPubKeyHash(pkHash, util.Bech32PrefixKaspa) + if err != nil { + panic("invalid public key hash in test source") + } + + return addr +} + +// newAddressScriptHash returns a new util.AddressScriptHash from the +// provided hash. It panics if an error occurs. This is only used in the tests +// as a helper since the only way it can fail is if there is an error in the +// test source code. +func newAddressScriptHash(scriptHash []byte) util.Address { + addr, err := util.NewAddressScriptHashFromHash(scriptHash, + util.Bech32PrefixKaspa) + if err != nil { + panic("invalid script hash in test source") + } + + return addr +} + +// TestExtractScriptPubKeyAddrs ensures that extracting the type, addresses, and +// number of required signatures from scriptPubKeys works as intended. +func TestExtractScriptPubKeyAddrs(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + script []byte + addr util.Address + class ScriptClass + }{ + { + name: "standard p2pkh", + script: hexToBytes("76a914ad06dd6ddee55cbca9a9e3713bd" + + "7587509a3056488ac"), + addr: newAddressPubKeyHash(hexToBytes("ad06dd6ddee5" + + "5cbca9a9e3713bd7587509a30564")), + class: PubKeyHashTy, + }, + { + name: "standard p2sh", + script: hexToBytes("a91463bcc565f9e68ee0189dd5cc67f1b" + + "0e5f02f45cb87"), + addr: newAddressScriptHash(hexToBytes("63bcc565f9e6" + + "8ee0189dd5cc67f1b0e5f02f45cb")), + class: ScriptHashTy, + }, + + // The below are nonstandard script due to things such as + // invalid pubkeys, failure to parse, and not being of a + // standard form. + + { + name: "p2pk with uncompressed pk missing OP_CHECKSIG", + script: hexToBytes("410411db93e1dcdb8a016b49840f8c53b" + + "c1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddf" + + "b84ccf9744464f82e160bfa9b8b64f9d4c03f999b864" + + "3f656b412a3"), + addr: nil, + class: NonStandardTy, + }, + { + name: "valid signature from a sigscript - no addresses", + script: hexToBytes("47304402204e45e16932b8af514961a1d" + + "3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41022" + + "0181522ec8eca07de4860a4acdd12909d831cc56cbba" + + "c4622082221a8768d1d0901"), + addr: nil, + class: NonStandardTy, + }, + // Note the technically the pubkey is the second item on the + // stack, but since the address extraction intentionally only + // works with standard scriptPubKeys, this should not return any + // addresses. + { + name: "valid sigscript to reedeem p2pk - no addresses", + script: hexToBytes("493046022100ddc69738bf2336318e4e0" + + "41a5a77f305da87428ab1606f023260017854350ddc0" + + "22100817af09d2eec36862d16009852b7e3a0f6dd765" + + "98290b7834e1453660367e07a014104cd4240c198e12" + + "523b6f9cb9f5bed06de1ba37e96a1bbd13745fcf9d11" + + "c25b1dff9a519675d198804ba9962d3eca2d5937d58e" + + "5a75a71042d40388a4d307f887d"), + addr: nil, + class: NonStandardTy, + }, + { + name: "empty script", + script: []byte{}, + addr: nil, + class: NonStandardTy, + }, + { + name: "script that does not parse", + script: []byte{OpData45}, + addr: nil, + class: NonStandardTy, + }, + } + + t.Logf("Running %d tests.", len(tests)) + for i, test := range tests { + class, addr, _ := ExtractScriptPubKeyAddress( + test.script, &dagconfig.MainnetParams) + + if !reflect.DeepEqual(addr, test.addr) { + t.Errorf("ExtractScriptPubKeyAddress #%d (%s) unexpected "+ + "address\ngot %v\nwant %v", i, test.name, + addr, test.addr) + continue + } + + if class != test.class { + t.Errorf("ExtractScriptPubKeyAddress #%d (%s) unexpected "+ + "script type - got %s, want %s", i, test.name, + class, test.class) + continue + } + } +} + +// TestCalcScriptInfo ensures the CalcScriptInfo provides the expected results +// for various valid and invalid script pairs. +func TestCalcScriptInfo(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + sigScript string + scriptPubKey string + + isP2SH bool + + scriptInfo ScriptInfo + scriptInfoErr error + }{ + { + // Invented scripts, the hashes do not match + // Truncated version of test below: + name: "scriptPubKey doesn't parse", + sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " + + "SWAP ABS EQUAL", + scriptPubKey: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c", + isP2SH: true, + scriptInfoErr: scriptError(ErrMalformedPush, ""), + }, + { + name: "sigScript doesn't parse", + // Truncated version of p2sh script below. + sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " + + "SWAP ABS", + scriptPubKey: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c74 EQUAL", + isP2SH: true, + scriptInfoErr: scriptError(ErrMalformedPush, ""), + }, + { + // Invented scripts, the hashes do not match + name: "p2sh standard script", + sigScript: "1 81 DATA_25 DUP HASH160 DATA_20 0x010203" + + "0405060708090a0b0c0d0e0f1011121314 EQUALVERIFY " + + "CHECKSIG", + scriptPubKey: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c74 EQUAL", + isP2SH: true, + scriptInfo: ScriptInfo{ + ScriptPubKeyClass: ScriptHashTy, + NumInputs: 3, + ExpectedInputs: 3, // nonstandard p2sh. + SigOps: 1, + }, + }, + { + name: "p2sh nonstandard script", + sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " + + "SWAP ABS EQUAL", + scriptPubKey: "HASH160 DATA_20 0xfe441065b6532231de2fac56" + + "3152205ec4f59c74 EQUAL", + isP2SH: true, + scriptInfo: ScriptInfo{ + ScriptPubKeyClass: ScriptHashTy, + NumInputs: 3, + ExpectedInputs: -1, // nonstandard p2sh. + SigOps: 0, + }, + }, + } + + for _, test := range tests { + sigScript := mustParseShortForm(test.sigScript) + scriptPubKey := mustParseShortForm(test.scriptPubKey) + + si, err := CalcScriptInfo(sigScript, scriptPubKey, test.isP2SH) + if e := checkScriptError(err, test.scriptInfoErr); e != nil { + t.Errorf("scriptinfo test %q: %v", test.name, e) + continue + } + if err != nil { + continue + } + + if *si != test.scriptInfo { + t.Errorf("%s: scriptinfo doesn't match expected. "+ + "got: %q expected %q", test.name, *si, + test.scriptInfo) + continue + } + } +} + +// bogusAddress implements the util.Address interface so the tests can ensure +// unsupported address types are handled properly. +type bogusAddress struct{} + +// EncodeAddress simply returns an empty string. It exists to satisfy the +// util.Address interface. +func (b *bogusAddress) EncodeAddress() string { + return "" +} + +// ScriptAddress simply returns an empty byte slice. It exists to satisfy the +// util.Address interface. +func (b *bogusAddress) ScriptAddress() []byte { + return nil +} + +// IsForPrefix lies blatantly to satisfy the util.Address interface. +func (b *bogusAddress) IsForPrefix(prefix util.Bech32Prefix) bool { + return true // why not? +} + +// String simply returns an empty string. It exists to satisfy the +// util.Address interface. +func (b *bogusAddress) String() string { + return "" +} + +func (b *bogusAddress) Prefix() util.Bech32Prefix { + return util.Bech32PrefixUnknown +} + +// TestPayToAddrScript ensures the PayToAddrScript function generates the +// correct scripts for the various types of addresses. +func TestPayToAddrScript(t *testing.T) { + t.Parallel() + + // 1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX + p2pkhMain, err := util.NewAddressPubKeyHash(hexToBytes("e34cce70c86"+ + "373273efcc54ce7d2a491bb4a0e84"), util.Bech32PrefixKaspa) + if err != nil { + t.Fatalf("Unable to create public key hash address: %v", err) + } + + // Taken from transaction: + // b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d + p2shMain, _ := util.NewAddressScriptHashFromHash(hexToBytes("e8c300"+ + "c87986efa84c37c0519929019ef86eb5b4"), util.Bech32PrefixKaspa) + if err != nil { + t.Fatalf("Unable to create script hash address: %v", err) + } + + // Errors used in the tests below defined here for convenience and to + // keep the horizontal test size shorter. + errUnsupportedAddress := scriptError(ErrUnsupportedAddress, "") + + tests := []struct { + in util.Address + expected string + err error + }{ + // pay-to-pubkey-hash address on mainnet + { + p2pkhMain, + "DUP HASH160 DATA_20 0xe34cce70c86373273efcc54ce7d2a4" + + "91bb4a0e8488 CHECKSIG", + nil, + }, + // pay-to-script-hash address on mainnet + { + p2shMain, + "HASH160 DATA_20 0xe8c300c87986efa84c37c0519929019ef8" + + "6eb5b4 EQUAL", + nil, + }, + + // Supported address types with nil pointers. + {(*util.AddressPubKeyHash)(nil), "", errUnsupportedAddress}, + {(*util.AddressScriptHash)(nil), "", errUnsupportedAddress}, + + // Unsupported address type. + {&bogusAddress{}, "", errUnsupportedAddress}, + } + + t.Logf("Running %d tests", len(tests)) + for i, test := range tests { + scriptPubKey, err := PayToAddrScript(test.in) + if e := checkScriptError(err, test.err); e != nil { + t.Errorf("PayToAddrScript #%d unexpected error - "+ + "got %v, want %v", i, err, test.err) + continue + } + + expected := mustParseShortForm(test.expected) + if !bytes.Equal(scriptPubKey, expected) { + t.Errorf("PayToAddrScript #%d got: %x\nwant: %x", + i, scriptPubKey, expected) + continue + } + } +} + +// scriptClassTests houses several test scripts used to ensure various class +// determination is working as expected. It's defined as a test global versus +// inside a function scope since this spans both the standard tests and the +// consensus tests (pay-to-script-hash is part of consensus). +var scriptClassTests = []struct { + name string + script string + class ScriptClass +}{ + // p2pk + { + name: "Pay Pubkey", + script: "DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382e" + + "97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e16" + + "0bfa9b8b64f9d4c03f999b8643f656b412a3 CHECKSIG", + class: NonStandardTy, + }, + // tx 599e47a8114fe098103663029548811d2651991b62397e057f0c863c2bc9f9ea + { + name: "Pay PubkeyHash", + script: "DUP HASH160 DATA_20 0x660d4ef3a743e3e696ad990364e555" + + "c271ad504b EQUALVERIFY CHECKSIG", + class: PubKeyHashTy, + }, + // mutlisig + { + name: "multisig", + script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4" + + "5329a00357b3a7886211ab414d55a 1 CHECKMULTISIG", + class: NonStandardTy, + }, + // tx e5779b9e78f9650debc2893fd9636d827b26b4ddfa6a8172fe8708c924f5c39d + { + name: "P2SH", + script: "HASH160 DATA_20 0x433ec2ac1ffa1b7b7d027f564529c57197f" + + "9ae88 EQUAL", + class: ScriptHashTy, + }, + + { + // Nulldata. It is standard in Bitcoin but not in Kaspa + name: "nulldata", + script: "RETURN 0", + class: NonStandardTy, + }, + + // The next few are almost multisig (it is the more complex script type) + // but with various changes to make it fail. + { + // Multisig but invalid nsigs. + name: "strange 1", + script: "DUP DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da45" + + "329a00357b3a7886211ab414d55a 1 CHECKMULTISIG", + class: NonStandardTy, + }, + { + // Multisig but invalid pubkey. + name: "strange 2", + script: "1 1 1 CHECKMULTISIG", + class: NonStandardTy, + }, + { + // Multisig but no matching npubkeys opcode. + name: "strange 3", + script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4532" + + "9a00357b3a7886211ab414d55a DATA_33 0x0232abdc893e7f0" + + "631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a " + + "CHECKMULTISIG", + class: NonStandardTy, + }, + { + // Multisig but with multisigverify. + name: "strange 4", + script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4532" + + "9a00357b3a7886211ab414d55a 1 CHECKMULTISIGVERIFY", + class: NonStandardTy, + }, + { + // Multisig but wrong length. + name: "strange 5", + script: "1 CHECKMULTISIG", + class: NonStandardTy, + }, + { + name: "doesn't parse", + script: "DATA_5 0x01020304", + class: NonStandardTy, + }, + { + name: "multisig script with wrong number of pubkeys", + script: "2 " + + "DATA_33 " + + "0x027adf5df7c965a2d46203c781bd4dd8" + + "21f11844136f6673af7cc5a4a05cd29380 " + + "DATA_33 " + + "0x02c08f3de8ee2de9be7bd770f4c10eb0" + + "d6ff1dd81ee96eedd3a9d4aeaf86695e80 " + + "3 CHECKMULTISIG", + class: NonStandardTy, + }, +} + +// TestScriptClass ensures all the scripts in scriptClassTests have the expected +// class. +func TestScriptClass(t *testing.T) { + t.Parallel() + + for _, test := range scriptClassTests { + script := mustParseShortForm(test.script) + class := GetScriptClass(script) + if class != test.class { + t.Errorf("%s: expected %s got %s (script %x)", test.name, + test.class, class, script) + continue + } + } +} + +// TestStringifyClass ensures the script class string returns the expected +// string for each script class. +func TestStringifyClass(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + class ScriptClass + stringed string + }{ + { + name: "nonstandardty", + class: NonStandardTy, + stringed: "nonstandard", + }, + { + name: "pubkeyhash", + class: PubKeyHashTy, + stringed: "pubkeyhash", + }, + { + name: "scripthash", + class: ScriptHashTy, + stringed: "scripthash", + }, + { + name: "broken", + class: ScriptClass(255), + stringed: "Invalid", + }, + } + + for _, test := range tests { + typeString := test.class.String() + if typeString != test.stringed { + t.Errorf("%s: got %#q, want %#q", test.name, + typeString, test.stringed) + } + } +} diff --git a/domain/miningmanager/mempool/policy_test.go b/domain/miningmanager/mempool/policy_test.go index bff5db24f..15ccc13a1 100644 --- a/domain/miningmanager/mempool/policy_test.go +++ b/domain/miningmanager/mempool/policy_test.go @@ -8,7 +8,8 @@ import ( "bytes" "testing" - "github.com/kaspanet/kaspad/app/appmessage" + "github.com/kaspanet/kaspad/domain/consensus/utils/constants" + consensusexternalapi "github.com/kaspanet/kaspad/domain/consensus/model/externalapi" "github.com/kaspanet/kaspad/domain/consensus/utils/txscript" "github.com/kaspanet/kaspad/util" @@ -174,7 +175,7 @@ func TestCheckTransactionStandard(t *testing.T) { dummyTxIn := consensusexternalapi.DomainTransactionInput{ PreviousOutpoint: dummyPrevOut, SignatureScript: dummySigScript, - Sequence: appmessage.MaxTxInSequenceNum, + Sequence: constants.MaxTxInSequenceNum, } addrHash := [20]byte{0x01} addr, err := util.NewAddressPubKeyHash(addrHash[:], util.Bech32PrefixKaspaTest) @@ -205,7 +206,7 @@ func TestCheckTransactionStandard(t *testing.T) { }, { name: "Transaction version too high", - tx: consensusexternalapi.DomainTransaction{Version: appmessage.TxVersion + 1, Inputs: []*consensusexternalapi.DomainTransactionInput{&dummyTxIn}, Outputs: []*consensusexternalapi.DomainTransactionOutput{&dummyTxOut}}, + tx: consensusexternalapi.DomainTransaction{Version: constants.TransactionVersion + 1, Inputs: []*consensusexternalapi.DomainTransactionInput{&dummyTxIn}, Outputs: []*consensusexternalapi.DomainTransactionOutput{&dummyTxOut}}, height: 300000, isStandard: false, code: RejectNonstandard, @@ -238,7 +239,7 @@ func TestCheckTransactionStandard(t *testing.T) { PreviousOutpoint: dummyPrevOut, SignatureScript: bytes.Repeat([]byte{0x00}, maxStandardSigScriptSize+1), - Sequence: appmessage.MaxTxInSequenceNum, + Sequence: constants.MaxTxInSequenceNum, }}, Outputs: []*consensusexternalapi.DomainTransactionOutput{&dummyTxOut}}, height: 300000, isStandard: false, @@ -250,7 +251,7 @@ func TestCheckTransactionStandard(t *testing.T) { PreviousOutpoint: dummyPrevOut, SignatureScript: []byte{ txscript.OpCheckSigVerify}, - Sequence: appmessage.MaxTxInSequenceNum, + Sequence: constants.MaxTxInSequenceNum, }}, Outputs: []*consensusexternalapi.DomainTransactionOutput{&dummyTxOut}}, height: 300000, isStandard: false, diff --git a/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction.go b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction.go index a58af0b46..09353f68c 100644 --- a/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction.go +++ b/infrastructure/network/netadapter/server/grpcserver/protowire/p2p_transaction.go @@ -45,7 +45,7 @@ func (x *TransactionMessage) toAppMessage() (appmessage.Message, error) { return nil, err } - var payloadHash *externalapi.DomainHash + payloadHash := &externalapi.DomainHash{} if x.PayloadHash != nil { payloadHash, err = x.PayloadHash.toDomain() if err != nil { @@ -60,7 +60,7 @@ func (x *TransactionMessage) toAppMessage() (appmessage.Message, error) { LockTime: x.LockTime, SubnetworkID: *subnetworkID, Gas: x.Gas, - PayloadHash: payloadHash, + PayloadHash: *payloadHash, Payload: x.Payload, }, nil } @@ -86,10 +86,6 @@ func (x *TransactionMessage) fromAppMessage(msgTx *appmessage.MsgTx) { } } - var payloadHash *Hash - if msgTx.PayloadHash != nil { - payloadHash = domainHashToProto(msgTx.PayloadHash) - } *x = TransactionMessage{ Version: msgTx.Version, Inputs: protoInputs, @@ -97,7 +93,7 @@ func (x *TransactionMessage) fromAppMessage(msgTx *appmessage.MsgTx) { LockTime: msgTx.LockTime, SubnetworkID: domainSubnetworkIDToProto(&msgTx.SubnetworkID), Gas: msgTx.Gas, - PayloadHash: payloadHash, + PayloadHash: domainHashToProto(&msgTx.PayloadHash), Payload: msgTx.Payload, }