Merge btcscript repo into txscript directory.
This commit is contained in:
commit
be11f23e14
21 changed files with 21325 additions and 0 deletions
74
txscript/README.md
Normal file
74
txscript/README.md
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
txscript
|
||||||
|
========
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)]
|
||||||
|
(https://travis-ci.org/btcsuite/btcd)
|
||||||
|
|
||||||
|
Package txscript implements the bitcoin transaction script language. There is
|
||||||
|
a comprehensive test suite. Package txscript is licensed under the liberal ISC
|
||||||
|
license.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/btcsuite/btcd/txscript?status.png)]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/txscript)
|
||||||
|
|
||||||
|
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/btcsuite/btcd/txscript).
|
||||||
|
|
||||||
|
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/btcsuite/btcd/txscript
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ go get github.com/btcsuite/btcd/txscript
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* [Standard Pay-to-pubkey-hash Script]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/txscript#example-PayToAddrScript)
|
||||||
|
Demonstrates creating a script which pays to a bitcoin address. It also
|
||||||
|
prints the created script hex and uses the DisasmString function to display
|
||||||
|
the disassembled script.
|
||||||
|
|
||||||
|
* [Extracting Details from Standard Scripts]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/txscript#example-ExtractPkScriptAddrs)
|
||||||
|
Demonstrates extracting information from a standard public key script.
|
||||||
|
|
||||||
|
## 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 txscript is licensed under the liberal ISC License.
|
90
txscript/address.go
Normal file
90
txscript/address.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/btcsuite/btcnet"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtractPkScriptAddrs returns the type of script, addresses and required
|
||||||
|
// signatures associated with the passed PkScript. Note that it only works for
|
||||||
|
// 'standard' transaction script types. Any data such as public keys which are
|
||||||
|
// invalid are omitted from the results.
|
||||||
|
func ExtractPkScriptAddrs(pkScript []byte, net *btcnet.Params) (ScriptClass, []btcutil.Address, int, error) {
|
||||||
|
var addrs []btcutil.Address
|
||||||
|
var requiredSigs int
|
||||||
|
|
||||||
|
// No valid addresses or required signatures if the script doesn't
|
||||||
|
// parse.
|
||||||
|
pops, err := parseScript(pkScript)
|
||||||
|
if err != nil {
|
||||||
|
return NonStandardTy, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
scriptClass := typeOfScript(pops)
|
||||||
|
switch scriptClass {
|
||||||
|
case PubKeyHashTy:
|
||||||
|
// A pay-to-pubkey-hash script is of the form:
|
||||||
|
// OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG
|
||||||
|
// Therefore the pubkey hash is the 3rd item on the stack.
|
||||||
|
// Skip the pubkey hash if it's invalid for some reason.
|
||||||
|
requiredSigs = 1
|
||||||
|
addr, err := btcutil.NewAddressPubKeyHash(pops[2].data, net)
|
||||||
|
if err == nil {
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
case PubKeyTy:
|
||||||
|
// A pay-to-pubkey script is of the form:
|
||||||
|
// <pubkey> OP_CHECKSIG
|
||||||
|
// Therefore the pubkey is the first item on the stack.
|
||||||
|
// Skip the pubkey if it's invalid for some reason.
|
||||||
|
requiredSigs = 1
|
||||||
|
addr, err := btcutil.NewAddressPubKey(pops[0].data, net)
|
||||||
|
if err == nil {
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ScriptHashTy:
|
||||||
|
// A pay-to-script-hash script is of the form:
|
||||||
|
// OP_HASH160 <scripthash> OP_EQUAL
|
||||||
|
// Therefore the script hash is the 2nd item on the stack.
|
||||||
|
// Skip the script hash if it's invalid for some reason.
|
||||||
|
requiredSigs = 1
|
||||||
|
addr, err := btcutil.NewAddressScriptHashFromHash(pops[1].data, net)
|
||||||
|
if err == nil {
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
case MultiSigTy:
|
||||||
|
// A multi-signature script is of the form:
|
||||||
|
// <numsigs> <pubkey> <pubkey> <pubkey>... <numpubkeys> OP_CHECKMULTISIG
|
||||||
|
// Therefore the number of required signatures is the 1st item
|
||||||
|
// on the stack and the number of public keys is the 2nd to last
|
||||||
|
// item on the stack.
|
||||||
|
requiredSigs = asSmallInt(pops[0].opcode)
|
||||||
|
numPubKeys := asSmallInt(pops[len(pops)-2].opcode)
|
||||||
|
|
||||||
|
// Extract the public keys while skipping any that are invalid.
|
||||||
|
addrs = make([]btcutil.Address, 0, numPubKeys)
|
||||||
|
for i := 0; i < numPubKeys; i++ {
|
||||||
|
addr, err := btcutil.NewAddressPubKey(pops[i+1].data, net)
|
||||||
|
if err == nil {
|
||||||
|
addrs = append(addrs, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case NullDataTy:
|
||||||
|
// Null data transactions have no addresses or required
|
||||||
|
// signatures.
|
||||||
|
|
||||||
|
case NonStandardTy:
|
||||||
|
// Don't attempt to extract addresses or required signatures for
|
||||||
|
// nonstandard transactions.
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptClass, addrs, requiredSigs, nil
|
||||||
|
}
|
368
txscript/address_test.go
Normal file
368
txscript/address_test.go
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
package txscript_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/btcsuite/btcnet"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// decodeHex decodes the passed hex string and returns the resulting bytes. It
|
||||||
|
// panics if an error occurs. This is only used in the tests as a helper since
|
||||||
|
// the only way it can fail is if there is an error in the test source code.
|
||||||
|
func decodeHex(hexStr string) []byte {
|
||||||
|
b, err := hex.DecodeString(hexStr)
|
||||||
|
if err != nil {
|
||||||
|
panic("invalid hex string in test source: err " + err.Error() +
|
||||||
|
", hex: " + hexStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAddressPubKey returns a new btcutil.AddressPubKey from the provided
|
||||||
|
// serialized public key. It panics if an error occurs. This is only used in
|
||||||
|
// the tests as a helper since the only way it can fail is if there is an error
|
||||||
|
// in the test source code.
|
||||||
|
func newAddressPubKey(serializedPubKey []byte) btcutil.Address {
|
||||||
|
addr, err := btcutil.NewAddressPubKey(serializedPubKey,
|
||||||
|
&btcnet.MainNetParams)
|
||||||
|
if err != nil {
|
||||||
|
panic("invalid public key in test source")
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAddressPubKeyHash returns a new btcutil.AddressPubKeyHash from the
|
||||||
|
// provided hash. It panics if an error occurs. This is only used in the tests
|
||||||
|
// as a helper since the only way it can fail is if there is an error in the
|
||||||
|
// test source code.
|
||||||
|
func newAddressPubKeyHash(pkHash []byte) btcutil.Address {
|
||||||
|
addr, err := btcutil.NewAddressPubKeyHash(pkHash, &btcnet.MainNetParams)
|
||||||
|
if err != nil {
|
||||||
|
panic("invalid public key hash in test source")
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAddressScriptHash returns a new btcutil.AddressScriptHash from the
|
||||||
|
// provided hash. It panics if an error occurs. This is only used in the tests
|
||||||
|
// as a helper since the only way it can fail is if there is an error in the
|
||||||
|
// test source code.
|
||||||
|
func newAddressScriptHash(scriptHash []byte) btcutil.Address {
|
||||||
|
addr, err := btcutil.NewAddressScriptHashFromHash(scriptHash,
|
||||||
|
&btcnet.MainNetParams)
|
||||||
|
if err != nil {
|
||||||
|
panic("invalid script hash in test source")
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExtractPkScriptAddrs ensures that extracting the type, addresses, and
|
||||||
|
// number of required signatures from PkScripts works as intended.
|
||||||
|
func TestExtractPkScriptAddrs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
script []byte
|
||||||
|
addrs []btcutil.Address
|
||||||
|
reqSigs int
|
||||||
|
class txscript.ScriptClass
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "standard p2pk with compressed pubkey (0x02)",
|
||||||
|
script: decodeHex("2102192d74d0cb94344c9569c2e7790157" +
|
||||||
|
"3d8d7903c3ebec3a957724895dca52c6b4ac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("02192d74d0cb94344" +
|
||||||
|
"c9569c2e77901573d8d7903c3ebec3a95772" +
|
||||||
|
"4895dca52c6b4")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard p2pk with uncompressed pubkey (0x04)",
|
||||||
|
script: decodeHex("410411db93e1dcdb8a016b49840f8c53bc" +
|
||||||
|
"1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb" +
|
||||||
|
"84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643" +
|
||||||
|
"f656b412a3ac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("0411db93e1dcdb8a0" +
|
||||||
|
"16b49840f8c53bc1eb68a382e97b1482ecad" +
|
||||||
|
"7b148a6909a5cb2e0eaddfb84ccf9744464f" +
|
||||||
|
"82e160bfa9b8b64f9d4c03f999b8643f656b" +
|
||||||
|
"412a3")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard p2pk with hybrid pubkey (0x06)",
|
||||||
|
script: decodeHex("4106192d74d0cb94344c9569c2e7790157" +
|
||||||
|
"3d8d7903c3ebec3a957724895dca52c6b40d45264838" +
|
||||||
|
"c0bd96852662ce6a847b197376830160c6d2eb5e6a4c" +
|
||||||
|
"44d33f453eac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("06192d74d0cb94344" +
|
||||||
|
"c9569c2e77901573d8d7903c3ebec3a95772" +
|
||||||
|
"4895dca52c6b40d45264838c0bd96852662c" +
|
||||||
|
"e6a847b197376830160c6d2eb5e6a4c44d33" +
|
||||||
|
"f453e")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard p2pk with compressed pubkey (0x03)",
|
||||||
|
script: decodeHex("2103b0bd634234abbb1ba1e986e884185c" +
|
||||||
|
"61cf43e001f9137f23c2c409273eb16e65ac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("03b0bd634234abbb1" +
|
||||||
|
"ba1e986e884185c61cf43e001f9137f23c2c" +
|
||||||
|
"409273eb16e65")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "2nd standard p2pk with uncompressed pubkey (0x04)",
|
||||||
|
script: decodeHex("4104b0bd634234abbb1ba1e986e884185c" +
|
||||||
|
"61cf43e001f9137f23c2c409273eb16e6537a576782e" +
|
||||||
|
"ba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c" +
|
||||||
|
"1e0908ef7bac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("04b0bd634234abbb1" +
|
||||||
|
"ba1e986e884185c61cf43e001f9137f23c2c" +
|
||||||
|
"409273eb16e6537a576782eba668a7ef8bd3" +
|
||||||
|
"b3cfb1edb7117ab65129b8a2e681f3c1e090" +
|
||||||
|
"8ef7b")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard p2pk with hybrid pubkey (0x07)",
|
||||||
|
script: decodeHex("4107b0bd634234abbb1ba1e986e884185c" +
|
||||||
|
"61cf43e001f9137f23c2c409273eb16e6537a576782e" +
|
||||||
|
"ba668a7ef8bd3b3cfb1edb7117ab65129b8a2e681f3c" +
|
||||||
|
"1e0908ef7bac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("07b0bd634234abbb1" +
|
||||||
|
"ba1e986e884185c61cf43e001f9137f23c2c" +
|
||||||
|
"409273eb16e6537a576782eba668a7ef8bd3" +
|
||||||
|
"b3cfb1edb7117ab65129b8a2e681f3c1e090" +
|
||||||
|
"8ef7b")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard p2pkh",
|
||||||
|
script: decodeHex("76a914ad06dd6ddee55cbca9a9e3713bd7" +
|
||||||
|
"587509a3056488ac"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKeyHash(decodeHex("ad06dd6ddee55" +
|
||||||
|
"cbca9a9e3713bd7587509a30564")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.PubKeyHashTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "standard p2sh",
|
||||||
|
script: decodeHex("a91463bcc565f9e68ee0189dd5cc67f1b0" +
|
||||||
|
"e5f02f45cb87"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressScriptHash(decodeHex("63bcc565f9e68" +
|
||||||
|
"ee0189dd5cc67f1b0e5f02f45cb")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.ScriptHashTy,
|
||||||
|
},
|
||||||
|
// from real tx 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1, vout 0
|
||||||
|
{
|
||||||
|
name: "standard 1 of 2 multisig",
|
||||||
|
script: decodeHex("514104cc71eb30d653c0c3163990c47b97" +
|
||||||
|
"6f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473" +
|
||||||
|
"e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11" +
|
||||||
|
"fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381" +
|
||||||
|
"354d80e550078cb532a34bfa2fcfdeb7d76519aecc62" +
|
||||||
|
"770f5b0e4ef8551946d8a540911abe3e7854a26f39f5" +
|
||||||
|
"8b25c15342af52ae"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("04cc71eb30d653c0c" +
|
||||||
|
"3163990c47b976f3fb3f37cccdcbedb169a1" +
|
||||||
|
"dfef58bbfbfaff7d8a473e7e2e6d317b87ba" +
|
||||||
|
"fe8bde97e3cf8f065dec022b51d11fcdd0d3" +
|
||||||
|
"48ac4")),
|
||||||
|
newAddressPubKey(decodeHex("0461cbdcc5409fb4b" +
|
||||||
|
"4d42b51d33381354d80e550078cb532a34bf" +
|
||||||
|
"a2fcfdeb7d76519aecc62770f5b0e4ef8551" +
|
||||||
|
"946d8a540911abe3e7854a26f39f58b25c15" +
|
||||||
|
"342af")),
|
||||||
|
},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.MultiSigTy,
|
||||||
|
},
|
||||||
|
// from real tx d646f82bd5fbdb94a36872ce460f97662b80c3050ad3209bef9d1e398ea277ab, vin 1
|
||||||
|
{
|
||||||
|
name: "standard 2 of 3 multisig",
|
||||||
|
script: decodeHex("524104cb9c3c222c5f7a7d3b9bd152f363" +
|
||||||
|
"a0b6d54c9eb312c4d4f9af1e8551b6c421a6a4ab0e29" +
|
||||||
|
"105f24de20ff463c1c91fcf3bf662cdde4783d4799f7" +
|
||||||
|
"87cb7c08869b4104ccc588420deeebea22a7e900cc8b" +
|
||||||
|
"68620d2212c374604e3487ca08f1ff3ae12bdc639514" +
|
||||||
|
"d0ec8612a2d3c519f084d9a00cbbe3b53d071e9b09e7" +
|
||||||
|
"1e610b036aa24104ab47ad1939edcb3db65f7fedea62" +
|
||||||
|
"bbf781c5410d3f22a7a3a56ffefb2238af8627363bdf" +
|
||||||
|
"2ed97c1f89784a1aecdb43384f11d2acc64443c7fc29" +
|
||||||
|
"9cef0400421a53ae"),
|
||||||
|
addrs: []btcutil.Address{
|
||||||
|
newAddressPubKey(decodeHex("04cb9c3c222c5f7a7" +
|
||||||
|
"d3b9bd152f363a0b6d54c9eb312c4d4f9af1" +
|
||||||
|
"e8551b6c421a6a4ab0e29105f24de20ff463" +
|
||||||
|
"c1c91fcf3bf662cdde4783d4799f787cb7c0" +
|
||||||
|
"8869b")),
|
||||||
|
newAddressPubKey(decodeHex("04ccc588420deeebe" +
|
||||||
|
"a22a7e900cc8b68620d2212c374604e3487c" +
|
||||||
|
"a08f1ff3ae12bdc639514d0ec8612a2d3c51" +
|
||||||
|
"9f084d9a00cbbe3b53d071e9b09e71e610b0" +
|
||||||
|
"36aa2")),
|
||||||
|
newAddressPubKey(decodeHex("04ab47ad1939edcb3" +
|
||||||
|
"db65f7fedea62bbf781c5410d3f22a7a3a56" +
|
||||||
|
"ffefb2238af8627363bdf2ed97c1f89784a1" +
|
||||||
|
"aecdb43384f11d2acc64443c7fc299cef040" +
|
||||||
|
"0421a")),
|
||||||
|
},
|
||||||
|
reqSigs: 2,
|
||||||
|
class: txscript.MultiSigTy,
|
||||||
|
},
|
||||||
|
|
||||||
|
// The below are nonstandard script due to things such as
|
||||||
|
// invalid pubkeys, failure to parse, and not being of a
|
||||||
|
// standard form.
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "p2pk with uncompressed pk missing OP_CHECKSIG",
|
||||||
|
script: decodeHex("410411db93e1dcdb8a016b49840f8c53bc" +
|
||||||
|
"1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb" +
|
||||||
|
"84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643" +
|
||||||
|
"f656b412a3"),
|
||||||
|
addrs: nil,
|
||||||
|
reqSigs: 0,
|
||||||
|
class: txscript.NonStandardTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid signature from a sigscript - no addresses",
|
||||||
|
script: decodeHex("47304402204e45e16932b8af514961a1d3" +
|
||||||
|
"a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220" +
|
||||||
|
"181522ec8eca07de4860a4acdd12909d831cc56cbbac" +
|
||||||
|
"4622082221a8768d1d0901"),
|
||||||
|
addrs: nil,
|
||||||
|
reqSigs: 0,
|
||||||
|
class: txscript.NonStandardTy,
|
||||||
|
},
|
||||||
|
// Note the technically the pubkey is the second item on the
|
||||||
|
// stack, but since the address extraction intentionally only
|
||||||
|
// works with standard PkScripts, this should not return any
|
||||||
|
// addresses.
|
||||||
|
{
|
||||||
|
name: "valid sigscript to reedeem p2pk - no addresses",
|
||||||
|
script: decodeHex("493046022100ddc69738bf2336318e4e04" +
|
||||||
|
"1a5a77f305da87428ab1606f023260017854350ddc02" +
|
||||||
|
"2100817af09d2eec36862d16009852b7e3a0f6dd7659" +
|
||||||
|
"8290b7834e1453660367e07a014104cd4240c198e125" +
|
||||||
|
"23b6f9cb9f5bed06de1ba37e96a1bbd13745fcf9d11c" +
|
||||||
|
"25b1dff9a519675d198804ba9962d3eca2d5937d58e5" +
|
||||||
|
"a75a71042d40388a4d307f887d"),
|
||||||
|
addrs: nil,
|
||||||
|
reqSigs: 0,
|
||||||
|
class: txscript.NonStandardTy,
|
||||||
|
},
|
||||||
|
// from real tx 691dd277dc0e90a462a3d652a1171686de49cf19067cd33c7df0392833fb986a, vout 0
|
||||||
|
// invalid public keys
|
||||||
|
{
|
||||||
|
name: "1 of 3 multisig with invalid pubkeys",
|
||||||
|
script: decodeHex("51411c2200007353455857696b696c6561" +
|
||||||
|
"6b73204361626c6567617465204261636b75700a0a63" +
|
||||||
|
"61626c65676174652d3230313031323034313831312e" +
|
||||||
|
"377a0a0a446f41776e6c6f61642074686520666f6c6c" +
|
||||||
|
"6f77696e67207472616e73616374696f6e7320776974" +
|
||||||
|
"68205361746f736869204e616b616d6f746f27732064" +
|
||||||
|
"6f776e6c6f61416420746f6f6c2077686963680a6361" +
|
||||||
|
"6e20626520666f756e6420696e207472616e73616374" +
|
||||||
|
"696f6e20366335336364393837313139656637393764" +
|
||||||
|
"35616463636453ae"),
|
||||||
|
addrs: []btcutil.Address{},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.MultiSigTy,
|
||||||
|
},
|
||||||
|
// from real tx: 691dd277dc0e90a462a3d652a1171686de49cf19067cd33c7df0392833fb986a, vout 44
|
||||||
|
// invalid public keys
|
||||||
|
{
|
||||||
|
name: "1 of 3 multisig with invalid pubkeys 2",
|
||||||
|
script: decodeHex("5141346333656332353963373464616365" +
|
||||||
|
"36666430383862343463656638630a63363662633139" +
|
||||||
|
"39366338623934613338313162333635363138666531" +
|
||||||
|
"65396231623541366361636365393933613339383861" +
|
||||||
|
"34363966636336643664616266640a32363633636661" +
|
||||||
|
"39636634633033633630396335393363336539316665" +
|
||||||
|
"64653730323921313233646434326432353633396433" +
|
||||||
|
"38613663663530616234636434340a00000053ae"),
|
||||||
|
addrs: []btcutil.Address{},
|
||||||
|
reqSigs: 1,
|
||||||
|
class: txscript.MultiSigTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty script",
|
||||||
|
script: []byte{},
|
||||||
|
addrs: nil,
|
||||||
|
reqSigs: 0,
|
||||||
|
class: txscript.NonStandardTy,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "script that does not parse",
|
||||||
|
script: []byte{txscript.OP_DATA_45},
|
||||||
|
addrs: nil,
|
||||||
|
reqSigs: 0,
|
||||||
|
class: txscript.NonStandardTy,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests.", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
class, addrs, reqSigs, err := txscript.ExtractPkScriptAddrs(
|
||||||
|
test.script, &btcnet.MainNetParams)
|
||||||
|
if err != nil {
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(addrs, test.addrs) {
|
||||||
|
t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+
|
||||||
|
"addresses\ngot %v\nwant %v", i, test.name,
|
||||||
|
addrs, test.addrs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqSigs != test.reqSigs {
|
||||||
|
t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+
|
||||||
|
"number of required signatures - got %d, "+
|
||||||
|
"want %d", i, test.name, reqSigs, test.reqSigs)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if class != test.class {
|
||||||
|
t.Errorf("ExtractPkScriptAddrs #%d (%s) unexpected "+
|
||||||
|
"script type - got %s, want %s", i, test.name,
|
||||||
|
class, test.class)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
txscript/data/LICENSE
Normal file
8
txscript/data/LICENSE
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
The json files in this directory come from the bitcoind project
|
||||||
|
(https://github.com/bitcoin/bitcoin) and is released under the following
|
||||||
|
license:
|
||||||
|
|
||||||
|
Copyright (c) 2012-2014 The Bitcoin Core developers
|
||||||
|
Distributed under the MIT/X11 software license, see the accompanying
|
||||||
|
file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
413
txscript/data/script_invalid.json
Normal file
413
txscript/data/script_invalid.json
Normal file
File diff suppressed because one or more lines are too long
540
txscript/data/script_valid.json
Normal file
540
txscript/data/script_valid.json
Normal file
File diff suppressed because one or more lines are too long
111
txscript/data/tx_invalid.json
Normal file
111
txscript/data/tx_invalid.json
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
[
|
||||||
|
["The following are deserialized transactions which are invalid."],
|
||||||
|
["They are in the form"],
|
||||||
|
["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"],
|
||||||
|
["serializedTransaction, verifyFlags]"],
|
||||||
|
["Objects that are only a single string (like this one) are ignored"],
|
||||||
|
|
||||||
|
["0e1b5688cf179cd9f7cbda1fac0090f6e684bbf8cd946660120197c3f3681809 but with extra junk appended to the end of the scriptPubKey"],
|
||||||
|
[[["6ca7ec7b1847f6bdbd737176050e6a08d66ccd55bb94ad24f4018024107a5827", 0, "0x41 0x043b640e983c9690a14c039a2037ecc3467b27a0dcd58f19d76c7bc118d09fec45adc5370a1c5bf8067ca9f5557a4cf885fdb0fe0dcc9c3a7137226106fbc779a5 CHECKSIG VERIFY 1"]],
|
||||||
|
"010000000127587a10248001f424ad94bb55cd6cd6086a0e05767173bdbdf647187beca76c000000004948304502201b822ad10d6adc1a341ae8835be3f70a25201bbff31f59cbb9c5353a5f0eca18022100ea7b2f7074e9aa9cf70aa8d0ffee13e6b45dddabf1ab961bda378bcdb778fa4701ffffffff0100f2052a010000001976a914fc50c5907d86fed474ba5ce8b12a66e0a4c139d888ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["This is the nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG from tx_valid.json"],
|
||||||
|
["but with the signature duplicated in the scriptPubKey with a non-standard pushdata prefix"],
|
||||||
|
["See FindAndDelete, which will only remove if it uses the same pushdata prefix as is standard"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Same as above, but with the sig in the scriptSig also pushed with the same non-standard OP_PUSHDATA"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x4c 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006b4c473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["An invalid P2SH Transaction"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
|
||||||
|
"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Tests for CheckTransaction()"],
|
||||||
|
["No inputs"],
|
||||||
|
["Skipped because this is not checked by btcscript, this is a problem for chain."],
|
||||||
|
|
||||||
|
["No outputs"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x05ab9e14d983742513f0f451e105ffb4198d1dd4 EQUAL"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022100f16703104aab4e4088317c862daec83440242411b039d14280e03dd33b487ab802201318a7be236672c5c56083eb7a5a195bc57a40af7923ff8545016cd3b571e2a601232103c40e5d339df3f30bf753e7e04450ae4ef76c9e45587d1d993bdc4cd06f0651c7acffffffff0000000000", "P2SH"],
|
||||||
|
|
||||||
|
["Negative output"],
|
||||||
|
["Removed because btcscript doesn't do tx sanity checking."],
|
||||||
|
|
||||||
|
["MAX_MONEY + 1 output"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010140075af0750700015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["MAX_MONEY output + 1 output"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510001000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Duplicate inputs"],
|
||||||
|
["Removed because btcscript doesn't check input duplication, btcchain does"],
|
||||||
|
|
||||||
|
["Coinbase of size 1"],
|
||||||
|
["Note the input is just required to make the tester happy"],
|
||||||
|
["Removed because btcscript doesn't handle coinbase checking, btcchain does"],
|
||||||
|
|
||||||
|
["Coinbase of size 101"],
|
||||||
|
["Note the input is just required to make the tester happy"],
|
||||||
|
["Removed because btcscript doesn't handle coinbase checking, btcchain does"],
|
||||||
|
|
||||||
|
["Null txin"],
|
||||||
|
["Removed because btcscript doesn't do tx sanity checking."],
|
||||||
|
|
||||||
|
["Same as the transactions in valid with one input SIGHASH_ALL and one SIGHASH_ANYONECANPAY, but we set the _ANYONECANPAY sequence number, invalidating the SIGHASH_ALL signature"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"],
|
||||||
|
["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]],
|
||||||
|
"01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df10101000000000200000000000000000000000000000000000000000000000000000000000000000000484730440220201dc2d030e380e8f9cfb41b442d930fa5a685bb2c8db5906671f865507d0670022018d9e7a8d4c8d86a73c2a724ee38ef983ec249827e0e464841735955c707ece98101000000010100000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["CHECKMULTISIG with incorrect signature order"],
|
||||||
|
["Note the input is just required to make the tester happy"],
|
||||||
|
[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]],
|
||||||
|
"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe000048304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f401483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
|
||||||
|
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
|
||||||
|
["It is an OP_CHECKMULTISIG with the dummy value missing"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
|
||||||
|
["CHECKMULTISIG SCRIPT_VERIFY_NULLDUMMY tests:"],
|
||||||
|
|
||||||
|
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
|
||||||
|
["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a010047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
|
||||||
|
|
||||||
|
["As above, but using a OP_1"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
|
||||||
|
|
||||||
|
["As above, but using a OP_1NEGATE"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
|
||||||
|
|
||||||
|
["As above, but with the dummy byte missing"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004847304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
|
||||||
|
|
||||||
|
|
||||||
|
["Empty stack when we try to run CHECKSIG"],
|
||||||
|
[[["ad503f72c18df5801ee64d76090afe4c607fb2b822e9b7b63c5826c50e22fc3b", 0, "0x21 0x027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5 CHECKSIG NOT"]],
|
||||||
|
"01000000013bfc220ec526583cb6b7e922b8b27f604cfe0a09764de61e80f58dc1723f50ad0000000000ffffffff0101000000000000002321027c3a97665bf283a102a587a62a30a0c102d4d3b141015e2cae6f64e2543113e5ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Inverted versions of tx_valid CODESEPARATOR IF block tests"],
|
||||||
|
|
||||||
|
["CODESEPARATOR in an unexecuted IF block does not change what is hashed"],
|
||||||
|
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
|
||||||
|
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0151ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["As above, with the IF block executed"],
|
||||||
|
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
|
||||||
|
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510100ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Make diffs cleaner by leaving a comment here without comma at the end"]
|
||||||
|
]
|
190
txscript/data/tx_valid.json
Normal file
190
txscript/data/tx_valid.json
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
[
|
||||||
|
["The following are deserialized transactions which are valid."],
|
||||||
|
["They are in the form"],
|
||||||
|
["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"],
|
||||||
|
["serializedTransaction, verifyFlags]"],
|
||||||
|
["Objects that are only a single string (like this one) are ignored"],
|
||||||
|
|
||||||
|
["The following is 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
|
||||||
|
["It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts"],
|
||||||
|
["See http://r6.ca/blog/20111119T211504Z.html"],
|
||||||
|
["It is also the first OP_CHECKMULTISIG transaction in standard form"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
|
||||||
|
["It is an OP_CHECKMULTISIG with an arbitrary extra byte stuffed into the signature at pos length - 2"],
|
||||||
|
["The dummy byte is fine however, so the NULLDUMMY flag should be happy"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH,NULLDUMMY"],
|
||||||
|
|
||||||
|
["The following is a tweaked form of 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"],
|
||||||
|
["It is an OP_CHECKMULTISIG with the dummy value set to something other than an empty string"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004a01ff47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["As above, but using a OP_1"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000495147304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["As above, but using a OP_1NEGATE"],
|
||||||
|
[[["60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1", 0, "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"]],
|
||||||
|
"0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000494f47304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["The following is c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"],
|
||||||
|
["It is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)"],
|
||||||
|
[[["406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602", 0, "DUP HASH160 0x14 0xdc44b1164188067c3a32d4780f5996fa14a4f2d9 EQUALVERIFY CHECKSIG"]],
|
||||||
|
"01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["A nearly-standard transaction with CHECKSIGVERIFY 1 instead of CHECKSIG"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Same as above, but with the signature duplicated in the scriptPubKey with the proper pushdata prefix"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0x5b6462475454710f3c22f5fdf0b40704c92f25c3 EQUALVERIFY CHECKSIGVERIFY 1 0x47 0x3044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a01"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006a473044022067288ea50aa799543a536ff9306f8e1cba05b9c6b10951175b924f96732555ed022026d7b5265f38d21541519e4a1e55044d5b9e17e15cdbaf29ae3792e99e883e7a012103ba8c8b86dea131c22ab967e6dd99bdae8eff7a1f75a2c35f1f944109e3fe5e22ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["The following is f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"],
|
||||||
|
["It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation"],
|
||||||
|
[[["b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d", 0, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"],
|
||||||
|
["b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21", 1, "DUP HASH160 0x14 0xbef80ecf3a44500fda1bc92176e442891662aed2 EQUALVERIFY CHECKSIG"]],
|
||||||
|
"01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["The following tests for the presence of a bug in the handling of SIGHASH_SINGLE"],
|
||||||
|
["It results in signing the constant 1, instead of something generated based on the transaction,"],
|
||||||
|
["when the input doing the signing has an index greater than the maximum output index"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "DUP HASH160 0x14 0xe52b482f2faa8ecbf0db344f93c84ac908557f33 EQUALVERIFY CHECKSIG"], ["0000000000000000000000000000000000000000000000000000000000000200", 0, "1"]],
|
||||||
|
"01000000020002000000000000000000000000000000000000000000000000000000000000000000000151ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["An invalid P2SH Transaction"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x7a052c840ba73af26755de42cf01cc9e0a49fef0 EQUAL"]],
|
||||||
|
"010000000100010000000000000000000000000000000000000000000000000000000000000000000009085768617420697320ffffffff010000000000000000015100000000", "NONE"],
|
||||||
|
|
||||||
|
["A valid P2SH Transaction using the standard transaction type put forth in BIP 16"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x8febbed40483661de6958d957412f82deed8e2f7 EQUAL"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Tests for CheckTransaction()"],
|
||||||
|
["MAX_MONEY output"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0x32afac281462b822adbec5094b8d4d337dd5bd6a EQUAL"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["MAX_MONEY output + 0 output"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "HASH160 0x14 0xb558cbf4930954aa6a344363a15668d7477ae716 EQUAL"]],
|
||||||
|
"01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Coinbase of size 2"],
|
||||||
|
["Note the input is just required to make the tester happy"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
|
||||||
|
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025151ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Coinbase of size 100"],
|
||||||
|
["Note the input is just required to make the tester happy"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000000", -1, "1"]],
|
||||||
|
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6451515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151515151ffffffff010000000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"],
|
||||||
|
["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]],
|
||||||
|
"010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed"],
|
||||||
|
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"],
|
||||||
|
["0000000000000000000000000000000000000000000000000000000000000200", 0, "0x21 0x035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efc CHECKSIG"]],
|
||||||
|
"01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000", "P2SH"],
|
||||||
|
|
||||||
|
["afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae which has several SIGHASH_SINGLE signatures"],
|
||||||
|
[[["63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70", 0, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"],
|
||||||
|
["04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"],
|
||||||
|
["ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f", 1, "DUP HASH160 0x14 0xdcf72c4fd02f5a987cf9b02f2fabfcac3341a87d EQUALVERIFY CHECKSIG"]],
|
||||||
|
"010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["ddc454a1c0c35c188c98976b17670f69e586d9c0f3593ea879928332f0a069e7, which spends an input that pushes using a PUSHDATA1 that is negative when read as signed"],
|
||||||
|
[[["c5510a5dd97a25f43175af1fe649b707b1df8e1a41489bac33a23087027a2f48", 0, "0x4c 0xae 0x606563686f2022553246736447566b58312b5a536e587574356542793066794778625456415675534a6c376a6a334878416945325364667657734f53474f36633338584d7439435c6e543249584967306a486956304f376e775236644546673d3d22203e20743b206f70656e73736c20656e63202d7061737320706173733a5b314a564d7751432d707269766b65792d6865785d202d64202d6165732d3235362d636263202d61202d696e207460 DROP DUP HASH160 0x14 0xbfd7436b6265aa9de506f8a994f881ff08cc2872 EQUALVERIFY CHECKSIG"]],
|
||||||
|
"0100000001482f7a028730a233ac9b48411a8edfb107b749e61faf7531f4257ad95d0a51c5000000008b483045022100bf0bbae9bde51ad2b222e87fbf67530fbafc25c903519a1e5dcc52a32ff5844e022028c4d9ad49b006dd59974372a54291d5764be541574bb0c4dc208ec51f80b7190141049dd4aad62741dc27d5f267f7b70682eee22e7e9c1923b9c0957bdae0b96374569b460eb8d5b40d972e8c7c0ad441de3d94c4a29864b212d56050acb980b72b2bffffffff0180969800000000001976a914e336d0017a9d28de99d16472f6ca6d5a3a8ebc9988ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Correct signature order"],
|
||||||
|
["Note the input is just required to make the tester happy"],
|
||||||
|
[[["b3da01dd4aae683c7aee4d5d8b52a540a508e1115f77cd7fa9a291243f501223", 0, "HASH160 0x14 0xb1ce99298d5f07364b57b1e5c9cc00be0b04a954 EQUAL"]],
|
||||||
|
"01000000012312503f2491a2a97fcd775f11e108a540a5528b5d4dee7a3c68ae4add01dab300000000fdfe0000483045022100f6649b0eddfdfd4ad55426663385090d51ee86c3481bdc6b0c18ea6c0ece2c0b0220561c315b07cffa6f7dd9df96dbae9200c2dee09bf93cc35ca05e6cdf613340aa0148304502207aacee820e08b0b174e248abd8d7a34ed63b5da3abedb99934df9fddd65c05c4022100dfe87896ab5ee3df476c2655f9fbe5bd089dccbef3e4ea05b5d121169fe7f5f4014c695221031d11db38972b712a9fe1fc023577c7ae3ddb4a3004187d41c45121eecfdbb5b7210207ec36911b6ad2382860d32989c7b8728e9489d7bbc94a6b5509ef0029be128821024ea9fac06f666a4adc3fc1357b7bec1fd0bdece2b9d08579226a8ebde53058e453aeffffffff0180380100000000001976a914c9b99cddf847d10685a4fabaa0baf505f7c3dfab88ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["cc60b1f899ec0a69b7c3f25ddf32c4524096a9c5b01cbd84c6d0312a0c478984, which is a fairly strange transaction which relies on OP_CHECKSIG returning 0 when checking a completely invalid sig of length 0"],
|
||||||
|
[[["cbebc4da731e8995fe97f6fadcd731b36ad40e5ecb31e38e904f6e5982fa09f7", 0, "0x2102085c6600657566acc2d6382a47bc3f324008d2aa10940dd7705a48aa2a5a5e33ac7c2103f5d0fb955f95dd6be6115ce85661db412ec6a08abcbfce7da0ba8297c6cc0ec4ac7c5379a820d68df9e32a147cffa36193c6f7c43a1c8c69cda530e1c6db354bfabdcfefaf3c875379a820f531f3041d3136701ea09067c53e7159c8f9b2746a56c3d82966c54bbc553226879a5479827701200122a59a5379827701200122a59a6353798277537982778779679a68"]],
|
||||||
|
"0100000001f709fa82596e4f908ee331cb5e0ed46ab331d7dcfaf697fe95891e73dac4ebcb000000008c20ca42095840735e89283fec298e62ac2ddea9b5f34a8cbb7097ad965b87568100201b1b01dc829177da4a14551d2fc96a9db00c6501edfa12f22cd9cefd335c227f483045022100a9df60536df5733dd0de6bc921fab0b3eee6426501b43a228afa2c90072eb5ca02201c78b74266fac7d1db5deff080d8a403743203f109fbcabf6d5a760bf87386d20100ffffffff01c075790000000000232103611f9a45c18f28f06f19076ad571c344c82ce8fcfe34464cf8085217a2d294a6ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Empty pubkey"],
|
||||||
|
[[["229257c295e7f555421c1bfec8538dd30a4b5c37c1c8810bbe83cafa7811652c", 0, "0x00 CHECKSIG NOT"]],
|
||||||
|
"01000000012c651178faca83be0b81c8c1375c4b0ad38d53c8fe1b1c4255f5e795c25792220000000049483045022100d6044562284ac76c985018fc4a90127847708c9edb280996c507b28babdc4b2a02203d74eca3f1a4d1eea7ff77b528fde6d5dc324ec2dbfdb964ba885f643b9704cd01ffffffff010100000000000000232102c2410f8891ae918cab4ffc4bb4a3b0881be67c7a1e7faa8b5acf9ab8932ec30cac00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Empty signature"],
|
||||||
|
[[["9ca93cfd8e3806b9d9e2ba1cf64e3cc6946ee0119670b1796a09928d14ea25f7", 0, "0x21 0x028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02 CHECKSIG NOT"]],
|
||||||
|
"0100000001f725ea148d92096a79b1709611e06e94c63c4ef61cbae2d9b906388efd3ca99c000000000100ffffffff0101000000000000002321028a1d66975dbdf97897e3a4aef450ebeb5b5293e4a0b4a6d3a2daaa0b2b110e02ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
[[["444e00ed7840d41f20ecd9c11d3f91982326c731a02f3c05748414a4fa9e59be", 0, "1 0x00 0x21 0x02136b04758b0b6e363e7a6fbe83aaf527a153db2b060d36cc29f7f8309ba6e458 2 CHECKMULTISIG"]],
|
||||||
|
"0100000001be599efaa4148474053c2fa031c7262398913f1dc1d9ec201fd44078ed004e44000000004900473044022022b29706cb2ed9ef0cb3c97b72677ca2dfd7b4160f7b4beb3ba806aa856c401502202d1e52582412eba2ed474f1f437a427640306fd3838725fab173ade7fe4eae4a01ffffffff010100000000000000232103ac4bba7e7ca3e873eea49e08132ad30c7f03640b6539e9b59903cf14fd016bbbac00000000", "P2SH"],
|
||||||
|
|
||||||
|
[[["e16abbe80bf30c080f63830c8dbf669deaef08957446e95940227d8c5e6db612", 0, "1 0x21 0x03905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9f 0x00 2 CHECKMULTISIG"]],
|
||||||
|
"010000000112b66d5e8c7d224059e946749508efea9d66bf8d0c83630f080cf30be8bb6ae100000000490047304402206ffe3f14caf38ad5c1544428e99da76ffa5455675ec8d9780fac215ca17953520220779502985e194d84baa36b9bd40a0dbd981163fa191eb884ae83fc5bd1c86b1101ffffffff010100000000000000232103905380c7013e36e6e19d305311c1b81fce6581f5ee1c86ef0627c68c9362fc9fac00000000", "P2SH"],
|
||||||
|
|
||||||
|
[[["ebbcf4bfce13292bd791d6a65a2a858d59adbf737e387e40370d4e64cc70efb0", 0, "2 0x21 0x033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194 0x21 0x03a88b326f8767f4f192ce252afe33c94d25ab1d24f27f159b3cb3aa691ffe1423 2 CHECKMULTISIG NOT"]],
|
||||||
|
"0100000001b0ef70cc644e0d37407e387e73bfad598d852a5aa6d691d72b2913cebff4bceb000000004a00473044022068cd4851fc7f9a892ab910df7a24e616f293bcb5c5fbdfbc304a194b26b60fba022078e6da13d8cb881a22939b952c24f88b97afd06b4c47a47d7f804c9a352a6d6d0100ffffffff0101000000000000002321033bcaa0a602f0d44cc9d5637c6e515b0471db514c020883830b7cefd73af04194ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
[[["ba4cd7ae2ad4d4d13ebfc8ab1d93a63e4a6563f25089a18bf0fc68f282aa88c1", 0, "2 0x21 0x037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1 0x21 0x02edc823cd634f2c4033d94f5755207cb6b60c4b1f1f056ad7471c47de5f2e4d50 2 CHECKMULTISIG NOT"]],
|
||||||
|
"0100000001c188aa82f268fcf08ba18950f263654a3ea6931dabc8bf3ed1d4d42aaed74cba000000004b0000483045022100940378576e069aca261a6b26fb38344e4497ca6751bb10905c76bb689f4222b002204833806b014c26fd801727b792b1260003c55710f87c5adbd7a9cb57446dbc9801ffffffff0101000000000000002321037c615d761e71d38903609bf4f46847266edc2fb37532047d747ba47eaae5ffe1ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
|
||||||
|
["OP_CODESEPARATOR tests"],
|
||||||
|
|
||||||
|
["Test that SignatureHash() removes OP_CODESEPARATOR with FindAndDelete()"],
|
||||||
|
[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
|
||||||
|
"01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]],
|
||||||
|
"01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Hashed data starts at the CODESEPARATOR"],
|
||||||
|
[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]],
|
||||||
|
"01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["But only if execution has reached it"],
|
||||||
|
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]],
|
||||||
|
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["CODESEPARATOR in an unexecuted IF block does not change what is hashed"],
|
||||||
|
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
|
||||||
|
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["As above, with the IF block executed"],
|
||||||
|
[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]],
|
||||||
|
"010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["CHECKSIG is legal in scriptSigs"],
|
||||||
|
[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
|
||||||
|
"0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Same semantics for OP_CODESEPARATOR"],
|
||||||
|
[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
|
||||||
|
"01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Signatures are removed from the script they are in by FindAndDelete() in the CHECKSIG code; even multiple instances of one signature can be removed."],
|
||||||
|
[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
|
||||||
|
"01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["That also includes ahead of the opcode being executed."],
|
||||||
|
[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]],
|
||||||
|
"01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH"],
|
||||||
|
|
||||||
|
["Finally CHECKMULTISIG removes all signatures prior to hashing the script containing those signatures. In conjunction with the SIGHASH_SINGLE bug this lets us test whether or not FindAndDelete() is actually present in scriptPubKey/redeemScript evaluation by including a signature of the digest 0x01 We can compute in advance for our pubkey, embed it it in the scriptPubKey, and then also using a normal SIGHASH_ALL signature. If FindAndDelete() wasn't run, the 'bugged' signature would still be in the hashed script, and the normal signature would fail."],
|
||||||
|
|
||||||
|
["Here's an example on mainnet within a P2SH redeemScript. Remarkably it's a standard transaction in <0.9"],
|
||||||
|
[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
|
||||||
|
["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]],
|
||||||
|
"0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH"],
|
||||||
|
|
||||||
|
["Same idea, but with bare CHECKMULTISIG"],
|
||||||
|
[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"],
|
||||||
|
["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]],
|
||||||
|
"0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH"],
|
||||||
|
|
||||||
|
|
||||||
|
["Make diffs cleaner by leaving a comment here without comma at the end"]
|
||||||
|
]
|
39
txscript/doc.go
Normal file
39
txscript/doc.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package txscript implements the bitcoin transaction script language.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Errors
|
||||||
|
|
||||||
|
Errors returned by this package are of the form txscript.ErrStackX where X
|
||||||
|
indicates the specific error. See Variables in the package documentation for a
|
||||||
|
full list.
|
||||||
|
*/
|
||||||
|
package txscript
|
77
txscript/example_test.go
Normal file
77
txscript/example_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (c) 2014-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
"github.com/btcsuite/btcnet"
|
||||||
|
"github.com/btcsuite/btcutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This example demonstrates creating a script which pays to a bitcoin address.
|
||||||
|
// It also prints the created script hex and uses the DisasmString function to
|
||||||
|
// display the disassembled script.
|
||||||
|
func ExamplePayToAddrScript() {
|
||||||
|
// Parse the address to send the coins to into a btcutil.Address
|
||||||
|
// which is useful to ensure the accuracy of the address and determine
|
||||||
|
// the address type. It is also required for the upcoming call to
|
||||||
|
// PayToAddrScript.
|
||||||
|
addressStr := "12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV"
|
||||||
|
address, err := btcutil.DecodeAddress(addressStr, &btcnet.MainNetParams)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a public key script that pays to the address.
|
||||||
|
script, err := txscript.PayToAddrScript(address)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("Script Hex: %x\n", script)
|
||||||
|
|
||||||
|
disasm, err := txscript.DisasmString(script)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Script Disassembly:", disasm)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Script Hex: 76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac
|
||||||
|
// Script Disassembly: OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG
|
||||||
|
}
|
||||||
|
|
||||||
|
// This example demonstrates extracting information from a standard public key
|
||||||
|
// script.
|
||||||
|
func ExampleExtractPkScriptAddrs() {
|
||||||
|
// Start with a standard pay-to-pubkey-hash script.
|
||||||
|
scriptHex := "76a914128004ff2fcaf13b2b91eb654b1dc2b674f7ec6188ac"
|
||||||
|
script, err := hex.DecodeString(scriptHex)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract and print details from the script.
|
||||||
|
scriptClass, addresses, reqSigs, err := txscript.ExtractPkScriptAddrs(
|
||||||
|
script, &btcnet.MainNetParams)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Script Class:", scriptClass)
|
||||||
|
fmt.Println("Addresses:", addresses)
|
||||||
|
fmt.Println("Required Signatures:", reqSigs)
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// Script Class: pubkeyhash
|
||||||
|
// Addresses: [12gpXQVcCL2qhTNQgyLVdCFG2Qs2px98nV]
|
||||||
|
// Required Signatures: 1
|
||||||
|
}
|
4265
txscript/internal_test.go
Normal file
4265
txscript/internal_test.go
Normal file
File diff suppressed because it is too large
Load diff
71
txscript/log.go
Normal file
71
txscript/log.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 btclog.Logger
|
||||||
|
|
||||||
|
// 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 = btclog.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 btclog.
|
||||||
|
func UseLogger(logger btclog.Logger) {
|
||||||
|
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 btclog, UseLogger should
|
||||||
|
// be used instead.
|
||||||
|
func SetLogWriter(w io.Writer, level string) error {
|
||||||
|
if w == nil {
|
||||||
|
return errors.New("nil writer")
|
||||||
|
}
|
||||||
|
|
||||||
|
lvl, ok := btclog.LogLevelFromString(level)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid log level")
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := btclog.NewLoggerFromWriter(w, lvl)
|
||||||
|
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)
|
||||||
|
}
|
66
txscript/log_test.go
Normal file
66
txscript/log_test.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetLogWriter(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
w io.Writer
|
||||||
|
level string
|
||||||
|
expected error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil writer",
|
||||||
|
w: nil,
|
||||||
|
level: "trace",
|
||||||
|
expected: errors.New("nil writer"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid log level",
|
||||||
|
w: os.Stdout,
|
||||||
|
level: "wrong",
|
||||||
|
expected: errors.New("invalid log level"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "use off level",
|
||||||
|
w: os.Stdout,
|
||||||
|
level: "off",
|
||||||
|
expected: errors.New("min level can't be greater than max. Got min: 6, max: 5"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pass",
|
||||||
|
w: os.Stdout,
|
||||||
|
level: "debug",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
err := txscript.SetLogWriter(test.w, test.level)
|
||||||
|
if err != nil {
|
||||||
|
if err.Error() != test.expected.Error() {
|
||||||
|
t.Errorf("SetLogWriter #%d (%s) wrong result\n"+
|
||||||
|
"got: %v\nwant: %v", i, test.name, err,
|
||||||
|
test.expected)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if test.expected != nil {
|
||||||
|
t.Errorf("SetLogWriter #%d (%s) wrong result\n"+
|
||||||
|
"got: %v\nwant: %v", i, test.name, err,
|
||||||
|
test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1977
txscript/opcode.go
Normal file
1977
txscript/opcode.go
Normal file
File diff suppressed because it is too large
Load diff
4452
txscript/opcode_test.go
Normal file
4452
txscript/opcode_test.go
Normal file
File diff suppressed because it is too large
Load diff
1666
txscript/script.go
Normal file
1666
txscript/script.go
Normal file
File diff suppressed because it is too large
Load diff
4784
txscript/script_test.go
Normal file
4784
txscript/script_test.go
Normal file
File diff suppressed because it is too large
Load diff
285
txscript/scriptbuilder.go
Normal file
285
txscript/scriptbuilder.go
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// defaultScriptAlloc is the default size used for the backing array
|
||||||
|
// for a script being built by the ScriptBuilder. The array will
|
||||||
|
// dynamically grow as needed, but this figure is intended to provide
|
||||||
|
// enough space for vast majority of scripts without needing to grow the
|
||||||
|
// backing array multiple times.
|
||||||
|
defaultScriptAlloc = 500
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrScriptNotCanonical identifies a non-canonical script. The caller can use
|
||||||
|
// a type assertion to detect this error type.
|
||||||
|
type ErrScriptNotCanonical string
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e ErrScriptNotCanonical) Error() string {
|
||||||
|
return string(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScriptBuilder provides a facility for building custom scripts. It allows
|
||||||
|
// you to push opcodes, ints, and data while respecting canonical encoding. In
|
||||||
|
// general it does not ensure the script will execute correctly, however any
|
||||||
|
// data pushes which would exceed the maximum allowed script engine limits and
|
||||||
|
// are therefore guaranteed not to execute will not be pushed and will result in
|
||||||
|
// the Script function returning an error.
|
||||||
|
//
|
||||||
|
// For example, the following would build a 2-of-3 multisig script for usage in
|
||||||
|
// a pay-to-script-hash (although in this situation MultiSigScript() would be a
|
||||||
|
// better choice to generate the script):
|
||||||
|
// builder := txscript.NewScriptBuilder()
|
||||||
|
// builder.AddOp(txscript.OP_2).AddData(pubKey1).AddData(pubKey2)
|
||||||
|
// builder.AddData(pubKey3).AddOp(txscript.OP_3)
|
||||||
|
// builder.AddOp(txscript.OP_CHECKMULTISIG)
|
||||||
|
// script, err := builder.Script()
|
||||||
|
// if err != nil {
|
||||||
|
// // Handle the error.
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// fmt.Printf("Final multi-sig script: %x\n", script)
|
||||||
|
type ScriptBuilder struct {
|
||||||
|
script []byte
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOp pushes the passed opcode to the end of the script. The script will not
|
||||||
|
// be modified if pushing the opcode would cause the script to exceed the
|
||||||
|
// maximum allowed script engine size.
|
||||||
|
func (b *ScriptBuilder) AddOp(opcode byte) *ScriptBuilder {
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes that would cause the script to exceed the largest allowed
|
||||||
|
// script size would result in a non-canonical script.
|
||||||
|
if len(b.script)+1 > maxScriptSize {
|
||||||
|
str := fmt.Sprintf("adding an opcode would exceed the maximum "+
|
||||||
|
"allowed canonical script length of %d", maxScriptSize)
|
||||||
|
b.err = ErrScriptNotCanonical(str)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
b.script = append(b.script, opcode)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// canonicalDataSize returns the number of bytes the canonical encoding of the
|
||||||
|
// data will take.
|
||||||
|
func canonicalDataSize(data []byte) int {
|
||||||
|
dataLen := len(data)
|
||||||
|
|
||||||
|
// When the data consists of a single number that can be represented
|
||||||
|
// by one of the "small integer" opcodes, that opcode will be instead
|
||||||
|
// of a data push opcode followed by the number.
|
||||||
|
if dataLen == 0 {
|
||||||
|
return 1
|
||||||
|
} else if dataLen == 1 && data[0] <= 16 {
|
||||||
|
return 1
|
||||||
|
} else if dataLen == 1 && data[0] == 0x81 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataLen < OP_PUSHDATA1 {
|
||||||
|
return 1 + dataLen
|
||||||
|
} else if dataLen <= 0xff {
|
||||||
|
return 2 + dataLen
|
||||||
|
} else if dataLen <= 0xffff {
|
||||||
|
return 3 + dataLen
|
||||||
|
}
|
||||||
|
|
||||||
|
return 5 + dataLen
|
||||||
|
}
|
||||||
|
|
||||||
|
// addData is the internal function that actually pushes the passed data to the
|
||||||
|
// end of the script. It automatically chooses canonical opcodes depending on
|
||||||
|
// the length of the data. A zero length buffer will lead to a push of empty
|
||||||
|
// data onto the stack (OP_0). No data limits are enforced with this function.
|
||||||
|
func (b *ScriptBuilder) addData(data []byte) *ScriptBuilder {
|
||||||
|
dataLen := len(data)
|
||||||
|
|
||||||
|
// When the data consists of a single number that can be represented
|
||||||
|
// by one of the "small integer" opcodes, use that opcode instead of
|
||||||
|
// a data push opcode followed by the number.
|
||||||
|
if dataLen == 0 || dataLen == 1 && data[0] == 0 {
|
||||||
|
b.script = append(b.script, OP_0)
|
||||||
|
return b
|
||||||
|
} else if dataLen == 1 && data[0] <= 16 {
|
||||||
|
b.script = append(b.script, byte((OP_1-1)+data[0]))
|
||||||
|
return b
|
||||||
|
} else if dataLen == 1 && data[0] == 0x81 {
|
||||||
|
b.script = append(b.script, byte(OP_1NEGATE))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use one of the OP_DATA_# opcodes if the length of the data is small
|
||||||
|
// enough so the data push instruction is only a single byte.
|
||||||
|
// Otherwise, choose the smallest possible OP_PUSHDATA# opcode that
|
||||||
|
// can represent the length of the data.
|
||||||
|
if dataLen < OP_PUSHDATA1 {
|
||||||
|
b.script = append(b.script, byte((OP_DATA_1-1)+dataLen))
|
||||||
|
} else if dataLen <= 0xff {
|
||||||
|
b.script = append(b.script, OP_PUSHDATA1, byte(dataLen))
|
||||||
|
} else if dataLen <= 0xffff {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
binary.LittleEndian.PutUint16(buf, uint16(dataLen))
|
||||||
|
b.script = append(b.script, OP_PUSHDATA2)
|
||||||
|
b.script = append(b.script, buf...)
|
||||||
|
} else {
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
binary.LittleEndian.PutUint32(buf, uint32(dataLen))
|
||||||
|
b.script = append(b.script, OP_PUSHDATA4)
|
||||||
|
b.script = append(b.script, buf...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the actual data.
|
||||||
|
b.script = append(b.script, data...)
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFullData should not typically be used by ordinary users as it does not
|
||||||
|
// include the checks which prevent data pushes larger than the maximum allowed
|
||||||
|
// sizes which leads to scripts that can't be executed. This is provided for
|
||||||
|
// testing purposes such as regression tests where sizes are intentionally made
|
||||||
|
// larger than allowed.
|
||||||
|
//
|
||||||
|
// Use AddData instead.
|
||||||
|
func (b *ScriptBuilder) AddFullData(data []byte) *ScriptBuilder {
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.addData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddData pushes the passed data to the end of the script. It automatically
|
||||||
|
// chooses canonical opcodes depending on the length of the data. A zero length
|
||||||
|
// buffer will lead to a push of empty data onto the stack (OP_0) and any push
|
||||||
|
// of data greater than MaxScriptElementSize will not modify the script since
|
||||||
|
// that is not allowed by the script engine. Also, the script will not be
|
||||||
|
// modified if pushing the data would cause the script to exceed the maximum
|
||||||
|
// allowed script engine size.
|
||||||
|
func (b *ScriptBuilder) AddData(data []byte) *ScriptBuilder {
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes that would cause the script to exceed the largest allowed
|
||||||
|
// script size would result in a non-canonical script.
|
||||||
|
dataSize := canonicalDataSize(data)
|
||||||
|
if len(b.script)+dataSize > maxScriptSize {
|
||||||
|
str := fmt.Sprintf("adding %d bytes of data would exceed the "+
|
||||||
|
"maximum allowed canonical script length of %d",
|
||||||
|
dataSize, maxScriptSize)
|
||||||
|
b.err = ErrScriptNotCanonical(str)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes larger than the max script element size would result in a
|
||||||
|
// script that is not canonical.
|
||||||
|
dataLen := len(data)
|
||||||
|
if dataLen > MaxScriptElementSize {
|
||||||
|
str := fmt.Sprintf("adding a data element of %d bytes would "+
|
||||||
|
"exceed the maximum allowed script element size of %d",
|
||||||
|
dataLen, maxScriptSize)
|
||||||
|
b.err = ErrScriptNotCanonical(str)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.addData(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInt64 pushes the passed integer to the end of the script. The script will
|
||||||
|
// not be modified if pushing the data would cause the script to exceed the
|
||||||
|
// maximum allowed script engine size.
|
||||||
|
func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder {
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes that would cause the script to exceed the largest allowed
|
||||||
|
// script size would result in a non-canonical script.
|
||||||
|
if len(b.script)+1 > maxScriptSize {
|
||||||
|
str := fmt.Sprintf("adding an integer would exceed the "+
|
||||||
|
"maximum allow canonical script length of %d",
|
||||||
|
maxScriptSize)
|
||||||
|
b.err = ErrScriptNotCanonical(str)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path for small integers and OP_1NEGATE.
|
||||||
|
if val == 0 {
|
||||||
|
b.script = append(b.script, OP_0)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if val == -1 || (val >= 1 && val <= 16) {
|
||||||
|
b.script = append(b.script, byte((OP_1-1)+val))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.AddData(fromInt(new(big.Int).SetInt64(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUint64 pushes the passed integer to the end of the script. The script
|
||||||
|
// will not be modified if pushing the data would cause the script to
|
||||||
|
// exceed the maximum allowed script engine size.
|
||||||
|
func (b *ScriptBuilder) AddUint64(val uint64) *ScriptBuilder {
|
||||||
|
if b.err != nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes that would cause the script to exceed the largest allowed
|
||||||
|
// script size would result in a non-canonical script.
|
||||||
|
if len(b.script)+1 > maxScriptSize {
|
||||||
|
str := fmt.Sprintf("adding an unsigned integer would exceed "+
|
||||||
|
"the maximum allow canonical script length of %d",
|
||||||
|
maxScriptSize)
|
||||||
|
b.err = ErrScriptNotCanonical(str)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path for small integers.
|
||||||
|
if val == 0 {
|
||||||
|
b.script = append(b.script, OP_0)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if val >= 1 && val <= 16 {
|
||||||
|
b.script = append(b.script, byte((OP_1-1)+val))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.AddData(fromInt(new(big.Int).SetUint64(val)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the script so it has no content.
|
||||||
|
func (b *ScriptBuilder) Reset() *ScriptBuilder {
|
||||||
|
b.script = b.script[0:0]
|
||||||
|
b.err = nil
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script returns the currently built script. When any errors occured while
|
||||||
|
// building the script, the script will be returned up the point of the first
|
||||||
|
// error along with the error.
|
||||||
|
func (b *ScriptBuilder) Script() ([]byte, error) {
|
||||||
|
return b.script, b.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScriptBuilder returns a new instance of a script builder. See
|
||||||
|
// ScriptBuilder for details.
|
||||||
|
func NewScriptBuilder() *ScriptBuilder {
|
||||||
|
return &ScriptBuilder{
|
||||||
|
script: make([]byte, 0, defaultScriptAlloc),
|
||||||
|
}
|
||||||
|
}
|
475
txscript/scriptbuilder_test.go
Normal file
475
txscript/scriptbuilder_test.go
Normal file
|
@ -0,0 +1,475 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestScriptBuilderAddOp tests that pushing opcodes to a script via the
|
||||||
|
// ScriptBuilder API works as expected.
|
||||||
|
func TestScriptBuilderAddOp(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
opcodes []byte
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "push OP_0",
|
||||||
|
opcodes: []byte{txscript.OP_0},
|
||||||
|
expected: []byte{txscript.OP_0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push OP_1 OP_2",
|
||||||
|
opcodes: []byte{txscript.OP_1, txscript.OP_2},
|
||||||
|
expected: []byte{txscript.OP_1, txscript.OP_2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push OP_HASH160 OP_EQUAL",
|
||||||
|
opcodes: []byte{txscript.OP_HASH160, txscript.OP_EQUAL},
|
||||||
|
expected: []byte{txscript.OP_HASH160, txscript.OP_EQUAL},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
builder.Reset()
|
||||||
|
for _, opcode := range test.opcodes {
|
||||||
|
builder.AddOp(opcode)
|
||||||
|
}
|
||||||
|
result, err := builder.Script()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ScriptBuilder.AddOp #%d (%s) unexpected "+
|
||||||
|
"error: %v", i, test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, test.expected) {
|
||||||
|
t.Errorf("ScriptBuilder.AddOp #%d (%s) wrong result\n"+
|
||||||
|
"got: %x\nwant: %x", i, test.name, result,
|
||||||
|
test.expected)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestScriptBuilderAddInt64 tests that pushing signed integers to a script via
|
||||||
|
// the ScriptBuilder API works as expected.
|
||||||
|
func TestScriptBuilderAddInt64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
val int64
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{name: "push -1", val: -1, expected: []byte{txscript.OP_1NEGATE}},
|
||||||
|
{name: "push small int 0", val: 0, expected: []byte{txscript.OP_0}},
|
||||||
|
{name: "push small int 1", val: 1, expected: []byte{txscript.OP_1}},
|
||||||
|
{name: "push small int 2", val: 2, expected: []byte{txscript.OP_2}},
|
||||||
|
{name: "push small int 3", val: 3, expected: []byte{txscript.OP_3}},
|
||||||
|
{name: "push small int 4", val: 4, expected: []byte{txscript.OP_4}},
|
||||||
|
{name: "push small int 5", val: 5, expected: []byte{txscript.OP_5}},
|
||||||
|
{name: "push small int 6", val: 6, expected: []byte{txscript.OP_6}},
|
||||||
|
{name: "push small int 7", val: 7, expected: []byte{txscript.OP_7}},
|
||||||
|
{name: "push small int 8", val: 8, expected: []byte{txscript.OP_8}},
|
||||||
|
{name: "push small int 9", val: 9, expected: []byte{txscript.OP_9}},
|
||||||
|
{name: "push small int 10", val: 10, expected: []byte{txscript.OP_10}},
|
||||||
|
{name: "push small int 11", val: 11, expected: []byte{txscript.OP_11}},
|
||||||
|
{name: "push small int 12", val: 12, expected: []byte{txscript.OP_12}},
|
||||||
|
{name: "push small int 13", val: 13, expected: []byte{txscript.OP_13}},
|
||||||
|
{name: "push small int 14", val: 14, expected: []byte{txscript.OP_14}},
|
||||||
|
{name: "push small int 15", val: 15, expected: []byte{txscript.OP_15}},
|
||||||
|
{name: "push small int 16", val: 16, expected: []byte{txscript.OP_16}},
|
||||||
|
{name: "push 17", val: 17, expected: []byte{txscript.OP_DATA_1, 0x11}},
|
||||||
|
{name: "push 65", val: 65, expected: []byte{txscript.OP_DATA_1, 0x41}},
|
||||||
|
{name: "push 127", val: 127, expected: []byte{txscript.OP_DATA_1, 0x7f}},
|
||||||
|
{name: "push 128", val: 128, expected: []byte{txscript.OP_DATA_2, 0x80, 0}},
|
||||||
|
{name: "push 255", val: 255, expected: []byte{txscript.OP_DATA_2, 0xff, 0}},
|
||||||
|
{name: "push 256", val: 256, expected: []byte{txscript.OP_DATA_2, 0, 0x01}},
|
||||||
|
{name: "push 32767", val: 32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0x7f}},
|
||||||
|
{name: "push 32768", val: 32768, expected: []byte{txscript.OP_DATA_3, 0, 0x80, 0}},
|
||||||
|
{name: "push -2", val: -2, expected: []byte{txscript.OP_DATA_1, 0x82}},
|
||||||
|
{name: "push -3", val: -3, expected: []byte{txscript.OP_DATA_1, 0x83}},
|
||||||
|
{name: "push -4", val: -4, expected: []byte{txscript.OP_DATA_1, 0x84}},
|
||||||
|
{name: "push -5", val: -5, expected: []byte{txscript.OP_DATA_1, 0x85}},
|
||||||
|
{name: "push -17", val: -17, expected: []byte{txscript.OP_DATA_1, 0x91}},
|
||||||
|
{name: "push -65", val: -65, expected: []byte{txscript.OP_DATA_1, 0xc1}},
|
||||||
|
{name: "push -127", val: -127, expected: []byte{txscript.OP_DATA_1, 0xff}},
|
||||||
|
{name: "push -128", val: -128, expected: []byte{txscript.OP_DATA_2, 0x80, 0x80}},
|
||||||
|
{name: "push -255", val: -255, expected: []byte{txscript.OP_DATA_2, 0xff, 0x80}},
|
||||||
|
{name: "push -256", val: -256, expected: []byte{txscript.OP_DATA_2, 0x00, 0x81}},
|
||||||
|
{name: "push -32767", val: -32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0xff}},
|
||||||
|
{name: "push -32768", val: -32768, expected: []byte{txscript.OP_DATA_3, 0x00, 0x80, 0x80}},
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
builder.Reset().AddInt64(test.val)
|
||||||
|
result, err := builder.Script()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ScriptBuilder.AddInt64 #%d (%s) unexpected "+
|
||||||
|
"error: %v", i, test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, test.expected) {
|
||||||
|
t.Errorf("ScriptBuilder.AddInt64 #%d (%s) wrong result\n"+
|
||||||
|
"got: %x\nwant: %x", i, test.name, result,
|
||||||
|
test.expected)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestScriptBuilderAddUint64 tests that pushing unsigned integers to a script
|
||||||
|
// via the ScriptBuilder API works as expected.
|
||||||
|
func TestScriptBuilderAddUint64(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
val uint64
|
||||||
|
expected []byte
|
||||||
|
}{
|
||||||
|
{name: "push small int 0", val: 0, expected: []byte{txscript.OP_0}},
|
||||||
|
{name: "push small int 1", val: 1, expected: []byte{txscript.OP_1}},
|
||||||
|
{name: "push small int 2", val: 2, expected: []byte{txscript.OP_2}},
|
||||||
|
{name: "push small int 3", val: 3, expected: []byte{txscript.OP_3}},
|
||||||
|
{name: "push small int 4", val: 4, expected: []byte{txscript.OP_4}},
|
||||||
|
{name: "push small int 5", val: 5, expected: []byte{txscript.OP_5}},
|
||||||
|
{name: "push small int 6", val: 6, expected: []byte{txscript.OP_6}},
|
||||||
|
{name: "push small int 7", val: 7, expected: []byte{txscript.OP_7}},
|
||||||
|
{name: "push small int 8", val: 8, expected: []byte{txscript.OP_8}},
|
||||||
|
{name: "push small int 9", val: 9, expected: []byte{txscript.OP_9}},
|
||||||
|
{name: "push small int 10", val: 10, expected: []byte{txscript.OP_10}},
|
||||||
|
{name: "push small int 11", val: 11, expected: []byte{txscript.OP_11}},
|
||||||
|
{name: "push small int 12", val: 12, expected: []byte{txscript.OP_12}},
|
||||||
|
{name: "push small int 13", val: 13, expected: []byte{txscript.OP_13}},
|
||||||
|
{name: "push small int 14", val: 14, expected: []byte{txscript.OP_14}},
|
||||||
|
{name: "push small int 15", val: 15, expected: []byte{txscript.OP_15}},
|
||||||
|
{name: "push small int 16", val: 16, expected: []byte{txscript.OP_16}},
|
||||||
|
{name: "push 17", val: 17, expected: []byte{txscript.OP_DATA_1, 0x11}},
|
||||||
|
{name: "push 65", val: 65, expected: []byte{txscript.OP_DATA_1, 0x41}},
|
||||||
|
{name: "push 127", val: 127, expected: []byte{txscript.OP_DATA_1, 0x7f}},
|
||||||
|
{name: "push 128", val: 128, expected: []byte{txscript.OP_DATA_2, 0x80, 0}},
|
||||||
|
{name: "push 255", val: 255, expected: []byte{txscript.OP_DATA_2, 0xff, 0}},
|
||||||
|
{name: "push 256", val: 256, expected: []byte{txscript.OP_DATA_2, 0, 0x01}},
|
||||||
|
{name: "push 32767", val: 32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0x7f}},
|
||||||
|
{name: "push 32768", val: 32768, expected: []byte{txscript.OP_DATA_3, 0, 0x80, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
builder.Reset().AddUint64(test.val)
|
||||||
|
result, err := builder.Script()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) unexpected "+
|
||||||
|
"error: %v", i, test.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, test.expected) {
|
||||||
|
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) wrong result\n"+
|
||||||
|
"got: %x\nwant: %x", i, test.name, result,
|
||||||
|
test.expected)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestScriptBuilderAddData tests that pushing data to a script via the
|
||||||
|
// ScriptBuilder API works as expected and conforms to BIP0062.
|
||||||
|
func TestScriptBuilderAddData(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
expected []byte
|
||||||
|
useFull bool // use AddFullData instead of AddData.
|
||||||
|
}{
|
||||||
|
// BIP0062: Pushing an empty byte sequence must use OP_0.
|
||||||
|
{name: "push empty byte sequence", data: []byte{}, expected: []byte{txscript.OP_0}},
|
||||||
|
{name: "push 1 byte 0x00", data: []byte{0x00}, expected: []byte{txscript.OP_0}},
|
||||||
|
|
||||||
|
// BIP0062: Pushing a 1-byte sequence of byte 0x01 through 0x10 must use OP_n.
|
||||||
|
{name: "push 1 byte 0x01", data: []byte{0x01}, expected: []byte{txscript.OP_1}},
|
||||||
|
{name: "push 1 byte 0x02", data: []byte{0x02}, expected: []byte{txscript.OP_2}},
|
||||||
|
{name: "push 1 byte 0x03", data: []byte{0x03}, expected: []byte{txscript.OP_3}},
|
||||||
|
{name: "push 1 byte 0x04", data: []byte{0x04}, expected: []byte{txscript.OP_4}},
|
||||||
|
{name: "push 1 byte 0x05", data: []byte{0x05}, expected: []byte{txscript.OP_5}},
|
||||||
|
{name: "push 1 byte 0x06", data: []byte{0x06}, expected: []byte{txscript.OP_6}},
|
||||||
|
{name: "push 1 byte 0x07", data: []byte{0x07}, expected: []byte{txscript.OP_7}},
|
||||||
|
{name: "push 1 byte 0x08", data: []byte{0x08}, expected: []byte{txscript.OP_8}},
|
||||||
|
{name: "push 1 byte 0x09", data: []byte{0x09}, expected: []byte{txscript.OP_9}},
|
||||||
|
{name: "push 1 byte 0x0a", data: []byte{0x0a}, expected: []byte{txscript.OP_10}},
|
||||||
|
{name: "push 1 byte 0x0b", data: []byte{0x0b}, expected: []byte{txscript.OP_11}},
|
||||||
|
{name: "push 1 byte 0x0c", data: []byte{0x0c}, expected: []byte{txscript.OP_12}},
|
||||||
|
{name: "push 1 byte 0x0d", data: []byte{0x0d}, expected: []byte{txscript.OP_13}},
|
||||||
|
{name: "push 1 byte 0x0e", data: []byte{0x0e}, expected: []byte{txscript.OP_14}},
|
||||||
|
{name: "push 1 byte 0x0f", data: []byte{0x0f}, expected: []byte{txscript.OP_15}},
|
||||||
|
{name: "push 1 byte 0x10", data: []byte{0x10}, expected: []byte{txscript.OP_16}},
|
||||||
|
|
||||||
|
// BIP0062: Pushing the byte 0x81 must use OP_1NEGATE.
|
||||||
|
{name: "push 1 byte 0x81", data: []byte{0x81}, expected: []byte{txscript.OP_1NEGATE}},
|
||||||
|
|
||||||
|
// BIP0062: Pushing any other byte sequence up to 75 bytes must
|
||||||
|
// use the normal data push (opcode byte n, with n the number of
|
||||||
|
// bytes, followed n bytes of data being pushed).
|
||||||
|
{name: "push 1 byte 0x11", data: []byte{0x11}, expected: []byte{txscript.OP_DATA_1, 0x11}},
|
||||||
|
{name: "push 1 byte 0x80", data: []byte{0x80}, expected: []byte{txscript.OP_DATA_1, 0x80}},
|
||||||
|
{name: "push 1 byte 0x82", data: []byte{0x82}, expected: []byte{txscript.OP_DATA_1, 0x82}},
|
||||||
|
{name: "push 1 byte 0xff", data: []byte{0xff}, expected: []byte{txscript.OP_DATA_1, 0xff}},
|
||||||
|
{
|
||||||
|
name: "push data len 17",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 17),
|
||||||
|
expected: append([]byte{txscript.OP_DATA_17}, bytes.Repeat([]byte{0x49}, 17)...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push data len 75",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 75),
|
||||||
|
expected: append([]byte{txscript.OP_DATA_75}, bytes.Repeat([]byte{0x49}, 75)...),
|
||||||
|
},
|
||||||
|
|
||||||
|
// BIP0062: Pushing 76 to 255 bytes must use OP_PUSHDATA1.
|
||||||
|
{
|
||||||
|
name: "push data len 76",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 76),
|
||||||
|
expected: append([]byte{txscript.OP_PUSHDATA1, 76}, bytes.Repeat([]byte{0x49}, 76)...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push data len 255",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 255),
|
||||||
|
expected: append([]byte{txscript.OP_PUSHDATA1, 255}, bytes.Repeat([]byte{0x49}, 255)...),
|
||||||
|
},
|
||||||
|
|
||||||
|
// BIP0062: Pushing 256 to 520 bytes must use OP_PUSHDATA2.
|
||||||
|
{
|
||||||
|
name: "push data len 256",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 256),
|
||||||
|
expected: append([]byte{txscript.OP_PUSHDATA2, 0, 1}, bytes.Repeat([]byte{0x49}, 256)...),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push data len 520",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 520),
|
||||||
|
expected: append([]byte{txscript.OP_PUSHDATA2, 0x08, 0x02}, bytes.Repeat([]byte{0x49}, 520)...),
|
||||||
|
},
|
||||||
|
|
||||||
|
// BIP0062: OP_PUSHDATA4 can never be used, as pushes over 520
|
||||||
|
// bytes are not allowed, and those below can be done using
|
||||||
|
// other operators.
|
||||||
|
{
|
||||||
|
name: "push data len 521",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 521),
|
||||||
|
expected: []byte{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push data len 32767 (canonical)",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 32767),
|
||||||
|
expected: []byte{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "push data len 65536 (canonical)",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 65536),
|
||||||
|
expected: []byte{},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Additional tests for the PushFullData function that
|
||||||
|
// intentionally allows data pushes to exceed the limit for
|
||||||
|
// regression testing purposes.
|
||||||
|
|
||||||
|
// 3-byte data push via OP_PUSHDATA_2.
|
||||||
|
{
|
||||||
|
name: "push data len 32767 (non-canonical)",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 32767),
|
||||||
|
expected: append([]byte{txscript.OP_PUSHDATA2, 255, 127}, bytes.Repeat([]byte{0x49}, 32767)...),
|
||||||
|
useFull: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// 5-byte data push via OP_PUSHDATA_4.
|
||||||
|
{
|
||||||
|
name: "push data len 65536 (non-canonical)",
|
||||||
|
data: bytes.Repeat([]byte{0x49}, 65536),
|
||||||
|
expected: append([]byte{txscript.OP_PUSHDATA4, 0, 0, 1, 0}, bytes.Repeat([]byte{0x49}, 65536)...),
|
||||||
|
useFull: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
t.Logf("Running %d tests", len(tests))
|
||||||
|
for i, test := range tests {
|
||||||
|
if !test.useFull {
|
||||||
|
builder.Reset().AddData(test.data)
|
||||||
|
} else {
|
||||||
|
builder.Reset().AddFullData(test.data)
|
||||||
|
}
|
||||||
|
result, _ := builder.Script()
|
||||||
|
if !bytes.Equal(result, test.expected) {
|
||||||
|
t.Errorf("ScriptBuilder.AddData #%d (%s) wrong result\n"+
|
||||||
|
"got: %x\nwant: %x", i, test.name, result,
|
||||||
|
test.expected)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestExceedMaxScriptSize ensures that all of the functions that can be used
|
||||||
|
// to add data to a script don't allow the script to exceed the max allowed
|
||||||
|
// size.
|
||||||
|
func TestExceedMaxScriptSize(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Start off by constructing a max size script.
|
||||||
|
maxScriptSize := txscript.TstMaxScriptSize
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
|
||||||
|
origScript, err := builder.Script()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error for max size script: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding data that would exceed the maximum size of the script
|
||||||
|
// does not add the data.
|
||||||
|
script, err := builder.AddData([]byte{0x00}).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+
|
||||||
|
"size: %v", len(script))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding an opcode that would exceed the maximum size of the
|
||||||
|
// script does not add the data.
|
||||||
|
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
|
||||||
|
script, err = builder.AddOp(txscript.OP_0).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding an integer that would exceed the maximum size of the
|
||||||
|
// script does not add the data.
|
||||||
|
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
|
||||||
|
script, err = builder.AddInt64(0).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding an unsigned integer that would exceed the maximum size
|
||||||
|
// of the script does not add the data.
|
||||||
|
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
|
||||||
|
script, err = builder.AddUint64(0).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestErroredScript ensures that all of the functions that can be used to add
|
||||||
|
// data to a script don't modify the script once an error has happened.
|
||||||
|
func TestErroredScript(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Start off by constructing a near max size script that has enough
|
||||||
|
// space left to add each data type without an error and force an
|
||||||
|
// initial error condition.
|
||||||
|
maxScriptSize := txscript.TstMaxScriptSize
|
||||||
|
builder := txscript.NewScriptBuilder()
|
||||||
|
builder.Reset().AddFullData(make([]byte, maxScriptSize-8))
|
||||||
|
origScript, err := builder.Script()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ScriptBuilder.AddFullData unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
script, err := builder.AddData([]byte{0x00, 0x00, 0x00, 0x00, 0x00}).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatalf("ScriptBuilder.AddData allowed exceeding max script "+
|
||||||
|
"size: %v", len(script))
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddData unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding data, even using the non-canonical path, to a script
|
||||||
|
// that has errored doesn't succeed.
|
||||||
|
script, err = builder.AddFullData([]byte{0x00}).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatal("ScriptBuilder.AddFullData succeeded on errored script")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddFullData unexpected modified "+
|
||||||
|
"script - got len %d, want len %d", len(script),
|
||||||
|
len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding data to a script that has errored doesn't succeed.
|
||||||
|
script, err = builder.AddData([]byte{0x00}).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatal("ScriptBuilder.AddData succeeded on errored script")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddData unexpected modified "+
|
||||||
|
"script - got len %d, want len %d", len(script),
|
||||||
|
len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding an opcode to a script that has errored doesn't succeed.
|
||||||
|
script, err = builder.AddOp(txscript.OP_0).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatal("ScriptBuilder.AddOp succeeded on errored script")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddOp unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding an integer to a script that has errored doesn't
|
||||||
|
// succeed.
|
||||||
|
script, err = builder.AddInt64(0).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatal("ScriptBuilder.AddInt64 succeeded on errored script")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure adding an unsigned integer to a script that has errored
|
||||||
|
// doesn't succeed.
|
||||||
|
script, err = builder.AddUint64(0).Script()
|
||||||
|
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
||||||
|
t.Fatal("ScriptBuilder.AddUint64 succeeded on errored script")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(script, origScript) {
|
||||||
|
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
||||||
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the error has a message set.
|
||||||
|
if err.Error() == "" {
|
||||||
|
t.Fatal("ErrScriptNotCanonical.Error does not have any text")
|
||||||
|
}
|
||||||
|
}
|
369
txscript/stack.go
Normal file
369
txscript/stack.go
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
// Copyright (c) 2013-2015 Conformal Systems LLC.
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package txscript
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"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, error) {
|
||||||
|
// Only 32bit numbers allowed.
|
||||||
|
if len(v) > 4 {
|
||||||
|
return nil, ErrStackNumberTooBig
|
||||||
|
}
|
||||||
|
if len(v) == 0 {
|
||||||
|
return big.NewInt(0), nil
|
||||||
|
}
|
||||||
|
negative := false
|
||||||
|
origlen := len(v)
|
||||||
|
msb := v[len(v)-1]
|
||||||
|
if msb&0x80 == 0x80 {
|
||||||
|
negative = true
|
||||||
|
// remove sign bit
|
||||||
|
msb &= 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]
|
||||||
|
}
|
||||||
|
// IFF the value is negative and no 0 bytes were trimmed,
|
||||||
|
// the leading byte needs to be sign corrected
|
||||||
|
if negative && len(intArray) == origlen {
|
||||||
|
intArray[0] &= 0x7f
|
||||||
|
}
|
||||||
|
|
||||||
|
num := new(big.Int).SetBytes(intArray)
|
||||||
|
if negative {
|
||||||
|
num = num.Neg(num)
|
||||||
|
}
|
||||||
|
return num, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, ErrStackUnderflow
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = ErrStackUnderflow
|
||||||
|
return
|
||||||
|
}
|
||||||
|
so = s.stk[sz-idx-1]
|
||||||
|
if idx == 0 {
|
||||||
|
s.stk = s.stk[:sz-1]
|
||||||
|
} else if idx == sz-1 {
|
||||||
|
s1 := make([][]byte, sz-1, sz-1)
|
||||||
|
copy(s1, s.stk[1:])
|
||||||
|
s.stk = s1
|
||||||
|
} 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 ErrStackInvalidArgs
|
||||||
|
}
|
||||||
|
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 ErrStackInvalidArgs
|
||||||
|
}
|
||||||
|
// 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 ErrStackInvalidArgs
|
||||||
|
}
|
||||||
|
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 ErrStackInvalidArgs
|
||||||
|
}
|
||||||
|
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 ErrStackInvalidArgs
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the stack in a readable format.
|
||||||
|
func (s *Stack) String() string {
|
||||||
|
var result string
|
||||||
|
|
||||||
|
for _, stack := range s.stk {
|
||||||
|
result += hex.Dump(stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
1005
txscript/stack_test.go
Normal file
1005
txscript/stack_test.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue