2018-06-29 16:58:31 +00:00
|
|
|
// Copyright (c) 2013-2017 The btcsuite developers
|
2019-03-13 06:11:06 +00:00
|
|
|
// Copyright (c) 2015-2019 The Decred developers
|
2013-06-12 21:35:27 +00:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2018-04-29 23:27:13 +00:00
|
|
|
package txscript
|
2013-06-12 21:35:27 +00:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
2015-05-02 19:56:55 +00:00
|
|
|
"reflect"
|
2014-07-03 00:37:49 +00:00
|
|
|
"testing"
|
2018-04-29 23:27:13 +00:00
|
|
|
)
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
// TestPushedData ensured the PushedData function extracts the expected data out
|
|
|
|
|
// of various scripts.
|
2014-05-08 17:14:01 +00:00
|
|
|
func TestPushedData(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2014-05-08 17:14:01 +00:00
|
|
|
var tests = []struct {
|
2015-05-02 19:56:55 +00:00
|
|
|
script string
|
|
|
|
|
out [][]byte
|
|
|
|
|
valid bool
|
2014-05-08 17:14:01 +00:00
|
|
|
}{
|
|
|
|
|
{
|
2015-05-02 19:56:55 +00:00
|
|
|
"0 IF 0 ELSE 2 ENDIF",
|
|
|
|
|
[][]byte{nil, nil},
|
2014-05-08 17:14:01 +00:00
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
2015-05-02 19:56:55 +00:00
|
|
|
"16777216 10000000",
|
2014-05-08 17:14:01 +00:00
|
|
|
[][]byte{
|
|
|
|
|
{0x00, 0x00, 0x00, 0x01}, // 16777216
|
|
|
|
|
{0x80, 0x96, 0x98, 0x00}, // 10000000
|
|
|
|
|
},
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
2015-05-02 19:56:55 +00:00
|
|
|
"DUP HASH160 '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem' EQUALVERIFY CHECKSIG",
|
2014-05-08 17:14:01 +00:00
|
|
|
[][]byte{
|
|
|
|
|
// 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem
|
|
|
|
|
{
|
|
|
|
|
0x31, 0x37, 0x56, 0x5a, 0x4e, 0x58, 0x31, 0x53, 0x4e, 0x35,
|
|
|
|
|
0x4e, 0x74, 0x4b, 0x61, 0x38, 0x55, 0x51, 0x46, 0x78, 0x77,
|
|
|
|
|
0x51, 0x62, 0x46, 0x65, 0x46, 0x63, 0x33, 0x69, 0x71, 0x52,
|
|
|
|
|
0x59, 0x68, 0x65, 0x6d,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
true,
|
|
|
|
|
},
|
|
|
|
|
{
|
2015-05-02 19:56:55 +00:00
|
|
|
"PUSHDATA4 1000 EQUAL",
|
|
|
|
|
nil,
|
2014-05-08 17:14:01 +00:00
|
|
|
false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
for i, test := range tests {
|
|
|
|
|
script := mustParseShortForm(test.script)
|
2018-04-29 23:27:13 +00:00
|
|
|
data, err := PushedData(script)
|
2014-05-08 17:14:01 +00:00
|
|
|
if test.valid && err != nil {
|
2015-05-02 19:56:55 +00:00
|
|
|
t.Errorf("TestPushedData failed test #%d: %v\n", i, err)
|
2014-05-08 17:14:01 +00:00
|
|
|
continue
|
|
|
|
|
} else if !test.valid && err == nil {
|
2015-05-02 19:56:55 +00:00
|
|
|
t.Errorf("TestPushedData failed test #%d: test should "+
|
|
|
|
|
"be invalid\n", i)
|
2014-05-08 17:14:01 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2015-05-02 19:56:55 +00:00
|
|
|
if !reflect.DeepEqual(data, test.out) {
|
|
|
|
|
t.Errorf("TestPushedData failed test #%d: want: %x "+
|
|
|
|
|
"got: %x\n", i, test.out, data)
|
2014-05-08 17:14:01 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 06:12:22 +00:00
|
|
|
// TestHasCanonicalPush ensures the isCanonicalPush function works as expected.
|
2015-05-02 19:56:55 +00:00
|
|
|
func TestHasCanonicalPush(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2019-03-13 06:12:22 +00:00
|
|
|
const scriptVersion = 0
|
2014-12-20 07:34:39 +00:00
|
|
|
for i := 0; i < 65535; i++ {
|
2018-04-29 23:27:13 +00:00
|
|
|
builder := NewScriptBuilder()
|
2014-02-20 17:42:06 +00:00
|
|
|
builder.AddInt64(int64(i))
|
2014-12-20 07:34:39 +00:00
|
|
|
script, err := builder.Script()
|
|
|
|
|
if err != nil {
|
2019-03-13 06:12:22 +00:00
|
|
|
t.Errorf("Script: test #%d unexpected error: %v\n", i, err)
|
2014-12-20 07:34:39 +00:00
|
|
|
continue
|
2014-02-20 05:05:35 +00:00
|
|
|
}
|
2019-03-13 06:12:22 +00:00
|
|
|
if !IsPushOnlyScript(script) {
|
|
|
|
|
t.Errorf("IsPushOnlyScript: test #%d failed: %x\n", i, script)
|
2014-12-20 07:34:39 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2019-03-13 06:12:22 +00:00
|
|
|
tokenizer := MakeScriptTokenizer(scriptVersion, script)
|
|
|
|
|
for tokenizer.Next() {
|
|
|
|
|
if !isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) {
|
|
|
|
|
t.Errorf("isCanonicalPush: test #%d failed: %x\n", i, script)
|
2015-02-25 01:11:57 +00:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-20 05:05:35 +00:00
|
|
|
}
|
2018-04-29 23:27:13 +00:00
|
|
|
for i := 0; i <= MaxScriptElementSize; i++ {
|
|
|
|
|
builder := NewScriptBuilder()
|
2014-02-20 17:42:06 +00:00
|
|
|
builder.AddData(bytes.Repeat([]byte{0x49}, i))
|
2014-12-20 07:34:39 +00:00
|
|
|
script, err := builder.Script()
|
|
|
|
|
if err != nil {
|
2019-03-13 06:12:22 +00:00
|
|
|
t.Errorf("Script: test #%d unexpected error: %v\n", i, err)
|
2014-12-20 07:34:39 +00:00
|
|
|
continue
|
2014-02-20 05:05:35 +00:00
|
|
|
}
|
2019-03-13 06:12:22 +00:00
|
|
|
if !IsPushOnlyScript(script) {
|
|
|
|
|
t.Errorf("IsPushOnlyScript: test #%d failed: %x\n", i, script)
|
2014-02-20 05:05:35 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2019-03-13 06:12:22 +00:00
|
|
|
tokenizer := MakeScriptTokenizer(scriptVersion, script)
|
|
|
|
|
for tokenizer.Next() {
|
|
|
|
|
if !isCanonicalPush(tokenizer.Opcode(), tokenizer.Data()) {
|
|
|
|
|
t.Errorf("isCanonicalPush: test #%d failed: %x\n", i, script)
|
2015-02-25 01:11:57 +00:00
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-20 05:05:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
// TestGetPreciseSigOps ensures the more precise signature operation counting
|
|
|
|
|
// mechanism which includes signatures in P2SH scripts works as expected.
|
|
|
|
|
func TestGetPreciseSigOps(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
tests := []struct {
|
2013-06-25 12:12:15 +00:00
|
|
|
name string
|
2013-06-24 15:32:41 +00:00
|
|
|
scriptSig []byte
|
2013-06-25 12:12:15 +00:00
|
|
|
nSigOps int
|
2015-05-02 19:56:55 +00:00
|
|
|
}{
|
2014-02-04 21:18:37 +00:00
|
|
|
{
|
2013-06-25 12:12:15 +00:00
|
|
|
name: "scriptSig doesn't parse",
|
2018-04-29 23:27:13 +00:00
|
|
|
scriptSig: mustParseShortForm("PUSHDATA1 0x02"),
|
2013-06-24 15:32:41 +00:00
|
|
|
},
|
2014-02-04 21:18:37 +00:00
|
|
|
{
|
2013-06-25 12:12:15 +00:00
|
|
|
name: "scriptSig isn't push only",
|
2018-04-29 23:27:13 +00:00
|
|
|
scriptSig: mustParseShortForm("1 DUP"),
|
2013-06-25 12:12:15 +00:00
|
|
|
nSigOps: 0,
|
2013-06-24 15:32:41 +00:00
|
|
|
},
|
2014-02-04 21:18:37 +00:00
|
|
|
{
|
2013-06-25 12:12:15 +00:00
|
|
|
name: "scriptSig length 0",
|
2015-05-02 19:56:55 +00:00
|
|
|
scriptSig: nil,
|
2013-06-25 12:12:15 +00:00
|
|
|
nSigOps: 0,
|
2013-06-24 15:32:41 +00:00
|
|
|
},
|
2014-02-04 21:18:37 +00:00
|
|
|
{
|
2013-06-24 15:32:41 +00:00
|
|
|
name: "No script at the end",
|
|
|
|
|
// No script at end but still push only.
|
2018-04-29 23:27:13 +00:00
|
|
|
scriptSig: mustParseShortForm("1 1"),
|
2013-06-25 12:12:15 +00:00
|
|
|
nSigOps: 0,
|
2013-06-24 15:32:41 +00:00
|
|
|
},
|
2014-02-04 21:18:37 +00:00
|
|
|
{
|
2018-04-29 23:27:13 +00:00
|
|
|
name: "pushed script doesn't parse",
|
|
|
|
|
scriptSig: mustParseShortForm("DATA_2 PUSHDATA1 0x02"),
|
2013-06-24 15:32:41 +00:00
|
|
|
},
|
|
|
|
|
}
|
2015-05-02 19:56:55 +00:00
|
|
|
|
2013-06-24 15:32:41 +00:00
|
|
|
// The signature in the p2sh script is nonsensical for the tests since
|
2015-05-02 19:56:55 +00:00
|
|
|
// this script will never be executed. What matters is that it matches
|
2013-06-24 15:32:41 +00:00
|
|
|
// the right pattern.
|
2015-05-02 19:56:55 +00:00
|
|
|
pkScript := mustParseShortForm("HASH160 DATA_20 0x433ec2ac1ffa1b7b7d0" +
|
|
|
|
|
"27f564529c57197f9ae88 EQUAL")
|
|
|
|
|
for _, test := range tests {
|
2019-06-19 04:50:29 +00:00
|
|
|
count := GetPreciseSigOpCount(test.scriptSig, pkScript)
|
2013-06-24 15:32:41 +00:00
|
|
|
if count != test.nSigOps {
|
|
|
|
|
t.Errorf("%s: expected count of %d, got %d", test.name,
|
|
|
|
|
test.nSigOps, count)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-20 17:07:44 +00:00
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
// TestRemoveOpcodeByData ensures that removing data carrying opcodes based on
|
|
|
|
|
// the data they contain works as expected.
|
|
|
|
|
func TestRemoveOpcodeByData(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
|
|
|
|
before []byte
|
|
|
|
|
remove []byte
|
|
|
|
|
err error
|
|
|
|
|
after []byte
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "nothing to do",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("NOP"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("NOP"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "simple case",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("DATA_4 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
|
|
|
|
after: nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "simple case (miss)",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("DATA_4 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("DATA_4 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
2019-03-26 19:49:15 +00:00
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakesubmission simple case p2pkh",
|
|
|
|
|
before: mustParseShortForm("SSTX DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSTX DUP HASH160 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakesubmission simple case p2pkh (miss)",
|
|
|
|
|
before: mustParseShortForm("SSTX DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSTX DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakesubmission simple case p2sh",
|
|
|
|
|
before: mustParseShortForm("SSTX HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSTX HASH160 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakesubmission simple case p2sh (miss)",
|
|
|
|
|
before: mustParseShortForm("SSTX HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSTX HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakegen simple case p2pkh",
|
|
|
|
|
before: mustParseShortForm("SSGEN DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSGEN DUP HASH160 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakegen simple case p2pkh (miss)",
|
|
|
|
|
before: mustParseShortForm("SSGEN DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSGEN DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakegen simple case p2sh",
|
|
|
|
|
before: mustParseShortForm("SSGEN HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSGEN HASH160 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakegen simple case p2sh (miss)",
|
|
|
|
|
before: mustParseShortForm("SSGEN HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSGEN HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakerevoke simple case p2pkh",
|
|
|
|
|
before: mustParseShortForm("SSRTX DUP HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
|
|
|
|
after: []byte{OP_SSRTX, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG},
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakerevoke simple case p2pkh (miss)",
|
|
|
|
|
before: mustParseShortForm("SSRTX DUP HASH160 DATA_20 0x00{20} " +
|
|
|
|
|
"EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: bytes.Repeat([]byte{0}, 21),
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSRTX DUP HASH160 DATA_20 0x00{20} " +
|
|
|
|
|
"EQUALVERIFY CHECKSIG"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakerevoke simple case p2sh",
|
|
|
|
|
before: mustParseShortForm("SSRTX HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSRTX HASH160 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "stakerevoke simple case p2sh (miss)",
|
|
|
|
|
before: mustParseShortForm("SSRTX HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("SSRTX HASH160 DATA_20 0x00{16} " +
|
|
|
|
|
"0x01020304 EQUAL"),
|
2019-03-26 19:49:15 +00:00
|
|
|
},
|
2015-05-02 19:56:55 +00:00
|
|
|
{
|
|
|
|
|
// padded to keep it canonical.
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "simple case (pushdata1)",
|
|
|
|
|
before: mustParseShortForm("PUSHDATA1 0x4c 0x00{72} 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
|
|
|
|
after: nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "simple case (pushdata1 miss)",
|
|
|
|
|
before: mustParseShortForm("PUSHDATA1 0x4c 0x00{72} 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("PUSHDATA1 0x4c 0x00{72} 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "simple case (pushdata1 miss noncanonical)",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("PUSHDATA1 0x04 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("PUSHDATA1 0x04 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "simple case (pushdata2)",
|
|
|
|
|
before: mustParseShortForm("PUSHDATA2 0x0001 0x00{252} 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
|
|
|
|
after: nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-03-26 20:29:56 +00:00
|
|
|
name: "simple case (pushdata2 miss)",
|
|
|
|
|
before: mustParseShortForm("PUSHDATA2 0x0001 0x00{252} 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("PUSHDATA2 0x0001 0x00{252} 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "simple case (pushdata2 miss noncanonical)",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("PUSHDATA2 0x0400 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("PUSHDATA2 0x0400 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// This is padded to make the push canonical.
|
|
|
|
|
name: "simple case (pushdata4)",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("PUSHDATA4 0x00000100 0x00{65532} " +
|
|
|
|
|
"0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
|
|
|
|
after: nil,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "simple case (pushdata4 miss noncanonical)",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("PUSHDATA4 0x04000000 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("PUSHDATA4 0x04000000 0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// This is padded to make the push canonical.
|
|
|
|
|
name: "simple case (pushdata4 miss)",
|
2019-03-26 20:29:56 +00:00
|
|
|
before: mustParseShortForm("PUSHDATA4 0x00000100 0x00{65532} " +
|
|
|
|
|
"0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4, 5},
|
2019-03-26 20:29:56 +00:00
|
|
|
after: mustParseShortForm("PUSHDATA4 0x00000100 0x00{65532} " +
|
|
|
|
|
"0x01020304"),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
2019-08-22 13:23:36 +00:00
|
|
|
name: "invalid opcode",
|
2018-04-29 23:27:13 +00:00
|
|
|
before: []byte{OP_UNKNOWN193},
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2018-04-29 23:27:13 +00:00
|
|
|
after: []byte{OP_UNKNOWN193},
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid length (instruction)",
|
2018-04-29 23:27:13 +00:00
|
|
|
before: []byte{OP_PUSHDATA1},
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2018-06-29 16:58:31 +00:00
|
|
|
err: scriptError(ErrMalformedPush, ""),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "invalid length (data)",
|
2018-04-29 23:27:13 +00:00
|
|
|
before: []byte{OP_PUSHDATA1, 255, 254},
|
2015-05-02 19:56:55 +00:00
|
|
|
remove: []byte{1, 2, 3, 4},
|
2018-06-29 16:58:31 +00:00
|
|
|
err: scriptError(ErrMalformedPush, ""),
|
2015-05-02 19:56:55 +00:00
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 06:12:51 +00:00
|
|
|
// tstRemoveOpcodeByData is a convenience function to ensure the provided
|
|
|
|
|
// script parses before attempting to remove the passed data.
|
|
|
|
|
const scriptVersion = 0
|
2018-04-29 23:27:13 +00:00
|
|
|
tstRemoveOpcodeByData := func(script []byte, data []byte) ([]byte, error) {
|
2019-03-13 06:12:51 +00:00
|
|
|
if err := checkScriptParses(scriptVersion, script); err != nil {
|
2018-04-29 23:27:13 +00:00
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-03-13 06:12:51 +00:00
|
|
|
|
2019-03-13 06:13:00 +00:00
|
|
|
return removeOpcodeByData(script, data), nil
|
2018-04-29 23:27:13 +00:00
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
for _, test := range tests {
|
2018-04-29 23:27:13 +00:00
|
|
|
result, err := tstRemoveOpcodeByData(test.before, test.remove)
|
2018-06-29 16:58:31 +00:00
|
|
|
if e := tstCheckScriptError(err, test.err); e != nil {
|
|
|
|
|
t.Errorf("%s: %v", test.name, e)
|
|
|
|
|
continue
|
2015-05-02 19:56:55 +00:00
|
|
|
}
|
2018-06-29 16:58:31 +00:00
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
if !bytes.Equal(test.after, result) {
|
2018-06-29 16:58:31 +00:00
|
|
|
t.Errorf("%s: value does not equal expected: exp: %q"+
|
|
|
|
|
" got: %q", test.name, test.after, result)
|
2015-05-02 19:56:55 +00:00
|
|
|
}
|
2013-06-12 21:35:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
// TestIsPayToScriptHash ensures the IsPayToScriptHash function returns the
|
|
|
|
|
// expected results for all the scripts in scriptClassTests.
|
2013-06-26 23:32:11 +00:00
|
|
|
func TestIsPayToScriptHash(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
for _, test := range scriptClassTests {
|
|
|
|
|
script := mustParseShortForm(test.script)
|
2018-04-29 23:27:13 +00:00
|
|
|
shouldBe := (test.class == ScriptHashTy)
|
|
|
|
|
p2sh := IsPayToScriptHash(script)
|
2013-06-26 23:32:11 +00:00
|
|
|
if p2sh != shouldBe {
|
|
|
|
|
t.Errorf("%s: epxected p2sh %v, got %v", test.name,
|
|
|
|
|
shouldBe, p2sh)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 06:11:22 +00:00
|
|
|
// TestIsAnyKindOfScriptHash ensures the isAnyKindOfScriptHash function returns
|
|
|
|
|
// the expected results for all the scripts in scriptClassTests.
|
|
|
|
|
func TestIsAnyKindOfScriptHash(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
for _, test := range scriptClassTests {
|
|
|
|
|
script := mustParseShortForm(test.script)
|
|
|
|
|
want := (test.class == ScriptHashTy || test.subClass == ScriptHashTy)
|
2019-03-13 06:11:24 +00:00
|
|
|
p2sh := isAnyKindOfScriptHash(script)
|
2019-03-13 06:11:22 +00:00
|
|
|
if p2sh != want {
|
|
|
|
|
t.Errorf("%s: epxected p2sh %v, got %v", test.name,
|
|
|
|
|
want, p2sh)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 06:12:22 +00:00
|
|
|
// TestHasCanonicalPushes ensures the isCanonicalPush function properly
|
|
|
|
|
// determines what is considered a canonical push for the purposes of
|
|
|
|
|
// removeOpcodeByData.
|
2014-08-08 11:46:15 +00:00
|
|
|
func TestHasCanonicalPushes(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2019-03-13 06:12:22 +00:00
|
|
|
const scriptVersion = 0
|
2014-08-08 11:46:15 +00:00
|
|
|
tests := []struct {
|
|
|
|
|
name string
|
2015-05-02 19:56:55 +00:00
|
|
|
script string
|
2014-08-08 11:46:15 +00:00
|
|
|
expected bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "does not parse",
|
2015-05-02 19:56:55 +00:00
|
|
|
script: "0x046708afdb0fe5548271967f1a67130b7105cd6a82" +
|
|
|
|
|
"8e03909a67962e0ea1f61d",
|
2014-08-08 11:46:15 +00:00
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "non-canonical push",
|
2015-05-02 19:56:55 +00:00
|
|
|
script: "PUSHDATA1 0x04 0x01020304",
|
2014-08-08 11:46:15 +00:00
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 06:12:22 +00:00
|
|
|
for _, test := range tests {
|
2015-05-02 19:56:55 +00:00
|
|
|
script := mustParseShortForm(test.script)
|
2019-03-13 06:12:22 +00:00
|
|
|
if err := checkScriptParses(scriptVersion, script); err != nil {
|
2015-02-25 01:11:57 +00:00
|
|
|
if test.expected {
|
2019-03-13 06:12:22 +00:00
|
|
|
t.Errorf("%q: script parse failed: %v", test.name, err)
|
2015-02-25 01:11:57 +00:00
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-03-13 06:12:22 +00:00
|
|
|
tokenizer := MakeScriptTokenizer(scriptVersion, script)
|
|
|
|
|
for tokenizer.Next() {
|
|
|
|
|
result := isCanonicalPush(tokenizer.Opcode(), tokenizer.Data())
|
|
|
|
|
if result != test.expected {
|
|
|
|
|
t.Errorf("%q: isCanonicalPush wrong result\ngot: %v\nwant: %v",
|
|
|
|
|
test.name, result, test.expected)
|
2015-02-25 01:11:57 +00:00
|
|
|
break
|
|
|
|
|
}
|
2014-08-08 11:46:15 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-02 19:56:55 +00:00
|
|
|
// TestIsPushOnlyScript ensures the IsPushOnlyScript function returns the
|
|
|
|
|
// expected results.
|
2014-08-08 11:46:15 +00:00
|
|
|
func TestIsPushOnlyScript(t *testing.T) {
|
2015-01-29 20:39:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2014-08-08 11:46:15 +00:00
|
|
|
test := struct {
|
|
|
|
|
name string
|
|
|
|
|
script []byte
|
|
|
|
|
expected bool
|
|
|
|
|
}{
|
|
|
|
|
name: "does not parse",
|
2015-05-02 19:56:55 +00:00
|
|
|
script: mustParseShortForm("0x046708afdb0fe5548271967f1a67130" +
|
|
|
|
|
"b7105cd6a828e03909a67962e0ea1f61d"),
|
2014-08-08 11:46:15 +00:00
|
|
|
expected: false,
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-29 23:27:13 +00:00
|
|
|
if IsPushOnlyScript(test.script) != test.expected {
|
2015-05-02 19:56:55 +00:00
|
|
|
t.Errorf("IsPushOnlyScript (%s) wrong result\ngot: %v\nwant: "+
|
|
|
|
|
"%v", test.name, true, test.expected)
|
2015-02-25 20:04:37 +00:00
|
|
|
}
|
|
|
|
|
}
|
2016-01-20 21:46:42 +00:00
|
|
|
|
2015-08-01 14:43:06 +00:00
|
|
|
// TestIsUnspendable ensures the IsUnspendable function returns the expected
|
|
|
|
|
// results.
|
|
|
|
|
func TestIsUnspendable(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
tests := []struct {
|
2015-08-26 04:03:18 +00:00
|
|
|
amount int64
|
2015-08-01 14:43:06 +00:00
|
|
|
pkScript []byte
|
|
|
|
|
expected bool
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
// Unspendable
|
2015-08-26 04:03:18 +00:00
|
|
|
amount: 100,
|
2015-08-01 14:43:06 +00:00
|
|
|
pkScript: []byte{0x6a, 0x04, 0x74, 0x65, 0x73, 0x74},
|
|
|
|
|
expected: true,
|
|
|
|
|
},
|
2015-08-26 04:03:18 +00:00
|
|
|
{
|
|
|
|
|
// Unspendable
|
|
|
|
|
amount: 0,
|
|
|
|
|
pkScript: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0,
|
|
|
|
|
0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45,
|
|
|
|
|
0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6,
|
|
|
|
|
0xfa, 0x0b, 0x5c, 0x88, 0xac},
|
|
|
|
|
expected: true,
|
|
|
|
|
},
|
2015-08-01 14:43:06 +00:00
|
|
|
{
|
|
|
|
|
// Spendable
|
2015-08-26 04:03:18 +00:00
|
|
|
amount: 100,
|
2015-08-01 14:43:06 +00:00
|
|
|
pkScript: []byte{0x76, 0xa9, 0x14, 0x29, 0x95, 0xa0,
|
|
|
|
|
0xfe, 0x68, 0x43, 0xfa, 0x9b, 0x95, 0x45,
|
|
|
|
|
0x97, 0xf0, 0xdc, 0xa7, 0xa4, 0x4d, 0xf6,
|
|
|
|
|
0xfa, 0x0b, 0x5c, 0x88, 0xac},
|
|
|
|
|
expected: false,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for i, test := range tests {
|
2018-04-29 23:27:13 +00:00
|
|
|
res := IsUnspendable(test.amount, test.pkScript)
|
2015-08-01 14:43:06 +00:00
|
|
|
if res != test.expected {
|
2018-04-29 23:27:13 +00:00
|
|
|
t.Errorf("IsUnspendable #%d failed: got %v want %v", i,
|
|
|
|
|
res, test.expected)
|
2015-08-01 14:43:06 +00:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|