mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-21 22:36:42 +00:00
526 lines
14 KiB
Go
526 lines
14 KiB
Go
// Copyright (c) 2013-2017 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txscript
|
|
|
|
import (
|
|
"bytes"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/daglabs/btcd/dagconfig"
|
|
"github.com/daglabs/btcd/util"
|
|
)
|
|
|
|
// mustParseShortForm parses the passed short form script and returns the
|
|
// resulting bytes. It panics if an error occurs. This is only used in the
|
|
// tests as a helper since the only way it can fail is if there is an error in
|
|
// the test source code.
|
|
func mustParseShortForm(script string) []byte {
|
|
s, err := parseShortForm(script)
|
|
if err != nil {
|
|
panic("invalid short form script in test source: err " +
|
|
err.Error() + ", script: " + script)
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// newAddressPubKeyHash returns a new util.AddressPubKeyHash from the
|
|
// provided hash. It panics if an error occurs. This is only used in the tests
|
|
// as a helper since the only way it can fail is if there is an error in the
|
|
// test source code.
|
|
func newAddressPubKeyHash(pkHash []byte) util.Address {
|
|
addr, err := util.NewAddressPubKeyHash(pkHash, util.Bech32PrefixDAGCoin)
|
|
if err != nil {
|
|
panic("invalid public key hash in test source")
|
|
}
|
|
|
|
return addr
|
|
}
|
|
|
|
// newAddressScriptHash returns a new util.AddressScriptHash from the
|
|
// provided hash. It panics if an error occurs. This is only used in the tests
|
|
// as a helper since the only way it can fail is if there is an error in the
|
|
// test source code.
|
|
func newAddressScriptHash(scriptHash []byte) util.Address {
|
|
addr, err := util.NewAddressScriptHashFromHash(scriptHash,
|
|
util.Bech32PrefixDAGCoin)
|
|
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) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
script []byte
|
|
addrs []util.Address
|
|
reqSigs int
|
|
class ScriptClass
|
|
}{
|
|
{
|
|
name: "standard p2pkh",
|
|
script: hexToBytes("76a914ad06dd6ddee55cbca9a9e3713bd" +
|
|
"7587509a3056488ac"),
|
|
addrs: []util.Address{
|
|
newAddressPubKeyHash(hexToBytes("ad06dd6ddee5" +
|
|
"5cbca9a9e3713bd7587509a30564")),
|
|
},
|
|
reqSigs: 1,
|
|
class: PubKeyHashTy,
|
|
},
|
|
{
|
|
name: "standard p2sh",
|
|
script: hexToBytes("a91463bcc565f9e68ee0189dd5cc67f1b" +
|
|
"0e5f02f45cb87"),
|
|
addrs: []util.Address{
|
|
newAddressScriptHash(hexToBytes("63bcc565f9e6" +
|
|
"8ee0189dd5cc67f1b0e5f02f45cb")),
|
|
},
|
|
reqSigs: 1,
|
|
class: ScriptHashTy,
|
|
},
|
|
|
|
// The below are nonstandard script due to things such as
|
|
// invalid pubkeys, failure to parse, and not being of a
|
|
// standard form.
|
|
|
|
{
|
|
name: "p2pk with uncompressed pk missing OP_CHECKSIG",
|
|
script: hexToBytes("410411db93e1dcdb8a016b49840f8c53b" +
|
|
"c1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddf" +
|
|
"b84ccf9744464f82e160bfa9b8b64f9d4c03f999b864" +
|
|
"3f656b412a3"),
|
|
addrs: nil,
|
|
reqSigs: 0,
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
name: "valid signature from a sigscript - no addresses",
|
|
script: hexToBytes("47304402204e45e16932b8af514961a1d" +
|
|
"3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd41022" +
|
|
"0181522ec8eca07de4860a4acdd12909d831cc56cbba" +
|
|
"c4622082221a8768d1d0901"),
|
|
addrs: nil,
|
|
reqSigs: 0,
|
|
class: 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: hexToBytes("493046022100ddc69738bf2336318e4e0" +
|
|
"41a5a77f305da87428ab1606f023260017854350ddc0" +
|
|
"22100817af09d2eec36862d16009852b7e3a0f6dd765" +
|
|
"98290b7834e1453660367e07a014104cd4240c198e12" +
|
|
"523b6f9cb9f5bed06de1ba37e96a1bbd13745fcf9d11" +
|
|
"c25b1dff9a519675d198804ba9962d3eca2d5937d58e" +
|
|
"5a75a71042d40388a4d307f887d"),
|
|
addrs: nil,
|
|
reqSigs: 0,
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
name: "empty script",
|
|
script: []byte{},
|
|
addrs: nil,
|
|
reqSigs: 0,
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
name: "script that does not parse",
|
|
script: []byte{OpData45},
|
|
addrs: nil,
|
|
reqSigs: 0,
|
|
class: NonStandardTy,
|
|
},
|
|
}
|
|
|
|
t.Logf("Running %d tests.", len(tests))
|
|
for i, test := range tests {
|
|
class, addrs, reqSigs, err := ExtractPkScriptAddrs(
|
|
test.script, &dagconfig.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
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestCalcScriptInfo ensures the CalcScriptInfo provides the expected results
|
|
// for various valid and invalid script pairs.
|
|
func TestCalcScriptInfo(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
sigScript string
|
|
pkScript string
|
|
|
|
isP2SH bool
|
|
|
|
scriptInfo ScriptInfo
|
|
scriptInfoErr error
|
|
}{
|
|
{
|
|
// Invented scripts, the hashes do not match
|
|
// Truncated version of test below:
|
|
name: "pkscript doesn't parse",
|
|
sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " +
|
|
"SWAP ABS EQUAL",
|
|
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
|
|
"3152205ec4f59c",
|
|
isP2SH: true,
|
|
scriptInfoErr: scriptError(ErrMalformedPush, ""),
|
|
},
|
|
{
|
|
name: "sigScript doesn't parse",
|
|
// Truncated version of p2sh script below.
|
|
sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " +
|
|
"SWAP ABS",
|
|
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
|
|
"3152205ec4f59c74 EQUAL",
|
|
isP2SH: true,
|
|
scriptInfoErr: scriptError(ErrMalformedPush, ""),
|
|
},
|
|
{
|
|
// Invented scripts, the hashes do not match
|
|
name: "p2sh standard script",
|
|
sigScript: "1 81 DATA_25 DUP HASH160 DATA_20 0x010203" +
|
|
"0405060708090a0b0c0d0e0f1011121314 EQUALVERIFY " +
|
|
"CHECKSIG",
|
|
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
|
|
"3152205ec4f59c74 EQUAL",
|
|
isP2SH: true,
|
|
scriptInfo: ScriptInfo{
|
|
PkScriptClass: ScriptHashTy,
|
|
NumInputs: 3,
|
|
ExpectedInputs: 3, // nonstandard p2sh.
|
|
SigOps: 1,
|
|
},
|
|
},
|
|
{
|
|
// from 567a53d1ce19ce3d07711885168484439965501536d0d0294c5d46d46c10e53b
|
|
// from the blockchain.
|
|
name: "p2sh nonstandard script",
|
|
sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " +
|
|
"SWAP ABS EQUAL",
|
|
pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
|
|
"3152205ec4f59c74 EQUAL",
|
|
isP2SH: true,
|
|
scriptInfo: ScriptInfo{
|
|
PkScriptClass: ScriptHashTy,
|
|
NumInputs: 3,
|
|
ExpectedInputs: -1, // nonstandard p2sh.
|
|
SigOps: 0,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
sigScript := mustParseShortForm(test.sigScript)
|
|
pkScript := mustParseShortForm(test.pkScript)
|
|
|
|
si, err := CalcScriptInfo(sigScript, pkScript, test.isP2SH)
|
|
if e := checkScriptError(err, test.scriptInfoErr); e != nil {
|
|
t.Errorf("scriptinfo test %q: %v", test.name, e)
|
|
continue
|
|
}
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if *si != test.scriptInfo {
|
|
t.Errorf("%s: scriptinfo doesn't match expected. "+
|
|
"got: %q expected %q", test.name, *si,
|
|
test.scriptInfo)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// bogusAddress implements the util.Address interface so the tests can ensure
|
|
// unsupported address types are handled properly.
|
|
type bogusAddress struct{}
|
|
|
|
// EncodeAddress simply returns an empty string. It exists to satisfy the
|
|
// util.Address interface.
|
|
func (b *bogusAddress) EncodeAddress() string {
|
|
return ""
|
|
}
|
|
|
|
// ScriptAddress simply returns an empty byte slice. It exists to satisfy the
|
|
// util.Address interface.
|
|
func (b *bogusAddress) ScriptAddress() []byte {
|
|
return nil
|
|
}
|
|
|
|
// IsForPrefix lies blatantly to satisfy the util.Address interface.
|
|
func (b *bogusAddress) IsForPrefix(prefix util.Bech32Prefix) bool {
|
|
return true // why not?
|
|
}
|
|
|
|
// String simply returns an empty string. It exists to satisfy the
|
|
// util.Address interface.
|
|
func (b *bogusAddress) String() string {
|
|
return ""
|
|
}
|
|
|
|
// TestPayToAddrScript ensures the PayToAddrScript function generates the
|
|
// correct scripts for the various types of addresses.
|
|
func TestPayToAddrScript(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
// 1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX
|
|
p2pkhMain, err := util.NewAddressPubKeyHash(hexToBytes("e34cce70c86"+
|
|
"373273efcc54ce7d2a491bb4a0e84"), util.Bech32PrefixDAGCoin)
|
|
if err != nil {
|
|
t.Fatalf("Unable to create public key hash address: %v", err)
|
|
}
|
|
|
|
// Taken from transaction:
|
|
// b0539a45de13b3e0403909b8bd1a555b8cbe45fd4e3f3fda76f3a5f52835c29d
|
|
p2shMain, _ := util.NewAddressScriptHashFromHash(hexToBytes("e8c300"+
|
|
"c87986efa84c37c0519929019ef86eb5b4"), util.Bech32PrefixDAGCoin)
|
|
if err != nil {
|
|
t.Fatalf("Unable to create script hash address: %v", err)
|
|
}
|
|
|
|
// Errors used in the tests below defined here for convenience and to
|
|
// keep the horizontal test size shorter.
|
|
errUnsupportedAddress := scriptError(ErrUnsupportedAddress, "")
|
|
|
|
tests := []struct {
|
|
in util.Address
|
|
expected string
|
|
err error
|
|
}{
|
|
// pay-to-pubkey-hash address on mainnet
|
|
{
|
|
p2pkhMain,
|
|
"DUP HASH160 DATA_20 0xe34cce70c86373273efcc54ce7d2a4" +
|
|
"91bb4a0e8488 CHECKSIG",
|
|
nil,
|
|
},
|
|
// pay-to-script-hash address on mainnet
|
|
{
|
|
p2shMain,
|
|
"HASH160 DATA_20 0xe8c300c87986efa84c37c0519929019ef8" +
|
|
"6eb5b4 EQUAL",
|
|
nil,
|
|
},
|
|
|
|
// Supported address types with nil pointers.
|
|
{(*util.AddressPubKeyHash)(nil), "", errUnsupportedAddress},
|
|
{(*util.AddressScriptHash)(nil), "", errUnsupportedAddress},
|
|
|
|
// Unsupported address type.
|
|
{&bogusAddress{}, "", errUnsupportedAddress},
|
|
}
|
|
|
|
t.Logf("Running %d tests", len(tests))
|
|
for i, test := range tests {
|
|
pkScript, err := PayToAddrScript(test.in)
|
|
if e := checkScriptError(err, test.err); e != nil {
|
|
t.Errorf("PayToAddrScript #%d unexpected error - "+
|
|
"got %v, want %v", i, err, test.err)
|
|
continue
|
|
}
|
|
|
|
expected := mustParseShortForm(test.expected)
|
|
if !bytes.Equal(pkScript, expected) {
|
|
t.Errorf("PayToAddrScript #%d got: %x\nwant: %x",
|
|
i, pkScript, expected)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// scriptClassTests houses several test scripts used to ensure various class
|
|
// determination is working as expected. It's defined as a test global versus
|
|
// inside a function scope since this spans both the standard tests and the
|
|
// consensus tests (pay-to-script-hash is part of consensus).
|
|
var scriptClassTests = []struct {
|
|
name string
|
|
script string
|
|
class ScriptClass
|
|
}{
|
|
// p2pk. It is standard in BTC but not in DAGCoin
|
|
{
|
|
name: "Pay Pubkey",
|
|
script: "DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382e" +
|
|
"97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e16" +
|
|
"0bfa9b8b64f9d4c03f999b8643f656b412a3 CHECKSIG",
|
|
class: NonStandardTy,
|
|
},
|
|
// tx 599e47a8114fe098103663029548811d2651991b62397e057f0c863c2bc9f9ea
|
|
{
|
|
name: "Pay PubkeyHash",
|
|
script: "DUP HASH160 DATA_20 0x660d4ef3a743e3e696ad990364e555" +
|
|
"c271ad504b EQUALVERIFY CHECKSIG",
|
|
class: PubKeyHashTy,
|
|
},
|
|
// mutlisig. It is standard in BTC but not in DAGCoin
|
|
{
|
|
name: "multisig",
|
|
script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4" +
|
|
"5329a00357b3a7886211ab414d55a 1 CHECKMULTISIG",
|
|
class: NonStandardTy,
|
|
},
|
|
// tx e5779b9e78f9650debc2893fd9636d827b26b4ddfa6a8172fe8708c924f5c39d
|
|
{
|
|
name: "P2SH",
|
|
script: "HASH160 DATA_20 0x433ec2ac1ffa1b7b7d027f564529c57197f" +
|
|
"9ae88 EQUAL",
|
|
class: ScriptHashTy,
|
|
},
|
|
|
|
{
|
|
// Nulldata. It is standard in BTC but not in DAGCoin
|
|
name: "nulldata",
|
|
script: "RETURN 0",
|
|
class: NonStandardTy,
|
|
},
|
|
|
|
// The next few are almost multisig (it is the more complex script type)
|
|
// but with various changes to make it fail.
|
|
{
|
|
// Multisig but invalid nsigs.
|
|
name: "strange 1",
|
|
script: "DUP DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da45" +
|
|
"329a00357b3a7886211ab414d55a 1 CHECKMULTISIG",
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
// Multisig but invalid pubkey.
|
|
name: "strange 2",
|
|
script: "1 1 1 CHECKMULTISIG",
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
// Multisig but no matching npubkeys opcode.
|
|
name: "strange 3",
|
|
script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4532" +
|
|
"9a00357b3a7886211ab414d55a DATA_33 0x0232abdc893e7f0" +
|
|
"631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a " +
|
|
"CHECKMULTISIG",
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
// Multisig but with multisigverify.
|
|
name: "strange 4",
|
|
script: "1 DATA_33 0x0232abdc893e7f0631364d7fd01cb33d24da4532" +
|
|
"9a00357b3a7886211ab414d55a 1 CHECKMULTISIGVERIFY",
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
// Multisig but wrong length.
|
|
name: "strange 5",
|
|
script: "1 CHECKMULTISIG",
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
name: "doesn't parse",
|
|
script: "DATA_5 0x01020304",
|
|
class: NonStandardTy,
|
|
},
|
|
{
|
|
name: "multisig script with wrong number of pubkeys",
|
|
script: "2 " +
|
|
"DATA_33 " +
|
|
"0x027adf5df7c965a2d46203c781bd4dd8" +
|
|
"21f11844136f6673af7cc5a4a05cd29380 " +
|
|
"DATA_33 " +
|
|
"0x02c08f3de8ee2de9be7bd770f4c10eb0" +
|
|
"d6ff1dd81ee96eedd3a9d4aeaf86695e80 " +
|
|
"3 CHECKMULTISIG",
|
|
class: NonStandardTy,
|
|
},
|
|
}
|
|
|
|
// TestScriptClass ensures all the scripts in scriptClassTests have the expected
|
|
// class.
|
|
func TestScriptClass(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, test := range scriptClassTests {
|
|
script := mustParseShortForm(test.script)
|
|
class := GetScriptClass(script)
|
|
if class != test.class {
|
|
t.Errorf("%s: expected %s got %s (script %x)", test.name,
|
|
test.class, class, script)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestStringifyClass ensures the script class string returns the expected
|
|
// string for each script class.
|
|
func TestStringifyClass(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
class ScriptClass
|
|
stringed string
|
|
}{
|
|
{
|
|
name: "nonstandardty",
|
|
class: NonStandardTy,
|
|
stringed: "nonstandard",
|
|
},
|
|
{
|
|
name: "pubkeyhash",
|
|
class: PubKeyHashTy,
|
|
stringed: "pubkeyhash",
|
|
},
|
|
{
|
|
name: "scripthash",
|
|
class: ScriptHashTy,
|
|
stringed: "scripthash",
|
|
},
|
|
{
|
|
name: "broken",
|
|
class: ScriptClass(255),
|
|
stringed: "Invalid",
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
typeString := test.class.String()
|
|
if typeString != test.stringed {
|
|
t.Errorf("%s: got %#q, want %#q", test.name,
|
|
typeString, test.stringed)
|
|
}
|
|
}
|
|
}
|