decred.org/dcrdex@v1.0.3/dex/networks/btc/script_test.go (about)

     1  package btc
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"encoding/hex"
     7  	"fmt"
     8  	"math/rand"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/btcsuite/btcd/btcec/v2"
    13  	"github.com/btcsuite/btcd/btcutil"
    14  	"github.com/btcsuite/btcd/chaincfg"
    15  	"github.com/btcsuite/btcd/txscript"
    16  	"github.com/btcsuite/btcd/wire"
    17  )
    18  
    19  var (
    20  	tStamp         = int64(1574264305)
    21  	tParams        = &chaincfg.MainNetParams
    22  	invalidScript  = []byte{txscript.OP_DATA_75}
    23  	invalidWitness = [][]byte{{txscript.OP_DATA_75}}
    24  )
    25  
    26  func randBytes(l int) []byte {
    27  	b := make([]byte, l)
    28  	rand.Read(b)
    29  	return b
    30  }
    31  
    32  func newPubKey() []byte {
    33  	priv, err := btcec.NewPrivateKey()
    34  	if err != nil {
    35  		fmt.Printf("error creating pubkey: %v\n", err)
    36  	}
    37  	return priv.PubKey().SerializeCompressed()
    38  }
    39  
    40  type tAddrs struct {
    41  	pkh      *btcutil.AddressPubKeyHash
    42  	wpkh     *btcutil.AddressWitnessPubKeyHash
    43  	sh       *btcutil.AddressScriptHash
    44  	wsh      *btcutil.AddressWitnessScriptHash
    45  	pk1      *btcutil.AddressPubKey
    46  	pk2      *btcutil.AddressPubKey
    47  	multiSig []byte
    48  }
    49  
    50  func testAddresses() *tAddrs {
    51  	p2pkh, _ := btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
    52  	p2wpkh, _ := btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
    53  	p2wsh, _ := btcutil.NewAddressWitnessScriptHash(randBytes(32), tParams)
    54  	pk1, _ := btcutil.NewAddressPubKey(newPubKey(), tParams)
    55  	pk2, _ := btcutil.NewAddressPubKey(newPubKey(), tParams)
    56  	multiSig, _ := txscript.MultiSigScript([]*btcutil.AddressPubKey{pk1, pk2}, 1)
    57  	p2sh, _ := btcutil.NewAddressScriptHash(multiSig, tParams)
    58  	return &tAddrs{
    59  		pkh:      p2pkh,
    60  		wpkh:     p2wpkh,
    61  		sh:       p2sh,
    62  		wsh:      p2wsh,
    63  		pk1:      pk1,
    64  		pk2:      pk2,
    65  		multiSig: multiSig,
    66  	}
    67  }
    68  
    69  func TestVarIntSizes(t *testing.T) {
    70  	got := wire.VarIntSerializeSize(uint64(RedeemP2PKHSigScriptSize))
    71  	if got != 1 {
    72  		t.Errorf("Incorrect opcode prefix size %d, want 1", got)
    73  	}
    74  
    75  	got = wire.VarIntSerializeSize(uint64(RedeemP2PKHSigScriptSize))
    76  	if got != 1 {
    77  		t.Errorf("Incorrect opcode prefix size %d, want 1", got)
    78  	}
    79  }
    80  
    81  func TestParseScriptType(t *testing.T) {
    82  	addrs := testAddresses()
    83  
    84  	var scriptType BTCScriptType
    85  	parse := func(addr btcutil.Address, redeem []byte) {
    86  		pkScript, err := txscript.PayToAddrScript(addr)
    87  		if err != nil {
    88  			t.Fatalf("error creating script for address %s", addr)
    89  		}
    90  		scriptType = ParseScriptType(pkScript, redeem)
    91  	}
    92  
    93  	check := func(name string, res bool, exp bool) {
    94  		if res != exp {
    95  			t.Fatalf("%s check failed. wanted %t, got %t", name, exp, res)
    96  		}
    97  	}
    98  
    99  	parse(addrs.pk1, nil)
   100  	check("p2pk-IsP2PK", scriptType.IsP2PK(), true)
   101  	check("p2pk-IsP2PKH", scriptType.IsP2PKH(), false)
   102  	check("p2pk-IsP2SH", scriptType.IsP2SH(), false)
   103  	check("p2pk-IsP2WPKH", scriptType.IsP2WPKH(), false)
   104  	check("p2pk-IsP2WSH", scriptType.IsP2WSH(), false)
   105  	check("p2pk-IsMultiSig", scriptType.IsMultiSig(), false)
   106  	check("p2pk-IsSegwit", scriptType.IsSegwit(), false)
   107  
   108  	parse(addrs.pkh, nil)
   109  	check("p2pkh-IsP2PK", scriptType.IsP2PK(), false)
   110  	check("p2pkh-IsP2PKH", scriptType.IsP2PKH(), true)
   111  	check("p2pkh-IsP2SH", scriptType.IsP2SH(), false)
   112  	check("p2pkh-IsP2WPKH", scriptType.IsP2WPKH(), false)
   113  	check("p2pkh-IsP2WSH", scriptType.IsP2WSH(), false)
   114  	check("p2pkh-IsMultiSig", scriptType.IsMultiSig(), false)
   115  	check("p2pkh-IsSegwit", scriptType.IsSegwit(), false)
   116  
   117  	parse(addrs.wpkh, nil)
   118  	check("p2wpkh-IsP2PK", scriptType.IsP2PK(), false)
   119  	check("p2wpkh-IsP2PKH", scriptType.IsP2PKH(), false)
   120  	check("p2wpkh-IsP2SH", scriptType.IsP2SH(), false)
   121  	check("p2wpkh-IsP2WPKH", scriptType.IsP2WPKH(), true)
   122  	check("p2wpkh-IsP2WSH", scriptType.IsP2WSH(), false)
   123  	check("p2wpkh-IsMultiSig", scriptType.IsMultiSig(), false)
   124  	check("p2wpkh-IsSegwit", scriptType.IsSegwit(), true)
   125  
   126  	parse(addrs.sh, addrs.multiSig)
   127  	check("p2sh-IsP2PK", scriptType.IsP2PK(), false)
   128  	check("p2sh-IsP2PKH", scriptType.IsP2PKH(), false)
   129  	check("p2sh-IsP2SH", scriptType.IsP2SH(), true)
   130  	check("p2sh-IsP2WPKH", scriptType.IsP2WPKH(), false)
   131  	check("p2sh-IsP2WSH", scriptType.IsP2WSH(), false)
   132  	check("p2sh-IsMultiSig", scriptType.IsMultiSig(), true)
   133  	check("p2sh-IsSegwit", scriptType.IsSegwit(), false)
   134  
   135  	parse(addrs.wsh, nil)
   136  	check("p2wsh-IsP2PK", scriptType.IsP2PK(), false)
   137  	check("p2wsh-IsP2PKH", scriptType.IsP2PKH(), false)
   138  	check("p2wsh-IsP2SH", scriptType.IsP2SH(), false)
   139  	check("p2wsh-IsP2WPKH", scriptType.IsP2WPKH(), false)
   140  	check("p2wsh-IsP2WSH", scriptType.IsP2WSH(), true)
   141  	check("p2wsh-IsMultiSig", scriptType.IsMultiSig(), false)
   142  	check("p2wsh-IsSegwit", scriptType.IsSegwit(), true)
   143  }
   144  
   145  func TestMakeContract(t *testing.T) {
   146  	t.Run("segwit", func(t *testing.T) {
   147  		testMakeContract(t, true)
   148  	})
   149  	t.Run("non-segwit", func(t *testing.T) {
   150  		testMakeContract(t, false)
   151  	})
   152  }
   153  
   154  func testMakeContract(t *testing.T, segwit bool) {
   155  	var ra, sa, p2sh, p2pkh btcutil.Address
   156  	if segwit {
   157  		ra, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   158  		sa, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   159  		p2sh, _ = btcutil.NewAddressScriptHash(randBytes(50), tParams)
   160  		p2pkh, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   161  	} else {
   162  		ra, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   163  		sa, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   164  		p2sh, _ = btcutil.NewAddressScriptHash(randBytes(50), tParams)
   165  		p2pkh, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   166  	}
   167  
   168  	// Wrong recipient address type
   169  	_, err := MakeContract(p2sh, sa, randBytes(32), tStamp, segwit, tParams)
   170  	if err == nil {
   171  		t.Fatalf("no error for wrong recipient address type")
   172  	}
   173  
   174  	// Wrong sender address type.
   175  	_, err = MakeContract(ra, p2pkh, randBytes(32), tStamp, segwit, tParams)
   176  	if err == nil {
   177  		t.Fatalf("no error for wrong sender address type")
   178  	}
   179  
   180  	// Bad secret hash
   181  	_, err = MakeContract(ra, sa, randBytes(10), tStamp, segwit, tParams)
   182  	if err == nil {
   183  		t.Fatalf("no error for bad secret hash")
   184  	}
   185  
   186  	// Good to go
   187  	_, err = MakeContract(ra, sa, randBytes(32), tStamp, segwit, tParams)
   188  	if err != nil {
   189  		t.Fatalf("error for valid contract parameters: %v", err)
   190  	}
   191  }
   192  
   193  func TestIsDust(t *testing.T) {
   194  	pkScript := []byte{0x76, 0xa9, 0x21, 0x03, 0x2f, 0x7e, 0x43,
   195  		0x0a, 0xa4, 0xc9, 0xd1, 0x59, 0x43, 0x7e, 0x84, 0xb9,
   196  		0x75, 0xdc, 0x76, 0xd9, 0x00, 0x3b, 0xf0, 0x92, 0x2c,
   197  		0xf3, 0xaa, 0x45, 0x28, 0x46, 0x4b, 0xab, 0x78, 0x0d,
   198  		0xba, 0x5e, 0x88, 0xac}
   199  
   200  	tests := []struct {
   201  		name     string // test description
   202  		txOut    wire.TxOut
   203  		relayFee uint64 // minimum relay transaction fee.
   204  		isDust   bool
   205  	}{
   206  		{
   207  			// Any value is allowed with a zero relay fee.
   208  			"zero value with zero relay fee",
   209  			wire.TxOut{Value: 0, PkScript: pkScript},
   210  			0,
   211  			false,
   212  		},
   213  		{
   214  			// Zero value is dust with any relay fee"
   215  			"zero value with very small tx fee",
   216  			wire.TxOut{Value: 0, PkScript: pkScript},
   217  			1,
   218  			true,
   219  		},
   220  		{
   221  			"38 byte public key script with value 584",
   222  			wire.TxOut{Value: 584, PkScript: pkScript},
   223  			1,
   224  			true,
   225  		},
   226  		{
   227  			"38 byte public key script with value 585",
   228  			wire.TxOut{Value: 585, PkScript: pkScript},
   229  			1,
   230  			false,
   231  		},
   232  		{
   233  			// Maximum allowed value is never dust.
   234  			"max satoshi amount is never dust",
   235  			wire.TxOut{Value: btcutil.MaxSatoshi, PkScript: pkScript},
   236  			btcutil.MaxSatoshi / 1000,
   237  			false,
   238  		},
   239  		{
   240  			// Maximum int64 value causes overflow.
   241  			"maximum int64 value",
   242  			wire.TxOut{Value: 1<<63 - 1, PkScript: pkScript},
   243  			1<<63 - 1,
   244  			true,
   245  		},
   246  		{
   247  			// Unspendable pkScript due to an invalid public key
   248  			// script.
   249  			"unspendable pkScript",
   250  			wire.TxOut{Value: 5000, PkScript: []byte{0x01}},
   251  			0, // no relay fee
   252  			true,
   253  		},
   254  	}
   255  	for _, test := range tests {
   256  		res := IsDust(&test.txOut, test.relayFee)
   257  		if res != test.isDust {
   258  			t.Fatalf("Dust test '%s' failed: want %v got %v",
   259  				test.name, test.isDust, res)
   260  		}
   261  	}
   262  }
   263  
   264  func TestExtractScriptAddrs(t *testing.T) {
   265  	// Invalid script
   266  	_, nonStd, _ := ExtractScriptAddrs(invalidScript, tParams)
   267  	if !nonStd {
   268  		t.Errorf("expected non-standard script")
   269  	}
   270  
   271  	addrs := testAddresses()
   272  	type test struct {
   273  		addr   btcutil.Address
   274  		nonStd bool
   275  		script []byte
   276  		pk     int
   277  		pkh    int
   278  		sigs   int
   279  	}
   280  
   281  	tests := []test{
   282  		{addrs.pkh, false, nil, 0, 1, 1},
   283  		{addrs.sh, false, nil, 0, 1, 1},
   284  		{addrs.wpkh, false, nil, 0, 1, 1},
   285  		{addrs.wsh, false, nil, 0, 1, 1},
   286  		{nil, false, addrs.multiSig, 2, 0, 1},
   287  	}
   288  
   289  	for _, tt := range tests {
   290  		s := tt.script
   291  		if s == nil {
   292  			s, _ = txscript.PayToAddrScript(tt.addr)
   293  		}
   294  		scriptAddrs, nonStd, err := ExtractScriptAddrs(s, tParams)
   295  		if err != nil {
   296  			t.Fatalf("error extracting script addresses: %v", err)
   297  		}
   298  		if nonStd != tt.nonStd {
   299  			t.Fatalf("expected nonStd=%v, got %v", tt.nonStd, nonStd)
   300  		}
   301  		if scriptAddrs.NumPK != tt.pk {
   302  			t.Fatalf("wrong number of hash addresses. wanted %d, got %d", tt.pk, scriptAddrs.NumPK)
   303  		}
   304  		if scriptAddrs.NumPKH != tt.pkh {
   305  			t.Fatalf("wrong number of pubkey-hash addresses. wanted %d, got %d", tt.pkh, scriptAddrs.NumPKH)
   306  		}
   307  		if scriptAddrs.NRequired != tt.sigs {
   308  			t.Fatalf("wrong number of required signatures. wanted %d, got %d", tt.sigs, scriptAddrs.NRequired)
   309  		}
   310  	}
   311  
   312  }
   313  
   314  func TestExtractSwapDetails(t *testing.T) {
   315  	t.Run("segwit", func(t *testing.T) {
   316  		testExtractSwapDetails(t, true)
   317  	})
   318  	t.Run("non-segwit", func(t *testing.T) {
   319  		testExtractSwapDetails(t, false)
   320  	})
   321  }
   322  
   323  func testExtractSwapDetails(t *testing.T, segwit bool) {
   324  	var rAddr, sAddr btcutil.Address
   325  	if segwit {
   326  		rAddr, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   327  		sAddr, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   328  	} else {
   329  		rAddr, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   330  		sAddr, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   331  	}
   332  
   333  	keyHash := randBytes(32)
   334  	contract, err := MakeContract(rAddr, sAddr, keyHash, tStamp, segwit, tParams)
   335  	if err != nil {
   336  		t.Fatalf("error creating contract: %v", err)
   337  	}
   338  
   339  	sa, ra, lockTime, secretHash, err := ExtractSwapDetails(contract, segwit, tParams)
   340  	if err != nil {
   341  		t.Fatalf("error for valid contract: %v", err)
   342  	}
   343  	if sa.String() != sAddr.String() {
   344  		t.Fatalf("sender address mismatch. wanted %s, got %s", sAddr.String(), sa.String())
   345  	}
   346  	if ra.String() != rAddr.String() {
   347  		t.Fatalf("recipient address mismatch. wanted %s, got %s", rAddr.String(), ra.String())
   348  	}
   349  	if lockTime != uint64(tStamp) {
   350  		t.Fatalf("incorrect lock time. wanted 5, got %d", lockTime)
   351  	}
   352  	if !bytes.Equal(secretHash, keyHash) {
   353  		t.Fatalf("wrong secret hash. wanted %x, got %x", keyHash, secretHash)
   354  	}
   355  
   356  	// incorrect length
   357  	_, _, _, _, err = ExtractSwapDetails(contract[:len(contract)-1], segwit, tParams)
   358  	if err == nil {
   359  		t.Fatalf("no error for vandalized contract")
   360  	} else if !strings.HasPrefix(err.Error(), "incorrect swap contract length") {
   361  		t.Errorf("incorrect error for incorrect swap contract length: %v", err)
   362  	}
   363  
   364  	// bad secret size
   365  	contract[3] = 250
   366  	_, _, _, _, err = ExtractSwapDetails(contract, segwit, tParams)
   367  	if err == nil {
   368  		t.Fatalf("no error for contract with invalid secret size")
   369  	} else if !strings.HasPrefix(err.Error(), "invalid secret size") {
   370  		t.Errorf("incorrect error for invalid secret size: %v", err)
   371  	}
   372  }
   373  
   374  func TestInputInfo(t *testing.T) {
   375  	addrs := testAddresses()
   376  	var spendInfo *SpendInfo
   377  	var err error
   378  
   379  	check := func(name string, sigScriptSize, witnessSize uint32, scriptType BTCScriptType) {
   380  		t.Helper()
   381  		if spendInfo.SigScriptSize != sigScriptSize {
   382  			t.Fatalf("%s: wrong SigScriptSize, wanted %d, got %d", name, sigScriptSize, spendInfo.SigScriptSize)
   383  		}
   384  		if spendInfo.WitnessSize != witnessSize {
   385  			t.Fatalf("%s: wrong WitnessSize, wanted %d, got %d", name, witnessSize, spendInfo.WitnessSize)
   386  		}
   387  		if spendInfo.ScriptType != scriptType {
   388  			t.Fatalf("%s: wrong ScriptType, wanted %d, got %d", name, scriptType, spendInfo.ScriptType)
   389  		}
   390  	}
   391  
   392  	var script []byte
   393  	payToAddr := func(addr btcutil.Address, redeem []byte) {
   394  		t.Helper()
   395  		script, _ = txscript.PayToAddrScript(addr)
   396  		spendInfo, err = InputInfo(script, redeem, tParams)
   397  		if err != nil {
   398  			t.Fatalf("InputInfo script: %v", err)
   399  		}
   400  	}
   401  
   402  	payToAddr(addrs.pkh, nil)
   403  	check("p2pkh", RedeemP2PKHSigScriptSize, 0, ScriptP2PKH)
   404  
   405  	payToAddr(addrs.sh, addrs.multiSig)
   406  	check("p2sh", 74+uint32(len(addrs.multiSig))+2, 0, ScriptP2SH|ScriptMultiSig)
   407  
   408  	payToAddr(addrs.wpkh, nil)
   409  	check("p2wpkh", 0, RedeemP2WPKHInputWitnessWeight, ScriptP2PKH|ScriptTypeSegwit)
   410  
   411  	payToAddr(addrs.wsh, addrs.multiSig)
   412  	check("p2wsh", 0, 74+uint32(len(addrs.multiSig))+1, ScriptP2SH|ScriptTypeSegwit|ScriptMultiSig)
   413  
   414  	// Unknown script type.
   415  	_, err = InputInfo([]byte{0x02, 0x03}, nil, tParams)
   416  	if err == nil {
   417  		t.Fatalf("no error for unknown script type")
   418  	}
   419  
   420  	// InputInfo P2SH requires a redeem script
   421  	script, _ = txscript.PayToAddrScript(addrs.sh)
   422  	_, err = InputInfo(script, nil, tParams)
   423  	if err == nil {
   424  		t.Fatalf("no error for missing redeem script")
   425  	}
   426  	// Redeem script must be parseable.
   427  	spendInfo, err = InputInfo(script, invalidScript, tParams)
   428  	if err != nil {
   429  		t.Fatalf("failed to parse non-standard script")
   430  	}
   431  	if !spendInfo.NonStandardScript {
   432  		t.Errorf("non-standard script was not detected as such")
   433  	}
   434  }
   435  
   436  func TestFindKeyPush(t *testing.T) {
   437  	t.Run("segwit", func(t *testing.T) {
   438  		testFindKeyPush(t, true)
   439  	})
   440  	t.Run("non-segwit", func(t *testing.T) {
   441  		testFindKeyPush(t, false)
   442  	})
   443  }
   444  
   445  func testFindKeyPush(t *testing.T, segwit bool) {
   446  	dummyPrevOut := new(wire.OutPoint)
   447  	var rAddr, sAddr btcutil.Address
   448  	if segwit {
   449  		rAddr, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   450  		sAddr, _ = btcutil.NewAddressWitnessPubKeyHash(randBytes(20), tParams)
   451  	} else {
   452  		rAddr, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   453  		sAddr, _ = btcutil.NewAddressPubKeyHash(randBytes(20), tParams)
   454  	}
   455  
   456  	secret := randBytes(32)
   457  	secretHash := sha256.Sum256(secret)
   458  	contract, _ := MakeContract(rAddr, sAddr, secretHash[:], tStamp, segwit, tParams)
   459  	randomContract, _ := MakeContract(rAddr, sAddr, randBytes(32), tStamp, segwit, tParams)
   460  
   461  	var sigScript, contractHash, randoSigScript []byte
   462  	var witness, randoWitness [][]byte
   463  	var err error
   464  	if segwit {
   465  		witness = RedeemP2WSHContract(contract, randBytes(73), randBytes(33), secret)
   466  		h := sha256.Sum256(contract)
   467  		contractHash = h[:]
   468  		randoWitness = RedeemP2WSHContract(randomContract, randBytes(73), randBytes(33), secret)
   469  
   470  	} else {
   471  		sigScript, err = RedeemP2SHContract(contract, randBytes(73), randBytes(33), secret)
   472  		if err != nil {
   473  			t.Fatalf("error creating redeem script: %v", err)
   474  		}
   475  		contractHash = btcutil.Hash160(contract)
   476  		randoSigScript, _ = RedeemP2SHContract(randomContract, randBytes(73), randBytes(33), secret)
   477  	}
   478  
   479  	txIn := wire.NewTxIn(dummyPrevOut, sigScript, witness)
   480  
   481  	key, err := FindKeyPush(txIn.Witness, txIn.SignatureScript, contractHash, segwit, tParams)
   482  	if err != nil {
   483  		t.Fatalf("findKeyPush error: %v", err)
   484  	}
   485  	if !bytes.Equal(key, secret) {
   486  		t.Fatalf("wrong secret. expected %x, got %x", secret, key)
   487  	}
   488  
   489  	// Empty script is an error.
   490  	badTx := wire.NewTxIn(dummyPrevOut, nil, nil)
   491  	_, err = FindKeyPush(badTx.Witness, badTx.SignatureScript, contractHash, segwit, tParams)
   492  	if err == nil {
   493  		t.Fatalf("no error for empty script")
   494  	}
   495  
   496  	// Bad script
   497  	badTx = wire.NewTxIn(dummyPrevOut, invalidScript, invalidWitness)
   498  	_, err = FindKeyPush(badTx.Witness, badTx.SignatureScript, contractHash, segwit, tParams)
   499  	if err == nil {
   500  		t.Fatalf("no error for bad script")
   501  	}
   502  
   503  	// Random but valid contract won't work.
   504  	badTx = wire.NewTxIn(dummyPrevOut, randoSigScript, randoWitness)
   505  	_, err = FindKeyPush(badTx.Witness, badTx.SignatureScript, contractHash, segwit, tParams)
   506  	if err == nil {
   507  		t.Fatalf("no error for bad script")
   508  	}
   509  }
   510  
   511  func TestExtractContractHash(t *testing.T) {
   512  	addrs := testAddresses()
   513  	// non-hex
   514  	_, err := ExtractContractHash("zz")
   515  	if err == nil {
   516  		t.Fatalf("no error for non-hex contract")
   517  	}
   518  	// invalid script
   519  	_, err = ExtractContractHash(hex.EncodeToString(invalidScript))
   520  	if err == nil {
   521  		t.Fatalf("no error for non-hex contract")
   522  	}
   523  	// multi-sig
   524  	_, err = ExtractContractHash(hex.EncodeToString(addrs.multiSig))
   525  	if err == nil {
   526  		t.Fatalf("no error for non-hex contract")
   527  	}
   528  	// wrong script types
   529  	p2pkh, _ := txscript.PayToAddrScript(addrs.pkh)
   530  	_, err = ExtractContractHash(hex.EncodeToString(p2pkh))
   531  	if err == nil {
   532  		t.Fatalf("no error for non-hex contract")
   533  	}
   534  	// ok p2sh
   535  	p2sh, _ := txscript.PayToAddrScript(addrs.sh)
   536  	checkHash0 := ExtractScriptHash(p2sh)
   537  	if !bytes.Equal(checkHash0, addrs.sh.ScriptAddress()) {
   538  		t.Fatalf("hash mismatch. wanted %x, got %x", addrs.sh.ScriptAddress(), checkHash0)
   539  	}
   540  	checkHash, err := ExtractContractHash(hex.EncodeToString(p2sh))
   541  	if err != nil {
   542  		t.Fatalf("error extracting contract hash: %v", err)
   543  	}
   544  	if !bytes.Equal(checkHash, addrs.sh.ScriptAddress()) {
   545  		t.Fatalf("hash mismatch. wanted %x, got %x", addrs.sh.ScriptAddress(), checkHash)
   546  	}
   547  	// ok p2wsh
   548  	p2wsh, _ := txscript.PayToAddrScript(addrs.wsh)
   549  	checkHash0 = ExtractScriptHash(p2wsh)
   550  	if !bytes.Equal(checkHash0, addrs.wsh.ScriptAddress()) {
   551  		t.Fatalf("hash mismatch. wanted %x, got %x", addrs.wsh.ScriptAddress(), checkHash0)
   552  	}
   553  	checkHash, err = ExtractContractHash(hex.EncodeToString(p2wsh))
   554  	if err != nil {
   555  		t.Fatalf("error extracting contract hash: %v", err)
   556  	}
   557  	if !bytes.Equal(checkHash, addrs.wsh.ScriptAddress()) {
   558  		t.Fatalf("hash mismatch. wanted %x, got %x", addrs.wsh.ScriptAddress(), checkHash)
   559  	}
   560  }
   561  
   562  func TestMsgTxVBytes(t *testing.T) {
   563  	// segwit txn
   564  	segwitTx := "010000000001015018feb13925a5ea9a548ff1af92bca55d3004dc8b1020" +
   565  		"d99978878f073142d80000000000ffffffff02406c85000000000017a914" +
   566  		"1feef1e6f0b360d639dba54c5aa337954f09a48087cba6aa000000000016" +
   567  		"00147916d4dc770017806a316bd907668429b5778b480247304402203d39" +
   568  		"c9b8088d19beafae1fbb531ae58d3ff2b4d374304729bd8df9ab1d1047d2" +
   569  		"022061cb714f12d0b60197da46199ed7151426284ec929a23fe457e65417" +
   570  		"c9b35b980121030375b9725c21dba1dbb6fdb1627a647357052f09bacdde" +
   571  		"9fc2d05e1a36e6180e00000000"
   572  	wantSerSize := 223
   573  	wantVSize := 142
   574  
   575  	txHex, _ := hex.DecodeString(segwitTx)
   576  	msgTx := wire.NewMsgTx(wire.TxVersion)
   577  	err := msgTx.Deserialize(bytes.NewBuffer(txHex))
   578  	if err != nil {
   579  		t.Fatal(err)
   580  	}
   581  
   582  	gotSerSize := msgTx.SerializeSize()
   583  	if gotSerSize != wantSerSize {
   584  		t.Fatalf("wanted serialized tx size %d, got %d", wantSerSize, gotSerSize)
   585  	}
   586  	gotVSize := MsgTxVBytes(msgTx)
   587  	if gotVSize != uint64(wantVSize) {
   588  		t.Errorf("wanted tx virtual size %d, got %d", wantVSize, gotVSize)
   589  	}
   590  
   591  	// non-segwit txn
   592  	nonSegwitTX := "01000000019bb9e11de8c39f2102def30807b3124e92a2cb8b2474f85f9e" +
   593  		"a36d177f74f68100000000f04830450221009f0ea5ba317c1648aefa5864" +
   594  		"c1bafe9b86b3be742a877a6d362b0fdb7cce81d40220625604c3a0fd89b9" +
   595  		"3cfc5096fe0af98d32e5e9fe36f65736bee65ea7329641b60121022d099d" +
   596  		"2055bea94164527afcb0d6bf06a06ddb1186b87331ed48737302b0ec7d20" +
   597  		"179581e2e2e8abbaadf0231c52071e4f88588fccb78ad5afc0644f88011f" +
   598  		"a5d4514c616382012088a820c6de3217594af525fb57eaf1f2aae04c305d" +
   599  		"dc67d465edd325151685fc5a5e428876a914e05c5d2a5f850eee37d12242" +
   600  		"b64dafdf030a0fb467042609865eb17576a914e9288d333d5a8f343169f0" +
   601  		"709cb9b577fc758a506888acfeffffff01b860800200000000160014805b" +
   602  		"83e6b86fc7511f47d4cf95a3048954d3282e00000000"
   603  
   604  	wantSerSize = 322
   605  	wantVSize = 322
   606  
   607  	txHex, _ = hex.DecodeString(nonSegwitTX)
   608  	msgTx = wire.NewMsgTx(wire.TxVersion)
   609  	err = msgTx.Deserialize(bytes.NewBuffer(txHex))
   610  	if err != nil {
   611  		t.Fatal(err)
   612  	}
   613  
   614  	gotSerSize = msgTx.SerializeSize()
   615  	if gotSerSize != wantSerSize {
   616  		t.Fatalf("wanted serialized tx size %d, got %d", wantSerSize, gotSerSize)
   617  	}
   618  	gotVSize = MsgTxVBytes(msgTx)
   619  	if gotVSize != uint64(wantVSize) {
   620  		t.Errorf("wanted tx virtual size %d, got %d", wantVSize, gotVSize)
   621  	}
   622  }