diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..62a53cea --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2013 Conformal Systems LLC. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md index f8bf0ac0..28662ad6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,75 @@ btcscript ========= -Package btcscript implements the bitcoin transaction scripts. +Package btcscript implements the bitcoin transaction scripts. There is a test +suite which is aiming to reach 100% code coverage. See +`test_coverage.txt` for the current coverage (using gocov). On a +UNIX-like OS, the script `cov_report.sh` can be used to generate the +report. Package btcscript is licensed under the liberal ISC license. + +This package is one of the core packages from btcd, an alternative full-node +implementation of bitcoin which is under active development by Conformal. +Although it was primarily written for btcd, this package has intentionally been +designed so it can be used as a standalone package for any projects needing to +use or validate bitcoin transaction scripts. + +## Bitcoin Scripts + +Bitcoin provides a stack-based, FORTH-like langauge for the scripts in +the bitcoin transactions. This language is not turing complete +although it is still fairly powerful. A description of the language +can be found at https://en.bitcoin.it/wiki/Script + +## Sample Use + +```Go + pkscript := txS.TxOut[origintxidx].PkScript + engine, err := btcscript.NewScript(sigScript, pkscript, txInIdx, + txValidator, pver, + timestamp.After(btcscript.Bip16Activation)) + err = engine.Execute() +``` + +## Documentation + +Full `go doc` style documentation for the project can be viewed online without +installing this package by using the GoDoc site +[here](http://godoc.org/github.com/conformal/btcscript). + +You can also view the documentation locally once the package is installed with +the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to +http://localhost:6060/pkg/github.com/conformal/btcscript + +## Installation + +```bash +$ go get github.com/conformal/btcscript +``` + +## TODO + +- Increase test coverage to 100% + +## GPG Verification Key + +All official release tags are signed by Conformal so users can ensure the code +has not been tampered with and is coming from Conformal. To verify the +signature perform the following: + +- Download the public key from the Conformal website at + https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt + +- Import the public key into your GPG keyring: + ```bash + gpg --import GIT-GPG-KEY-conformal.txt + ``` + +- Verify the release tag with the following command where `TAG_NAME` is a + placeholder for the specific tag: + ```bash + git tag -v TAG_NAME + ``` + +## License + +Package btcscript is licensed under the liberal ISC License. diff --git a/address.go b/address.go new file mode 100644 index 00000000..02eacc8a --- /dev/null +++ b/address.go @@ -0,0 +1,190 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript + +import ( + "github.com/conformal/btcutil" + "github.com/conformal/btcwire" +) + +// ScriptType is an enum type that represents the type of a script. It is +// returned from ScriptToAddress as part of the metadata about the script. +// It implements the Stringer interface for nice printing. +type ScriptType int + +// String Converts the enumeration to a nice string value instead of a number. +func (t ScriptType) String() string { + if int(t) > len(scriptTypeToName) || int(t) < 0 { + return "Invalid" + } + return scriptTypeToName[t] +} + +// Constant types representing known types of script found in the wild +const ( + ScriptUnknown ScriptType = iota + ScriptAddr + ScriptPubKey + ScriptStrange + ScriptGeneration +) + +var scriptTypeToName = []string{ + ScriptUnknown: "Unknown", + ScriptAddr: "Addr", + ScriptPubKey: "Pubkey", + ScriptStrange: "Strange", + ScriptGeneration: "Generation", // ScriptToAddress does not recieve enough information to identify Generation scripts. +} + +type pkformat struct { + addrtype ScriptType + parsetype int + length int + databytes []pkbytes + allowmore bool +} + +type pkbytes struct { + off int + val byte +} + +const ( + scrPayAddr = iota + scrCollectAddr + scrCollectAddrComp + scrGeneratePubkeyAddr + scrPubkeyAddr + scrPubkeyAddrComp + scrNoAddr +) + +// ScriptToAddress extracts a payment address and the type out of a PkScript +func ScriptToAddress(script []byte) (ScriptType, string, error) { + // Currently this only understands one form of PkScript + validformats := []pkformat{ + {ScriptAddr, scrPayAddr, 25, []pkbytes{{0, OP_DUP}, {1, OP_HASH160}, {2, OP_DATA_20}, {23, OP_EQUALVERIFY}, {24, OP_CHECKSIG}}, true}, + {ScriptAddr, scrCollectAddr, 142, []pkbytes{{0, OP_DATA_75}, {76, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddr, 141, []pkbytes{{0, OP_DATA_74}, {75, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddr, 140, []pkbytes{{0, OP_DATA_73}, {74, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddr, 139, []pkbytes{{0, OP_DATA_72}, {73, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddr, 138, []pkbytes{{0, OP_DATA_71}, {72, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddr, 137, []pkbytes{{0, OP_DATA_70}, {71, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddr, 136, []pkbytes{{0, OP_DATA_69}, {70, OP_DATA_65}}, false}, + {ScriptAddr, scrCollectAddrComp, 110, []pkbytes{{0, OP_DATA_75}, {76, OP_DATA_33}}, false}, + {ScriptAddr, scrCollectAddrComp, 109, []pkbytes{{0, OP_DATA_74}, {75, OP_DATA_33}}, false}, + {ScriptAddr, scrCollectAddrComp, 108, []pkbytes{{0, OP_DATA_73}, {74, OP_DATA_33}}, false}, + {ScriptAddr, scrCollectAddrComp, 107, []pkbytes{{0, OP_DATA_72}, {73, OP_DATA_33}}, false}, + {ScriptAddr, scrCollectAddrComp, 106, []pkbytes{{0, OP_DATA_71}, {72, OP_DATA_33}}, false}, + {ScriptAddr, scrCollectAddrComp, 105, []pkbytes{{0, OP_DATA_70}, {71, OP_DATA_33}}, false}, + {ScriptAddr, scrCollectAddrComp, 104, []pkbytes{{0, OP_DATA_69}, {70, OP_DATA_33}}, false}, + {ScriptPubKey, scrGeneratePubkeyAddr, 74, []pkbytes{{0, OP_DATA_73}}, false}, + {ScriptPubKey, scrGeneratePubkeyAddr, 73, []pkbytes{{0, OP_DATA_72}}, false}, + {ScriptPubKey, scrGeneratePubkeyAddr, 72, []pkbytes{{0, OP_DATA_71}}, false}, + {ScriptPubKey, scrGeneratePubkeyAddr, 71, []pkbytes{{0, OP_DATA_70}}, false}, + {ScriptPubKey, scrGeneratePubkeyAddr, 70, []pkbytes{{0, OP_DATA_69}}, false}, + {ScriptPubKey, scrPubkeyAddr, 67, []pkbytes{{0, OP_DATA_65}, {66, OP_CHECKSIG}}, true}, + {ScriptPubKey, scrPubkeyAddrComp, 35, []pkbytes{{0, OP_DATA_33}, {34, OP_CHECKSIG}}, true}, + {ScriptStrange, scrNoAddr, 33, []pkbytes{{0, OP_DATA_32}}, false}, + {ScriptStrange, scrNoAddr, 33, []pkbytes{{0, OP_HASH160}, {1, OP_DATA_20}, {22, OP_EQUAL}}, false}, + } + + var format pkformat + var success bool + for _, format = range validformats { + if format.length != len(script) { + if len(script) < format.length { + continue + } + if !format.allowmore { + continue + } + } + success = true + for _, pkbyte := range format.databytes { + if pkbyte.off >= len(script) { + success = false + break + } + if script[pkbyte.off] != pkbyte.val { + log.Tracef("off at byte %v %v %v", pkbyte.off, script[pkbyte.off], pkbyte.val) + success = false + break + } else { + log.Tracef("match at byte %v: ok", pkbyte.off) + } + } + if success == true { + break + } + } + + if success == false && len(script) > 1 { + // check for a few special case + if script[len(script)-1] == OP_CHECK_MULTISIG { + // Multisig ScriptPubKey + return ScriptStrange, "Unknown", nil + } + if script[0] == OP_0 && (len(script) <= 75 && byte(len(script)) == script[1]+2) { + // Multisig ScriptSig + return ScriptStrange, "Unknown", nil + } + if script[0] == OP_HASH160 && len(script) == 23 && script[22] == OP_EQUAL { + // Multisig ScriptSig + return ScriptStrange, "Unknown", nil + } + if script[0] == OP_DATA_36 && len(script) == 37 { + // Multisig ScriptSig + return ScriptStrange, "Unknown", nil + } + + return ScriptUnknown, "Unknown", StackErrUnknownAddress + } + + var atype byte + var abuf []byte + var addr string + switch format.parsetype { + case scrPayAddr: + atype = 0x00 + abuf = script[3:23] + case scrCollectAddr: + // script is replaced with the md160 of the pubkey + slen := len(script) + pubkey := script[slen-65:] + abuf = calcHash160(pubkey) + case scrCollectAddrComp: + // script is replaced with the md160 of the pubkey + slen := len(script) + pubkey := script[slen-33:] + abuf = calcHash160(pubkey) + case scrGeneratePubkeyAddr: + atype = 0x00 + addr = "Unknown" + case scrNoAddr: + addr = "Unknown" + case scrPubkeyAddr: + atype = 0x00 + pubkey := script[1:66] + abuf = calcHash160(pubkey) + case scrPubkeyAddrComp: + atype = 0x00 + pubkey := script[1:34] + abuf = calcHash160(pubkey) + default: + log.Warnf("parsetype is %v", format.parsetype) + } + + if abuf != nil { + addrbytes := append([]byte{atype}, abuf[:]...) + + cksum := btcwire.DoubleSha256(addrbytes) + addrbytes = append(addrbytes, cksum[:4]...) + addr = btcutil.Base58Encode(addrbytes) + } + + return format.addrtype, addr, nil +} diff --git a/address_test.go b/address_test.go new file mode 100644 index 00000000..d1f3f96a --- /dev/null +++ b/address_test.go @@ -0,0 +1,245 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. +package btcscript_test + +import ( + "github.com/conformal/btcscript" + "testing" +) + +type addressTest struct { + script []byte + address string + shouldFail error + class btcscript.ScriptType +} + +var addressTests = []addressTest{ + {script: []byte{btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, + 0x1e, 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, + 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, + 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, + 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, + 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, + 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, + 0xa3, btcscript.OP_CHECKSIG}, + address: "12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", + class: btcscript.ScriptPubKey, + }, + {script: []byte{btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, + 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, + 0x1e, 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, + 0x2e, 0xca, 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, + 0x5c, 0xb2, 0xe0, 0xea, 0xdd, 0xfb, 0x84, 0xcc, + 0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, + 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, + 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, 0x12, + 0xa3}, + shouldFail: btcscript.StackErrUnknownAddress, + }, + {script: []byte{btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, + 0x32, 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, + 0xa1, 0xa2, 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, + 0xe9, 0xd6, 0x24, 0xc6, 0xc6, 0x15, 0x48, 0xab, + 0x5f, 0xb8, 0xcd, 0x41, 0x02, 0x20, 0x18, 0x15, + 0x22, 0xec, 0x8e, 0xca, 0x07, 0xde, 0x48, 0x60, + 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, 0x83, 0x1c, + 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, 0x22, + 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01}, + address: "Unknown", + class: btcscript.ScriptPubKey, + }, + {script: []byte{btcscript.OP_DUP, btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0xad, 0x06, 0xdd, 0x6d, 0xde, 0xe5, 0x5c, 0xbc, + 0xa9, 0xa9, 0xe3, 0x71, 0x3b, 0xd7, 0x58, 0x75, + 0x09, 0xa3, 0x05, 0x64, + btcscript.OP_EQUALVERIFY, btcscript.OP_CHECKSIG, + }, + address: "1Gmt8AzabtngttF3PcZzLR1p7uCMaHNuGY", + class: btcscript.ScriptAddr, + }, + {script: []byte{btcscript.OP_DATA_73, + 0x30, 0x46, 0x02, 0x21, 0x00, 0xdd, 0xc6, 0x97, + 0x38, 0xbf, 0x23, 0x36, 0x31, 0x8e, 0x4e, 0x04, + 0x1a, 0x5a, 0x77, 0xf3, 0x05, 0xda, 0x87, 0x42, + 0x8a, 0xb1, 0x60, 0x6f, 0x02, 0x32, 0x60, 0x01, + 0x78, 0x54, 0x35, 0x0d, 0xdc, 0x02, 0x21, 0x00, + 0x81, 0x7a, 0xf0, 0x9d, 0x2e, 0xec, 0x36, 0x86, + 0x2d, 0x16, 0x00, 0x98, 0x52, 0xb7, 0xe3, 0xa0, + 0xf6, 0xdd, 0x76, 0x59, 0x82, 0x90, 0xb7, 0x83, + 0x4e, 0x14, 0x53, 0x66, 0x03, 0x67, 0xe0, 0x7a, + 0x01, + btcscript.OP_DATA_65, + 0x04, 0xcd, 0x42, 0x40, 0xc1, 0x98, 0xe1, 0x25, + 0x23, 0xb6, 0xf9, 0xcb, 0x9f, 0x5b, 0xed, 0x06, + 0xde, 0x1b, 0xa3, 0x7e, 0x96, 0xa1, 0xbb, 0xd1, + 0x37, 0x45, 0xfc, 0xf9, 0xd1, 0x1c, 0x25, 0xb1, + 0xdf, 0xf9, 0xa5, 0x19, 0x67, 0x5d, 0x19, 0x88, + 0x04, 0xba, 0x99, 0x62, 0xd3, 0xec, 0xa2, 0xd5, + 0x93, 0x7d, 0x58, 0xe5, 0xa7, 0x5a, 0x71, 0x04, + 0x2d, 0x40, 0x38, 0x8a, 0x4d, 0x30, 0x7f, 0x88, + 0x7d}, + address: "16tRBxwU7t5hEHaPLqiE35gS3jaGBppraH", + class: btcscript.ScriptAddr, + }, + {script: []byte{btcscript.OP_DATA_73, + 0x30, 0x46, 0x02, 0x21, 0x00, 0xac, 0x7e, 0x4e, + 0x27, 0xf2, 0xb1, 0x1c, 0xb8, 0x6f, 0xb5, 0xaa, + 0x87, 0x2a, 0xb9, 0xd3, 0x2c, 0xdc, 0x08, 0x33, + 0x80, 0x73, 0x3e, 0x3e, 0x98, 0x47, 0xff, 0x77, + 0xa0, 0x69, 0xcd, 0xdf, 0xab, 0x02, 0x21, 0x00, + 0xc0, 0x4c, 0x3e, 0x6f, 0xfe, 0x88, 0xa1, 0x5b, + 0xc5, 0x07, 0xb8, 0xe5, 0x71, 0xaa, 0x35, 0x92, + 0x8a, 0xcf, 0xe1, 0x5a, 0x4a, 0x23, 0x20, 0x1b, + 0x08, 0xfe, 0x3c, 0x7b, 0x3c, 0x97, 0xc8, 0x8f, + 0x01, + btcscript.OP_DATA_33, + 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xa7, 0xa8, 0x45, 0xbd, 0x25, 0x68, 0x9e, 0xdb, + 0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30, + 0x36, + }, + address: "1272555ceTPn2WepjzVgFESWdfNQjqdjgp", + class: btcscript.ScriptAddr, + }, + {script: []byte{btcscript.OP_DATA_32, + 0x30, 0x46, 0x02, 0x21, 0x00, 0xac, 0x7e, 0x4e, + 0x27, 0xf2, 0xb1, 0x1c, 0xb8, 0x6f, 0xb5, 0xaa, + 0x87, 0x2a, 0xb9, 0xd3, 0x2c, 0xdc, 0x08, 0x33, + 0x80, 0x73, 0x3e, 0x3e, 0x98, 0x47, 0xff, 0x77, + }, + address: "Unknown", + class: btcscript.ScriptStrange, + }, + {script: []byte{btcscript.OP_DATA_33, + 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xa7, 0xa8, 0x45, 0xbd, 0x25, 0x68, 0x9e, 0xdb, + 0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30, + 0x36, + btcscript.OP_CHECKSIG, + }, + address: "1272555ceTPn2WepjzVgFESWdfNQjqdjgp", + class: btcscript.ScriptPubKey, + }, + {script: []byte{btcscript.OP_DATA_33, + 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xa7, 0xa8, 0x45, 0xbd, 0x25, 0x68, 0x9e, 0xdb, + 0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30, + 0x36, + btcscript.OP_CHECK_MULTISIG, // note this isn't a real tx + }, + address: "Unknown", + class: btcscript.ScriptStrange, + }, + {script: []byte{btcscript.OP_0, btcscript.OP_DATA_33, + 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xa7, 0xa8, 0x45, 0xbd, 0x25, 0x68, 0x9e, 0xdb, + 0x72, 0x3d, 0x5a, 0xd4, 0x06, 0x8d, 0xdd, 0x30, + 0x36, // note this isn't a real tx + }, + address: "Unknown", + class: btcscript.ScriptStrange, + }, + {script: []byte{btcscript.OP_HASH160, btcscript.OP_DATA_20, + 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xa7, 0xa8, 0x45, 0xbd, + btcscript.OP_EQUAL, // note this isn't a real tx + }, + address: "Unknown", + class: btcscript.ScriptStrange, + }, + {script: []byte{btcscript.OP_DATA_36, + 0x02, 0x40, 0x05, 0xc9, 0x45, 0xd8, 0x6a, 0xc6, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xb0, 0x1f, 0xb0, 0x42, 0x58, 0x34, 0x5a, 0xbe, + 0xa7, 0xa8, 0x45, 0xbd, + // note this isn't a real tx + }, + address: "Unknown", + class: btcscript.ScriptStrange, + }, +} + +func TestAddresses(t *testing.T) { + for i, s := range addressTests { + class, address, err := btcscript.ScriptToAddress(s.script) + if s.shouldFail != nil { + if err != s.shouldFail { + t.Errorf("Address test %v failed is err [%v] should be [%v]", i, err, s.shouldFail) + } + } else { + if err != nil { + t.Errorf("Address test %v failed err %v", i, err) + } else { + if s.address != address { + t.Errorf("Address test %v mismatch is [%v] want [%v]", i, address, s.address) + } + if s.class != class { + t.Errorf("Address test %v class mismatch is [%v] want [%v]", i, class, s.class) + } + } + } + + } +} + +type stringifyTest struct { + name string + scripttype btcscript.ScriptType + stringed string +} + +var stringifyTests = []stringifyTest{ + stringifyTest{ + name: "unknown", + scripttype: btcscript.ScriptUnknown, + stringed: "Unknown", + }, + stringifyTest{ + name: "addr", + scripttype: btcscript.ScriptAddr, + stringed: "Addr", + }, + stringifyTest{ + name: "pubkey", + scripttype: btcscript.ScriptPubKey, + stringed: "Pubkey", + }, + stringifyTest{ + name: "strange", + scripttype: btcscript.ScriptStrange, + stringed: "Strange", + }, + stringifyTest{ + name: "generation", + scripttype: btcscript.ScriptGeneration, + stringed: "Generation", + }, + stringifyTest{ + name: "not in enum", + scripttype: btcscript.ScriptType(255), + stringed: "Invalid", + }, +} + +func TestStringify(t *testing.T) { + for _, test := range stringifyTests { + typeString := test.scripttype.String() + if typeString != test.stringed { + t.Errorf("%s: got \"%s\" expected \"%s\"", test.name, + typeString, test.stringed) + } + } +} diff --git a/cov_report.sh b/cov_report.sh new file mode 100644 index 00000000..307f05b7 --- /dev/null +++ b/cov_report.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# This script uses gocov to generate a test coverage report. +# The gocov tool my be obtained with the following command: +# go get github.com/axw/gocov/gocov +# +# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. + +# Check for gocov. +type gocov >/dev/null 2>&1 +if [ $? -ne 0 ]; then + echo >&2 "This script requires the gocov tool." + echo >&2 "You may obtain it with the following command:" + echo >&2 "go get github.com/axw/gocov/gocov" + exit 1 +fi +gocov test | gocov report diff --git a/doc.go b/doc.go new file mode 100644 index 00000000..0fb28f23 --- /dev/null +++ b/doc.go @@ -0,0 +1,59 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package btcscript implements bitcoin transaction scripts. + +A complete description of the script language used by bitcoin can be found at +https://en.bitcoin.it/wiki/Script. The following only serves as a quick +overview to provide information on how to use the package. + +This package provides data structures and functions to parse and execute +bitcoin transaction scripts. + +Script Overview + +Bitcoin transaction scripts are written in a stack-base, FORTH-like language. + +The bitcoin script language consists of a number of opcodes which fall into +several categories such pushing and popping data to and from the stack, +performing basic and bitwise arithmetic, conditional branching, comparing +hashes, and checking cryptographic signatures. Scripts are processed from left +to right and intentionally do not provide loops. + +The vast majority of Bitcoin scripts at the time of this writing are of several +standard forms which consist of a spender providing a public key and a signature +which proves the spender owns the associated private key. This information +is used to prove the the spender is authorized to perform the transaction. + +One benefit of using a scripting language is added flexibility in specifying +what conditions must be met in order to spend bitcoins. + +Usage + +The usage of this package consists of creating a new script engine for a pair +of transaction inputs and outputs and using the engine to execute the scripts. + +The following function is an example of how to create and execute a script +engine to validate a transaction. + + // ValidateTx validates the txIdx'th input of tx. The output transaction + // corresponding to the this input is the txInIdx'th output of txIn. The + // block timestamp of tx is timestamp and the protocol version involved + // is pver. + func ValidateTx(tx *btcwire.MsgTx, txIdx int, txIn *btcwire.MsgTx, txInIdx int, pver int, timestamp time.Time) { + pkScript := txIn.TxOut[txInIdx].PkScript + sigScript := tx.txIn[TxIdx] + engine, err := btcscript.NewScript(sigScript, pkScript, txInIdx, + tx, pver, timestamp.After(btcscript.Bip16Activation)) + return engine.Execute() + } + +Errors + +Errors returned by this package are of the form btcscript.StackErrX where X +indicates the specific error. See Variables in the package documentation for a +full list. +*/ +package btcscript diff --git a/internal_test.go b/internal_test.go new file mode 100644 index 00000000..a4196b31 --- /dev/null +++ b/internal_test.go @@ -0,0 +1,44 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript + +// this file is present to export some internal interfaces so that we can +// test them reliably. + +func TstRemoveOpcode(pkscript []byte, opcode byte) ([]byte, error) { + pops, err := parseScript(pkscript) + if err != nil { + return nil, err + } + pops = removeOpcode(pops, opcode) + return unparseScript(pops), nil +} + +func TstRemoveOpcodeByData(pkscript []byte, data []byte) ([]byte, error) { + pops, err := parseScript(pkscript) + if err != nil { + return nil, err + } + pops = removeOpcodeByData(pops, data) + return unparseScript(pops), nil +} + +type TstScriptType scriptType + +const ( + TstPubKeyTy TstScriptType = TstScriptType(pubKeyTy) + TstPubKeyHashTy = TstScriptType(pubKeyHashTy) + TstScriptHashTy = TstScriptType(scriptHashTy) + TstMultiSigTy = TstScriptType(multiSigTy) + TstNonStandardTy = TstScriptType(nonStandardTy) +) + +func TstTypeOfScript(script []byte) TstScriptType { + pops, err := parseScript(script) + if err != nil { + return TstNonStandardTy + } + return TstScriptType(typeOfScript(pops)) +} diff --git a/log.go b/log.go new file mode 100644 index 00000000..28faf744 --- /dev/null +++ b/log.go @@ -0,0 +1,65 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript + +import ( + "errors" + "github.com/conformal/seelog" + "io" +) + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log seelog.LoggerInterface + +// The default amount of logging is none. +func init() { + DisableLog() +} + +// DisableLog disables all library log output. Logging output is disabled +// by default until either UseLogger or SetLogWriter are called. +func DisableLog() { + log = seelog.Disabled +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using seelog. +func UseLogger(logger seelog.LoggerInterface) { + log = logger +} + +// SetLogWriter uses a specified io.Writer to output package logging info. +// This allows a caller to direct package logging output without needing a +// dependency on seelog. If the caller is also using seelog, UseLogger should +// be used instead. +func SetLogWriter(w io.Writer) error { + if w == nil { + return errors.New("nil writer") + } + + l, err := seelog.LoggerFromWriterWithMinLevel(w, seelog.TraceLvl) + if err != nil { + return err + } + + UseLogger(l) + return nil +} + +// LogClosure is a closure that can be printed with %v to be used to +// generate expensive-to-create data for a detailed log level and avoid doing +// the work if the data isn't printed. +type logClosure func() string + +func (c logClosure) String() string { + return c() +} + +func newLogClosure(c func() string) logClosure { + return logClosure(c) +} diff --git a/opcode.go b/opcode.go new file mode 100644 index 00000000..504d8ff3 --- /dev/null +++ b/opcode.go @@ -0,0 +1,1603 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript + +import ( + "bytes" + "code.google.com/p/go.crypto/ripemd160" + "crypto/ecdsa" + "crypto/sha1" + "crypto/sha256" + "fmt" + "github.com/conformal/btcwire" + "github.com/davecgh/go-spew/spew" + "hash" + "math/big" + "opensource.conformal.com/go/btcd/btcec" +) + +// An opcode defines the information related to a btcscript opcode. +// opfunc if present is the function to call to perform the opcode on +// the script. The current script is passed in as a slice with the firs +// member being the opcode itself. +type opcode struct { + value byte + name string + length int + opfunc func(*parsedOpcode, *Script) error + parsefunc func(*opcode, *Script, []byte) error +} + +// These constants are the values of the official opcode used on the btc wiki, +// in bitcoind and in most if not all other references and software related to +// handling BTC scripts. +const ( + OP_FALSE byte = 0 // AKA OP_0 + OP_0 = 0 + OP_DATA_1 = 1 + OP_DATA_2 = 2 + OP_DATA_3 = 3 + OP_DATA_4 = 4 + OP_DATA_5 = 5 + OP_DATA_6 = 6 + OP_DATA_7 = 7 + OP_DATA_8 = 8 + OP_DATA_9 = 9 + OP_DATA_10 = 10 + OP_DATA_11 = 11 + OP_DATA_12 = 12 + OP_DATA_13 = 13 + OP_DATA_14 = 14 + OP_DATA_15 = 15 + OP_DATA_16 = 16 + OP_DATA_17 = 17 + OP_DATA_18 = 18 + OP_DATA_19 = 19 + OP_DATA_20 = 20 + OP_DATA_21 = 21 + OP_DATA_22 = 22 + OP_DATA_23 = 23 + OP_DATA_24 = 24 + OP_DATA_25 = 25 + OP_DATA_26 = 26 + OP_DATA_27 = 27 + OP_DATA_28 = 28 + OP_DATA_29 = 29 + OP_DATA_30 = 30 + OP_DATA_31 = 31 + OP_DATA_32 = 32 + OP_DATA_33 = 33 + OP_DATA_34 = 34 + OP_DATA_35 = 35 + OP_DATA_36 = 36 + OP_DATA_37 = 37 + OP_DATA_38 = 38 + OP_DATA_39 = 39 + OP_DATA_40 = 40 + OP_DATA_41 = 41 + OP_DATA_42 = 42 + OP_DATA_43 = 43 + OP_DATA_44 = 44 + OP_DATA_45 = 45 + OP_DATA_46 = 46 + OP_DATA_47 = 47 + OP_DATA_48 = 48 + OP_DATA_49 = 49 + OP_DATA_50 = 50 + OP_DATA_51 = 51 + OP_DATA_52 = 52 + OP_DATA_53 = 53 + OP_DATA_54 = 54 + OP_DATA_55 = 55 + OP_DATA_56 = 56 + OP_DATA_57 = 57 + OP_DATA_58 = 58 + OP_DATA_59 = 59 + OP_DATA_60 = 60 + OP_DATA_61 = 61 + OP_DATA_62 = 62 + OP_DATA_63 = 63 + OP_DATA_64 = 64 + OP_DATA_65 = 65 + OP_DATA_66 = 66 + OP_DATA_67 = 67 + OP_DATA_68 = 68 + OP_DATA_69 = 69 + OP_DATA_70 = 70 + OP_DATA_71 = 71 + OP_DATA_72 = 72 + OP_DATA_73 = 73 + OP_DATA_74 = 74 + OP_DATA_75 = 75 + OP_PUSHDATA1 = 76 + OP_PUSHDATA2 = 77 + OP_PUSHDATA4 = 78 + OP_1NEGATE = 79 + OP_RESERVED = 80 + OP_1 = 81 // AKA OP_TRUE + OP_TRUE = 81 + OP_2 = 82 + OP_3 = 83 + OP_4 = 84 + OP_5 = 85 + OP_6 = 86 + OP_7 = 87 + OP_8 = 88 + OP_9 = 89 + OP_10 = 90 + OP_11 = 91 + OP_12 = 92 + OP_13 = 93 + OP_14 = 94 + OP_15 = 95 + OP_16 = 96 + OP_NOP = 97 + OP_VER = 98 + OP_IF = 99 + OP_NOTIF = 100 + OP_VERIF = 101 + OP_VERNOTIF = 102 + OP_ELSE = 103 + OP_ENDIF = 104 + OP_VERIFY = 105 + OP_RETURN = 106 + OP_TOALTSTACK = 107 + OP_FROMALTSTACK = 108 + OP_2DROP = 109 + OP_2DUP = 110 + OP_3DUP = 111 + OP_2OVER = 112 + OP_2ROT = 113 + OP_2SWAP = 114 + OP_IFDUP = 115 + OP_DEPTH = 116 + OP_DROP = 117 + OP_DUP = 118 + OP_NIP = 119 + OP_OVER = 120 + OP_PICK = 121 + OP_ROLL = 122 + OP_ROT = 123 + OP_SWAP = 124 + OP_TUCK = 125 + OP_CAT = 126 + OP_SUBSTR = 127 + OP_LEFT = 128 + OP_RIGHT = 129 + OP_SIZE = 130 + OP_INVERT = 131 + OP_AND = 132 + OP_OR = 133 + OP_XOR = 134 + OP_EQUAL = 135 + OP_EQUALVERIFY = 136 + OP_RESERVED1 = 137 + OP_RESERVED2 = 138 + OP_1ADD = 139 + OP_1SUB = 140 + OP_2MUL = 141 + OP_2DIV = 142 + OP_NEGATE = 143 + OP_ABS = 144 + OP_NOT = 145 + OP_0NOTEQUAL = 146 + OP_ADD = 147 + OP_SUB = 148 + OP_MUL = 149 + OP_DIV = 150 + OP_MOD = 151 + OP_LSHIFT = 152 + OP_RSHIFT = 153 + OP_BOOLAND = 154 + OP_BOOLOR = 155 + OP_NUMEQUAL = 156 + OP_NUMEQUALVERIFY = 157 + OP_NUMNOTEQUAL = 158 + OP_LESSTHAN = 159 + OP_GREATERTHAN = 160 + OP_LESSTHANOREQUAL = 161 + OP_GREATERTHANOREQUAL = 162 + OP_MIN = 163 + OP_MAX = 164 + OP_WITHIN = 165 + OP_RIPEMD160 = 166 + OP_SHA1 = 167 + OP_SHA256 = 168 + OP_HASH160 = 169 + OP_HASH256 = 170 + OP_CODESEPARATOR = 171 + OP_CHECKSIG = 172 + OP_CHECKSIGVERIFY = 173 + OP_CHECK_MULTISIG = 174 + OP_CHECKMULTISIGVERIFY = 175 + OP_NOP1 = 176 + OP_NOP2 = 177 + OP_NOP3 = 178 + OP_NOP4 = 179 + OP_NOP5 = 180 + OP_NOP6 = 181 + OP_NOP7 = 182 + OP_NOP8 = 183 + OP_NOP9 = 184 + OP_NOP10 = 185 + OP_PUBKEYHASH = 253 // bitcoind internal, for completeness + OP_PUBKEY = 254 // bitcoind internal, for completeness + OP_INVALIDOPCODE = 255 // bitcoind internal, for completeness +) + +// conditional execution constants +const ( + OpCondFalse = 0 + OpCondTrue = 1 + OpCondSkip = 2 +) + +// Some of the functions in opcodemap call things that themselves then will +// reference the opcodemap to make decisions (things like op_checksig which +// needs to parse scripts to remove opcodes, for example). +// The go compiler is very conservative in this matter and will think there +// is an initialisation loop. In order to work around this we have the fake +// ``prevariable'' opcodemapPreinit and then set the real variable to the +// preinit in init() +var opcodemap map[byte]*opcode + +func init() { + opcodemap = opcodemapPreinit +} + +var opcodemapPreinit = map[byte]*opcode{ + OP_FALSE: {value: OP_FALSE, name: "OP_0", length: 1, + opfunc: opcodeFalse}, + OP_DATA_1: {value: OP_DATA_1, name: "OP_DATA_1", length: 2, + opfunc: opcodePushData}, + OP_DATA_2: {value: OP_DATA_2, name: "OP_DATA_2", length: 3, + opfunc: opcodePushData}, + OP_DATA_3: {value: OP_DATA_3, name: "OP_DATA_3", length: 4, + opfunc: opcodePushData}, + OP_DATA_4: {value: OP_DATA_4, name: "OP_DATA_4", length: 5, + opfunc: opcodePushData}, + OP_DATA_5: {value: OP_DATA_5, name: "OP_DATA_5", length: 6, + opfunc: opcodePushData}, + OP_DATA_6: {value: OP_DATA_6, name: "OP_DATA_6", length: 7, + opfunc: opcodePushData}, + OP_DATA_7: {value: OP_DATA_7, name: "OP_DATA_7", length: 8, + opfunc: opcodePushData}, + OP_DATA_8: {value: OP_DATA_8, name: "OP_DATA_8", length: 9, + opfunc: opcodePushData}, + OP_DATA_9: {value: OP_DATA_9, name: "OP_DATA_9", length: 10, + opfunc: opcodePushData}, + OP_DATA_10: {value: OP_DATA_10, name: "OP_DATA_10", length: 11, + opfunc: opcodePushData}, + OP_DATA_11: {value: OP_DATA_11, name: "OP_DATA_11", length: 12, + opfunc: opcodePushData}, + OP_DATA_12: {value: OP_DATA_12, name: "OP_DATA_12", length: 13, + opfunc: opcodePushData}, + OP_DATA_13: {value: OP_DATA_13, name: "OP_DATA_13", length: 14, + opfunc: opcodePushData}, + OP_DATA_14: {value: OP_DATA_14, name: "OP_DATA_14", length: 15, + opfunc: opcodePushData}, + OP_DATA_15: {value: OP_DATA_15, name: "OP_DATA_15", length: 16, + opfunc: opcodePushData}, + OP_DATA_16: {value: OP_DATA_16, name: "OP_DATA_16", length: 17, + opfunc: opcodePushData}, + OP_DATA_17: {value: OP_DATA_17, name: "OP_DATA_17", length: 18, + opfunc: opcodePushData}, + OP_DATA_18: {value: OP_DATA_18, name: "OP_DATA_18", length: 19, + opfunc: opcodePushData}, + OP_DATA_19: {value: OP_DATA_19, name: "OP_DATA_19", length: 20, + opfunc: opcodePushData}, + OP_DATA_20: {value: OP_DATA_20, name: "OP_DATA_20", length: 21, + opfunc: opcodePushData}, + OP_DATA_21: {value: OP_DATA_21, name: "OP_DATA_21", length: 22, + opfunc: opcodePushData}, + OP_DATA_22: {value: OP_DATA_22, name: "OP_DATA_22", length: 23, + opfunc: opcodePushData}, + OP_DATA_23: {value: OP_DATA_23, name: "OP_DATA_23", length: 24, + opfunc: opcodePushData}, + OP_DATA_24: {value: OP_DATA_24, name: "OP_DATA_24", length: 25, + opfunc: opcodePushData}, + OP_DATA_25: {value: OP_DATA_25, name: "OP_DATA_25", length: 26, + opfunc: opcodePushData}, + OP_DATA_26: {value: OP_DATA_26, name: "OP_DATA_26", length: 27, + opfunc: opcodePushData}, + OP_DATA_27: {value: OP_DATA_27, name: "OP_DATA_27", length: 28, + opfunc: opcodePushData}, + OP_DATA_28: {value: OP_DATA_28, name: "OP_DATA_28", length: 29, + opfunc: opcodePushData}, + OP_DATA_29: {value: OP_DATA_29, name: "OP_DATA_29", length: 30, + opfunc: opcodePushData}, + OP_DATA_30: {value: OP_DATA_30, name: "OP_DATA_30", length: 31, + opfunc: opcodePushData}, + OP_DATA_31: {value: OP_DATA_31, name: "OP_DATA_31", length: 32, + opfunc: opcodePushData}, + OP_DATA_32: {value: OP_DATA_32, name: "OP_DATA_32", length: 33, + opfunc: opcodePushData}, + OP_DATA_33: {value: OP_DATA_33, name: "OP_DATA_33", length: 34, + opfunc: opcodePushData}, + OP_DATA_34: {value: OP_DATA_34, name: "OP_DATA_34", length: 35, + opfunc: opcodePushData}, + OP_DATA_35: {value: OP_DATA_35, name: "OP_DATA_35", length: 36, + opfunc: opcodePushData}, + OP_DATA_36: {value: OP_DATA_36, name: "OP_DATA_36", length: 37, + opfunc: opcodePushData}, + OP_DATA_37: {value: OP_DATA_37, name: "OP_DATA_37", length: 38, + opfunc: opcodePushData}, + OP_DATA_38: {value: OP_DATA_38, name: "OP_DATA_38", length: 39, + opfunc: opcodePushData}, + OP_DATA_39: {value: OP_DATA_39, name: "OP_DATA_39", length: 40, + opfunc: opcodePushData}, + OP_DATA_40: {value: OP_DATA_40, name: "OP_DATA_40", length: 41, + opfunc: opcodePushData}, + OP_DATA_41: {value: OP_DATA_41, name: "OP_DATA_41", length: 42, + opfunc: opcodePushData}, + OP_DATA_42: {value: OP_DATA_42, name: "OP_DATA_42", length: 43, + opfunc: opcodePushData}, + OP_DATA_43: {value: OP_DATA_43, name: "OP_DATA_43", length: 44, + opfunc: opcodePushData}, + OP_DATA_44: {value: OP_DATA_44, name: "OP_DATA_44", length: 45, + opfunc: opcodePushData}, + OP_DATA_45: {value: OP_DATA_45, name: "OP_DATA_45", length: 46, + opfunc: opcodePushData}, + OP_DATA_46: {value: OP_DATA_46, name: "OP_DATA_46", length: 47, + opfunc: opcodePushData}, + OP_DATA_47: {value: OP_DATA_47, name: "OP_DATA_47", length: 48, + opfunc: opcodePushData}, + OP_DATA_48: {value: OP_DATA_48, name: "OP_DATA_48", length: 49, + opfunc: opcodePushData}, + OP_DATA_49: {value: OP_DATA_49, name: "OP_DATA_49", length: 50, + opfunc: opcodePushData}, + OP_DATA_50: {value: OP_DATA_50, name: "OP_DATA_50", length: 51, + opfunc: opcodePushData}, + OP_DATA_51: {value: OP_DATA_51, name: "OP_DATA_51", length: 52, + opfunc: opcodePushData}, + OP_DATA_52: {value: OP_DATA_52, name: "OP_DATA_52", length: 53, + opfunc: opcodePushData}, + OP_DATA_53: {value: OP_DATA_53, name: "OP_DATA_53", length: 54, + opfunc: opcodePushData}, + OP_DATA_54: {value: OP_DATA_54, name: "OP_DATA_54", length: 55, + opfunc: opcodePushData}, + OP_DATA_55: {value: OP_DATA_55, name: "OP_DATA_55", length: 56, + opfunc: opcodePushData}, + OP_DATA_56: {value: OP_DATA_56, name: "OP_DATA_56", length: 57, + opfunc: opcodePushData}, + OP_DATA_57: {value: OP_DATA_57, name: "OP_DATA_57", length: 58, + opfunc: opcodePushData}, + OP_DATA_58: {value: OP_DATA_58, name: "OP_DATA_58", length: 59, + opfunc: opcodePushData}, + OP_DATA_59: {value: OP_DATA_59, name: "OP_DATA_59", length: 60, + opfunc: opcodePushData}, + OP_DATA_60: {value: OP_DATA_60, name: "OP_DATA_60", length: 61, + opfunc: opcodePushData}, + OP_DATA_61: {value: OP_DATA_61, name: "OP_DATA_61", length: 62, + opfunc: opcodePushData}, + OP_DATA_62: {value: OP_DATA_62, name: "OP_DATA_62", length: 63, + opfunc: opcodePushData}, + OP_DATA_63: {value: OP_DATA_63, name: "OP_DATA_63", length: 64, + opfunc: opcodePushData}, + OP_DATA_64: {value: OP_DATA_64, name: "OP_DATA_64", length: 65, + opfunc: opcodePushData}, + OP_DATA_65: {value: OP_DATA_65, name: "OP_DATA_65", length: 66, + opfunc: opcodePushData}, + OP_DATA_66: {value: OP_DATA_66, name: "OP_DATA_66", length: 67, + opfunc: opcodePushData}, + OP_DATA_67: {value: OP_DATA_67, name: "OP_DATA_67", length: 68, + opfunc: opcodePushData}, + OP_DATA_68: {value: OP_DATA_68, name: "OP_DATA_68", length: 69, + opfunc: opcodePushData}, + OP_DATA_69: {value: OP_DATA_69, name: "OP_DATA_69", length: 70, + opfunc: opcodePushData}, + OP_DATA_70: {value: OP_DATA_70, name: "OP_DATA_70", length: 71, + opfunc: opcodePushData}, + OP_DATA_71: {value: OP_DATA_71, name: "OP_DATA_71", length: 72, + opfunc: opcodePushData}, + OP_DATA_72: {value: OP_DATA_72, name: "OP_DATA_72", length: 73, + opfunc: opcodePushData}, + OP_DATA_73: {value: OP_DATA_73, name: "OP_DATA_73", length: 74, + opfunc: opcodePushData}, + OP_DATA_74: {value: OP_DATA_74, name: "OP_DATA_74", length: 75, + opfunc: opcodePushData}, + OP_DATA_75: {value: OP_DATA_75, name: "OP_DATA_75", length: 76, + opfunc: opcodePushData}, + OP_PUSHDATA1: {value: OP_PUSHDATA1, name: "OP_PUSHDATA1", length: -1, + opfunc: opcodePushData}, + OP_PUSHDATA2: {value: OP_PUSHDATA2, name: "OP_PUSHDATA2", length: -2, + opfunc: opcodePushData}, + OP_PUSHDATA4: {value: OP_PUSHDATA4, name: "OP_PUSHDATA4", length: -4, + opfunc: opcodePushData}, + OP_1NEGATE: {value: OP_1NEGATE, name: "OP_1NEGATE", length: 1, + opfunc: opcode1Negate}, + OP_RESERVED: {value: OP_RESERVED, name: "OP_RESERVED", length: 1, + opfunc: opcodeReserved}, + OP_TRUE: {value: OP_TRUE, name: "OP_1", length: 1, + opfunc: opcodeN}, + OP_2: {value: OP_2, name: "OP_2", length: 1, + opfunc: opcodeN}, + OP_3: {value: OP_3, name: "OP_3", length: 1, + opfunc: opcodeN}, + OP_4: {value: OP_4, name: "OP_4", length: 1, + opfunc: opcodeN}, + OP_5: {value: OP_5, name: "OP_5", length: 1, + opfunc: opcodeN}, + OP_6: {value: OP_6, name: "OP_6", length: 1, + opfunc: opcodeN}, + OP_7: {value: OP_7, name: "OP_7", length: 1, + opfunc: opcodeN}, + OP_8: {value: OP_8, name: "OP_8", length: 1, + opfunc: opcodeN}, + OP_9: {value: OP_9, name: "OP_9", length: 1, + opfunc: opcodeN}, + OP_10: {value: OP_10, name: "OP_10", length: 1, + opfunc: opcodeN}, + OP_11: {value: OP_11, name: "OP_11", length: 1, + opfunc: opcodeN}, + OP_12: {value: OP_12, name: "OP_12", length: 1, + opfunc: opcodeN}, + OP_13: {value: OP_13, name: "OP_13", length: 1, + opfunc: opcodeN}, + OP_14: {value: OP_14, name: "OP_14", length: 1, + opfunc: opcodeN}, + OP_15: {value: OP_15, name: "OP_15", length: 1, + opfunc: opcodeN}, + OP_16: {value: OP_16, name: "OP_16", length: 1, + opfunc: opcodeN}, + OP_NOP: {value: OP_NOP, name: "OP_NOP", length: 1, + opfunc: opcodeNop}, + OP_VER: {value: OP_VER, name: "OP_VER", length: 1, + opfunc: opcodeReserved}, + OP_IF: {value: OP_IF, name: "OP_IF", length: 1, + opfunc: opcodeIf}, + OP_NOTIF: {value: OP_NOTIF, name: "OP_NOTIF", length: 1, + opfunc: opcodeNotIf}, + OP_VERIF: {value: OP_VERIF, name: "OP_VERIF", length: 1, + opfunc: opcodeReserved}, + OP_VERNOTIF: {value: OP_VERNOTIF, name: "OP_VERNOTIF", length: 1, + opfunc: opcodeReserved}, + OP_ELSE: {value: OP_ELSE, name: "OP_ELSE", length: 1, + opfunc: opcodeElse}, + OP_ENDIF: {value: OP_ENDIF, name: "OP_ENDIF", length: 1, + opfunc: opcodeEndif}, + OP_VERIFY: {value: OP_VERIFY, name: "OP_VERIFY", length: 1, + opfunc: opcodeVerify}, + OP_RETURN: {value: OP_RETURN, name: "OP_RETURN", length: 1, + opfunc: opcodeReturn}, + OP_TOALTSTACK: {value: OP_TOALTSTACK, name: "OP_TOALTSTACK", length: 1, + opfunc: opcodeToAltStack}, + OP_FROMALTSTACK: {value: OP_FROMALTSTACK, name: "OP_FROMALTSTACK", length: 1, + opfunc: opcodeFromAltStack}, + OP_2DROP: {value: OP_2DROP, name: "OP_2DROP", length: 1, + opfunc: opcode2Drop}, + OP_2DUP: {value: OP_2DUP, name: "OP_2DUP", length: 1, + opfunc: opcode2Dup}, + OP_3DUP: {value: OP_3DUP, name: "OP_3DUP", length: 1, + opfunc: opcode3Dup}, + OP_2OVER: {value: OP_2OVER, name: "OP_2OVER", length: 1, + opfunc: opcode2Over}, + OP_2ROT: {value: OP_2ROT, name: "OP_2ROT", length: 1, + opfunc: opcode2Rot}, + OP_2SWAP: {value: OP_2SWAP, name: "OP_2SWAP", length: 1, + opfunc: opcode2Swap}, + OP_IFDUP: {value: OP_IFDUP, name: "OP_IFDUP", length: 1, + opfunc: opcodeIfDup}, + OP_DEPTH: {value: OP_DEPTH, name: "OP_DEPTH", length: 1, + opfunc: opcodeDepth}, + OP_DROP: {value: OP_DROP, name: "OP_DROP", length: 1, + opfunc: opcodeDrop}, + OP_DUP: {value: OP_DUP, name: "OP_DUP", length: 1, + opfunc: opcodeDup}, + OP_NIP: {value: OP_NIP, name: "OP_NIP", length: 1, + opfunc: opcodeNip}, + OP_OVER: {value: OP_OVER, name: "OP_OVER", length: 1, + opfunc: opcodeOver}, + OP_PICK: {value: OP_PICK, name: "OP_PICK", length: 1, + opfunc: opcodePick}, + OP_ROLL: {value: OP_ROLL, name: "OP_ROLL", length: 1, + opfunc: opcodeRoll}, + OP_ROT: {value: OP_ROT, name: "OP_ROT", length: 1, + opfunc: opcodeRot}, + OP_SWAP: {value: OP_SWAP, name: "OP_SWAP", length: 1, + opfunc: opcodeSwap}, + OP_TUCK: {value: OP_TUCK, name: "OP_TUCK", length: 1, + opfunc: opcodeTuck}, + OP_CAT: {value: OP_CAT, name: "OP_CAT", length: 1, + opfunc: opcodeDisabled}, + OP_SUBSTR: {value: OP_SUBSTR, name: "OP_SUBSTR", length: 1, + opfunc: opcodeDisabled}, + OP_LEFT: {value: OP_LEFT, name: "OP_LEFT", length: 1, + opfunc: opcodeDisabled}, + OP_RIGHT: {value: OP_RIGHT, name: "OP_RIGHT", length: 1, + opfunc: opcodeDisabled}, + OP_SIZE: {value: OP_SIZE, name: "OP_SIZE", length: 1, + opfunc: opcodeSize}, + OP_INVERT: {value: OP_INVERT, name: "OP_INVERT", length: 1, + opfunc: opcodeDisabled}, + OP_AND: {value: OP_AND, name: "OP_AND", length: 1, + opfunc: opcodeDisabled}, + OP_OR: {value: OP_OR, name: "OP_OR", length: 1, + opfunc: opcodeDisabled}, + OP_XOR: {value: OP_XOR, name: "OP_XOR", length: 1, + opfunc: opcodeDisabled}, + OP_EQUAL: {value: OP_EQUAL, name: "OP_EQUAL", length: 1, + opfunc: opcodeEqual}, + OP_EQUALVERIFY: {value: OP_EQUALVERIFY, name: "OP_EQUALVERIFY", length: 1, + opfunc: opcodeEqualVerify}, + OP_RESERVED1: {value: OP_RESERVED1, name: "OP_RESERVED1", length: 1, + opfunc: opcodeReserved}, + OP_RESERVED2: {value: OP_RESERVED2, name: "OP_RESERVED2", length: 1, + opfunc: opcodeReserved}, + OP_1ADD: {value: OP_1ADD, name: "OP_1ADD", length: 1, + opfunc: opcode1Add}, + OP_1SUB: {value: OP_1SUB, name: "OP_1SUB", length: 1, + opfunc: opcode1Sub}, + OP_2MUL: {value: OP_2MUL, name: "OP_2MUL", length: 1, + opfunc: opcodeDisabled}, + OP_2DIV: {value: OP_2DIV, name: "OP_2DIV", length: 1, + opfunc: opcodeDisabled}, + OP_NEGATE: {value: OP_NEGATE, name: "OP_NEGATE", length: 1, + opfunc: opcodeNegate}, + OP_ABS: {value: OP_ABS, name: "OP_ABS", length: 1, + opfunc: opcodeAbs}, + OP_NOT: {value: OP_NOT, name: "OP_NOT", length: 1, + opfunc: opcodeNot}, + OP_0NOTEQUAL: {value: OP_0NOTEQUAL, name: "OP_0NOTEQUAL", length: 1, + opfunc: opcode0NotEqual}, + OP_ADD: {value: OP_ADD, name: "OP_ADD", length: 1, + opfunc: opcodeAdd}, + OP_SUB: {value: OP_SUB, name: "OP_SUB", length: 1, + opfunc: opcodeSub}, + OP_MUL: {value: OP_MUL, name: "OP_MUL", length: 1, + opfunc: opcodeDisabled}, + OP_DIV: {value: OP_DIV, name: "OP_DIV", length: 1, + opfunc: opcodeDisabled}, + OP_MOD: {value: OP_MOD, name: "OP_MOD", length: 1, + opfunc: opcodeDisabled}, + OP_LSHIFT: {value: OP_LSHIFT, name: "OP_LSHIFT", length: 1, + opfunc: opcodeDisabled}, + OP_RSHIFT: {value: OP_RSHIFT, name: "OP_RSHIFT", length: 1, + opfunc: opcodeDisabled}, + OP_BOOLAND: {value: OP_BOOLAND, name: "OP_BOOLAND", length: 1, + opfunc: opcodeBoolAnd}, + OP_BOOLOR: {value: OP_BOOLOR, name: "OP_BOOLOR", length: 1, + opfunc: opcodeBoolOr}, + OP_NUMEQUAL: {value: OP_NUMEQUAL, name: "OP_NUMEQUAL", length: 1, + opfunc: opcodeNumEqual}, + OP_NUMEQUALVERIFY: {value: OP_NUMEQUALVERIFY, name: "OP_NUMEQUALVERIFY", length: 1, + opfunc: opcodeNumEqualVerify}, + OP_NUMNOTEQUAL: {value: OP_NUMNOTEQUAL, name: "OP_NUMNOTEQUAL", length: 1, + opfunc: opcodeNumNotEqual}, + OP_LESSTHAN: {value: OP_LESSTHAN, name: "OP_LESSTHAN", length: 1, + opfunc: opcodeLessThan}, + OP_GREATERTHAN: {value: OP_GREATERTHAN, name: "OP_GREATERTHAN", length: 1, + opfunc: opcodeGreaterThan}, + OP_LESSTHANOREQUAL: {value: OP_LESSTHANOREQUAL, name: "OP_LESSTHANOREQUAL", length: 1, + opfunc: opcodeLessThanOrEqual}, + OP_GREATERTHANOREQUAL: {value: OP_GREATERTHANOREQUAL, name: "OP_GREATERTHANOREQUAL", length: 1, + opfunc: opcodeGreaterThanOrEqual}, + OP_MIN: {value: OP_MIN, name: "OP_MIN", length: 1, + opfunc: opcodeMin}, + OP_MAX: {value: OP_MAX, name: "OP_MAX", length: 1, + opfunc: opcodeMax}, + OP_WITHIN: {value: OP_WITHIN, name: "OP_WITHIN", length: 1, + opfunc: opcodeWithin}, + OP_RIPEMD160: {value: OP_RIPEMD160, name: "OP_RIPEMD160", length: 1, + opfunc: opcodeRipemd160}, + OP_SHA1: {value: OP_SHA1, name: "OP_SHA1", length: 1, + opfunc: opcodeSha1}, + OP_SHA256: {value: OP_SHA256, name: "OP_SHA256", length: 1, + opfunc: opcodeSha256}, + OP_HASH160: {value: OP_HASH160, name: "OP_HASH160", length: 1, + opfunc: opcodeHash160}, + OP_HASH256: {value: OP_HASH256, name: "OP_HASH256", length: 1, + opfunc: opcodeHash256}, + OP_CODESEPARATOR: {value: OP_CODESEPARATOR, name: "OP_CODESEPARATOR", length: 1, + opfunc: opcodeCodeSeparator}, + OP_CHECKSIG: {value: OP_CHECKSIG, name: "OP_CHECKSIG", length: 1, + opfunc: opcodeCheckSig}, + OP_CHECKSIGVERIFY: {value: OP_CHECKSIGVERIFY, name: "OP_CHECKSIGVERIFY", length: 1, + opfunc: opcodeCheckSigVerify}, + OP_CHECK_MULTISIG: {value: OP_CHECK_MULTISIG, name: "OP_CHECK_MULTISIG", length: 1, + opfunc: opcodeCheckMultiSig}, + OP_CHECKMULTISIGVERIFY: {value: OP_CHECKMULTISIGVERIFY, name: "OP_CHECKMULTISIGVERIFY", length: 1, + + opfunc: opcodeCheckMultiSigVerify}, + OP_NOP1: {value: OP_NOP1, name: "OP_NOP1", length: 1, + opfunc: opcodeNop}, + OP_NOP2: {value: OP_NOP2, name: "OP_NOP2", length: 1, + opfunc: opcodeNop}, + OP_NOP3: {value: OP_NOP3, name: "OP_NOP3", length: 1, + opfunc: opcodeNop}, + OP_NOP4: {value: OP_NOP4, name: "OP_NOP4", length: 1, + opfunc: opcodeNop}, + OP_NOP5: {value: OP_NOP5, name: "OP_NOP5", length: 1, + opfunc: opcodeNop}, + OP_NOP6: {value: OP_NOP6, name: "OP_NOP6", length: 1, + opfunc: opcodeNop}, + OP_NOP7: {value: OP_NOP7, name: "OP_NOP7", length: 1, + opfunc: opcodeNop}, + OP_NOP8: {value: OP_NOP8, name: "OP_NOP8", length: 1, + opfunc: opcodeNop}, + OP_NOP9: {value: OP_NOP9, name: "OP_NOP9", length: 1, + opfunc: opcodeNop}, + OP_NOP10: {value: OP_NOP10, name: "OP_NOP10", length: 1, + opfunc: opcodeNop}, + OP_PUBKEYHASH: {value: OP_PUBKEYHASH, name: "OP_PUBKEYHASH", length: 1, + opfunc: opcodeInvalid}, + OP_PUBKEY: {value: OP_PUBKEY, name: "OP_PUBKEY", length: 1, + opfunc: opcodeInvalid}, + OP_INVALIDOPCODE: {value: OP_INVALIDOPCODE, name: "OP_INVALIDOPCODE", length: 1, + opfunc: opcodeInvalid}, +} + +type parsedOpcode struct { + opcode *opcode + data []byte + opfunc func(op parsedOpcode, s Script) error +} + +func (pop *parsedOpcode) conditional() bool { + switch pop.opcode.value { + case OP_IF: + return true + case OP_NOTIF: + return true + case OP_ELSE: + return true + case OP_ENDIF: + return true + default: + return false + } +} + +func (pop *parsedOpcode) exec(s *Script) error { + // *sigh* bitcoind pretty much mandates that we violate layering here. + // Any opcode that isn't just adding data to the stack counts here + // as an operation. + if pop.opcode.value < OP_16 { + s.numOps++ + if s.numOps > MaxOpsPerScript { + return StackErrTooManyOperations + } + + } + return pop.opcode.opfunc(pop, s) +} + +func (pop *parsedOpcode) print(oneline bool) string { + retString := pop.opcode.name + if pop.opcode.length == 1 { + return retString + } + if oneline { + retString = "" + } + if !oneline && pop.opcode.length < 0 { + //add length to the end of retString + retString += fmt.Sprintf(" 0x%0*x", 2*-pop.opcode.length, + len(pop.data)) + } + for _, val := range pop.data { + if !oneline { + retString += " " + } + retString += fmt.Sprintf("%02x", val) + } + return retString +} + +func (pop *parsedOpcode) bytes() []byte { + retbytes := []byte{pop.opcode.value} + if pop.opcode.length == 1 { + return retbytes + } + if pop.opcode.length < 0 { + l := len(pop.data) + // tempting just to hardcode to avoid the complexity here. + switch pop.opcode.length { + case -1: + retbytes = append(retbytes, byte(l)) + case -2: + retbytes = append(retbytes, byte(l&0xff), + byte(l>>8&0xff)) + case -4: + retbytes = append(retbytes, byte(l&0xff), + byte((l>>8)&0xff), byte((l>>16)&0xff), + byte((l>>24)&0xff)) + } + } + + for i := range pop.data { + retbytes = append(retbytes, pop.data[i]) + } + + return retbytes +} + +// opcode implementation functions from here + +func opcodeDisabled(op *parsedOpcode, s *Script) error { + return StackErrOpDisabled +} + +func opcodeReserved(op *parsedOpcode, s *Script) error { + return StackErrReservedOpcode +} + +// Recognised opcode, but for bitcoind internal use only. +func opcodeInvalid(op *parsedOpcode, s *Script) error { + return StackErrInvalidOpcode +} + +func opcodeFalse(op *parsedOpcode, s *Script) error { + s.dstack.PushByteArray([]byte("")) + + return nil +} + +func opcodePushData(op *parsedOpcode, s *Script) error { + s.dstack.PushByteArray(op.data) + return nil +} + +func opcode1Negate(op *parsedOpcode, s *Script) error { + s.dstack.PushInt(big.NewInt(-1)) + return nil +} + +func opcodeN(op *parsedOpcode, s *Script) error { + // 16 consecutive opcodes add increasing numbers to the stack. + s.dstack.PushInt(big.NewInt(int64(op.opcode.value - (OP_1 - 1)))) + return nil +} + +func opcodeNop(op *parsedOpcode, s *Script) error { + // This page left intentionally blank + return nil +} + +// opcodeIf computes true/false based on the value on the stack and pushes +// the condition on the condStack (conditional execution stack) +func opcodeIf(op *parsedOpcode, s *Script) error { + // opcodeIf will be executed even if it is on the non-execute side + // of the conditional, this is so proper nesting is maintained + var condval int + if s.condStack[0] == OpCondTrue { + ok, err := s.dstack.PopBool() + if err != nil { + return err + } + if ok { + condval = OpCondTrue + } + } else { + condval = OpCondSkip + } + cond := []int{condval} + // push condition to the 'head' of the slice + s.condStack = append(cond, s.condStack...) + // TODO(drahn) check if a maximum condtitional stack limit exists + return nil +} + +// opcodeNotIf computes true/false based on the value on the stack and pushes +// the (inverted) condition on the condStack (conditional execution stack) +func opcodeNotIf(op *parsedOpcode, s *Script) error { + // opcodeIf will be executed even if it is on the non-execute side + // of the conditional, this is so proper nesting is maintained + var condval int + if s.condStack[0] == OpCondTrue { + ok, err := s.dstack.PopBool() + if err != nil { + return err + } + if !ok { + condval = OpCondTrue + } + } else { + condval = OpCondSkip + } + cond := []int{condval} + // push condition to the 'head' of the slice + s.condStack = append(cond, s.condStack...) + // TODO(drahn) check if a maximum condtitional stack limit exists + return nil +} + +// opcodeElse inverts conditional execution for other half of if/else/endif +func opcodeElse(op *parsedOpcode, s *Script) error { + if len(s.condStack) < 2 { + // intial true cannot be toggled, only pushed conditionals + return StackErrNoIf + } + + switch s.condStack[0] { + case OpCondTrue: + s.condStack[0] = OpCondFalse + case OpCondFalse: + s.condStack[0] = OpCondTrue + case OpCondSkip: + // value doesn't change in skip + } + return nil +} + +// opcodeEndif terminates a conditional block, removing the value from the +// conditional execution stack. +func opcodeEndif(op *parsedOpcode, s *Script) error { + if len(s.condStack) < 2 { + // intial true cannot be popped, only pushed conditionals + return StackErrNoIf + } + + s.condStack = s.condStack[1:] + return nil +} + +func opcodeVerify(op *parsedOpcode, s *Script) error { + verified, err := s.dstack.PopBool() + if err != nil { + return err + } + + if verified != true { + return StackErrVerifyFailed + } + return nil +} + +func opcodeReturn(op *parsedOpcode, s *Script) error { + return StackErrEarlyReturn +} + +func opcodeToAltStack(op *parsedOpcode, s *Script) error { + so, err := s.dstack.PopByteArray() + if err != nil { + return err + } + s.astack.PushByteArray(so) + + return nil +} + +func opcodeFromAltStack(op *parsedOpcode, s *Script) error { + so, err := s.astack.PopByteArray() + if err != nil { + return err + } + s.dstack.PushByteArray(so) + + return nil +} + +func opcode2Drop(op *parsedOpcode, s *Script) error { + return s.dstack.DropN(2) +} + +func opcode2Dup(op *parsedOpcode, s *Script) error { + return s.dstack.DupN(2) +} + +func opcode3Dup(op *parsedOpcode, s *Script) error { + return s.dstack.DupN(3) +} + +func opcode2Over(op *parsedOpcode, s *Script) error { + return s.dstack.OverN(2) +} + +func opcode2Rot(op *parsedOpcode, s *Script) error { + return s.dstack.RotN(2) +} + +func opcode2Swap(op *parsedOpcode, s *Script) error { + return s.dstack.SwapN(2) +} + +func opcodeIfDup(op *parsedOpcode, s *Script) error { + val, err := s.dstack.PeekInt(0) + if err != nil { + return err + } + + // Push copy of data iff it isn't zero + if val.Sign() != 0 { + s.dstack.PushInt(val) + } + + return nil +} + +func opcodeDepth(op *parsedOpcode, s *Script) error { + s.dstack.PushInt(big.NewInt(int64(s.dstack.Depth()))) + return nil +} + +func opcodeDrop(op *parsedOpcode, s *Script) error { + return s.dstack.DropN(1) +} + +func opcodeDup(op *parsedOpcode, s *Script) error { + return s.dstack.DupN(1) +} + +func opcodeNip(op *parsedOpcode, s *Script) error { + return s.dstack.NipN(1) +} + +func opcodeOver(op *parsedOpcode, s *Script) error { + return s.dstack.OverN(1) +} + +// Copy object N items back in the stack to the top. Where N is the value in +// the top of the stack. +func opcodePick(op *parsedOpcode, s *Script) error { + pidx, err := s.dstack.PopInt() + if err != nil { + return err + } + + if pidx.BitLen() > 32 { + return StackErrNumberTooBig + } + + val := int(pidx.Int64()) + + return s.dstack.PickN(val) +} + +// Move object N items back in the stack to the top. Where N is the value in +// the top of the stack. +func opcodeRoll(op *parsedOpcode, s *Script) error { + ridx, err := s.dstack.PopInt() + if err != nil { + return err + } + + if ridx.BitLen() > 32 { + return StackErrNumberTooBig + } + + val := int(ridx.Int64()) + + return s.dstack.RollN(val) +} + +// Rotate top three items on the stack to the left. +// e.g. 1,2,3 -> 2,3,1 +func opcodeRot(op *parsedOpcode, s *Script) error { + return s.dstack.RotN(1) +} + +// Swap the top two items on the stack: 1,2 -> 2,1 +func opcodeSwap(op *parsedOpcode, s *Script) error { + return s.dstack.SwapN(1) +} + +// The item at the top of the stack is copied and inserted before the +// second-to-top item. e.g.: 2,1, -> 2,1,2 +func opcodeTuck(op *parsedOpcode, s *Script) error { + return s.dstack.Tuck() +} + +// Push the size of the item on top of the stack onto the stack. +func opcodeSize(op *parsedOpcode, s *Script) error { + i, err := s.dstack.PeekByteArray(0) + if err != nil { + return err + } + + s.dstack.PushInt(big.NewInt(int64(len(i)))) + return nil +} + +func opcodeEqual(op *parsedOpcode, s *Script) error { + a, err := s.dstack.PopByteArray() + if err != nil { + return err + } + b, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + s.dstack.PushBool(bytes.Equal(a, b)) + return nil +} + +func opcodeEqualVerify(op *parsedOpcode, s *Script) error { + err := opcodeEqual(op, s) + if err == nil { + err = opcodeVerify(op, s) + } + return err +} + +func opcode1Add(op *parsedOpcode, s *Script) error { + m, err := s.dstack.PopInt() + if err != nil { + return err + } + + s.dstack.PushInt(new(big.Int).Add(m, big.NewInt(1))) + + return nil +} + +func opcode1Sub(op *parsedOpcode, s *Script) error { + m, err := s.dstack.PopInt() + if err != nil { + return err + } + s.dstack.PushInt(new(big.Int).Sub(m, big.NewInt(1))) + + return nil +} + +func opcodeNegate(op *parsedOpcode, s *Script) error { + // XXX when we remove types just flip the 0x80 bit of msb + m, err := s.dstack.PopInt() + if err != nil { + return err + } + + s.dstack.PushInt(new(big.Int).Neg(m)) + return nil +} + +func opcodeAbs(op *parsedOpcode, s *Script) error { + // XXX when we remove types just &= ~0x80 on msb + m, err := s.dstack.PopInt() + if err != nil { + return err + } + + s.dstack.PushInt(new(big.Int).Abs(m)) + + return nil +} + +// If then input is 0 or 1, it is flipped. Otherwise the output will be 0. +// (n.b. official client just has 1 is 0, else 0) +func opcodeNot(op *parsedOpcode, s *Script) error { + m, err := s.dstack.PopInt() + if err != nil { + return err + } + if m.Sign() == 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + return nil +} + +// opcode returns 0 if the input is 0, 1 otherwise. +func opcode0NotEqual(op *parsedOpcode, s *Script) error { + m, err := s.dstack.PopInt() + if err != nil { + return err + } + if m.Sign() != 0 { + m.SetInt64(1) + } + s.dstack.PushInt(m) + + return nil +} + +// Push result of adding top two entries on stack +func opcodeAdd(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + s.dstack.PushInt(new(big.Int).Add(v0, v1)) + return nil +} + +// Push result of subtracting 2nd entry on stack from first. +func opcodeSub(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + s.dstack.PushInt(new(big.Int).Sub(v1, v0)) + return nil +} + +// If both of the top two entries on the stack are not zero output is 1. +// Otherwise, 0. +func opcodeBoolAnd(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v0.Sign() != 0 && v1.Sign() != 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + + return nil +} + +// If either of the top two entries on the stack are not zero output is 1. +// Otherwise, 0. +func opcodeBoolOr(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v0.Sign() != 0 || v1.Sign() != 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + + return nil +} + +func opcodeNumEqual(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v0.Cmp(v1) == 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + + return nil +} + +func opcodeNumEqualVerify(op *parsedOpcode, s *Script) error { + err := opcodeNumEqual(op, s) + if err == nil { + err = opcodeVerify(op, s) + } + return err +} + +func opcodeNumNotEqual(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v0.Cmp(v1) != 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + + return nil +} + +func opcodeLessThan(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v1.Cmp(v0) == -1 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + + return nil +} + +func opcodeGreaterThan(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v1.Cmp(v0) == 1 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + return nil +} + +func opcodeLessThanOrEqual(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v1.Cmp(v0) <= 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + return nil +} + +func opcodeGreaterThanOrEqual(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v1.Cmp(v0) >= 0 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + + return nil +} + +func opcodeMin(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v1.Cmp(v0) == -1 { + s.dstack.PushInt(new(big.Int).Set(v1)) + } else { + s.dstack.PushInt(new(big.Int).Set(v0)) + } + + return nil +} + +func opcodeMax(op *parsedOpcode, s *Script) error { + v0, err := s.dstack.PopInt() + if err != nil { + return err + } + + v1, err := s.dstack.PopInt() + if err != nil { + return err + } + + if v1.Cmp(v0) == 1 { + s.dstack.PushInt(new(big.Int).Set(v1)) + } else { + s.dstack.PushInt(new(big.Int).Set(v0)) + } + + return nil +} + +// stack input: x, min, max. Returns 1 if x is within specified range +// (left inclusive), 0 otherwise +func opcodeWithin(op *parsedOpcode, s *Script) error { + maxVal, err := s.dstack.PopInt() + if err != nil { + return err + } + + minVal, err := s.dstack.PopInt() + if err != nil { + return err + } + + x, err := s.dstack.PopInt() + if err != nil { + return err + } + + if x.Cmp(minVal) >= 0 && x.Cmp(maxVal) == -1 { + s.dstack.PushInt(big.NewInt(1)) + } else { + s.dstack.PushInt(big.NewInt(0)) + } + return nil +} + +// Calculate the hash of hasher over buf. +func calcHash(buf []byte, hasher hash.Hash) []byte { + hasher.Write(buf) + return hasher.Sum(nil) +} + +// calculate hash160 which is ripemd160(sha256(data)) +func calcHash160(buf []byte) []byte { + return calcHash(calcHash(buf, sha256.New()), ripemd160.New()) +} + +func opcodeRipemd160(op *parsedOpcode, s *Script) error { + buf, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + s.dstack.PushByteArray(calcHash(buf, ripemd160.New())) + return nil +} + +func opcodeSha1(op *parsedOpcode, s *Script) error { + buf, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + s.dstack.PushByteArray(calcHash(buf, sha1.New())) + return nil +} + +func opcodeSha256(op *parsedOpcode, s *Script) error { + buf, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + s.dstack.PushByteArray(calcHash(buf, sha256.New())) + return nil +} + +func opcodeHash160(op *parsedOpcode, s *Script) error { + buf, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + s.dstack.PushByteArray(calcHash160(buf)) + return nil +} + +func opcodeHash256(op *parsedOpcode, s *Script) error { + buf, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + s.dstack.PushByteArray(btcwire.DoubleSha256(buf)) + return nil +} + +func opcodeCodeSeparator(op *parsedOpcode, s *Script) error { + s.lastcodesep = s.scriptoff + + return nil +} + +func opcodeCheckSig(op *parsedOpcode, s *Script) error { + + pkStr, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + sigStr, err := s.dstack.PopByteArray() + if err != nil { + return err + } + + // Trim off hashtype from the signature string. + hashType := sigStr[len(sigStr)-1] + sigStr = sigStr[:len(sigStr)-1] + + // Get script from the last OP_CODESEPARATOR and without any subsequent + // OP_CODESEPARATORs + subScript := s.subScript() + + // Unlikely to hit any cases here, but remove the signature from + // the script if present. + subScript = removeOpcodeByData(subScript, sigStr) + + hash := s.calcScriptHash(subScript, hashType) + + pubKey, err := btcec.ParsePubKey(pkStr, btcec.S256()) + if err != nil { + log.Warnf("can't parse public key from string: %v", err) + return err + } + + signature, err := btcec.ParseSignature(sigStr, btcec.S256()) + if err != nil { + log.Warnf("can't parse signature from string: %v", err) + return err + } + + log.Tracef("%v", newLogClosure(func() string { + return fmt.Sprintf("op_checksig pubKey %v\npk.x: %v\n "+ + "pk.y: %v\n r: %v\n s: %v\ncheckScriptHash %v", + spew.Sdump(pkStr), pubKey.X, pubKey.Y, + signature.R, signature.S, spew.Sdump(hash)) + })) + ok := ecdsa.Verify(pubKey, hash, signature.R, signature.S) + if !ok { + log.Warnf("ecdsa.Verify valid: %v", ok) + } + + s.dstack.PushBool(ok) + return nil +} + +func opcodeCheckSigVerify(op *parsedOpcode, s *Script) error { + err := opcodeCheckSig(op, s) + if err == nil { + err = opcodeVerify(op, s) + } + return err +} + +// stack; sigs pubkeys +func opcodeCheckMultiSig(op *parsedOpcode, s *Script) error { + + numPubkeys, err := s.dstack.PopInt() + if err != nil { + return err + } + + // XXX arbitrary limits + // nore more than 20 pubkeyhs, or 201 operations + + if numPubkeys.BitLen() > 32 { + return StackErrNumberTooBig + } + + npk := int(numPubkeys.Int64()) + if npk < 0 || npk > MaxPubKeysPerMultiSig { + return StackErrTooManyPubkeys + } + s.numOps += npk + if s.numOps > MaxOpsPerScript { + return StackErrTooManyOperations + } + pubKeyStrings := make([][]byte, npk) + pubKeys := make([]*ecdsa.PublicKey, npk) + for i := range pubKeys { + pubKeyStrings[i], err = s.dstack.PopByteArray() + if err != nil { + return err + } + } + + numSignatures, err := s.dstack.PopInt() + if err != nil { + return err + } + if numSignatures.BitLen() > 32 { + return StackErrNumberTooBig + } + + nsig := int(numSignatures.Int64()) + + sigStrings := make([][]byte, nsig) + signatures := make([]*btcec.Signature, nsig) + for i := range signatures { + sigStrings[i], err = s.dstack.PopByteArray() + if err != nil { + return err + } + // skip off the last byte for hashtype + signatures[i], err = + btcec.ParseSignature( + sigStrings[i][:len(sigStrings[i])-1], + btcec.S256()) + if err != nil { + return err + } + } + + // bug in bitcoind mean we pop one more stack value than should be used. + _, err = s.dstack.PopByteArray() + if err != nil { + return nil + } + + // Trim OP_CODESEPARATORs + script := s.subScript() + + // Remove any of the signatures that happen to be in the script. + // can't sign somthing containing the signature you're making, after + // all + for i := range sigStrings { + script = removeOpcodeByData(script, sigStrings[i]) + } + + curPk := 0 + for i := range signatures { + // check signatures. + success := false + // get hashtype from original byte string + hashType := sigStrings[i][len(sigStrings[i])-1] + + hash := s.calcScriptHash(script, hashType) + inner: + // Find first pubkey that successfully validates signature. + // we start off the search from the key that was successful + // last time. + for ; curPk < len(pubKeys); curPk++ { + if pubKeys[curPk] == nil { + pubKeys[curPk], err = + btcec.ParsePubKey(pubKeyStrings[curPk], + btcec.S256()) + if err != nil { + continue + } + } + success = ecdsa.Verify(pubKeys[curPk], hash, + signatures[i].R, signatures[i].S) + if success { + break inner + } + } + if success == false { + s.dstack.PushBool(false) + return nil + } + } + s.dstack.PushBool(true) + + return nil +} + +func opcodeCheckMultiSigVerify(op *parsedOpcode, s *Script) error { + err := opcodeCheckMultiSig(op, s) + if err == nil { + err = opcodeVerify(op, s) + } + return err +} diff --git a/opcode_test.go b/opcode_test.go new file mode 100644 index 00000000..3970b10d --- /dev/null +++ b/opcode_test.go @@ -0,0 +1,3104 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript_test + +import ( + "bytes" + "fmt" + "github.com/conformal/btcscript" + "github.com/conformal/btcwire" + "github.com/conformal/seelog" + "github.com/davecgh/go-spew/spew" + "os" + "testing" +) + +// test scripts to test as many opcodes as possible. +// All run on a fake tx with a single in, single out. +type opcodeTest struct { + script []byte + shouldPass bool + shouldFail error +} + +var opcodeTests = []opcodeTest{ + // does nothing, but doesn't put a true on the stack, should fail + {script: []byte{btcscript.OP_NOP}, shouldPass: false}, + // should just put true on the stack, thus passes. + {script: []byte{btcscript.OP_TRUE}, shouldPass: true}, + // should just put false on the stack, thus fails. + {script: []byte{btcscript.OP_FALSE}, shouldPass: false}, + // tests OP_VERIFY (true). true is needed since else stack is empty. + {script: []byte{btcscript.OP_TRUE, btcscript.OP_VERIFY, + btcscript.OP_TRUE}, shouldPass: true}, + // tests OP_VERIFY (false), will error out. + {script: []byte{btcscript.OP_FALSE, btcscript.OP_VERIFY, + btcscript.OP_TRUE}, shouldPass: false}, + // tests OP_VERIFY with empty stack (errors) + {script: []byte{btcscript.OP_VERIFY}, shouldPass: false}, + // test OP_RETURN immediately fails the script (empty stack) + {script: []byte{btcscript.OP_RETURN}, shouldPass: false}, + // test OP_RETURN immediately fails the script (full stack) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_RETURN}, + shouldPass: false}, + // tests numequal with a trivial example (passing) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_NUMEQUAL}, shouldPass: true}, + // tests numequal with a trivial example (failing) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_NUMEQUAL}, shouldPass: false}, + // tests numequal with insufficient arguments (1/2) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_NUMEQUAL}, + shouldPass: false}, + // tests numequal with insufficient arguments (0/2) + {script: []byte{btcscript.OP_NUMEQUAL}, shouldPass: false}, + // tests numnotequal with a trivial example (passing) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_NUMNOTEQUAL}, shouldPass: true}, + // tests numnotequal with a trivial example (failing) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_NUMNOTEQUAL}, shouldPass: false}, + // tests numnotequal with insufficient arguments (1/2) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_NUMNOTEQUAL}, + shouldPass: false}, + // tests numnotequal with insufficient arguments (0/2) + {script: []byte{btcscript.OP_NUMNOTEQUAL}, shouldPass: false}, + // test numequal_verify with a trivial example (passing) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_NUMEQUALVERIFY, btcscript.OP_TRUE}, + shouldPass: true}, + // test numequal_verify with a trivial example (failing) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_NUMEQUALVERIFY, btcscript.OP_TRUE}, + shouldPass: false}, + // test OP_1ADD by adding 1 to 0 + {script: []byte{btcscript.OP_FALSE, btcscript.OP_1ADD}, + shouldPass: true}, + // test OP_1ADD without args (should error) + {script: []byte{btcscript.OP_1ADD}, shouldPass: false}, + // test OP_1NEGATE by adding 1 to -1 + {script: []byte{btcscript.OP_1NEGATE, btcscript.OP_1ADD}, + shouldPass: false}, + // test OP_1NEGATE by adding negating -1 + {script: []byte{btcscript.OP_1NEGATE, btcscript.OP_NEGATE}, + shouldPass: true}, + // test OP_NEGATE by adding 1 to -1 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_NEGATE, + btcscript.OP_1ADD}, shouldPass: false}, + // test OP_NEGATE with no args + {script: []byte{btcscript.OP_NEGATE}, shouldPass: false}, + // test OP_1SUB -> 1 - 1 = 0 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_1SUB}, + shouldPass: false}, + // test OP_1SUB -> negate(0 -1) = 1 + {script: []byte{btcscript.OP_FALSE, btcscript.OP_1SUB, + btcscript.OP_NEGATE}, shouldPass: true}, + // test OP_1SUB with empty stack + {script: []byte{btcscript.OP_1SUB}, shouldPass: false}, + // OP_DEPTH with empty stack, means 0 on stack at end + {script: []byte{btcscript.OP_DEPTH}, shouldPass: false}, + // 1 +1 -1 = 1. tests depth + add + {script: []byte{btcscript.OP_TRUE, btcscript.OP_DEPTH, btcscript.OP_ADD, + btcscript.OP_1SUB}, shouldPass: true}, + // 1 +1 -1 = 0 . tests dept + add + {script: []byte{btcscript.OP_TRUE, btcscript.OP_DEPTH, + btcscript.OP_ADD, btcscript.OP_1SUB, btcscript.OP_1SUB}, + shouldPass: false}, + // OP_ADD with only one thing on stack should error + {script: []byte{btcscript.OP_TRUE, btcscript.OP_ADD}, + shouldPass: false}, + // OP_ADD with nothing on stack should error + {script: []byte{btcscript.OP_ADD}, shouldPass: false}, + // OP_SUB: 1-1=0 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_SUB}, shouldPass: false}, + // OP_SUB: 1+1-1=1 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_ADD, btcscript.OP_SUB}, shouldPass: true}, + // OP_SUB with only one thing on stack should error + {script: []byte{btcscript.OP_TRUE, btcscript.OP_SUB}, + shouldPass: false}, + // OP_SUB with nothing on stack should error + {script: []byte{btcscript.OP_SUB}, shouldPass: false}, + // OP_LESSTHAN 1 < 1 == false + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_LESSTHAN}, shouldPass: false}, + // OP_LESSTHAN 1 < 0 == false + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_LESSTHAN}, shouldPass: false}, + // OP_LESSTHAN 0 < 1 == true + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_LESSTHAN}, shouldPass: true}, + // OP_LESSTHAN only one arg + {script: []byte{btcscript.OP_TRUE, btcscript.OP_LESSTHAN}, + shouldPass: false}, + // OP_LESSTHAN no args + {script: []byte{btcscript.OP_LESSTHAN}, shouldPass: false}, + + // OP_LESSTHANOREQUAL 1 <= 1 == true + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_LESSTHANOREQUAL}, shouldPass: true}, + // OP_LESSTHANOREQUAL 1 <= 0 == false + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_LESSTHANOREQUAL}, shouldPass: false}, + // OP_LESSTHANOREQUAL 0 <= 1 == true + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_LESSTHANOREQUAL}, shouldPass: true}, + // OP_LESSTHANOREQUAL only one arg + {script: []byte{btcscript.OP_TRUE, btcscript.OP_LESSTHANOREQUAL}, + shouldPass: false}, + // OP_LESSTHANOREQUAL no args + {script: []byte{btcscript.OP_LESSTHANOREQUAL}, shouldPass: false}, + + // OP_GREATERTHAN 1 > 1 == false + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_GREATERTHAN}, shouldPass: false}, + // OP_GREATERTHAN 1 > 0 == true + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_GREATERTHAN}, shouldPass: true}, + // OP_GREATERTHAN 0 > 1 == false + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_GREATERTHAN}, shouldPass: false}, + // OP_GREATERTHAN only one arg + {script: []byte{btcscript.OP_TRUE, btcscript.OP_GREATERTHAN}, + shouldPass: false}, + // OP_GREATERTHAN no args + {script: []byte{btcscript.OP_GREATERTHAN}, shouldPass: false}, + + // OP_GREATERTHANOREQUAL 1 >= 1 == true + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_GREATERTHANOREQUAL}, shouldPass: true}, + // OP_GREATERTHANOREQUAL 1 >= 0 == false + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_GREATERTHANOREQUAL}, shouldPass: true}, + // OP_GREATERTHANOREQUAL 0 >= 1 == true + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_GREATERTHANOREQUAL}, shouldPass: false}, + // OP_GREATERTHANOREQUAL only one arg + {script: []byte{btcscript.OP_TRUE, btcscript.OP_GREATERTHANOREQUAL}, + shouldPass: false}, + // OP_GREATERTHANOREQUAL no args + {script: []byte{btcscript.OP_GREATERTHANOREQUAL}, shouldPass: false}, + + // OP_MIN basic functionality -> min(0,1) = 0 = min(1,0) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_MIN}, shouldPass: false}, + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_MIN}, shouldPass: false}, + // OP_MIN -> 1 arg errors + {script: []byte{btcscript.OP_TRUE, btcscript.OP_MIN}, + shouldPass: false}, + // OP_MIN -> 0 arg errors + {script: []byte{btcscript.OP_MIN}, shouldPass: false}, + // OP_MAX basic functionality -> max(0,1) = 1 = max(1,0) + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_MAX}, shouldPass: true}, + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_MAX}, shouldPass: true}, + // OP_MAX -> 1 arg errors + {script: []byte{btcscript.OP_TRUE, btcscript.OP_MAX}, + shouldPass: false}, + // OP_MAX -> 0 arg errors + {script: []byte{btcscript.OP_MAX}, shouldPass: false}, + + // By this point we know a number of operations appear to be working + // correctly. we can use them to test the other number pushing + // operations + {script: []byte{btcscript.OP_TRUE, btcscript.OP_1ADD, btcscript.OP_2, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_2, btcscript.OP_1ADD, btcscript.OP_3, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_3, btcscript.OP_1ADD, btcscript.OP_4, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_4, btcscript.OP_1ADD, btcscript.OP_5, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_5, btcscript.OP_1ADD, btcscript.OP_6, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_6, btcscript.OP_1ADD, btcscript.OP_7, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_7, btcscript.OP_1ADD, btcscript.OP_8, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_8, btcscript.OP_1ADD, btcscript.OP_9, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_9, btcscript.OP_1ADD, btcscript.OP_10, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_10, btcscript.OP_1ADD, btcscript.OP_11, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_11, btcscript.OP_1ADD, btcscript.OP_12, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_12, btcscript.OP_1ADD, btcscript.OP_13, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_13, btcscript.OP_1ADD, btcscript.OP_14, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_14, btcscript.OP_1ADD, btcscript.OP_15, + btcscript.OP_EQUAL}, shouldPass: true}, + {script: []byte{btcscript.OP_15, btcscript.OP_1ADD, btcscript.OP_16, + btcscript.OP_EQUAL}, shouldPass: true}, + + // Test OP_WITHIN x, min, max + // 0 <= 1 < 2 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, btcscript.OP_2, + btcscript.OP_WITHIN}, shouldPass: true}, + // 1 <= 0 < 2 FAIL + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, btcscript.OP_2, + btcscript.OP_WITHIN}, shouldPass: false}, + // 1 <= 1 < 2 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, btcscript.OP_2, + btcscript.OP_WITHIN}, shouldPass: true}, + // 1 <= 2 < 2 FAIL + {script: []byte{btcscript.OP_2, btcscript.OP_TRUE, btcscript.OP_2, + btcscript.OP_WITHIN}, shouldPass: false}, + // only two arguments + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_WITHIN}, shouldPass: false}, + // only one argument + {script: []byte{btcscript.OP_TRUE, btcscript.OP_WITHIN}, + shouldPass: false}, + // no arguments + {script: []byte{btcscript.OP_WITHIN}, shouldPass: false}, + + // OP_BOOLAND + // 1 && 1 == 1 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_BOOLAND}, shouldPass: true}, + // 1 && 0 == 0 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_BOOLAND}, shouldPass: false}, + // 0 && 1 == 0 + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_BOOLAND}, shouldPass: false}, + // 0 && 0 == 0 + {script: []byte{btcscript.OP_FALSE, btcscript.OP_FALSE, + btcscript.OP_BOOLAND}, shouldPass: false}, + // 0 && - boom + {script: []byte{btcscript.OP_TRUE, btcscript.OP_BOOLAND}, + shouldPass: false}, + // && - boom + {script: []byte{btcscript.OP_BOOLAND}, shouldPass: false}, + + // OP_BOOLOR + // 1 || 1 == 1 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_TRUE, + btcscript.OP_BOOLOR}, shouldPass: true}, + // 1 || 0 == 1 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_FALSE, + btcscript.OP_BOOLOR}, shouldPass: true}, + // 0 || 1 == 1 + {script: []byte{btcscript.OP_FALSE, btcscript.OP_TRUE, + btcscript.OP_BOOLOR}, shouldPass: true}, + // 0 || 0 == 0 + {script: []byte{btcscript.OP_FALSE, btcscript.OP_FALSE, + btcscript.OP_BOOLOR}, shouldPass: false}, + // 0 && - boom + {script: []byte{btcscript.OP_TRUE, btcscript.OP_BOOLOR}, + shouldPass: false}, + // && - boom + {script: []byte{btcscript.OP_BOOLOR}, shouldPass: false}, + + // OP_0NOTEQUAL + // 1 with input != 0 XXX check output is actually 1. + {script: []byte{btcscript.OP_TRUE, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_2, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_3, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_4, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_5, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_6, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_7, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_8, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_9, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_10, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_11, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_12, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_13, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_14, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_15, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_16, btcscript.OP_0NOTEQUAL}, + shouldPass: true}, + {script: []byte{btcscript.OP_FALSE, btcscript.OP_0NOTEQUAL}, shouldPass: false}, + // No arguments also blows up + {script: []byte{btcscript.OP_0NOTEQUAL}, shouldPass: false}, + + // OP_NOT: 1 i input is 0, else 0 + {script: []byte{btcscript.OP_TRUE, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_2, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_3, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_4, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_5, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_6, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_7, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_8, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_9, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_10, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_11, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_12, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_13, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_14, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_15, btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_16, btcscript.OP_NOT}, shouldPass: false}, + // check negative numbers too + {script: []byte{btcscript.OP_TRUE, btcscript.OP_NEGATE, + btcscript.OP_NOT}, shouldPass: false}, + {script: []byte{btcscript.OP_FALSE, btcscript.OP_NOT}, + shouldPass: true}, + // No arguments also blows up + {script: []byte{btcscript.OP_NOT}, shouldPass: false}, + + // Conditional Execution + {script: []byte{btcscript.OP_0, btcscript.OP_IF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_2, btcscript.OP_ENDIF}, shouldPass: true}, + {script: []byte{btcscript.OP_1, btcscript.OP_IF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_2, btcscript.OP_ENDIF}, shouldPass: false}, + {script: []byte{btcscript.OP_1, btcscript.OP_NOTIF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_2, btcscript.OP_ENDIF}, shouldPass: true}, + {script: []byte{btcscript.OP_0, btcscript.OP_NOTIF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_2, btcscript.OP_ENDIF}, shouldPass: false}, + {script: []byte{btcscript.OP_0, btcscript.OP_IF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_2}, shouldFail: btcscript.StackErrMissingEndif}, + {script: []byte{btcscript.OP_1, btcscript.OP_NOTIF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_2}, shouldFail: btcscript.StackErrMissingEndif}, + {script: []byte{btcscript.OP_1, btcscript.OP_1, btcscript.OP_IF, btcscript.OP_IF, btcscript.OP_1, btcscript.OP_ELSE, btcscript.OP_0, btcscript.OP_ENDIF, btcscript.OP_ENDIF}, shouldPass: true}, + {script: []byte{btcscript.OP_1, btcscript.OP_IF, btcscript.OP_IF, btcscript.OP_1, btcscript.OP_ELSE, btcscript.OP_0, btcscript.OP_ENDIF, btcscript.OP_ENDIF}, shouldFail: btcscript.StackErrUnderflow}, + {script: []byte{btcscript.OP_0, btcscript.OP_IF, btcscript.OP_IF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_0, btcscript.OP_ENDIF, btcscript.OP_ELSE, btcscript.OP_1, btcscript.OP_ENDIF}, shouldPass: true}, + {script: []byte{btcscript.OP_0, btcscript.OP_IF, btcscript.OP_NOTIF, btcscript.OP_0, btcscript.OP_ELSE, btcscript.OP_0, btcscript.OP_ENDIF, btcscript.OP_ELSE, btcscript.OP_1, btcscript.OP_ENDIF}, shouldPass: true}, + {script: []byte{btcscript.OP_NOTIF, btcscript.OP_0, btcscript.OP_ENDIF}, shouldFail: btcscript.StackErrUnderflow}, + {script: []byte{btcscript.OP_ELSE, btcscript.OP_0, btcscript.OP_ENDIF}, shouldFail: btcscript.StackErrNoIf}, + {script: []byte{btcscript.OP_ENDIF}, shouldFail: btcscript.StackErrNoIf}, + /* up here because error from sig parsing is undefined. */ + {script: []byte{btcscript.OP_1, btcscript.OP_1, btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, 0x13, + 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, 0x15, 0x9b, + 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, 0x71, 0x30, 0x2f, + 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, 0x73, 0x97, 0xf5, 0x54, + 0xa7, 0xdf, 0x5f, 0x14, 0x2c, 0x21, 0xc1, 0xb7, 0x30, 0x3b, + 0x8a, 0x06, 0x26, 0xf1, 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, + 0x4f, 0x7e, 0x6c, 0xd8, 0x4c, + btcscript.OP_1, btcscript.OP_CHECK_MULTISIG}, + shouldPass: false}, + /* up here because no defined error case. */ + {script: []byte{btcscript.OP_1, btcscript.OP_1, btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, 0x13, + 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, 0x15, 0x9b, + 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, 0x71, 0x30, 0x2f, + 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, 0x73, 0x97, 0xf5, 0x54, + 0xa7, 0xdf, 0x5f, 0x14, 0x2c, 0x21, 0xc1, 0xb7, 0x30, 0x3b, + 0x8a, 0x06, 0x26, 0xf1, 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, + 0x4f, 0x7e, 0x6c, 0xd8, 0x4c, + btcscript.OP_1, btcscript.OP_CHECKMULTISIGVERIFY}, + shouldPass: false}, + + // Invalid Opcodes + {script: []byte{186}, shouldPass: false}, + {script: []byte{187}, shouldPass: false}, + {script: []byte{188}, shouldPass: false}, + {script: []byte{189}, shouldPass: false}, + {script: []byte{190}, shouldPass: false}, + {script: []byte{191}, shouldPass: false}, + {script: []byte{192}, shouldPass: false}, + {script: []byte{193}, shouldPass: false}, + {script: []byte{194}, shouldPass: false}, + {script: []byte{195}, shouldPass: false}, + {script: []byte{195}, shouldPass: false}, + {script: []byte{196}, shouldPass: false}, + {script: []byte{197}, shouldPass: false}, + {script: []byte{198}, shouldPass: false}, + {script: []byte{199}, shouldPass: false}, + {script: []byte{200}, shouldPass: false}, + {script: []byte{201}, shouldPass: false}, + {script: []byte{202}, shouldPass: false}, + {script: []byte{203}, shouldPass: false}, + {script: []byte{204}, shouldPass: false}, + {script: []byte{205}, shouldPass: false}, + {script: []byte{206}, shouldPass: false}, + {script: []byte{207}, shouldPass: false}, + {script: []byte{208}, shouldPass: false}, + {script: []byte{209}, shouldPass: false}, + {script: []byte{210}, shouldPass: false}, + {script: []byte{211}, shouldPass: false}, + {script: []byte{212}, shouldPass: false}, + {script: []byte{213}, shouldPass: false}, + {script: []byte{214}, shouldPass: false}, + {script: []byte{215}, shouldPass: false}, + {script: []byte{216}, shouldPass: false}, + {script: []byte{217}, shouldPass: false}, + {script: []byte{218}, shouldPass: false}, + {script: []byte{219}, shouldPass: false}, + {script: []byte{220}, shouldPass: false}, + {script: []byte{221}, shouldPass: false}, + {script: []byte{222}, shouldPass: false}, + {script: []byte{223}, shouldPass: false}, + {script: []byte{224}, shouldPass: false}, + {script: []byte{225}, shouldPass: false}, + {script: []byte{226}, shouldPass: false}, + {script: []byte{227}, shouldPass: false}, + {script: []byte{228}, shouldPass: false}, + {script: []byte{229}, shouldPass: false}, + {script: []byte{230}, shouldPass: false}, + {script: []byte{231}, shouldPass: false}, + {script: []byte{232}, shouldPass: false}, + {script: []byte{233}, shouldPass: false}, + {script: []byte{234}, shouldPass: false}, + {script: []byte{235}, shouldPass: false}, + {script: []byte{236}, shouldPass: false}, + {script: []byte{237}, shouldPass: false}, + {script: []byte{238}, shouldPass: false}, + {script: []byte{239}, shouldPass: false}, + {script: []byte{240}, shouldPass: false}, + {script: []byte{241}, shouldPass: false}, + {script: []byte{242}, shouldPass: false}, + {script: []byte{243}, shouldPass: false}, + {script: []byte{244}, shouldPass: false}, + {script: []byte{245}, shouldPass: false}, + {script: []byte{246}, shouldPass: false}, + {script: []byte{247}, shouldPass: false}, + {script: []byte{248}, shouldPass: false}, + {script: []byte{249}, shouldPass: false}, + {script: []byte{250}, shouldPass: false}, + {script: []byte{251}, shouldPass: false}, + {script: []byte{252}, shouldPass: false}, +} + +func testScript(t *testing.T, script []byte) (err error) { + // mock up fake tx. + tx := &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{btcscript.OP_NOP}, + Sequence: 0xffffffff, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 0x12a05f200, + PkScript: []byte{}, + }, + }, + LockTime: 0, + } + + tx.TxOut[0].PkScript = script + + engine, err := btcscript.NewScript(tx.TxIn[0].SignatureScript, + tx.TxOut[0].PkScript, 0, tx, 1, false) + if err != nil { + return err + } + return engine.Execute() +} + +func TestScripts(t *testing.T) { + log, err := seelog.LoggerFromWriterWithMinLevel(os.Stdout, + seelog.InfoLvl) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to create logger: %v", err) + return + } + defer log.Flush() + btcscript.UseLogger(log) + // for each entry in the list + for i := range opcodeTests { + shouldPass := opcodeTests[i].shouldPass + shouldFail := opcodeTests[i].shouldFail + err := testScript(t, opcodeTests[i].script) + if shouldFail != nil { + if err == nil { + t.Errorf("test %d passed should fail with %v", i, err) + } else if shouldFail != err { + t.Errorf("test %d failed with wrong error [%v], expected [%v]", i, err, shouldFail) + } + } + if shouldPass && err != nil { + t.Errorf("test %d failed: %v", i, err) + } else if !shouldPass && err == nil { + t.Errorf("test %d passed, should fail", i) + } + } +} + +// Detailed tests for opcodes, we inspect machine state before and after the +// opcode and check that it has the effect on the state that we expect. +type detailedTest struct { + name string + before [][]byte + altbefore [][]byte + script []byte + expectedReturn error + after [][]byte + altafter [][]byte + disassembly string + disassemblyerr error +} + +var detailedTests = []detailedTest{ + { + name: "noop", + before: [][]byte{{1}, {2}, {3}, {4}, {5}}, + script: []byte{btcscript.OP_NOP}, + after: [][]byte{{1}, {2}, {3}, {4}, {5}}, + disassembly: "OP_NOP", + }, + { + name: "dup", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_DUP}, + after: [][]byte{{1}, {1}}, + disassembly: "OP_DUP", + }, + { + name: "dup2", + before: [][]byte{{1}, {2}}, + script: []byte{btcscript.OP_2DUP}, + after: [][]byte{{1}, {2}, {1}, {2}}, + disassembly: "OP_2DUP", + }, + { + name: "dup3", + before: [][]byte{{1}, {2}, {3}}, + script: []byte{btcscript.OP_3DUP}, + after: [][]byte{{1}, {2}, {3}, {1}, {2}, {3}}, + disassembly: "OP_3DUP", + }, + { + name: "dup too much", + before: [][]byte{}, + script: []byte{btcscript.OP_DUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_DUP", + }, + { + name: "2dup too much", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_2DUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_2DUP", + }, + { + name: "2dup way too much", + before: [][]byte{}, + script: []byte{btcscript.OP_2DUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_2DUP", + }, + { + name: "3dup too much", + before: [][]byte{{1}, {2}}, + script: []byte{btcscript.OP_3DUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_3DUP", + }, + { + name: "3dup kinda too much", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_3DUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_3DUP", + }, + { + name: "3dup way too much", + before: [][]byte{}, + script: []byte{btcscript.OP_3DUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_3DUP", + }, + { + name: "Nip", + before: [][]byte{{1}, {2}, {3}}, + script: []byte{btcscript.OP_NIP}, + after: [][]byte{{1}, {3}}, + disassembly: "OP_NIP", + }, + { + name: "Nip too much", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_NIP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{{2}, {3}}, + disassembly: "OP_NIP", + }, + { + name: "keep on tucking", + before: [][]byte{{1}, {2}, {3}}, + script: []byte{btcscript.OP_TUCK}, + after: [][]byte{{1}, {3}, {2}, {3}}, + disassembly: "OP_TUCK", + }, + { + name: "a little tucked up", + before: [][]byte{{1}}, // too few arguments for tuck + script: []byte{btcscript.OP_TUCK}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{}, + disassembly: "OP_TUCK", + }, + { + name: "all tucked up", + before: [][]byte{}, // too few arguments for tuck + script: []byte{btcscript.OP_TUCK}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_TUCK", + }, + { + name: "drop 1", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_DROP}, + after: [][]byte{{1}, {2}, {3}}, + disassembly: "OP_DROP", + }, + { + name: "drop 2", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_2DROP}, + after: [][]byte{{1}, {2}}, + disassembly: "OP_2DROP", + }, + { + name: "drop too much", + before: [][]byte{}, + script: []byte{btcscript.OP_DROP}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_DROP", + }, + { + name: "2drop too much", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_2DROP}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_2DROP", + }, + { + name: "2drop far too much", + before: [][]byte{}, + script: []byte{btcscript.OP_2DROP}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_2DROP", + }, + { + name: "Rot1", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_ROT}, + after: [][]byte{{1}, {3}, {4}, {2}}, + disassembly: "OP_ROT", + }, + { + name: "Rot2", + before: [][]byte{{1}, {2}, {3}, {4}, {5}, {6}}, + script: []byte{btcscript.OP_2ROT}, + after: [][]byte{{3}, {4}, {5}, {6}, {1}, {2}}, + disassembly: "OP_2ROT", + }, + { + name: "Rot too little", + before: [][]byte{{1}, {2}}, + script: []byte{btcscript.OP_ROT}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_ROT", + }, + { + name: "Swap1", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_SWAP}, + after: [][]byte{{1}, {2}, {4}, {3}}, + disassembly: "OP_SWAP", + }, + { + name: "Swap2", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_2SWAP}, + after: [][]byte{{3}, {4}, {1}, {2}}, + disassembly: "OP_2SWAP", + }, + { + name: "Swap too little", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_SWAP}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_SWAP", + }, + { + name: "Over1", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_OVER}, + after: [][]byte{{1}, {2}, {3}, {4}, {3}}, + disassembly: "OP_OVER", + }, + { + name: "Over2", + before: [][]byte{{1}, {2}, {3}, {4}}, + script: []byte{btcscript.OP_2OVER}, + after: [][]byte{{1}, {2}, {3}, {4}, {1}, {2}}, + disassembly: "OP_2OVER", + }, + { + name: "Over too little", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_OVER}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_OVER", + }, + { + name: "Pick1", + before: [][]byte{{1}, {2}, {3}, {4}, {1}}, + script: []byte{btcscript.OP_PICK}, + after: [][]byte{{1}, {2}, {3}, {4}, {3}}, + disassembly: "OP_PICK", + }, + { + name: "Pick2", + before: [][]byte{{1}, {2}, {3}, {4}, {2}}, + script: []byte{btcscript.OP_PICK}, + after: [][]byte{{1}, {2}, {3}, {4}, {2}}, + disassembly: "OP_PICK", + }, + { + name: "Pick too little", + before: [][]byte{{1}, {1}}, + script: []byte{btcscript.OP_PICK}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_PICK", + }, + { + name: "Pick nothing", + before: [][]byte{{}}, + script: []byte{btcscript.OP_PICK}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_PICK", + }, + { + name: "Pick no args", + before: [][]byte{}, + script: []byte{btcscript.OP_PICK}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_PICK", + }, + { + name: "Pick stupid numbers", + before: [][]byte{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + script: []byte{btcscript.OP_PICK}, + expectedReturn: btcscript.StackErrNumberTooBig, + disassembly: "OP_PICK", + }, + { + name: "Roll1", + before: [][]byte{{1}, {2}, {3}, {4}, {1}}, + script: []byte{btcscript.OP_ROLL}, + after: [][]byte{{1}, {2}, {4}, {3}}, + disassembly: "OP_ROLL", + }, + { + name: "Roll2", + before: [][]byte{{1}, {2}, {3}, {4}, {2}}, + script: []byte{btcscript.OP_ROLL}, + after: [][]byte{{1}, {3}, {4}, {2}}, + disassembly: "OP_ROLL", + }, + { + name: "Roll too little", + before: [][]byte{{1}, {1}}, + script: []byte{btcscript.OP_ROLL}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_ROLL", + }, + { + name: "Roll nothing ", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_ROLL}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_ROLL", + }, + { + name: "Roll no args ", + before: [][]byte{}, + script: []byte{btcscript.OP_ROLL}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_ROLL", + }, + { + name: "Roll stupid numbers", + before: [][]byte{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + script: []byte{btcscript.OP_ROLL}, + expectedReturn: btcscript.StackErrNumberTooBig, + disassembly: "OP_ROLL", + }, + { + name: "ifdup (positive)", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_IFDUP}, + after: [][]byte{{1}, {1}}, + disassembly: "OP_IFDUP", + }, + { + name: "ifdup (negative)", + before: [][]byte{{0}}, + script: []byte{btcscript.OP_IFDUP}, + after: [][]byte{{0}}, + disassembly: "OP_IFDUP", + }, + { + name: "ifdup (empty)", + before: [][]byte{}, + script: []byte{btcscript.OP_IFDUP}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{{0}}, + disassembly: "OP_IFDUP", + }, + { + name: "toaltastack", + before: [][]byte{{1}}, + altbefore: [][]byte{}, + script: []byte{btcscript.OP_TOALTSTACK}, + after: [][]byte{}, + altafter: [][]byte{{1}}, + disassembly: "OP_TOALTSTACK", + }, + { + name: "toaltastack (empty)", + before: [][]byte{}, + altbefore: [][]byte{}, + script: []byte{btcscript.OP_TOALTSTACK}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_TOALTSTACK", + }, + { + name: "fromaltastack", + before: [][]byte{}, + altbefore: [][]byte{{1}}, + script: []byte{btcscript.OP_FROMALTSTACK}, + after: [][]byte{{1}}, + altafter: [][]byte{}, + disassembly: "OP_FROMALTSTACK", + }, + { + name: "fromaltastack (empty)", + before: [][]byte{}, + altbefore: [][]byte{}, + script: []byte{btcscript.OP_FROMALTSTACK}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_FROMALTSTACK", + }, + { + name: "op_size (1)", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_SIZE}, + after: [][]byte{{1}, {1}}, + disassembly: "OP_SIZE", + }, + { + name: "op_size (5)", + before: [][]byte{{1, 2, 3, 4, 5}}, + script: []byte{btcscript.OP_SIZE}, + after: [][]byte{{1, 2, 3, 4, 5}, {5}}, + disassembly: "OP_SIZE", + }, + { + name: "op_size (0)", + before: [][]byte{{}}, + script: []byte{btcscript.OP_SIZE}, + // pushInt(0) actually gives an empty array, still counts as 0 + after: [][]byte{{}, {}}, + disassembly: "OP_SIZE", + }, + { + name: "op_size (invalid)", + before: [][]byte{}, + script: []byte{btcscript.OP_SIZE}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_SIZE", + }, + { + name: "OP_EQUAL (valid)", + before: [][]byte{{1, 2, 3, 4}, {1, 2, 3, 4}}, + script: []byte{btcscript.OP_EQUAL}, + after: [][]byte{{1}}, + disassembly: "OP_EQUAL", + }, + { + name: "OP_EQUAL (invalid)", + before: [][]byte{{1, 2, 3, 4}, {1, 2, 3, 3}}, + script: []byte{btcscript.OP_EQUAL}, + after: [][]byte{{0}}, + disassembly: "OP_EQUAL", + }, + { + name: "OP_EQUAL (one arg)", + before: [][]byte{{1, 2, 3, 4}}, + script: []byte{btcscript.OP_EQUAL}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{{0}}, + disassembly: "OP_EQUAL", + }, + { + name: "OP_EQUAL (no arg)", + before: [][]byte{}, + script: []byte{btcscript.OP_EQUAL}, + expectedReturn: btcscript.StackErrUnderflow, + after: [][]byte{{0}}, + disassembly: "OP_EQUAL", + }, + { + name: "OP_EQUALVERIFY (valid)", + before: [][]byte{{1, 2, 3, 4}, {1, 2, 3, 4}}, + script: []byte{btcscript.OP_EQUALVERIFY}, + after: [][]byte{}, + disassembly: "OP_EQUALVERIFY", + }, + { + name: "OP_EQUALVERIFY (invalid)", + before: [][]byte{{1, 2, 3, 4}, {1, 2, 3, 3}}, + script: []byte{btcscript.OP_EQUALVERIFY}, + expectedReturn: btcscript.StackErrVerifyFailed, + after: [][]byte{}, + disassembly: "OP_EQUALVERIFY", + }, + { + name: "OP_EQUALVERIFY (one arg)", + before: [][]byte{{1, 2, 3, 4}}, + script: []byte{btcscript.OP_EQUALVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_EQUALVERIFY", + }, + { + name: "OP_EQUALVERIFY (no arg)", + before: [][]byte{}, + script: []byte{btcscript.OP_EQUALVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_EQUALVERIFY", + }, + { + name: "OP_1NEGATE", + before: [][]byte{}, + script: []byte{btcscript.OP_1NEGATE}, + after: [][]byte{{0x81}}, + disassembly: "OP_1NEGATE", + }, + { + name: "add one to minus one", + before: [][]byte{}, + script: []byte{btcscript.OP_1NEGATE, btcscript.OP_1ADD}, + after: [][]byte{{}}, // 0 + disassembly: "OP_1NEGATE OP_1ADD", + }, + { + name: "OP_ABS (positive)", + before: [][]byte{{1}}, + script: []byte{btcscript.OP_ABS}, + after: [][]byte{{1}}, + disassembly: "OP_ABS", + }, + { + name: "OP_ABS (negative)", + before: [][]byte{{0x81}}, + script: []byte{btcscript.OP_ABS}, + after: [][]byte{{1}}, + disassembly: "OP_ABS", + }, + { + name: "OP_ABS (empty)", + before: [][]byte{}, + script: []byte{btcscript.OP_ABS}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_ABS", + }, + { + name: "op_data_1", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_1, 1}, + after: [][]byte{{1}}, + disassembly: "01", + }, + { + name: "op_data_2", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_2, 1, 2}, + after: [][]byte{{1, 2}}, + disassembly: "0102", + }, + { + name: "op_data_3", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_3, 1, 2, 3}, + after: [][]byte{{1, 2, 3}}, + disassembly: "010203", + }, + { + name: "op_data_4", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_4, 1, 2, 3, 4}, + after: [][]byte{{1, 2, 3, 4}}, + disassembly: "01020304", + }, + { + name: "op_data_5", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_5, 1, 2, 3, 4, 5}, + after: [][]byte{{1, 2, 3, 4, 5}}, + disassembly: "0102030405", + }, + { + name: "op_data_6", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_6, 1, 2, 3, 4, 5, 6}, + after: [][]byte{{1, 2, 3, 4, 5, 6}}, + disassembly: "010203040506", + }, + { + name: "op_data_7", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_7, 1, 2, 3, 4, 5, 6, 7}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7}}, + disassembly: "01020304050607", + }, + { + name: "op_data_8", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_8, 1, 2, 3, 4, 5, 6, 7, 8}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8}}, + disassembly: "0102030405060708", + }, + { + name: "op_data_9", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_9, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9}}, + disassembly: "010203040506070809", + }, + { + name: "op_data_10", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, + disassembly: "0102030405060708090a", + }, + { + name: "op_data_11", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_11, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + disassembly: "0102030405060708090a0b", + }, + { + name: "op_data_12", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_12, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, + disassembly: "0102030405060708090a0b0c", + }, + { + name: "op_data_13", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_13, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + disassembly: "0102030405060708090a0b0c0d", + }, + { + name: "op_data_14", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_14, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}}, + disassembly: "0102030405060708090a0b0c0d0e", + }, + { + name: "op_data_15", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_15, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15}}, + disassembly: "0102030405060708090a0b0c0d0e0f", + }, + { + name: "op_data_16", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_16, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16}}, + disassembly: "0102030405060708090a0b0c0d0e0f10", + }, + { + name: "op_data_17", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_17, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17}}, + disassembly: "0102030405060708090a0b0c0d0e0f1011", + }, + { + name: "op_data_18", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_18, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112", + }, + { + name: "op_data_19", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_19, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19}}, + disassembly: "0102030405060708090a0b0c0d0e0f10111213", + }, + { + name: "op_data_20", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_20, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20}}, + disassembly: "0102030405060708090a0b0c0d0e0f1011121314", + }, + { + name: "op_data_21", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_21, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415", + }, + { + name: "op_data_22", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_22, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22}}, + disassembly: "0102030405060708090a0b0c0d0e0f10111213141516", + }, + { + name: "op_data_23", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_23, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23}}, + disassembly: "0102030405060708090a0b0c0d0e0f1011121314151617", + }, + { + name: "op_data_24", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_24, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718", + }, + { + name: "op_data_25", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_25, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}}, + disassembly: "0102030405060708090a0b0c0d0e0f10111213141516171819", + }, + { + name: "op_data_26", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_26, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a", + }, + { + name: "op_data_27", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_27, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b", + }, + { + name: "op_data_28", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_28, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c", + }, + { + name: "op_data_29", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_29, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d", + }, + { + name: "op_data_30", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_30, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e", + }, + { + name: "op_data_31", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_31, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", + }, + { + name: "op_data_32", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_32, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + }, + { + name: "op_data_33", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_33, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021", + }, + { + name: "op_data_34", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_34, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122", + }, + { + name: "op_data_35", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_35, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223", + }, + { + name: "op_data_36", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_36, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324", + }, + { + name: "op_data_37", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_37, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425", + }, + { + name: "op_data_38", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_38, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526", + }, + { + name: "op_data_39", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_39, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627", + }, + { + name: "op_data_40", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_40, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728", + }, + { + name: "op_data_41", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_41, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829", + }, + { + name: "op_data_42", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_42, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a", + }, + { + name: "op_data_43", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_43, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b", + }, + { + name: "op_data_44", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_44, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c", + }, + { + name: "op_data_45", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_45, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d", + }, + { + name: "op_data_46", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_46, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e", + }, + { + name: "op_data_47", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_47, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f", + }, + { + name: "op_data_48", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_48, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30", + }, + { + name: "op_data_49", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_49, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031", + }, + { + name: "op_data_50", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_50, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132", + }, + { + name: "op_data_51", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_51, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233", + }, + { + name: "op_data_52", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_52, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334", + }, + { + name: "op_data_53", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_53, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435", + }, + { + name: "op_data_54", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_54, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536", + }, + { + name: "op_data_55", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_55, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637", + }, + { + name: "op_data_56", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_56, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738", + }, + { + name: "op_data_57", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_57, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233343536373839", + }, + { + name: "op_data_58", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_58, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a", + }, + { + name: "op_data_59", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_59, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b", + }, + { + name: "op_data_60", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_60, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c", + }, + { + name: "op_data_61", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_61, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d", + }, + { + name: "op_data_62", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_62, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e", + }, + { + name: "op_data_63", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_63, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + }, + { + name: "op_data_64", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_64, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40", + }, + { + name: "op_data_65", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_65, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041", + }, + { + name: "op_data_66", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_66, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142", + }, + { + name: "op_data_67", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_67, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243", + }, + { + name: "op_data_68", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_68, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344", + }, + { + name: "op_data_69", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_69, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445", + }, + { + name: "op_data_70", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_70, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243444546", + }, + { + name: "op_data_71", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_71, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f4041424344454647", + }, + { + name: "op_data_72", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_72, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748", + }, + { + name: "op_data_73", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_73, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243444546474849", + }, + { + name: "op_data_74", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_74, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + }}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a", + }, + { + name: "op_data_75", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_75, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75}, + after: [][]byte{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75}}, + disassembly: "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b", + }, + { + name: "op_data too short", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_2, 1}, + expectedReturn: btcscript.StackErrShortScript, + disassemblyerr: btcscript.StackErrShortScript, + }, + { + name: "op_pushdata_1", + before: [][]byte{}, + script: []byte{btcscript.OP_PUSHDATA1, 1, 2}, + after: [][]byte{{2}}, + disassembly: "02", + }, + { + name: "op_pushdata_1 too short", + script: []byte{btcscript.OP_PUSHDATA1, 1}, + expectedReturn: btcscript.StackErrShortScript, + disassemblyerr: btcscript.StackErrShortScript, + }, + { + name: "op_pushdata_2", + before: [][]byte{}, + script: []byte{btcscript.OP_PUSHDATA2, 2, 0, 2, 4}, + after: [][]byte{{2, 4}}, + disassembly: "0204", + }, + { + name: "op_pushdata_2 too short", + script: []byte{btcscript.OP_PUSHDATA2, 2, 0}, + expectedReturn: btcscript.StackErrShortScript, + disassemblyerr: btcscript.StackErrShortScript, + }, + { + name: "op_pushdata_4", + before: [][]byte{}, + script: []byte{btcscript.OP_PUSHDATA4, 4, 0, 0, 0, 2, 4, 8, 16}, + after: [][]byte{{2, 4, 8, 16}}, + disassembly: "02040810", + }, + { + name: "op_pushdata_4 too short", + script: []byte{btcscript.OP_PUSHDATA4, 4, 0, 0, 0}, + expectedReturn: btcscript.StackErrShortScript, + disassemblyerr: btcscript.StackErrShortScript, + }, + // XXX also pushdata cases where the pushed data isn't long enough, + // no real error type defined for that as of yet. + + { + name: "OP_SHA1 no args", + before: [][]byte{}, + script: []byte{btcscript.OP_SHA1}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_SHA1", + }, + { + name: "OP_SHA256 no args", + before: [][]byte{}, + script: []byte{btcscript.OP_SHA256}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_SHA256", + }, + { + name: "OP_RIPEMD160 no args", + before: [][]byte{}, + script: []byte{btcscript.OP_RIPEMD160}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_RIPEMD160", + }, + // data taken from transaction + // 4cbb6924e5f9788d7fcf0a1ce8c175bf9befa43eb5e23386b69bc4dce49da71c + // in block 103307 + // First do it in component parts to make sure the sha256 and ripemd160 + // opcodes work + { + name: "op_hash160 the hard way", + before: [][]byte{{0x04, 0x0f, 0xa4, 0x92, 0xe3, 0x59, 0xde, 0xe8, 0x4b, + 0x53, 0xfe, 0xc5, 0xe9, 0x18, 0xb7, 0xfd, 0x62, 0x1e, + 0xb7, 0xe5, 0x63, 0x38, 0xc5, 0xfb, 0xff, 0x71, 0xd9, + 0x1d, 0x17, 0x22, 0xda, 0x58, 0xf1, 0x0f, 0x9e, 0x8f, + 0x41, 0x2f, 0x39, 0x9c, 0xb3, 0x06, 0x70, 0xa7, 0x27, + 0xe9, 0x91, 0x94, 0xaa, 0x69, 0x27, 0xaf, 0xf2, 0x54, + 0x16, 0xec, 0x48, 0x9d, 0x45, 0x3a, 0x80, 0x7e, 0x03, + 0xc0, 0x83}}, + script: []byte{btcscript.OP_SHA256, btcscript.OP_RIPEMD160}, + after: [][]byte{{0x8b, 0xfa, 0x5c, 0x1f, 0x68, 0x5f, 0x13, 0x86, 0x3e, + 0x74, 0x2e, 0x1b, 0xaf, 0x15, 0xf1, 0x71, 0xad, 0x49, + 0x8b, 0x8f}}, + disassembly: "OP_SHA256 OP_RIPEMD160", + }, + // Then test it the ``normal'' way. + { + name: "op_hash160", + before: [][]byte{{0x04, 0x0f, 0xa4, 0x92, 0xe3, 0x59, 0xde, 0xe8, 0x4b, + 0x53, 0xfe, 0xc5, 0xe9, 0x18, 0xb7, 0xfd, 0x62, 0x1e, + 0xb7, 0xe5, 0x63, 0x38, 0xc5, 0xfb, 0xff, 0x71, 0xd9, + 0x1d, 0x17, 0x22, 0xda, 0x58, 0xf1, 0x0f, 0x9e, 0x8f, + 0x41, 0x2f, 0x39, 0x9c, 0xb3, 0x06, 0x70, 0xa7, 0x27, + 0xe9, 0x91, 0x94, 0xaa, 0x69, 0x27, 0xaf, 0xf2, 0x54, + 0x16, 0xec, 0x48, 0x9d, 0x45, 0x3a, 0x80, 0x7e, 0x03, + 0xc0, 0x83}}, + script: []byte{btcscript.OP_HASH160}, + after: [][]byte{{0x8b, 0xfa, 0x5c, 0x1f, 0x68, 0x5f, 0x13, 0x86, 0x3e, + 0x74, 0x2e, 0x1b, 0xaf, 0x15, 0xf1, 0x71, 0xad, 0x49, + 0x8b, 0x8f}}, + disassembly: "OP_HASH160", + }, + // now with pushing. (mostly to check the disassembly) + { + name: "op_hash160 full script", + before: [][]byte{}, + script: []byte{btcscript.OP_DATA_65, + 0x04, 0x0f, 0xa4, 0x92, 0xe3, 0x59, 0xde, 0xe8, 0x4b, + 0x53, 0xfe, 0xc5, 0xe9, 0x18, 0xb7, 0xfd, 0x62, 0x1e, + 0xb7, 0xe5, 0x63, 0x38, 0xc5, 0xfb, 0xff, 0x71, 0xd9, + 0x1d, 0x17, 0x22, 0xda, 0x58, 0xf1, 0x0f, 0x9e, 0x8f, + 0x41, 0x2f, 0x39, 0x9c, 0xb3, 0x06, 0x70, 0xa7, 0x27, + 0xe9, 0x91, 0x94, 0xaa, 0x69, 0x27, 0xaf, 0xf2, 0x54, + 0x16, 0xec, 0x48, 0x9d, 0x45, 0x3a, 0x80, 0x7e, 0x03, + 0xc0, 0x83, + btcscript.OP_HASH160, btcscript.OP_DATA_20, + 0x8b, 0xfa, 0x5c, 0x1f, 0x68, 0x5f, 0x13, 0x86, 0x3e, + 0x74, 0x2e, 0x1b, 0xaf, 0x15, 0xf1, 0x71, 0xad, 0x49, + 0x8b, 0x8f, + btcscript.OP_EQUALVERIFY}, + after: [][]byte{}, + disassembly: "040fa492e359dee84b53fec5e918b7fd621eb7e56338c5fbff71d91d1722da58f10f9e8f412f399cb30670a727e99194aa6927aff25416ec489d453a807e03c083 OP_HASH160 8bfa5c1f685f13863e742e1baf15f171ad498b8f OP_EQUALVERIFY", + }, + { + name: "op_hash160 no args", + script: []byte{btcscript.OP_HASH160}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_HASH160", + }, + // hash256 test taken from spend of: + // 09f691b2263260e71f363d1db51ff3100d285956a40cc0e4f8c8c2c4a80559b1 + { + name: "op_hash256", + before: [][]byte{{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, + 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, 0x7f, + 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, 0x3a, 0x9f, + 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, 0x29, 0xab, 0x5f, + 0x49, 0xff, 0xff, 0x00, 0x1d, 0x1d, 0xac, 0x2b, 0x7c}}, + script: []byte{btcscript.OP_HASH256}, + after: [][]byte{{0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 0xc1, + 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, 0x93, 0x1e, + 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, 0x68, 0xd6, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00}}, + disassembly: "OP_HASH256", + }, + { + name: "OP_HASH256 no args", + script: []byte{btcscript.OP_HASH256}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_HASH256", + }, + // We need a more involved setup to test OP_CHECKSIG and + // OP_CHECKMULTISIG (see script_test.go) but we can test it with + // invalid arguments here quite easily. + { + name: "OP_CHECKSIG one arg", + script: []byte{btcscript.OP_1, btcscript.OP_CHECKSIG}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_1 OP_CHECKSIG", + }, + { + name: "OP_CHECKSIG no arg", + script: []byte{btcscript.OP_CHECKSIG}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_CHECKSIG", + }, + { + name: "OP_CHECKSIGVERIFY one arg", + script: []byte{btcscript.OP_1, + btcscript.OP_CHECKSIGVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_1 OP_CHECKSIGVERIFY", + }, + { + name: "OP_CHECKSIGVERIFY no arg", + script: []byte{btcscript.OP_CHECKSIGVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_CHECKSIGVERIFY", + }, + { + name: "OP_CHECK_MULTISIG no args", + script: []byte{btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_CHECK_MULTISIG", + }, + { + name: "OP_CHECK_MULTISIG huge number", + script: []byte{btcscript.OP_PUSHDATA1, + 0x9, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrNumberTooBig, + disassembly: "010203040506070809 OP_CHECK_MULTISIG", + }, + { + name: "OP_CHECK_MULTISIG too many keys", + script: []byte{btcscript.OP_DATA_1, 21, + btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrTooManyPubkeys, + disassembly: "15 OP_CHECK_MULTISIG", + }, + { + name: "OP_CHECK_MULTISIG lying about pubkeys", + script: []byte{btcscript.OP_1, + btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_1 OP_CHECK_MULTISIG", + }, + { + // pubkey comes from blockchain + name: "OP_CHECK_MULTISIG no sigs", + script: []byte{ + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECK_MULTISIG", + }, + { + // pubkey comes from blockchain + name: "OP_CHECK_MULTISIG sigs huge no", + script: []byte{ + btcscript.OP_PUSHDATA1, + 0x9, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrNumberTooBig, + disassembly: "010203040506070809 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECK_MULTISIG", + }, + { + name: "OP_CHECK_MULTISIG too few sigs", + script: []byte{btcscript.OP_1, + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECK_MULTISIG}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_1 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECK_MULTISIG", + }, + { + // pubkey and sig comes from blockchain, are unrelated + name: "OP_CHECK_MULTISIG won't verify", + script: []byte{btcscript.OP_1, + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, + 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, + 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, + 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, + 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, + 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, + 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, + 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01, + btcscript.OP_1, + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECK_MULTISIG}, + after: [][]byte{{0}}, + disassembly: "OP_1 304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901 OP_1 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECK_MULTISIG", + }, + { + // invalid pubkey means that it fails to validate, not an + // error. There are pubkeys in the blockchain that don't + // parse with any validity. + name: "OP_CHECK_MULTISIG sigs bad pubkey", + script: []byte{btcscript.OP_1, + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, + 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, + 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, + 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, + 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, + 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, + 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, + 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01, + btcscript.OP_1, + btcscript.OP_1, btcscript.OP_1, + btcscript.OP_CHECK_MULTISIG}, + after: [][]byte{{0}}, + disassembly: "OP_1 304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901 OP_1 OP_1 OP_1 OP_CHECK_MULTISIG", + }, + // XXX(oga) Test multisig when extra arg is missing. needs valid sig. + // disabled opcodes + { + name: "OP_CHECKMULTISIGVERIFY no args", + script: []byte{btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_CHECKMULTISIGVERIFY", + }, + { + name: "OP_CHECKMULTISIGVERIFY huge number", + script: []byte{btcscript.OP_PUSHDATA1, + 0x9, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrNumberTooBig, + disassembly: "010203040506070809 OP_CHECKMULTISIGVERIFY", + }, + { + name: "OP_CHECKMULTISIGVERIFY too many keys", + script: []byte{btcscript.OP_DATA_1, 21, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrTooManyPubkeys, + disassembly: "15 OP_CHECKMULTISIGVERIFY", + }, + { + name: "OP_CHECKMULTISIGVERIFY lying about pubkeys", + script: []byte{btcscript.OP_1, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_1 OP_CHECKMULTISIGVERIFY", + }, + { + // pubkey comes from blockchain + name: "OP_CHECKMULTISIGVERIFY no sigs", + script: []byte{ + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECKMULTISIGVERIFY", + }, + { + name: "OP_CHECKMULTISIGVERIFY sigs huge no", + script: []byte{ + btcscript.OP_PUSHDATA1, + 0x9, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrNumberTooBig, + disassembly: "010203040506070809 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECKMULTISIGVERIFY", + }, + { + name: "OP_CHECKMULTISIGVERIFY too few sigs", + script: []byte{btcscript.OP_1, + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrUnderflow, + disassembly: "OP_1 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECKMULTISIGVERIFY", + }, + { + // pubkey and sig comes from blockchain, are unrelated + name: "OP_CHECKMULTISIGVERIFY won't verify", + script: []byte{btcscript.OP_1, + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, + 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, + 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, + 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, + 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, + 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, + 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, + 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01, + btcscript.OP_1, + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, 0x09, 0xc5, 0xf5, 0x1b, + 0x13, 0x90, 0x5f, 0x07, 0xf0, 0x6b, 0x99, 0xa2, 0xf7, + 0x15, 0x9b, 0x22, 0x25, 0xf3, 0x74, 0xcd, 0x37, 0x8d, + 0x71, 0x30, 0x2f, 0xa2, 0x84, 0x14, 0xe7, 0xaa, 0xb3, + 0x73, 0x97, 0xf5, 0x54, 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, 0x8a, 0x06, 0x26, 0xf1, + 0xba, 0xde, 0xd5, 0xc7, 0x2a, 0x70, 0x4f, 0x7e, 0x6c, + 0xd8, 0x4c, + btcscript.OP_1, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrVerifyFailed, + disassembly: "OP_1 304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901 OP_1 04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c OP_1 OP_CHECKMULTISIGVERIFY", + }, + { + // invalid pubkey means that it fails to validate, not an + // error. There are pubkeys in the blockchain that don't + // parse with any validity. + name: "OP_CHECKMULTISIGVERIFY sigs bad pubkey", + script: []byte{btcscript.OP_1, + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, 0x45, 0xe1, 0x69, 0x32, + 0xb8, 0xaf, 0x51, 0x49, 0x61, 0xa1, 0xd3, 0xa1, 0xa2, + 0x5f, 0xdf, 0x3f, 0x4f, 0x77, 0x32, 0xe9, 0xd6, 0x24, + 0xc6, 0xc6, 0x15, 0x48, 0xab, 0x5f, 0xb8, 0xcd, 0x41, + 0x02, 0x20, 0x18, 0x15, 0x22, 0xec, 0x8e, 0xca, 0x07, + 0xde, 0x48, 0x60, 0xa4, 0xac, 0xdd, 0x12, 0x90, 0x9d, + 0x83, 0x1c, 0xc5, 0x6c, 0xbb, 0xac, 0x46, 0x22, 0x08, + 0x22, 0x21, 0xa8, 0x76, 0x8d, 0x1d, 0x09, 0x01, + btcscript.OP_1, + btcscript.OP_1, btcscript.OP_1, + btcscript.OP_CHECKMULTISIGVERIFY}, + expectedReturn: btcscript.StackErrVerifyFailed, + disassembly: "OP_1 304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901 OP_1 OP_1 OP_1 OP_CHECKMULTISIGVERIFY", + }, + { + name: "OP_CAT disabled", + script: []byte{btcscript.OP_CAT}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_CAT", + }, + { + name: "OP_SUBSTR disabled", + script: []byte{btcscript.OP_SUBSTR}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_SUBSTR", + }, + { + name: "OP_LEFT disabled", + script: []byte{btcscript.OP_LEFT}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_LEFT", + }, + { + name: "OP_RIGHT disabled", + script: []byte{btcscript.OP_RIGHT}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_RIGHT", + }, + { + name: "OP_INVERT disabled", + script: []byte{btcscript.OP_INVERT}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_INVERT", + }, + { + name: "OP_AND disabled", + script: []byte{btcscript.OP_AND}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_AND", + }, + { + name: "OP_OR disabled", + script: []byte{btcscript.OP_OR}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_OR", + }, + { + name: "OP_XOR disabled", + script: []byte{btcscript.OP_XOR}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_XOR", + }, + { + name: "OP_2MUL disabled", + script: []byte{btcscript.OP_2MUL}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_2MUL", + }, + { + name: "OP_2DIV disabled", + script: []byte{btcscript.OP_2DIV}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_2DIV", + }, + { + name: "OP_2DIV disabled", + script: []byte{btcscript.OP_2DIV}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_2DIV", + }, + { + name: "OP_MUL disabled", + script: []byte{btcscript.OP_MUL}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_MUL", + }, + { + name: "OP_DIV disabled", + script: []byte{btcscript.OP_DIV}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_DIV", + }, + { + name: "OP_MOD disabled", + script: []byte{btcscript.OP_MOD}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_MOD", + }, + { + name: "OP_LSHIFT disabled", + script: []byte{btcscript.OP_LSHIFT}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_LSHIFT", + }, + { + name: "OP_RSHIFT disabled", + script: []byte{btcscript.OP_RSHIFT}, + expectedReturn: btcscript.StackErrOpDisabled, + disassembly: "OP_RSHIFT", + }, + // Reserved opcodes + { + name: "OP_RESERVED reserved", + script: []byte{btcscript.OP_RESERVED}, + expectedReturn: btcscript.StackErrReservedOpcode, + disassembly: "OP_RESERVED", + }, + { + name: "OP_VER reserved", + script: []byte{btcscript.OP_VER}, + expectedReturn: btcscript.StackErrReservedOpcode, + disassembly: "OP_VER", + }, + { + name: "OP_VERIF reserved", + script: []byte{btcscript.OP_VERIF}, + expectedReturn: btcscript.StackErrReservedOpcode, + disassembly: "OP_VERIF", + }, + { + name: "OP_VERNOTIF reserved", + script: []byte{btcscript.OP_VERNOTIF}, + expectedReturn: btcscript.StackErrReservedOpcode, + disassembly: "OP_VERNOTIF", + }, + { + name: "OP_RESERVED1 reserved", + script: []byte{btcscript.OP_RESERVED1}, + expectedReturn: btcscript.StackErrReservedOpcode, + disassembly: "OP_RESERVED1", + }, + { + name: "OP_RESERVED2 reserved", + script: []byte{btcscript.OP_RESERVED2}, + expectedReturn: btcscript.StackErrReservedOpcode, + disassembly: "OP_RESERVED2", + }, + // Invalid Opcodes + { + name: "invalid opcode 186", + script: []byte{186}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 187", + script: []byte{187}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 188", + script: []byte{188}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 189", + script: []byte{189}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 190", + script: []byte{190}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 191", + script: []byte{191}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 192", + script: []byte{192}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 193", + script: []byte{193}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 194", + script: []byte{194}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 195", + script: []byte{195}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 196", + script: []byte{196}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 197", + script: []byte{197}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 198", + script: []byte{198}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 199", + script: []byte{199}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 200", + script: []byte{200}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 201", + script: []byte{201}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 202", + script: []byte{202}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 203", + script: []byte{203}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 204", + script: []byte{204}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 205", + script: []byte{205}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 206", + script: []byte{206}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 207", + script: []byte{207}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 208", + script: []byte{208}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 209", + script: []byte{209}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 210", + script: []byte{210}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 211", + script: []byte{211}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 212", + script: []byte{212}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 213", + script: []byte{213}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 214", + script: []byte{214}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 215", + script: []byte{215}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 216", + script: []byte{216}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 217", + script: []byte{217}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 218", + script: []byte{218}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 219", + script: []byte{219}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 220", + script: []byte{220}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 221", + script: []byte{221}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 222", + script: []byte{222}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 223", + script: []byte{223}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 224", + script: []byte{224}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 225", + script: []byte{225}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 226", + script: []byte{226}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 227", + script: []byte{227}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 228", + script: []byte{228}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 229", + script: []byte{229}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 230", + script: []byte{230}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 231", + script: []byte{231}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 232", + script: []byte{232}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 233", + script: []byte{233}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 234", + script: []byte{234}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 235", + script: []byte{235}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 236", + script: []byte{236}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 237", + script: []byte{237}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 238", + script: []byte{238}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 239", + script: []byte{239}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 240", + script: []byte{240}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 241", + script: []byte{241}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 242", + script: []byte{242}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 243", + script: []byte{243}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 244", + script: []byte{244}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 245", + script: []byte{245}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 246", + script: []byte{246}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 247", + script: []byte{247}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 248", + script: []byte{248}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 249", + script: []byte{249}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 250", + script: []byte{250}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 251", + script: []byte{251}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode 252", + script: []byte{252}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassemblyerr: btcscript.StackErrInvalidOpcode, + }, + { + name: "invalid opcode OP_PUBKEY", + script: []byte{btcscript.OP_PUBKEY}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassembly: "OP_PUBKEY", + }, + { + name: "invalid opcode OP_PUBKEYHASH", + script: []byte{btcscript.OP_PUBKEYHASH}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassembly: "OP_PUBKEYHASH", + }, + { + name: "invalid opcode OP_INVALIDOPCODE", + script: []byte{btcscript.OP_INVALIDOPCODE}, + expectedReturn: btcscript.StackErrInvalidOpcode, + disassembly: "OP_INVALIDOPCODE", + }, +} + +func stacksEqual(a, b [][]byte) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + if !bytes.Equal(a[i], b[i]) { + return false + } + } + return true +} + +func testOpcode(t *testing.T, test *detailedTest) { + // mock up fake tx. + tx := &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash{}, + Index: 0xffffffff, + }, + SignatureScript: []byte{}, + Sequence: 0xffffffff, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 0x12a05f200, + PkScript: []byte{}, + }, + }, + LockTime: 0, + } + + tx.TxOut[0].PkScript = test.script + + engine, err := btcscript.NewScript(tx.TxIn[0].SignatureScript, + tx.TxOut[0].PkScript, 0, tx, 1, false) + if err != nil { + if err != test.expectedReturn { + t.Errorf("Error return not expected %s: %v %v", + test.name, test.expectedReturn, err) + return + } + return + } + engine.SetStack(test.before) + engine.SetAltStack(test.altbefore) + + // test disassembly engine. + // pc is at start of script 1, so check that DisasmScript matches + // DisasmPc. Only run this if we have a disassembly for the test. + // sine one of them have invalid instruction sequences and won't + // disassemble. + var disScript, disPC string + if test.disassembly != "" { + var err error + disScript, err = engine.DisasmScript(1) + if err != nil { + t.Errorf("failed to disassemble script for %s: %v", + test.name, err) + } + } + + done := false + for !done { + if test.disassembly != "" { + disCurPC, err := engine.DisasmPC() + if err != nil { + t.Errorf("failed to disassemble pc for %s: %v", + test.name, err) + } + disPC += disCurPC + "\n" + } + + done, err = engine.Step() + if err != nil { + if err != test.expectedReturn { + t.Errorf("Error return not expected %s: %v %v", + test.name, test.expectedReturn, err) + return + } + return + } + } + if err != test.expectedReturn { + t.Errorf("Error return not expected %s: %v %v", + test.name, test.expectedReturn, err) + } + + if test.disassembly != "" { + if disScript != disPC { + t.Errorf("script disassembly doesn't match pc "+ + "disassembly for %s: pc: \"%s\" script: \"%s\"", + test.name, disScript, disPC) + } + } + + after := engine.GetStack() + if !stacksEqual(after, test.after) { + t.Errorf("Stacks not equal after %s:\ngot: %v\n exp: %v", + test.name, spew.Sdump(after), spew.Sdump(test.after)) + } + altafter := engine.GetAltStack() + if !stacksEqual(altafter, test.altafter) { + t.Errorf("AltStacks not equal after %s:\n got: %v\nexp: %v", + test.name, spew.Sdump(altafter), + spew.Sdump(test.altafter)) + } +} + +func TestOpcodes(t *testing.T) { + for i := range detailedTests { + testOpcode(t, &detailedTests[i]) + } +} + +func testDisasmString(t *testing.T, test *detailedTest) { + // mock up fake tx. + dis, err := btcscript.DisasmString(test.script) + if err != nil { + if err != test.disassemblyerr { + t.Errorf("%s: disassembly got error %v expected %v", test.name, + err, test.disassemblyerr) + } + return + } + if test.disassemblyerr != nil { + t.Errorf("%s: expected error %v, got %s", test.name, + test.disassemblyerr, dis) + return + } + if dis != test.disassembly { + t.Errorf("Disassembly for %s doesn't match expected "+ + "got: \"%s\" expected: \"%s\"", test.name, dis, + test.disassembly) + } +} + +func TestDisasmStrings(t *testing.T) { + for i := range detailedTests { + testDisasmString(t, &detailedTests[i]) + } +} diff --git a/script.go b/script.go new file mode 100644 index 00000000..63629afe --- /dev/null +++ b/script.go @@ -0,0 +1,697 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "github.com/conformal/btcwire" + "github.com/davecgh/go-spew/spew" + "time" +) + +// StackErrShortScript is returned if the script has an opcode that is too long +// for the length of the script. +var StackErrShortScript = errors.New("execute past end of script") + +// StackErrUnderflow is returned if an opcode requires more items on the stack +// than is present. +var StackErrUnderflow = errors.New("stack underflow") + +// StackErrInvalidArgs is returned if the argument for an opcode is out of +// acceptable range. +var StackErrInvalidArgs = errors.New("invalid argument") + +// StackErrOpDisabled is returned when a disabled opcode is encountered in the +// script. +var StackErrOpDisabled = errors.New("Disabled Opcode") + +// StackErrVerifyFailed is returned when one of the OP_VERIFY or OP_*VERIFY +// instructions is executed and the conditions fails. +var StackErrVerifyFailed = errors.New("Verify failed") + +// StackErrNumberTooBig is returned when the argument for an opcode that should +// be an offset is obviously far too large. +var StackErrNumberTooBig = errors.New("number too big") + +// StackErrInvalidOpcode is returned when an opcode marked as invalid or a +// completely undefined opcode is encountered. +var StackErrInvalidOpcode = errors.New("Invalid Opcode") + +// StackErrReservedOpcode is returned when an opcode marked as reserved is +// encountered. +var StackErrReservedOpcode = errors.New("Reserved Opcode") + +// StackErrEarlyReturn is returned when OP_RETURN is exectured in the script. +var StackErrEarlyReturn = errors.New("Script returned early") + +// StackErrNoIf is returned if an OP_ELSE or OP_ENDIF is encountered without +// first having an OP_IF or OP_NOTIF in the script. +var StackErrNoIf = errors.New("OP_ELSE or OP_ENDIF with no matching OP_IF") + +// StackErrMissingEndif is returned if the end of a script is reached without +// and OP_ENDIF to correspond to a conditional expression. +var StackErrMissingEndif = fmt.Errorf("execute fail, in conditional execution") + +// StackErrTooManyPubkeys is returned if an OP_CHECKMULTISIG is encountered +// with more than MaxPubKeysPerMultiSig pubkeys present. +var StackErrTooManyPubkeys = errors.New("Invalid pubkey count in OP_CHECKMULTISIG") + +// StackErrTooManyOperations is returned if a script has more then +// MaxOpsPerScript opcodes that do not push data. +var StackErrTooManyOperations = errors.New("Too many operations in script") + +// StackErrElementTooBig is returned if the size of an element to be pushed to +// the stack is over MaxScriptElementSize. +var StackErrElementTooBig = errors.New("Element in script too large") + +// StackErrUnknownAddress is returned when ScriptToAddress does not recognise +// the pattern of the script and thus can not find the address for payment. +var StackErrUnknownAddress = fmt.Errorf("non-recognised address") + +// StackErrScriptFailed is returned when at the end of a script the boolean +// on top of the stack is false signifying that the script has failed. +var StackErrScriptFailed = fmt.Errorf("execute fail, fail on stack") + +// Bip16Activation is the timestamp where BIP0016 is valid to use in the +// blockchain. To be used to determine if BIP0016 should be called for or not. +// This timestamp corresponds to Sun Apr 1 00:00:00 UTC 2012. +var Bip16Activation = time.Unix(1333238400, 0) + +// Hash type bits from the end of a signature. +const ( + SigHashOld = 0x0 + SigHashAll = 0x1 + SigHashNone = 0x2 + SigHashSingle = 0x3 + SigHashAnyOneCanPay = 0x80 +) + +// These are the constants specified for maximums in individual scripts. +const ( + MaxOpsPerScript = 201 // Max number of non-push operations. + MaxPubKeysPerMultiSig = 20 // Multisig can't have more sigs than this. + MaxScriptElementSize = 520 // Max bytes pushable to the stack. +) + +// ScriptType is an enumeration for the list of standard types of script. +type scriptType byte + +// Types of script payment known about in the blockchain. +const ( + pubKeyTy scriptType = iota // Pay pubkey. + pubKeyHashTy // Pay pubkey hash. + scriptHashTy // Pay to script hash. + multiSigTy // Multi signature. + nonStandardTy // None of the above. +) + +// Script is the virtual machine that executes btcscripts. +type Script struct { + scripts [][]parsedOpcode + scriptidx int + scriptoff int + lastcodesep int + dstack Stack // data stack + astack Stack // alt stack + tx btcwire.MsgTx + txidx int + pver uint32 + condStack []int + numOps int + bip16 bool // treat execution as pay-to-script-hash + savedFirstStack [][]byte // stack from first script for bip16 scripts +} + +// isPubkey returns true if the script passed is a pubkey transaction, false +// otherwise. +func isPubkey(pops []parsedOpcode) bool { + return len(pops) == 2 && + pops[0].opcode.value > OP_FALSE && + pops[0].opcode.value <= OP_DATA_75 && + pops[1].opcode.value == OP_CHECKSIG +} + +// isPubkeyHash returns true if the script passed is a pubkey hash transaction, +// false otherwise. +func isPubkeyHash(pops []parsedOpcode) bool { + return len(pops) == 5 && + pops[0].opcode.value == OP_DUP && + pops[1].opcode.value == OP_HASH160 && + pops[2].opcode.value == OP_DATA_20 && + pops[3].opcode.value == OP_EQUALVERIFY && + pops[4].opcode.value == OP_CHECKSIG + +} + +// isScriptHash returns true if the script passed is a pay-to-script-hash (P2SH) +// transction, false otherwise. +func isScriptHash(pops []parsedOpcode) bool { + return len(pops) == 3 && + pops[0].opcode.value == OP_HASH160 && + pops[1].opcode.value == OP_DATA_20 && + pops[2].opcode.value == OP_EQUAL +} + +// isMultiSig returns true if the passed script is a multisig transaction, false +// otherwise. +func isMultiSig(pops []parsedOpcode) bool { + l := len(pops) + // absolute minimum is 1 pubkey so + // OP_1-16, pubkey, OP_1, OP_CHECK_MULTISIG + if l < 4 { + return false + } + if pops[0].opcode.value < OP_1 || + pops[0].opcode.value > OP_16 { + return false + } + if pops[l-2].opcode.value < OP_1 || + pops[l-2].opcode.value > OP_16 { + return false + } + if pops[l-1].opcode.value != OP_CHECK_MULTISIG { + return false + } + for _, pop := range pops[1 : l-2] { + // valid pubkeys are either 65 or 33 bytes + if len(pop.data) != 33 && + len(pop.data) != 65 { + return false + } + } + return true +} + +// isPushOnly returns true if the script only pushes data, false otherwise. +func isPushOnly(pops []parsedOpcode) bool { + // technically we cheat here, we don't look at opcodes + for _, pop := range pops { + // all opcodes up to OP_16 are data instructions. + if pop.opcode.value < OP_FALSE || + pop.opcode.value > OP_16 { + return false + } + } + return true +} + +// scriptType returns the type of the script being inspected from the known +// standard types. +func typeOfScript(pops []parsedOpcode) scriptType { + // XXX dubious optimisation: order these in order of popularity in the + // blockchain + if isPubkey(pops) { + return pubKeyTy + } else if isPubkeyHash(pops) { + return pubKeyHashTy + } else if isScriptHash(pops) { + return scriptHashTy + } else if isMultiSig(pops) { + return multiSigTy + } + return nonStandardTy + +} + +// parseScript preparses the script in bytes into a list of parsedOpcodes while +// applying a number of sanity checks. +func parseScript(script []byte) ([]parsedOpcode, error) { + retScript := []parsedOpcode{} + for i := 0; i < len(script); { + instr := script[i] + op, ok := opcodemap[instr] + if !ok { + return nil, StackErrInvalidOpcode + } + pop := parsedOpcode{opcode: op} + // parse data out of instruction. + switch { + case op.length == 1: + // no data, done here + i++ + case op.length > 1: + if len(script[i:]) < op.length { + return nil, StackErrShortScript + } + // slice out the data. + pop.data = script[i+1 : i+op.length] + i += op.length + case op.length < 0: + var err error + var l uint + off := i + 1 + switch op.length { + case -1: + l, err = scriptUInt8(script[off:]) + case -2: + l, err = scriptUInt16(script[off:]) + case -4: + l, err = scriptUInt32(script[off:]) + default: + return nil, fmt.Errorf("invalid opcode length %d", op.length) + } + + if err != nil { + return nil, err + } + off = i + 1 - op.length // beginning of data + if int(l) > len(script[off:]) { + return nil, StackErrShortScript + } + if l > MaxScriptElementSize { + return nil, StackErrElementTooBig + } + pop.data = script[off : off+int(l)] + i += 1 - op.length + int(l) + } + retScript = append(retScript, pop) + } + return retScript, nil +} + +// unparseScript reversed the action of parseScript and returns the +// parsedOpcodes as a list of bytes +func unparseScript(pops []parsedOpcode) []byte { + script := []byte{} + for _, pop := range pops { + script = append(script, pop.bytes()...) + } + return script +} + +// NewScript returns a new script engine for the provided tx and input idx with +// a signature script scriptSig and a pubkeyscript scriptPubKey. If bip16 is +// true then it will be treated as if the bip16 threshhold has passed and thus +// pay-to-script hash transactions will be fully validated. +func NewScript(scriptSig []byte, scriptPubKey []byte, txidx int, tx *btcwire.MsgTx, pver uint32, bip16 bool) (*Script, error) { + var m Script + scripts := [][]byte{scriptSig, scriptPubKey} + m.scripts = make([][]parsedOpcode, len(scripts)) + for i, scr := range scripts { + var err error + m.scripts[i], err = parseScript(scr) + if err != nil { + return nil, err + } + + // if the first scripts(s) are empty, must set the PC to the next script. + if len(scr) == 0 { + // yes this could end up setting to an invalid intial PC if all scripts were empty + m.scriptidx = i + 1 + } + } + + if bip16 && isScriptHash(m.scripts[1]) { + // if we are pay to scripthash then we only accept input + // scripts that push data + if !isPushOnly(m.scripts[0]) { + return nil, errors.New("pay to script hash with non " + + "pushonly input") + } + m.bip16 = true + } + + for i, _ := range tx.TxIn { + tx.TxIn[i].SignatureScript = []byte{} + } + m.tx = *tx + m.txidx = txidx + m.pver = pver + m.condStack = []int{OpCondTrue} + + return &m, nil +} + +// Execute will execturte all script in the script engine and return either nil +// for successful validation or an error if one occurred. +func (s *Script) Execute() (err error) { + done := false + for done != true { + log.Tracef("%v", newLogClosure(func() string { + dis, err := s.DisasmPC() + if err != nil { + return fmt.Sprintf("stepping (%v)", err) + } + return fmt.Sprintf("stepping %v", dis) + })) + + done, err = s.Step() + if err != nil { + return err + } + log.Tracef("%v", newLogClosure(func() string { + var dstr, astr string + + // if we're tracing, dump the stacks. + if s.dstack.Depth() != 0 { + dstr = "Stack\n" + spew.Sdump(s.dstack) + } + if s.astack.Depth() != 0 { + astr = "AltStack\n" + spew.Sdump(s.astack) + } + + return dstr + astr + })) + } + if s.dstack.Depth() < 1 { + return fmt.Errorf("stack empty at end of execution") + } + v, err := s.dstack.PopBool() + if err == nil && v == false { + // log interesting data. + log.Tracef("%v", func() string { + dis0, _ := s.DisasmScript(0) + dis1, _ := s.DisasmScript(1) + return fmt.Sprintf("script0: %s\n script1: %s", + dis0, dis1) + }) + err = StackErrScriptFailed + } + if err == nil && len(s.condStack) != 1 { + // conditional execution stack context left active + err = StackErrMissingEndif + } + + return err +} + +// Step will execute the next instruction and move the program counter to the +// next opcode in the script, or the next script if the curent has ended. Step +// will return true in the case that the last opcode was successfully executed. +// if an error is returned then the result of calling Step or any other method +// is undefined. +func (m *Script) Step() (done bool, err error) { + // verify that it is pointing to a valid script address + err = m.validPC() + if err != nil { + return + } + opcode := m.scripts[m.scriptidx][m.scriptoff] + + executeInstr := true + if m.condStack[0] != OpCondTrue { + // some opcodes still 'activate' if on the non-executing side + // of conditional execution + if opcode.conditional() { + executeInstr = true + } else { + executeInstr = false + } + } + if executeInstr { + err = opcode.exec(m) + if err != nil { + return + } + } + + // prepare for next instruction + m.scriptoff++ + if m.scriptoff >= len(m.scripts[m.scriptidx]) { + // should only be == from checks before + m.scriptoff = 0 + if m.scriptidx == 0 && m.bip16 { + m.savedFirstStack = m.GetStack() + } else if m.scriptidx == 1 && m.bip16 { + // Check for successful completion of script + v, err := m.dstack.PopBool() + if err != nil { + return false, err + } + if v == false { + return false, StackErrScriptFailed + } + // check that first element on stack is a bool + script := m.savedFirstStack[len(m.savedFirstStack)-1] + pops, err := parseScript(script) + if err != nil { + return false, err + } + m.scripts = append(m.scripts, pops) + // Set stack to be the stack from first script + // minus the script itself + m.SetStack(m.savedFirstStack[:len(m.savedFirstStack)-1]) + } + m.scriptidx++ + // there are zero length scripts in the wild + if m.scriptidx < len(m.scripts) && m.scriptoff >= len(m.scripts[m.scriptidx]) { + m.scriptidx++ + } + m.lastcodesep = 0 + if m.scriptidx >= len(m.scripts) { + done = true + } + } + return +} + +// curPC returns either the current script and offset, or an error if the +// position isn't valid. +func (m *Script) curPC() (script int, off int, err error) { + err = m.validPC() + if err != nil { + return 0, 0, err + } + return m.scriptidx, m.scriptoff, nil +} + +// validPC returns an error if the current script position is valid for +// execution, nil otherwise. +func (m *Script) validPC() error { + if m.scriptidx >= len(m.scripts) { + return fmt.Errorf("Past input scripts %v:%v %v:xxxx", m.scriptidx, m.scriptoff, len(m.scripts)) + } + if m.scriptoff >= len(m.scripts[m.scriptidx]) { + return fmt.Errorf("Past input scripts %v:%v %v:%04d", m.scriptidx, m.scriptoff, m.scriptidx, len(m.scripts[m.scriptidx])) + } + return nil +} + +// DisasmScript returns the disassembly string for the script at offset +// ``idx''. Where 0 is the scriptSig and 1 is the scriptPubKey. +func (m *Script) DisasmScript(idx int) (disstr string, err error) { + if idx >= len(m.scripts) { + return "", fmt.Errorf("Invalid script index") + } + for i := range m.scripts[idx] { + disstr = disstr + m.disasm(idx, i) + "\n" + } + return disstr, nil +} + +// DisasmPC returns the string for the disassembly of the opcode that will be +// next to execute when Step() is called. +func (m *Script) DisasmPC() (disstr string, err error) { + scriptidx, scriptoff, err := m.curPC() + if err != nil { + return "", err + } + return m.disasm(scriptidx, scriptoff), nil +} + +// disasm is a helper member to produce the output for DisasmPC and +// DisasmScript. It produces the opcode prefixed by the program counter at the +// provided position in the script. it does no error checking and leaves that +// to the caller to provide a valid offse. +func (m *Script) disasm(scriptidx int, scriptoff int) string { + return fmt.Sprintf("%02x:%04x: %s", scriptidx, scriptoff, + m.scripts[scriptidx][scriptoff].print(false)) +} + +// subScript will return the script since the last OP_CODESEPARATOR +func (s *Script) subScript() []parsedOpcode { + return s.scripts[s.scriptidx][s.lastcodesep:] +} + +// removeOpcode will remove any opcode matching ``opcode'' from the opcode +// stream in pkscript +func removeOpcode(pkscript []parsedOpcode, opcode byte) []parsedOpcode { + retScript := []parsedOpcode{} + for _, pop := range pkscript { + if pop.opcode.value != opcode { + retScript = append(retScript, pop) + } + } + return retScript +} + +// removeOpcodeByData will return the pkscript minus any opcodes that would +// push the data in ``data'' to the stack. +func removeOpcodeByData(pkscript []parsedOpcode, data []byte) []parsedOpcode { + retScript := []parsedOpcode{} + for _, pop := range pkscript { + if !bytes.Equal(pop.data, data) { + retScript = append(retScript, pop) + } + } + return retScript + +} + +// DisasmString formats a disassembled script for one line printing. +func DisasmString(buf []byte) (string, error) { + disbuf := "" + opcodes, err := parseScript(buf) + if err != nil { + return "", err + } + for _, pop := range opcodes { + disbuf += pop.print(true) + " " + } + if disbuf != "" { + disbuf = disbuf[:len(disbuf)-1] + } + return disbuf, nil +} + +// calcScriptHash will, given the a script and hashtype for the current +// scriptmachine, calculate the doubleSha256 hash of the transaction and +// script to be used for signature signing and verification. +func (s *Script) calcScriptHash(script []parsedOpcode, hashType byte) []byte { + + // remove all instances of OP_CODESEPARATOR still left in the script + script = removeOpcode(script, OP_CODESEPARATOR) + + // Make a deep copy of the transaction, zeroing out the script + // for all inputs that are not currently being processed. + txCopy := s.tx.Copy() + txidx := s.txidx + for i := range txCopy.TxIn { + var txIn btcwire.TxIn + txIn = *txCopy.TxIn[i] + txCopy.TxIn[i] = &txIn + if i == txidx { + txCopy.TxIn[txidx].SignatureScript = + unparseScript(script) + } else { + txCopy.TxIn[i].SignatureScript = []byte{} + } + } + // Default behaviour has all outputs set up. + for i := range txCopy.TxOut { + var txOut btcwire.TxOut + txOut = *txCopy.TxOut[i] + txCopy.TxOut[i] = &txOut + } + + switch hashType & 31 { + case SigHashNone: + txCopy.TxOut = txCopy.TxOut[0:0] // empty slice + for i := range txCopy.TxIn { + if i != txidx { + txCopy.TxIn[i].Sequence = 0 + } + } + case SigHashSingle: + // resize output array to up to and including current output + txCopy.TxOut = txCopy.TxOut[:txidx+1] + // all but current output get zeroed out + for i := 0; i < txidx; i++ { + txCopy.TxOut[i].Value = -1 + txCopy.TxOut[i].PkScript = []byte{} + } + // Sequence on all other inputs is 0, too. + for i := range txCopy.TxIn { + if i != txidx { + txCopy.TxIn[i].Sequence = 0 + } + } + default: + // XXX bitcoind treats undefined hashtypes like normal + // SigHashAll for purposes of hash generation. + fallthrough + case SigHashOld: + fallthrough + case SigHashAll: + // nothing special here + } + if hashType&SigHashAnyOneCanPay != 0 { + txCopy.TxIn = txCopy.TxIn[s.txidx : s.txidx+1] + txidx = 0 + } + + var wbuf bytes.Buffer + txCopy.BtcEncode(&wbuf, s.pver) + // Append LE 4 bytes hash type + binary.Write(&wbuf, binary.LittleEndian, uint32(hashType)) + + return btcwire.DoubleSha256(wbuf.Bytes()) +} + +// scriptUInt8 return the number stored in the first byte of a slice. +func scriptUInt8(script []byte) (uint, error) { + if len(script) <= 1 { + return 0, StackErrShortScript + } + return uint(script[0]), nil +} + +// scriptUInt16 returns the number stored in the next 2 bytes of a slice. +func scriptUInt16(script []byte) (uint, error) { + if len(script) <= 2 { + return 0, StackErrShortScript + } + // Yes this is little endian + return ((uint(script[1]) << 8) | uint(script[0])), nil +} + +// scriptUInt32 returns the number stored in the first 4 bytes of a slice. +func scriptUInt32(script []byte) (uint, error) { + if len(script) <= 4 { + return 0, StackErrShortScript + } + // Yes this is little endian + return ((uint(script[3]) << 24) | (uint(script[2]) << 16) | + (uint(script[1]) << 8) | uint(script[0])), nil +} + +// getStack returns the contents of stack as a byte array bottom up +func getStack(stack *Stack) [][]byte { + array := make([][]byte, stack.Depth()) + for i := range array { + // PeekByteArry can't fail due to overflow, already checked + array[len(array)-i-1], _ = + stack.PeekByteArray(i) + } + return array +} + +// setStack sets the stack to the contents of the array where the last item in +// the array is the top item in the stack. +func setStack(stack *Stack, data [][]byte) { + // This can not error. Only errors are for invalid arguments. + _ = stack.DropN(stack.Depth()) + + for i := range data { + stack.PushByteArray(data[i]) + } +} + +// GetStack returns the contents of the primary stack as an array. where the +// last item in the array is the top of the stack. +func (s *Script) GetStack() [][]byte { + return getStack(&s.dstack) +} + +// SetStack sets the contents of the primary stack to the contents of the +// provided array where the last item in the array will be the top of the stack. +func (s *Script) SetStack(data [][]byte) { + setStack(&s.dstack, data) +} + +// GetAltStack returns the contents of the primary stack as an array. where the +// last item in the array is the top of the stack. +func (s *Script) GetAltStack() [][]byte { + return getStack(&s.astack) +} + +// SetAltStack sets the contents of the primary stack to the contents of the +// provided array where the last item in the array will be the top of the stack. +func (s *Script) SetAltStack(data [][]byte) { + setStack(&s.astack, data) +} diff --git a/script_test.go b/script_test.go new file mode 100644 index 00000000..463344d8 --- /dev/null +++ b/script_test.go @@ -0,0 +1,1060 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript_test + +import ( + "bytes" + "github.com/conformal/btcscript" + "github.com/conformal/btcwire" + "testing" +) + +type txTest struct { + name string + tx *btcwire.MsgTx + pkScript []byte // output script of previous tx + idx int + bip16 bool + err error +} + +var txTests = []txTest{ + // tx 0437cd7f8525ceed2324359c2d0ba26006d92d85. the first tx in the + // blockchain that verifies signatures. + txTest{ + name: "CheckSig", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0xc9, 0x97, 0xa5, 0xe5, + 0x6e, 0x10, 0x41, 0x02, + 0xfa, 0x20, 0x9c, 0x6a, + 0x85, 0x2d, 0xd9, 0x06, + 0x60, 0xa2, 0x0b, 0x2d, + 0x9c, 0x35, 0x24, 0x23, + 0xed, 0xce, 0x25, 0x85, + 0x7f, 0xcd, 0x37, 0x04, + }), + Index: 0, + }, + SignatureScript: []uint8{ + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, + 0x45, 0xe1, 0x69, 0x32, 0xb8, + 0xaf, 0x51, 0x49, 0x61, 0xa1, + 0xd3, 0xa1, 0xa2, 0x5f, 0xdf, + 0x3f, 0x4f, 0x77, 0x32, 0xe9, + 0xd6, 0x24, 0xc6, 0xc6, 0x15, + 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, + 0x22, 0xec, 0x8e, 0xca, 0x07, + 0xde, 0x48, 0x60, 0xa4, 0xac, + 0xdd, 0x12, 0x90, 0x9d, 0x83, + 0x1c, 0xc5, 0x6c, 0xbb, 0xac, + 0x46, 0x22, 0x08, 0x22, 0x21, + 0xa8, 0x76, 0x8d, 0x1d, 0x09, + 0x01, + }, + + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 1000000000, + PkScript: []byte{ + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, + 0x09, 0xc5, 0xf5, 0x1b, 0x13, + 0x90, 0x5f, 0x07, 0xf0, 0x6b, + 0x99, 0xa2, 0xf7, 0x15, 0x9b, + 0x22, 0x25, 0xf3, 0x74, 0xcd, + 0x37, 0x8d, 0x71, 0x30, 0x2f, + 0xa2, 0x84, 0x14, 0xe7, 0xaa, + 0xb3, 0x73, 0x97, 0xf5, 0x54, + 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, + 0x8a, 0x06, 0x26, 0xf1, 0xba, + 0xde, 0xd5, 0xc7, 0x2a, 0x70, + 0x4f, 0x7e, 0x6c, 0xd8, 0x4c, + btcscript.OP_CHECKSIG, + }, + }, + &btcwire.TxOut{ + Value: 4000000000, + PkScript: []byte{ + btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, + 0xdc, 0xdb, 0x8a, 0x01, 0x6b, + 0x49, 0x84, 0x0f, 0x8c, 0x53, + 0xbc, 0x1e, 0xb6, 0x8a, 0x38, + 0x2e, 0x97, 0xb1, 0x48, 0x2e, + 0xca, 0xd7, 0xb1, 0x48, 0xa6, + 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, + 0xf9, 0x74, 0x44, 0x64, 0xf8, + 0x2e, 0x16, 0x0b, 0xfa, 0x9b, + 0x8b, 0x64, 0xf9, 0xd4, 0xc0, + 0x3f, 0x99, 0x9b, 0x86, 0x43, + 0xf6, 0x56, 0xb4, 0x12, 0xa3, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + pkScript: []byte{ + btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, + 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, + 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, + 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, + 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, + 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, + 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, + 0x12, 0xa3, btcscript.OP_CHECKSIG, + }, + idx: 0, + }, + // Previous test with the value of one output changed. + txTest{ + name: "CheckSig Failure", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0xc9, 0x97, 0xa5, 0xe5, + 0x6e, 0x10, 0x41, 0x02, + 0xfa, 0x20, 0x9c, 0x6a, + 0x85, 0x2d, 0xd9, 0x06, + 0x60, 0xa2, 0x0b, 0x2d, + 0x9c, 0x35, 0x24, 0x23, + 0xed, 0xce, 0x25, 0x85, + 0x7f, 0xcd, 0x37, 0x04, + }), + Index: 0, + }, + SignatureScript: []uint8{ + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x4e, + 0x45, 0xe1, 0x69, 0x32, 0xb8, + 0xaf, 0x51, 0x49, 0x61, 0xa1, + 0xd3, 0xa1, 0xa2, 0x5f, 0xdf, + 0x3f, 0x4f, 0x77, 0x32, 0xe9, + 0xd6, 0x24, 0xc6, 0xc6, 0x15, + 0x48, 0xab, 0x5f, 0xb8, 0xcd, + 0x41, 0x02, 0x20, 0x18, 0x15, + 0x22, 0xec, 0x8e, 0xca, 0x07, + 0xde, 0x48, 0x60, 0xa4, 0xac, + 0xdd, 0x12, 0x90, 0x9d, 0x83, + 0x1c, 0xc5, 0x6c, 0xbb, 0xac, + 0x46, 0x22, 0x08, 0x22, 0x21, + 0xa8, 0x76, 0x8d, 0x1d, 0x09, + 0x01, + }, + + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 1000000000, + PkScript: []byte{ + btcscript.OP_DATA_65, + 0x04, 0xae, 0x1a, 0x62, 0xfe, + 0x09, 0xc5, 0xf5, 0x1b, 0x13, + 0x90, 0x5f, 0x07, 0xf0, 0x6b, + 0x99, 0xa2, 0xf7, 0x15, 0x9b, + 0x22, 0x25, 0xf3, 0x74, 0xcd, + 0x37, 0x8d, 0x71, 0x30, 0x2f, + 0xa2, 0x84, 0x14, 0xe7, 0xaa, + 0xb3, 0x73, 0x97, 0xf5, 0x54, + 0xa7, 0xdf, 0x5f, 0x14, 0x2c, + 0x21, 0xc1, 0xb7, 0x30, 0x3b, + 0x8a, 0x06, 0x26, 0xf1, 0xba, + 0xde, 0xd5, 0xc7, 0x2a, 0x70, + 0x4f, 0x7e, 0x6c, 0xd8, 0x4c, + btcscript.OP_CHECKSIG, + }, + }, + &btcwire.TxOut{ + Value: 5000000000, + PkScript: []byte{ + btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, + 0xdc, 0xdb, 0x8a, 0x01, 0x6b, + 0x49, 0x84, 0x0f, 0x8c, 0x53, + 0xbc, 0x1e, 0xb6, 0x8a, 0x38, + 0x2e, 0x97, 0xb1, 0x48, 0x2e, + 0xca, 0xd7, 0xb1, 0x48, 0xa6, + 0x90, 0x9a, 0x5c, 0xb2, 0xe0, + 0xea, 0xdd, 0xfb, 0x84, 0xcc, + 0xf9, 0x74, 0x44, 0x64, 0xf8, + 0x2e, 0x16, 0x0b, 0xfa, 0x9b, + 0x8b, 0x64, 0xf9, 0xd4, 0xc0, + 0x3f, 0x99, 0x9b, 0x86, 0x43, + 0xf6, 0x56, 0xb4, 0x12, 0xa3, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + pkScript: []byte{ + btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, + 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, + 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, + 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, + 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, + 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, + 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, + 0x12, 0xa3, btcscript.OP_CHECKSIG, + }, + idx: 0, + err: btcscript.StackErrScriptFailed, + }, + // tx 599e47a8114fe098103663029548811d2651991b62397e057f0c863c2bc9f9ea + // uses checksig with SigHashNone. + txTest{ + name: "CheckSigHashNone", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x5f, 0x38, 0x6c, 0x8a, + 0x38, 0x42, 0xc9, 0xa9, + 0xdc, 0xfa, 0x9b, 0x78, + 0xbe, 0x78, 0x5a, 0x40, + 0xa7, 0xbd, 0xa0, 0x8b, + 0x64, 0x64, 0x6b, 0xe3, + 0x65, 0x43, 0x01, 0xea, + 0xcc, 0xfc, 0x8d, 0x5e, + }), + Index: 1, + }, + SignatureScript: []byte{ + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0xbb, + 0x4f, 0xbc, 0x49, 0x5a, 0xa2, + 0x3b, 0xab, 0xb2, 0xc2, 0xbe, + 0x4e, 0x3f, 0xb4, 0xa5, 0xdf, + 0xfe, 0xfe, 0x20, 0xc8, 0xef, + 0xf5, 0x94, 0x0f, 0x13, 0x56, + 0x49, 0xc3, 0xea, 0x96, 0x44, + 0x4a, 0x02, 0x20, 0x04, 0xaf, + 0xcd, 0xa9, 0x66, 0xc8, 0x07, + 0xbb, 0x97, 0x62, 0x2d, 0x3e, + 0xef, 0xea, 0x82, 0x8f, 0x62, + 0x3a, 0xf3, 0x06, 0xef, 0x2b, + 0x75, 0x67, 0x82, 0xee, 0x6f, + 0x8a, 0x22, 0xa9, 0x59, 0xa2, + 0x02, + btcscript.OP_DATA_65, + 0x04, 0xf1, 0x93, 0x9a, 0xe6, + 0xb0, 0x1e, 0x84, 0x9b, 0xf0, + 0x5d, 0x0e, 0xd5, 0x1f, 0xd5, + 0xb9, 0x2b, 0x79, 0xa0, 0xe3, + 0x13, 0xe3, 0xf3, 0x89, 0xc7, + 0x26, 0xf1, 0x1f, 0xa3, 0xe1, + 0x44, 0xd9, 0x22, 0x7b, 0x07, + 0xe8, 0xa8, 0x7c, 0x0e, 0xe3, + 0x63, 0x72, 0xe9, 0x67, 0xe0, + 0x90, 0xd1, 0x1b, 0x77, 0x77, + 0x07, 0xaa, 0x73, 0xef, 0xac, + 0xab, 0xff, 0xff, 0xa2, 0x85, + 0xc0, 0x0b, 0x36, 0x22, 0xd6, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 1000000, + PkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x66, 0x0d, 0x4e, 0xf3, 0xa7, + 0x43, 0xe3, 0xe6, 0x96, 0xad, + 0x99, 0x03, 0x64, 0xe5, 0x55, + 0xc2, 0x71, 0xad, 0x50, 0x4b, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + }, + &btcwire.TxOut{ + Value: 29913632, + PkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x21, 0xc4, 0x3c, 0xe4, 0x00, + 0x90, 0x13, 0x12, 0xa6, 0x03, + 0xe4, 0x20, 0x7a, 0xad, 0xfd, + 0x74, 0x2b, 0xe8, 0xe7, 0xda, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + pkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x21, 0xc4, 0x3c, 0xe4, 0x00, 0x90, 0x13, 0x12, 0xa6, + 0x03, 0xe4, 0x20, 0x7a, 0xad, 0xfd, 0x74, 0x2b, 0xe8, + 0xe7, 0xda, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + idx: 0, + bip16: true, // after threshold + }, + // tx 51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e + // first instance of an AnyoneCanPay signature in the blockchain + txTest{ + name: "CheckSigHashAnyoneCanPay", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0xf6, 0x04, 0x4c, 0x0a, + 0xd4, 0x85, 0xf6, 0x33, + 0xb4, 0x1f, 0x97, 0xd0, + 0xd7, 0x93, 0xeb, 0x28, + 0x37, 0xae, 0x40, 0xf7, + 0x38, 0xff, 0x6d, 0x5f, + 0x50, 0xfd, 0xfd, 0x10, + 0x52, 0x8c, 0x1d, 0x76, + }), + Index: 1, + }, + SignatureScript: []byte{ + btcscript.OP_DATA_72, + 0x30, 0x45, 0x02, 0x20, 0x58, + 0x53, 0xc7, 0xf1, 0x39, 0x57, + 0x85, 0xbf, 0xab, 0xb0, 0x3c, + 0x57, 0xe9, 0x62, 0xeb, 0x07, + 0x6f, 0xf2, 0x4d, 0x8e, 0x4e, + 0x57, 0x3b, 0x04, 0xdb, 0x13, + 0xb4, 0x5e, 0xd3, 0xed, 0x6e, + 0xe2, 0x02, 0x21, 0x00, 0x9d, + 0xc8, 0x2a, 0xe4, 0x3b, 0xe9, + 0xd4, 0xb1, 0xfe, 0x28, 0x47, + 0x75, 0x4e, 0x1d, 0x36, 0xda, + 0xd4, 0x8b, 0xa8, 0x01, 0x81, + 0x7d, 0x48, 0x5d, 0xc5, 0x29, + 0xaf, 0xc5, 0x16, 0xc2, 0xdd, + 0xb4, 0x81, + btcscript.OP_DATA_33, + 0x03, 0x05, 0x58, 0x49, 0x80, + 0x36, 0x7b, 0x32, 0x1f, 0xad, + 0x7f, 0x1c, 0x1f, 0x4d, 0x5d, + 0x72, 0x3d, 0x0a, 0xc8, 0x0c, + 0x1d, 0x80, 0xc8, 0xba, 0x12, + 0x34, 0x39, 0x65, 0xb4, 0x83, + 0x64, 0x53, 0x7a, + }, + Sequence: 4294967295, + }, + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x9c, 0x6a, 0xf0, 0xdf, + 0x66, 0x69, 0xbc, 0xde, + 0xd1, 0x9e, 0x31, 0x7e, + 0x25, 0xbe, 0xbc, 0x8c, + 0x78, 0xe4, 0x8d, 0xf8, + 0xae, 0x1f, 0xe0, 0x2a, + 0x7f, 0x03, 0x08, 0x18, + 0xe7, 0x1e, 0xcd, 0x40, + }), + Index: 1, + }, + SignatureScript: []byte{ + btcscript.OP_DATA_73, + 0x30, 0x46, 0x02, 0x21, 0x00, + 0x82, 0x69, 0xc9, 0xd7, 0xba, + 0x0a, 0x7e, 0x73, 0x0d, 0xd1, + 0x6f, 0x40, 0x82, 0xd2, 0x9e, + 0x36, 0x84, 0xfb, 0x74, 0x63, + 0xba, 0x06, 0x4f, 0xd0, 0x93, + 0xaf, 0xc1, 0x70, 0xad, 0x6e, + 0x03, 0x88, 0x02, 0x21, 0x00, + 0xbc, 0x6d, 0x76, 0x37, 0x39, + 0x16, 0xa3, 0xff, 0x6e, 0xe4, + 0x1b, 0x2c, 0x75, 0x20, 0x01, + 0xfd, 0xa3, 0xc9, 0xe0, 0x48, + 0xbc, 0xff, 0x0d, 0x81, 0xd0, + 0x5b, 0x39, 0xff, 0x0f, 0x42, + 0x17, 0xb2, 0x81, + btcscript.OP_DATA_33, + 0x03, 0xaa, 0xe3, 0x03, 0xd8, + 0x25, 0x42, 0x15, 0x45, 0xc5, + 0xbc, 0x7c, 0xcd, 0x5a, 0xc8, + 0x7d, 0xd5, 0xad, 0xd3, 0xbc, + 0xc3, 0xa4, 0x32, 0xba, 0x7a, + 0xa2, 0xf2, 0x66, 0x16, 0x99, + 0xf9, 0xf6, 0x59, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 300000, + PkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x5c, 0x11, 0xf9, 0x17, 0x88, + 0x3b, 0x92, 0x7e, 0xef, 0x77, + 0xdc, 0x57, 0x70, 0x7a, 0xeb, + 0x85, 0x3f, 0x6d, 0x38, 0x94, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + pkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x85, 0x51, 0xe4, 0x8a, 0x53, 0xde, 0xcd, 0x1c, 0xfc, + 0x63, 0x07, 0x9a, 0x45, 0x81, 0xbc, 0xcc, 0xfa, 0xd1, + 0xa9, 0x3c, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + idx: 0, + bip16: true, // after threshold + }, + // tx 6d36bc17e947ce00bb6f12f8e7a56a1585c5a36188ffa2b05e10b4743273a74b + // Uses OP_CODESEPARATOR and OP_CHECKMULTISIG + txTest{ + name: "CheckMultiSig", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x37, 0xb1, 0x7d, 0x76, + 0x38, 0x51, 0xcd, 0x1a, + 0xb0, 0x4a, 0x42, 0x44, + 0x63, 0xd4, 0x13, 0xc4, + 0xee, 0x5c, 0xf6, 0x13, + 0x04, 0xc7, 0xfd, 0x76, + 0x97, 0x7b, 0xea, 0x7f, + 0xce, 0x07, 0x57, 0x05, + }), + Index: 0, + }, + SignatureScript: []byte{ + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x02, + 0xdb, 0xe4, 0xb5, 0xa2, 0xfb, + 0xb5, 0x21, 0xe4, 0xdc, 0x5f, + 0xbe, 0xc7, 0x5f, 0xd9, 0x60, + 0x65, 0x1a, 0x27, 0x54, 0xb0, + 0x3d, 0x08, 0x71, 0xb8, 0xc9, + 0x65, 0x46, 0x9b, 0xe5, 0x0f, + 0xa7, 0x02, 0x20, 0x6d, 0x97, + 0x42, 0x1f, 0xb7, 0xea, 0x93, + 0x59, 0xb6, 0x3e, 0x48, 0xc2, + 0x10, 0x82, 0x23, 0x28, 0x4b, + 0x9a, 0x71, 0x56, 0x0b, 0xd8, + 0x18, 0x24, 0x69, 0xb9, 0x03, + 0x92, 0x28, 0xd7, 0xb3, 0xd7, + 0x01, 0x21, 0x02, 0x95, 0xbf, + 0x72, 0x71, 0x11, 0xac, 0xde, + 0xab, 0x87, 0x78, 0x28, 0x4f, + 0x02, 0xb7, 0x68, 0xd1, 0xe2, + 0x1a, 0xcb, 0xcb, 0xae, 0x42, + }, + Sequence: 4294967295, + }, + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x37, 0xb1, 0x7d, 0x76, + 0x38, 0x51, 0xcd, 0x1a, + 0xb0, 0x4a, 0x42, 0x44, + 0x63, 0xd4, 0x13, 0xc4, + 0xee, 0x5c, 0xf6, 0x13, + 0x04, 0xc7, 0xfd, 0x76, + 0x97, 0x7b, 0xea, 0x7f, + 0xce, 0x07, 0x57, 0x05, + }), + Index: 1, + }, + SignatureScript: []uint8{ + btcscript.OP_FALSE, + btcscript.OP_DATA_72, + 0x30, 0x45, 0x02, 0x20, 0x10, + 0x6a, 0x3e, 0x4e, 0xf0, 0xb5, + 0x1b, 0x76, 0x4a, 0x28, 0x87, + 0x22, 0x62, 0xff, 0xef, 0x55, + 0x84, 0x65, 0x14, 0xda, 0xcb, + 0xdc, 0xbb, 0xdd, 0x65, 0x2c, + 0x84, 0x9d, 0x39, 0x5b, 0x43, + 0x84, 0x02, 0x21, 0x00, 0xe0, + 0x3a, 0xe5, 0x54, 0xc3, 0xcb, + 0xb4, 0x06, 0x00, 0xd3, 0x1d, + 0xd4, 0x6f, 0xc3, 0x3f, 0x25, + 0xe4, 0x7b, 0xf8, 0x52, 0x5b, + 0x1f, 0xe0, 0x72, 0x82, 0xe3, + 0xb6, 0xec, 0xb5, 0xf3, 0xbb, + 0x28, 0x01, + btcscript.OP_CODESEPARATOR, + btcscript.OP_TRUE, + btcscript.OP_DATA_33, + 0x02, 0x32, 0xab, 0xdc, 0x89, + 0x3e, 0x7f, 0x06, 0x31, 0x36, + 0x4d, 0x7f, 0xd0, 0x1c, 0xb3, + 0x3d, 0x24, 0xda, 0x45, 0x32, + 0x9a, 0x00, 0x35, 0x7b, 0x3a, + 0x78, 0x86, 0x21, 0x1a, 0xb4, + 0x14, 0xd5, 0x5a, + btcscript.OP_TRUE, + btcscript.OP_CHECK_MULTISIG, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 4800000, + PkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x0d, 0x77, 0x13, 0x64, 0x9f, + 0x9a, 0x06, 0x78, 0xf4, 0xe8, + 0x80, 0xb4, 0x0f, 0x86, 0xb9, + 0x32, 0x89, 0xd1, 0xbb, 0x27, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + // This is a very weird script... + pkScript: []byte{ + btcscript.OP_DATA_20, + 0x2a, 0x9b, 0xc5, 0x44, 0x7d, 0x66, 0x4c, 0x1d, 0x01, + 0x41, 0x39, 0x2a, 0x84, 0x2d, 0x23, 0xdb, 0xa4, 0x5c, + 0x4f, 0x13, + btcscript.OP_NOP2, btcscript.OP_DROP, + }, + idx: 1, + bip16: false, + }, + // same as previous but with one byte changed to make signature fail + txTest{ + name: "CheckMultiSig fail", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x37, 0xb1, 0x7d, 0x76, + 0x38, 0x51, 0xcd, 0x1a, + 0xb0, 0x4a, 0x42, 0x44, + 0x63, 0xd4, 0x13, 0xc4, + 0xee, 0x5c, 0xf6, 0x13, + 0x04, 0xc7, 0xfd, 0x76, + 0x97, 0x7b, 0xea, 0x7f, + 0xce, 0x07, 0x57, 0x05, + }), + Index: 0, + }, + SignatureScript: []byte{ + btcscript.OP_DATA_71, + 0x30, 0x44, 0x02, 0x20, 0x02, + 0xdb, 0xe4, 0xb5, 0xa2, 0xfb, + 0xb5, 0x21, 0xe4, 0xdc, 0x5f, + 0xbe, 0xc7, 0x5f, 0xd9, 0x60, + 0x65, 0x1a, 0x27, 0x54, 0xb0, + 0x3d, 0x08, 0x71, 0xb8, 0xc9, + 0x65, 0x46, 0x9b, 0xe5, 0x0f, + 0xa7, 0x02, 0x20, 0x6d, 0x97, + 0x42, 0x1f, 0xb7, 0xea, 0x93, + 0x59, 0xb6, 0x3e, 0x48, 0xc2, + 0x10, 0x82, 0x23, 0x28, 0x4b, + 0x9a, 0x71, 0x56, 0x0b, 0xd8, + 0x18, 0x24, 0x69, 0xb9, 0x03, + 0x92, 0x28, 0xd7, 0xb3, 0xd7, + 0x01, 0x21, 0x02, 0x95, 0xbf, + 0x72, 0x71, 0x11, 0xac, 0xde, + 0xab, 0x87, 0x78, 0x28, 0x4f, + 0x02, 0xb7, 0x68, 0xd1, 0xe2, + 0x1a, 0xcb, 0xcb, 0xae, 0x42, + }, + Sequence: 4294967295, + }, + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x37, 0xb1, 0x7d, 0x76, + 0x38, 0x51, 0xcd, 0x1a, + 0xb0, 0x4a, 0x42, 0x44, + 0x63, 0xd4, 0x13, 0xc4, + 0xee, 0x5c, 0xf6, 0x13, + 0x04, 0xc7, 0xfd, 0x76, + 0x97, 0x7b, 0xea, 0x7f, + 0xce, 0x07, 0x57, 0x05, + }), + Index: 1, + }, + SignatureScript: []uint8{ + btcscript.OP_FALSE, + btcscript.OP_DATA_72, + 0x30, 0x45, 0x02, 0x20, 0x10, + 0x6a, 0x3e, 0x4e, 0xf0, 0xb5, + 0x1b, 0x76, 0x4a, 0x28, 0x87, + 0x22, 0x62, 0xff, 0xef, 0x55, + 0x84, 0x65, 0x14, 0xda, 0xcb, + 0xdc, 0xbb, 0xdd, 0x65, 0x2c, + 0x84, 0x9d, 0x39, 0x5b, 0x43, + 0x84, 0x02, 0x21, 0x00, 0xe0, + 0x3a, 0xe5, 0x54, 0xc3, 0xcb, + 0xb4, 0x06, 0x00, 0xd3, 0x1d, + 0xd4, 0x6f, 0xc3, 0x3f, 0x25, + 0xe4, 0x7b, 0xf8, 0x52, 0x5b, + 0x1f, 0xe0, 0x72, 0x82, 0xe3, + 0xb6, 0xec, 0xb5, 0xf3, 0xbb, + 0x28, 0x01, + btcscript.OP_CODESEPARATOR, + btcscript.OP_TRUE, + btcscript.OP_DATA_33, + 0x02, 0x32, 0xab, 0xdc, 0x89, + 0x3e, 0x7f, 0x06, 0x31, 0x36, + 0x4d, 0x7f, 0xd0, 0x1c, 0xb3, + 0x3d, 0x24, 0xda, 0x45, 0x32, + 0x9a, 0x00, 0x35, 0x7b, 0x3a, + 0x78, 0x86, 0x21, 0x1a, 0xb4, + 0x14, 0xd5, 0x5a, + btcscript.OP_TRUE, + btcscript.OP_CHECK_MULTISIG, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 5800000, + PkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x0d, 0x77, 0x13, 0x64, 0x9f, + 0x9a, 0x06, 0x78, 0xf4, 0xe8, + 0x80, 0xb4, 0x0f, 0x86, 0xb9, + 0x32, 0x89, 0xd1, 0xbb, 0x27, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + // This is a very weird script... + pkScript: []byte{ + btcscript.OP_DATA_20, + 0x2a, 0x9b, 0xc5, 0x44, 0x7d, 0x66, 0x4c, 0x1d, 0x01, + 0x41, 0x39, 0x2a, 0x84, 0x2d, 0x23, 0xdb, 0xa4, 0x5c, + 0x4f, 0x13, + btcscript.OP_NOP2, btcscript.OP_DROP, + }, + idx: 1, + bip16: false, + err: btcscript.StackErrScriptFailed, + }, + // tx e5779b9e78f9650debc2893fd9636d827b26b4ddfa6a8172fe8708c924f5c39d + // First P2SH transaction in the blockchain + txTest{ + name: "P2SH", + tx: &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash([32]byte{ + 0x6d, 0x58, 0xf8, 0xa3, + 0xaa, 0x43, 0x0b, 0x84, + 0x78, 0x52, 0x3a, 0x65, + 0xc2, 0x03, 0xa2, 0x7b, + 0xb8, 0x81, 0x17, 0x8c, + 0xb1, 0x23, 0x13, 0xaf, + 0xde, 0x29, 0xf9, 0x2e, + 0xd7, 0x56, 0xaa, 0x7e, + }), + Index: 0, + }, + SignatureScript: []byte{ + btcscript.OP_DATA_2, + // OP_3 OP_7 + 0x53, 0x57, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 1000000, + PkScript: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x5b, 0x69, 0xd8, 0xb9, 0xdf, + 0xa6, 0xe4, 0x12, 0x26, 0x47, + 0xe1, 0x79, 0x4e, 0xaa, 0x3b, + 0xfc, 0x11, 0x1f, 0x70, 0xef, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + }, + }, + LockTime: 0, + }, + pkScript: []byte{ + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x43, 0x3e, 0xc2, 0xac, 0x1f, 0xfa, 0x1b, 0x7b, 0x7d, + 0x02, 0x7f, 0x56, 0x45, 0x29, 0xc5, 0x71, 0x97, 0xf9, + 0xae, 0x88, + btcscript.OP_EQUAL, + }, + idx: 0, + bip16: true, + }, +} + +// Test a number of tx from the blockchain to test otherwise difficult to test +// opcodes (i.e. those that involve signatures). Being from the blockchain, +// these transactions are known good. +// TODO(oga) For signatures we currently do not test SigHashSingle because +// nothing in the blockchain that we have yet seen uses them, making it hard +// to confirm we implemented the spec correctly. +func testTx(t *testing.T, test txTest) { + engine, err := btcscript.NewScript( + test.tx.TxIn[test.idx].SignatureScript, test.pkScript, + test.idx, test.tx, 70001, test.bip16) + if err != nil { + t.Errorf("Failed to parse %s: %v", test.name, err) + return + } + + err = engine.Execute() + if err != nil { + if err != test.err { + t.Errorf("Failed to validate %s tx: %v expected %v", + test.name, err, test.err) + } + return + } + if test.err != nil { + t.Errorf("%s: expected failure: %v, succeeded", test.name, + test.err) + } +} + +func TestTX(t *testing.T) { + for i := range txTests { + testTx(t, txTests[i]) + } +} + +type removeOpcodeTest struct { + name string + before []byte + remove byte + err error + after []byte +} + +var removeOpcodeTests = []removeOpcodeTest{ + // Nothing to remove. + removeOpcodeTest{ + name: "nothing to remove", + before: []byte{btcscript.OP_NOP}, + remove: btcscript.OP_CODESEPARATOR, + after: []byte{btcscript.OP_NOP}, + }, + // Test basic opcode removal + removeOpcodeTest{ + name: "codeseparator 1", + before: []byte{btcscript.OP_NOP, btcscript.OP_CODESEPARATOR, + btcscript.OP_TRUE}, + remove: btcscript.OP_CODESEPARATOR, + after: []byte{btcscript.OP_NOP, btcscript.OP_TRUE}, + }, + // The opcode in question is actually part of the data in a previous + // opcode + removeOpcodeTest{ + name: "codeseparator by coincidence", + before: []byte{btcscript.OP_NOP, btcscript.OP_DATA_1, btcscript.OP_CODESEPARATOR, + btcscript.OP_TRUE}, + remove: btcscript.OP_CODESEPARATOR, + after: []byte{btcscript.OP_NOP, btcscript.OP_DATA_1, btcscript.OP_CODESEPARATOR, + btcscript.OP_TRUE}, + }, + removeOpcodeTest{ + name: "invalid opcode", + before: []byte{186}, + remove: btcscript.OP_CODESEPARATOR, + err: btcscript.StackErrInvalidOpcode, + }, + removeOpcodeTest{ + name: "invalid length (insruction)", + before: []byte{btcscript.OP_PUSHDATA1}, + remove: btcscript.OP_CODESEPARATOR, + err: btcscript.StackErrShortScript, + }, + removeOpcodeTest{ + name: "invalid length (data)", + before: []byte{btcscript.OP_PUSHDATA1, 255, 254}, + remove: btcscript.OP_CODESEPARATOR, + err: btcscript.StackErrShortScript, + }, +} + +func testRemoveOpcode(t *testing.T, test *removeOpcodeTest) { + result, err := btcscript.TstRemoveOpcode(test.before, test.remove) + if test.err != nil { + if err != test.err { + t.Errorf("%s: got unexpected error. exp: \"%v\" "+ + "got: \"%v\"", test.name, test.err, err) + } + return + } + if err != nil { + t.Errorf("%s: unexpected failure: \"%v\"", test.name, err) + return + } + if !bytes.Equal(test.after, result) { + t.Errorf("%s: value does not equal expected: exp: \"%v\""+ + " got: \"%v\"", test.name, test.after, result) + } +} + +func TestRemoveOpcodes(t *testing.T) { + for i := range removeOpcodeTests { + testRemoveOpcode(t, &removeOpcodeTests[i]) + } +} + +type removeOpcodeByDataTest struct { + name string + before []byte + remove []byte + err error + after []byte +} + +var removeOpcodeByDataTests = []removeOpcodeByDataTest{ + removeOpcodeByDataTest{ + name: "nothing to do", + before: []byte{btcscript.OP_NOP}, + remove: []byte{1, 2, 3, 4}, + after: []byte{btcscript.OP_NOP}, + }, + removeOpcodeByDataTest{ + name: "simple case", + before: []byte{btcscript.OP_DATA_4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{}, + }, + removeOpcodeByDataTest{ + name: "simple case (miss)", + before: []byte{btcscript.OP_DATA_4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 5}, + after: []byte{btcscript.OP_DATA_4, 1, 2, 3, 4}, + }, + removeOpcodeByDataTest{ + name: "simple case (pushdata1)", + before: []byte{btcscript.OP_PUSHDATA1, 4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{}, + }, + removeOpcodeByDataTest{ + name: "simple case (pushdata1 miss)", + before: []byte{btcscript.OP_PUSHDATA1, 4, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 5}, + after: []byte{btcscript.OP_PUSHDATA1, 4, 1, 2, 3, 4}, + }, + removeOpcodeByDataTest{ + name: "simple case (pushdata2)", + before: []byte{btcscript.OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{}, + }, + removeOpcodeByDataTest{ + name: "simple case (pushdata2 miss)", + before: []byte{btcscript.OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 5}, + after: []byte{btcscript.OP_PUSHDATA2, 4, 0, 1, 2, 3, 4}, + }, + removeOpcodeByDataTest{ + name: "simple case (pushdata4)", + before: []byte{btcscript.OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4}, + after: []byte{}, + }, + removeOpcodeByDataTest{ + name: "simple case (pushdata4 miss)", + before: []byte{btcscript.OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, + remove: []byte{1, 2, 3, 4, 5}, + after: []byte{btcscript.OP_PUSHDATA4, 4, 0, 0, 0, 1, 2, 3, 4}, + }, + removeOpcodeByDataTest{ + name: "invalid opcode ", + before: []byte{187}, + remove: []byte{1, 2, 3, 4}, + err: btcscript.StackErrInvalidOpcode, + }, + removeOpcodeByDataTest{ + name: "invalid length (instruction)", + before: []byte{btcscript.OP_PUSHDATA1}, + remove: []byte{1, 2, 3, 4}, + err: btcscript.StackErrShortScript, + }, + removeOpcodeByDataTest{ + name: "invalid length (data)", + before: []byte{btcscript.OP_PUSHDATA1, 255, 254}, + remove: []byte{1, 2, 3, 4}, + err: btcscript.StackErrShortScript, + }, +} + +func testRemoveOpcodeByData(t *testing.T, test *removeOpcodeByDataTest) { + result, err := btcscript.TstRemoveOpcodeByData(test.before, + test.remove) + if test.err != nil { + if err != test.err { + t.Errorf("%s: got unexpected error. exp: \"%v\" "+ + "got: \"%v\"", test.name, test.err, err) + } + return + } + if err != nil { + t.Errorf("%s: unexpected failure: \"%v\"", test.name, err) + return + } + if !bytes.Equal(test.after, result) { + t.Errorf("%s: value does not equal expected: exp: \"%v\""+ + " got: \"%v\"", test.name, test.after, result) + } +} +func TestRemoveOpcodeByDatas(t *testing.T) { + for i := range removeOpcodeByDataTests { + testRemoveOpcodeByData(t, &removeOpcodeByDataTests[i]) + } +} + +// Tests for the script type logic + +type scriptTypeTest struct { + name string + script []byte + scripttype btcscript.TstScriptType +} + +var scriptTypeTests = []scriptTypeTest{ + // tx 0437cd7f8525ceed2324359c2d0ba26006d92d85. + scriptTypeTest{ + name: "Pay Pubkey", + script: []byte{ + btcscript.OP_DATA_65, + 0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, + 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 0xb6, + 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 0xd7, + 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 0xea, + 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 0xf8, + 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 0xd4, + 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 0xb4, + 0x12, 0xa3, + btcscript.OP_CHECKSIG, + }, + scripttype: btcscript.TstPubKeyTy, + }, + // tx 599e47a8114fe098103663029548811d2651991b62397e057f0c863c2bc9f9ea + scriptTypeTest{ + name: "Pay PubkeyHash", + script: []byte{ + btcscript.OP_DUP, + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x66, 0x0d, 0x4e, 0xf3, 0xa7, 0x43, 0xe3, 0xe6, 0x96, + 0xad, 0x99, 0x03, 0x64, 0xe5, 0x55, 0xc2, 0x71, 0xad, + 0x50, 0x4b, + btcscript.OP_EQUALVERIFY, + btcscript.OP_CHECKSIG, + }, + scripttype: btcscript.TstPubKeyHashTy, + }, + // part of tx 6d36bc17e947ce00bb6f12f8e7a56a1585c5a36188ffa2b05e10b4743273a74b + // codeseparator parts have been elided. (bitcoind's checks for multisig + // type doesn't have codesep etc either. + scriptTypeTest{ + name: "multisig", + script: []byte{ + btcscript.OP_TRUE, + btcscript.OP_DATA_33, + 0x02, 0x32, 0xab, 0xdc, 0x89, 0x3e, 0x7f, 0x06, 0x31, + 0x36, 0x4d, 0x7f, 0xd0, 0x1c, 0xb3, 0x3d, 0x24, 0xda, + 0x45, 0x32, 0x9a, 0x00, 0x35, 0x7b, 0x3a, 0x78, 0x86, + 0x21, 0x1a, 0xb4, 0x14, 0xd5, 0x5a, + btcscript.OP_TRUE, + btcscript.OP_CHECK_MULTISIG, + }, + scripttype: btcscript.TstMultiSigTy, + }, + // tx e5779b9e78f9650debc2893fd9636d827b26b4ddfa6a8172fe8708c924f5c39d + // P2SH + scriptTypeTest{ + name: "P2SH", + script: []byte{ + btcscript.OP_HASH160, + btcscript.OP_DATA_20, + 0x43, 0x3e, 0xc2, 0xac, 0x1f, 0xfa, 0x1b, 0x7b, 0x7d, + 0x02, 0x7f, 0x56, 0x45, 0x29, 0xc5, 0x71, 0x97, 0xf9, + 0xae, 0x88, + btcscript.OP_EQUAL, + }, + scripttype: btcscript.TstScriptHashTy, + }, +} + +func testScriptType(t *testing.T, test *scriptTypeTest) { + scripttype := btcscript.TstTypeOfScript(test.script) + if scripttype != test.scripttype { + t.Errorf("%s: expected %s got %s", test.name, test.scripttype, + scripttype) + } +} + +func TestScriptTypes(t *testing.T) { + for i := range scriptTypeTests { + testScriptType(t, &scriptTypeTests[i]) + } +} diff --git a/stack.go b/stack.go new file mode 100644 index 00000000..a1efa8f9 --- /dev/null +++ b/stack.go @@ -0,0 +1,350 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript + +import ( + "math/big" +) + +// asInt converts a byte array to a bignum by treating it as a little endian +// number with sign bit. +func asInt(v []byte) *big.Int { + if len(v) == 0 { + return big.NewInt(0) + } + negative := false + msb := v[len(v)-1] + if msb&0x80 == 0x80 { + negative = true + // remove sign bit + v[len(v)-1] &= 0x7f + } + // trim leading 0 bytes + for ; msb == 0; msb = v[len(v)-1] { + v = v[:len(v)-1] + if len(v) == 0 { + break + } + } + // reverse bytes with a copy since stack is immutable. + intArray := make([]byte, len(v)) + for i := range v { + intArray[len(v)-i-1] = v[i] + } + + num := new(big.Int).SetBytes(intArray) + if negative { + num = num.Neg(num) + } + return num +} + +// fromInt provies a Big.Int in little endian format with the high bit of the +// msb donating sign. +func fromInt(v *big.Int) []byte { + negative := false + if v.Sign() == -1 { + negative = true + } + // Int.Bytes() trims leading zeros for us, so we don't have to. + b := v.Bytes() + if len(b) == 0 { + // negative 0 + if negative { + return []byte{0x80} + } + // normal 0 + return []byte{} + } + arr := make([]byte, len(b)) + for i := range b { + arr[len(b)-i-1] = b[i] + } + // if would otherwise be negative, add a zero byte + if arr[len(arr)-1]&0x80 == 0x80 { + arr = append(arr, 0) + } + if negative { + arr[len(arr)-1] |= 0x80 + } + return arr +} + +// asBool gets the boolean value of the byte array. +func asBool(t []byte) bool { + for i := range t { + if t[i] != 0 { + return true + } + } + return false +} + +// fromBool converts a boolean into the appropriate byte array. +func fromBool(v bool) []byte { + if v { + return []byte{1} + } + return []byte{0} +} + +// Stack represents a stack of immutable objects to be used with bitcoin scripts +// Objects may be shared, therefore in usage if a value is to be changed it +// *must* be deep-copied first to avoid changing other values on the stack. +type Stack struct { + stk [][]byte +} + +// PushByteArray adds the given back array to the top of the stack. +func (s *Stack) PushByteArray(so []byte) { + s.stk = append(s.stk, so) +} + +// PushInt converts the provided bignum to a suitable byte array then pushes +// it onto the top of the stack. +func (s *Stack) PushInt(val *big.Int) { + s.PushByteArray(fromInt(val)) +} + +// PushBool converts the provided boolean to a suitable byte array then pushes +// it onto the top of the stack. +func (s *Stack) PushBool(val bool) { + s.PushByteArray(fromBool(val)) +} + +// PopByteArray pops the value off the top of the stack and returns it. +func (s *Stack) PopByteArray() ([]byte, error) { + return s.nipN(0) +} + +// PopInt pops the value off the top of the stack, converts it into a bignum and +// returns it. +func (s *Stack) PopInt() (*big.Int, error) { + so, err := s.PopByteArray() + if err != nil { + return nil, err + } + return asInt(so), nil +} + +// PopBool pops the value off the top of the stack, converts it into a bool and +// returns it. +func (s *Stack) PopBool() (bool, error) { + so, err := s.PopByteArray() + if err != nil { + return false, err + } + return asBool(so), nil +} + +// PeekByteArray returns the nth item on the stack without removing it. +func (s *Stack) PeekByteArray(idx int) (so []byte, err error) { + sz := len(s.stk) + if idx < 0 || idx >= sz { + return nil, StackErrUnderflow + } + return s.stk[sz-idx-1], nil +} + +// PeekInt returns the nth item on the stack as a bignum without removing it. +func (s *Stack) PeekInt(idx int) (i *big.Int, err error) { + so, err := s.PeekByteArray(idx) + if err != nil { + return nil, err + } + return asInt(so), nil +} + +// PeekBool returns the nth item on the stack as a bool without removing it. +func (s *Stack) PeekBool(idx int) (i bool, err error) { + so, err := s.PeekByteArray(idx) + if err != nil { + return false, err + } + return asBool(so), nil +} + +// nipN is an internal function that removes the nth item on the stack and +// returns it. +func (s *Stack) nipN(idx int) (so []byte, err error) { + sz := len(s.stk) + if idx < 0 || idx > sz-1 { + err = StackErrUnderflow + return + } + so = s.stk[sz-idx-1] + if idx == 0 { + s.stk = s.stk[:sz-1] + } else if idx == sz-1 { + s.stk = s.stk[1:] + } else { + s1 := s.stk[sz-idx : sz] + s.stk = s.stk[:sz-idx-1] + s.stk = append(s.stk, s1...) + } + return +} + +// NipN removes the Nth object on the stack +func (s *Stack) NipN(idx int) error { + _, err := s.nipN(idx) + return err +} + +// Tuck copies the item at the top of the stack and inserts it before the 2nd +// to top item. e.g.: 2,1 -> 2,1,2 +func (s *Stack) Tuck() error { + so2, err := s.PopByteArray() + if err != nil { + return err + } + so1, err := s.PopByteArray() + if err != nil { + return err + } + s.PushByteArray(so2) // stack 2 + s.PushByteArray(so1) // stack 1,2 + s.PushByteArray(so2) // stack 2,1,2 + + return nil +} + +// Depth returns the number of items on the stack. +func (s *Stack) Depth() (sz int) { + sz = len(s.stk) + return +} + +// DropN removes the top N items from the stack. +// e.g. +// DropN(1): 1,2,3 -> 1,2 +// DropN(2): 1,2,3 -> 1 +func (s *Stack) DropN(n int) error { + if n < 1 { + return StackErrInvalidArgs + } + for ; n > 0; n-- { + _, err := s.PopByteArray() + if err != nil { + return err + } + } + return nil +} + +// DupN duplicates the top N items on the stack. +// e.g. +// DupN(1): 1,2,3 -> 1,2,3,3 +// DupN(2): 1,2,3 -> 1,2,3,2,3 +func (s *Stack) DupN(n int) error { + if n < 1 { + return StackErrInvalidArgs + } + // Iteratively duplicate the value n-1 down the stack n times. + // this leaves us with an in-order duplicate of the top N items on the + // stack. + for i := n; i > 0; i-- { + so, err := s.PeekByteArray(n - 1) + if err != nil { + return err + } + s.PushByteArray(so) + } + return nil +} + +// RotN rotates the top 3N items on the stack to the left +// e.g. +// RotN(1): 1,2,3 -> 2,3,1 +func (s *Stack) RotN(n int) error { + if n < 1 { + return StackErrInvalidArgs + } + entry := 3*n - 1 + // Nip the 3n-1th item from the stack to the top n times to rotate + // them up to the head of the stack. + for i := n; i > 0; i-- { + so, err := s.nipN(entry) + if err != nil { + return err + } + + s.PushByteArray(so) + } + return nil +} + +// SwapN swaps the top N items on the stack with those below them. +// E.g.: +// SwapN(1): 1,2 -> 2,1 +// SwapN(2): 1,2,3,4 -> 3,4,1,2 +func (s *Stack) SwapN(n int) error { + if n < 1 { + return StackErrInvalidArgs + } + entry := 2*n - 1 + for i := n; i > 0; i-- { + // swap 2n-1th entry to topj + so, err := s.nipN(entry) + if err != nil { + return err + } + + s.PushByteArray(so) + } + return nil +} + +// OverN copies N items N spaces back to the top of the stack. +// e.g.: +// OverN(1): 1,2 -> 1,2,1 +// OverN(2): 1,2,3,4 -> 1,2,3,4,1,2 +func (s *Stack) OverN(n int) error { + if n < 1 { + return StackErrInvalidArgs + } + // Copy 2n-1th entry to top of the stack + entry := 2*n - 1 + for ; n > 0; n-- { + so, err := s.PeekByteArray(entry) + if err != nil { + return err + } + s.PushByteArray(so) + // 4,1,2,3,4, now code original 3rd entry to top. + } + + return nil +} + +// PickN copies the item N items back in the stack to the top. +// e.g.: +// PickN(1): 1,2,3 -> 1,2,3,2 +// PickN(2): 1,2,3 -> 1,2,3,1 +func (s *Stack) PickN(n int) error { + so, err := s.PeekByteArray(n) + if err != nil { + return err + } + + s.PushByteArray(so) + + return nil +} + +// RollN moves the item N items back in the stack to the top. +// e.g.: +// RollN(1): 1,2,3 -> 1,3,2 +// RollN(2): 1,2,3 -> 2,3,1 +func (s *Stack) RollN(n int) error { + so, err := s.nipN(n) + if err != nil { + return err + } + + s.PushByteArray(so) + + return nil +} diff --git a/stack_test.go b/stack_test.go new file mode 100644 index 00000000..cfdf97a3 --- /dev/null +++ b/stack_test.go @@ -0,0 +1,818 @@ +// Copyright (c) 2013 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package btcscript_test + +import ( + "bytes" + "errors" + "github.com/conformal/btcscript" + "math/big" + "testing" +) + +type stackTest struct { + name string + before [][]byte + operation func(*btcscript.Stack) error + expectedReturn error + after [][]byte +} + +var stackTests = []stackTest{ + { + "noop", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + return nil + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {5}}, + }, + { + "peek underflow (byte)", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + _, err := stack.PeekByteArray(5) + return err + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "peek underflow (int)", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + _, err := stack.PeekInt(5) + return err + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "peek underflow (bool)", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + _, err := stack.PeekBool(5) + return err + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "pop", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + val, err := stack.PopByteArray() + if err != nil { + return err + } + if !bytes.Equal(val, []byte{5}) { + return errors.New("not equal!") + } + return err + }, + nil, + [][]byte{{1}, {2}, {3}, {4}}, + }, + { + "pop", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + val, err := stack.PopByteArray() + if err != nil { + return err + } + if !bytes.Equal(val, []byte{5}) { + return errors.New("not equal!") + } + return err + }, + nil, + [][]byte{{1}, {2}, {3}, {4}}, + }, + { + "pop everything", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + for i := 0; i < 5; i++ { + _, err := stack.PopByteArray() + if err != nil { + return err + } + } + return nil + }, + nil, + [][]byte{}, + }, + { + "pop underflow", + [][]byte{{1}, {2}, {3}, {4}, {5}}, + func(stack *btcscript.Stack) error { + for i := 0; i < 6; i++ { + _, err := stack.PopByteArray() + if err != nil { + return err + } + } + return nil + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "pop bool", + [][]byte{{0}}, + func(stack *btcscript.Stack) error { + val, err := stack.PopBool() + if err != nil { + return err + } + + if val != false { + return errors.New("unexpected value") + } + return nil + }, + nil, + [][]byte{}, + }, + { + "pop bool", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + val, err := stack.PopBool() + if err != nil { + return err + } + + if val != true { + return errors.New("unexpected value") + } + return nil + }, + nil, + [][]byte{}, + }, + { + "pop bool", + [][]byte{}, + func(stack *btcscript.Stack) error { + _, err := stack.PopBool() + if err != nil { + return err + } + + return nil + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + // XXX test popInt -> byte format matters here. + { + "dup", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(1) + if err != nil { + return err + } + + return nil + }, + nil, + [][]byte{{1}, {1}}, + }, + { + "dup2", + [][]byte{{1}, {2}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(2) + if err != nil { + return err + } + + return nil + }, + nil, + [][]byte{{1}, {2}, {1}, {2}}, + }, + { + "dup3", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(3) + if err != nil { + return err + } + + return nil + }, + nil, + [][]byte{{1}, {2}, {3}, {1}, {2}, {3}}, + }, + { + "dup0", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(0) + if err != nil { + return err + } + + return nil + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "dup-1", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(-1) + if err != nil { + return err + } + + return nil + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "dup too much", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(2) + if err != nil { + return err + } + + return nil + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "dup-1", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + err := stack.DupN(-1) + if err != nil { + return err + } + + return nil + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "PushBool true", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushBool(true) + + return nil + }, + nil, + [][]byte{{1}}, + }, + { + "PushBool false", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushBool(false) + + return nil + }, + nil, + [][]byte{{0}}, + }, + { + "PushBool PopBool", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushBool(true) + val, err := stack.PopBool() + if err != nil { + return err + } + if val != true { + return errors.New("unexpected value") + } + + return nil + }, + nil, + [][]byte{}, + }, + { + "PushBool PopBool 2", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushBool(false) + val, err := stack.PopBool() + if err != nil { + return err + } + if val != false { + return errors.New("unexpected value") + } + + return nil + }, + nil, + [][]byte{}, + }, + { + "PushInt PopBool", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushInt(big.NewInt(1)) + val, err := stack.PopBool() + if err != nil { + return err + } + if val != true { + return errors.New("unexpected value") + } + + return nil + }, + nil, + [][]byte{}, + }, + { + "PushInt PopBool 2", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushInt(big.NewInt(0)) + val, err := stack.PopBool() + if err != nil { + return err + } + if val != false { + return errors.New("unexpected value") + } + + return nil + }, + nil, + [][]byte{}, + }, + { + "PushInt PopBool 2", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushInt(big.NewInt(0)) + val, err := stack.PopBool() + if err != nil { + return err + } + if val != false { + return errors.New("unexpected value") + } + + return nil + }, + nil, + [][]byte{}, + }, + { + "Nip top", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.NipN(0) + }, + nil, + [][]byte{{1}, {2}}, + }, + { + "Nip middle", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.NipN(1) + }, + nil, + [][]byte{{1}, {3}}, + }, + { + "Nip low", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.NipN(2) + }, + nil, + [][]byte{{2}, {3}}, + }, + { + "Nip too much", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + // bite off more than we can chew + return stack.NipN(3) + }, + btcscript.StackErrUnderflow, + [][]byte{{2}, {3}}, + }, + { + "Nip too much", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + // bite off more than we can chew + return stack.NipN(3) + }, + btcscript.StackErrUnderflow, + [][]byte{{2}, {3}}, + }, + { + "keep on tucking", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.Tuck() + }, + nil, + [][]byte{{1}, {3}, {2}, {3}}, + }, + { + "a little tucked up", + [][]byte{{1}}, // too few arguments for tuck + func(stack *btcscript.Stack) error { + return stack.Tuck() + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "all tucked up", + [][]byte{}, // too few arguments for tuck + func(stack *btcscript.Stack) error { + return stack.Tuck() + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "drop 1", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.DropN(1) + }, + nil, + [][]byte{{1}, {2}, {3}}, + }, + { + "drop 2", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.DropN(2) + }, + nil, + [][]byte{{1}, {2}}, + }, + { + "drop 3", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.DropN(3) + }, + nil, + [][]byte{{1}}, + }, + { + "drop 4", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.DropN(4) + }, + nil, + [][]byte{}, + }, + { + "drop 4/5", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.DropN(5) + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "drop invalid", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.DropN(0) + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "Rot1", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.RotN(1) + }, + nil, + [][]byte{{1}, {3}, {4}, {2}}, + }, + { + "Rot2", + [][]byte{{1}, {2}, {3}, {4}, {5}, {6}}, + func(stack *btcscript.Stack) error { + return stack.RotN(2) + }, + nil, + [][]byte{{3}, {4}, {5}, {6}, {1}, {2}}, + }, + { + "Rot too little", + [][]byte{{1}, {2}}, + func(stack *btcscript.Stack) error { + return stack.RotN(1) + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "Rot0", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.RotN(0) + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "Swap1", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.SwapN(1) + }, + nil, + [][]byte{{1}, {2}, {4}, {3}}, + }, + { + "Swap2", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.SwapN(2) + }, + nil, + [][]byte{{3}, {4}, {1}, {2}}, + }, + { + "Swap too little", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + return stack.SwapN(1) + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "Swap0", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.SwapN(0) + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "Over1", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.OverN(1) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {3}}, + }, + { + "Over2", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.OverN(2) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {1}, {2}}, + }, + { + "Over too little", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + return stack.OverN(1) + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "Over0", + [][]byte{{1}, {2}, {3}}, + func(stack *btcscript.Stack) error { + return stack.OverN(0) + }, + btcscript.StackErrInvalidArgs, + [][]byte{}, + }, + { + "Pick1", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.PickN(1) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {3}}, + }, + { + "Pick2", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.PickN(2) + }, + nil, + [][]byte{{1}, {2}, {3}, {4}, {2}}, + }, + { + "Pick too little", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + return stack.PickN(1) + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "Roll1", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.RollN(1) + }, + nil, + [][]byte{{1}, {2}, {4}, {3}}, + }, + { + "Roll2", + [][]byte{{1}, {2}, {3}, {4}}, + func(stack *btcscript.Stack) error { + return stack.RollN(2) + }, + nil, + [][]byte{{1}, {3}, {4}, {2}}, + }, + { + "Roll too little", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + return stack.RollN(1) + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, + { + "Peek bool", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + // Peek bool is otherwise pretty well tested, just check + // it works. + val, err := stack.PeekBool(0) + if err != nil { + return err + } + if val != true { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{1}}, + }, + { + "Peek bool 2", + [][]byte{{0}}, + func(stack *btcscript.Stack) error { + // Peek bool is otherwise pretty well tested, just check + // it works. + val, err := stack.PeekBool(0) + if err != nil { + return err + } + if val != false { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{0}}, + }, + { + "Peek int", + [][]byte{{1}}, + func(stack *btcscript.Stack) error { + // Peek int is otherwise pretty well tested, just check + // it works. + val, err := stack.PeekInt(0) + if err != nil { + return err + } + if val.Cmp(big.NewInt(1)) != 0 { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{1}}, + }, + { + "Peek int 2", + [][]byte{{0}}, + func(stack *btcscript.Stack) error { + // Peek int is otherwise pretty well tested, just check + // it works. + val, err := stack.PeekInt(0) + if err != nil { + return err + } + if val.Cmp(big.NewInt(0)) != 0 { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{{0}}, + }, + { + "pop int", + [][]byte{}, + func(stack *btcscript.Stack) error { + stack.PushInt(big.NewInt(1)) + // Peek int is otherwise pretty well tested, just check + // it works. + val, err := stack.PopInt() + if err != nil { + return err + } + if val.Cmp(big.NewInt(1)) != 0 { + return errors.New("invalid result") + } + return nil + }, + nil, + [][]byte{}, + }, + { + "pop empty", + [][]byte{}, + func(stack *btcscript.Stack) error { + // Peek int is otherwise pretty well tested, just check + // it works. + _, err := stack.PopInt() + return err + }, + btcscript.StackErrUnderflow, + [][]byte{}, + }, +} + +func doTest(t *testing.T, test stackTest) { + stack := btcscript.Stack{} + + for i := range test.before { + stack.PushByteArray(test.before[i]) + } + err := test.operation(&stack) + if err != test.expectedReturn { + t.Errorf("%s: operation return not what expected: %v vs %v", + test.name, err, test.expectedReturn) + } + if err != nil { + return + } + if len(test.after) != stack.Depth() { + t.Errorf("%s: stack depth doesn't match expected: %v vs %v", + test.name, len(test.after), stack.Depth()) + } + for i := range test.after { + val, err := stack.PeekByteArray(stack.Depth() - i - 1) + if err != nil { + t.Errorf("%s: can't peek %dth stack entry: %v", + test.name, i, err) + break + } + + if !bytes.Equal(val, test.after[i]) { + t.Errorf("%s: %dth stack entry doesn't match "+ + "expected: %v vs %v", test.name, i, val, + test.after[i]) + break + } + } +} + +func TestStack(t *testing.T) { + for i := range stackTests { + doTest(t, stackTests[i]) + } +} diff --git a/test_coverage.txt b/test_coverage.txt new file mode 100644 index 00000000..8aaf961e --- /dev/null +++ b/test_coverage.txt @@ -0,0 +1,138 @@ + +github.com/conformal/btcscript/stack.go asInt 100.00% (18/18) +github.com/conformal/btcscript/opcode.go opcodeWithin 100.00% (13/13) +github.com/conformal/btcscript/opcode.go parsedOpcode.print 100.00% (12/12) +github.com/conformal/btcscript/opcode.go parsedOpcode.bytes 100.00% (12/12) +github.com/conformal/btcscript/stack.go Stack.nipN 100.00% (12/12) +github.com/conformal/btcscript/opcode.go opcodeIf 100.00% (11/11) +github.com/conformal/btcscript/opcode.go opcodeNotIf 100.00% (11/11) +github.com/conformal/btcscript/opcode.go opcodeNumEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeBoolOr 100.00% (10/10) +github.com/conformal/btcscript/stack.go Stack.Tuck 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeBoolAnd 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeMin 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeGreaterThanOrEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeLessThanOrEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeGreaterThan 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeLessThan 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeNumNotEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeMax 100.00% (10/10) +github.com/conformal/btcscript/script.go DisasmString 100.00% (9/9) +github.com/conformal/btcscript/stack.go Stack.RotN 100.00% (9/9) +github.com/conformal/btcscript/stack.go Stack.SwapN 100.00% (9/9) +github.com/conformal/btcscript/stack.go Stack.OverN 100.00% (9/9) +github.com/conformal/btcscript/opcode.go opcodeAdd 100.00% (8/8) +github.com/conformal/btcscript/stack.go Stack.DupN 100.00% (8/8) +github.com/conformal/btcscript/opcode.go opcodeSub 100.00% (8/8) +github.com/conformal/btcscript/opcode.go opcodeEqual 100.00% (8/8) +github.com/conformal/btcscript/opcode.go opcodeRoll 100.00% (7/7) +github.com/conformal/btcscript/stack.go Stack.DropN 100.00% (7/7) +github.com/conformal/btcscript/opcode.go opcodePick 100.00% (7/7) +github.com/conformal/btcscript/opcode.go opcodeNot 100.00% (7/7) +github.com/conformal/btcscript/opcode.go opcode0NotEqual 100.00% (7/7) +github.com/conformal/btcscript/opcode.go opcodeIfDup 100.00% (6/6) +github.com/conformal/btcscript/opcode.go opcodeVerify 100.00% (6/6) +github.com/conformal/btcscript/opcode.go opcodeElse 100.00% (6/6) +github.com/conformal/btcscript/opcode.go parsedOpcode.conditional 100.00% (6/6) +github.com/conformal/btcscript/opcode.go opcode1Add 100.00% (5/5) +github.com/conformal/btcscript/stack.go Stack.RollN 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeRipemd160 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeHash256 100.00% (5/5) +github.com/conformal/btcscript/stack.go Stack.PickN 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeSize 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcode1Sub 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeNegate 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeAbs 100.00% (5/5) +github.com/conformal/btcscript/script.go removeOpcodeByData 100.00% (5/5) +github.com/conformal/btcscript/script.go removeOpcode 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeSha256 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeHash160 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeToAltStack 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeFromAltStack 100.00% (5/5) +github.com/conformal/btcscript/stack.go Stack.PopBool 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeCheckMultiSigVerify 100.00% (4/4) +github.com/conformal/btcscript/stack.go Stack.PeekInt 100.00% (4/4) +github.com/conformal/btcscript/script.go unparseScript 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeEqualVerify 100.00% (4/4) +github.com/conformal/btcscript/stack.go Stack.PeekByteArray 100.00% (4/4) +github.com/conformal/btcscript/stack.go Stack.PeekBool 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeNumEqualVerify 100.00% (4/4) +github.com/conformal/btcscript/stack.go asBool 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeEndif 100.00% (4/4) +github.com/conformal/btcscript/stack.go Stack.PopInt 100.00% (4/4) +github.com/conformal/btcscript/script.go getStack 100.00% (4/4) +github.com/conformal/btcscript/stack.go fromBool 100.00% (3/3) +github.com/conformal/btcscript/script.go scriptUInt8 100.00% (3/3) +github.com/conformal/btcscript/script.go scriptUInt32 100.00% (3/3) +github.com/conformal/btcscript/address.go ScriptType.String 100.00% (3/3) +github.com/conformal/btcscript/script.go setStack 100.00% (3/3) +github.com/conformal/btcscript/script.go scriptUInt16 100.00% (3/3) +github.com/conformal/btcscript/opcode.go opcodeFalse 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodePushData 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcode1Negate 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodeN 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodeDepth 100.00% (2/2) +github.com/conformal/btcscript/opcode.go calcHash 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodeCodeSeparator 100.00% (2/2) +github.com/conformal/btcscript/stack.go Stack.Depth 100.00% (2/2) +github.com/conformal/btcscript/stack.go Stack.NipN 100.00% (2/2) +github.com/conformal/btcscript/script.go Script.GetAltStack 100.00% (1/1) +github.com/conformal/btcscript/stack.go Stack.PushBool 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Drop 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeReturn 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeNop 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeInvalid 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeReserved 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.subScript 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeDisabled 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Swap 100.00% (1/1) +github.com/conformal/btcscript/opcode.go init 100.00% (1/1) +github.com/conformal/btcscript/script.go isPubkey 100.00% (1/1) +github.com/conformal/btcscript/script.go isPubkeyHash 100.00% (1/1) +github.com/conformal/btcscript/script.go isScriptHash 100.00% (1/1) +github.com/conformal/btcscript/log.go newLogClosure 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.disasm 100.00% (1/1) +github.com/conformal/btcscript/log.go UseLogger 100.00% (1/1) +github.com/conformal/btcscript/log.go DisableLog 100.00% (1/1) +github.com/conformal/btcscript/log.go init 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.GetStack 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.SetStack 100.00% (1/1) +github.com/conformal/btcscript/opcode.go calcHash160 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeTuck 100.00% (1/1) +github.com/conformal/btcscript/stack.go Stack.PushByteArray 100.00% (1/1) +github.com/conformal/btcscript/stack.go Stack.PushInt 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeSwap 100.00% (1/1) +github.com/conformal/btcscript/stack.go Stack.PopByteArray 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeRot 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeOver 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeNip 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeDup 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeDrop 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Rot 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Over 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode3Dup 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Dup 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.SetAltStack 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeCheckMultiSig 96.43% (54/56) +github.com/conformal/btcscript/script.go NewScript 95.24% (20/21) +github.com/conformal/btcscript/address.go ScriptToAddress 94.92% (56/59) +github.com/conformal/btcscript/script.go parseScript 93.75% (30/32) +github.com/conformal/btcscript/stack.go fromInt 87.50% (14/16) +github.com/conformal/btcscript/script.go Script.Step 86.11% (31/36) +github.com/conformal/btcscript/script.go typeOfScript 83.33% (5/6) +github.com/conformal/btcscript/opcode.go opcodeCheckSig 80.77% (21/26) +github.com/conformal/btcscript/opcode.go parsedOpcode.exec 80.00% (4/5) +github.com/conformal/btcscript/script.go Script.DisasmScript 80.00% (4/5) +github.com/conformal/btcscript/script.go Script.DisasmPC 75.00% (3/4) +github.com/conformal/btcscript/script.go Script.curPC 75.00% (3/4) +github.com/conformal/btcscript/script.go isPushOnly 75.00% (3/4) +github.com/conformal/btcscript/opcode.go opcodeCheckSigVerify 75.00% (3/4) +github.com/conformal/btcscript/script.go Script.calcScriptHash 71.43% (25/35) +github.com/conformal/btcscript/script.go isMultiSig 61.54% (8/13) +github.com/conformal/btcscript/opcode.go opcodeSha1 60.00% (3/5) +github.com/conformal/btcscript/script.go Script.validPC 60.00% (3/5) +github.com/conformal/btcscript/script.go Script.Execute 55.17% (16/29) +github.com/conformal/btcscript/log.go SetLogWriter 0.00% (0/7) +github.com/conformal/btcscript/log.go logClosure.String 0.00% (0/1) +github.com/conformal/btcscript ------------------------- 92.51% (828/895) +