decred.org/dcrdex@v1.0.5/server/asset/dcr/dcr_test.go (about)

     1  //go:build !dcrlive
     2  
     3  // These tests will not be run if the dcrlive build tag is set.
     4  
     5  package dcr
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/binary"
    11  	"encoding/hex"
    12  	"fmt"
    13  	"math"
    14  	"math/rand"
    15  	"os"
    16  	"path/filepath"
    17  	"strconv"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"decred.org/dcrdex/dex"
    23  	dexdcr "decred.org/dcrdex/dex/networks/dcr"
    24  	"decred.org/dcrdex/server/account"
    25  	"decred.org/dcrdex/server/asset"
    26  	"github.com/decred/dcrd/chaincfg/chainhash"
    27  	"github.com/decred/dcrd/chaincfg/v3"
    28  	"github.com/decred/dcrd/dcrec"
    29  	"github.com/decred/dcrd/dcrec/edwards/v2"
    30  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    31  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    32  	"github.com/decred/dcrd/dcrec/secp256k1/v4/schnorr"
    33  	"github.com/decred/dcrd/dcrutil/v4"
    34  	"github.com/decred/dcrd/hdkeychain/v3"
    35  	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v4"
    36  	"github.com/decred/dcrd/txscript/v4"
    37  	"github.com/decred/dcrd/txscript/v4/stdaddr"
    38  	"github.com/decred/dcrd/txscript/v4/stdscript"
    39  	"github.com/decred/dcrd/wire"
    40  	flags "github.com/jessevdk/go-flags"
    41  )
    42  
    43  var tLogger = dex.StdOutLogger("TEST", dex.LevelTrace)
    44  
    45  func TestMain(m *testing.M) {
    46  	// Set the global chainParams.
    47  	chainParams = chaincfg.MainNetParams()
    48  	os.Exit(m.Run())
    49  }
    50  
    51  // TestLoadConfig checks that configuration parsing works as expected.
    52  func TestLoadConfig(t *testing.T) {
    53  	cfg := &config{}
    54  	parsedCfg := &config{}
    55  
    56  	tempDir := t.TempDir()
    57  	filePath := filepath.Join(tempDir, "test.conf")
    58  	rootParser := flags.NewParser(cfg, flags.None)
    59  	iniParser := flags.NewIniParser(rootParser)
    60  
    61  	runCfg := func(config *config) error {
    62  		*cfg = *config
    63  		err := iniParser.WriteFile(filePath, flags.IniNone)
    64  		if err != nil {
    65  			return err
    66  		}
    67  		parsedCfg, err = loadConfig(filePath, dex.Mainnet)
    68  		return err
    69  	}
    70  
    71  	// Try with just the name. Error expected.
    72  	err := runCfg(&config{
    73  		RPCUser: "somename",
    74  	})
    75  	if err == nil {
    76  		t.Fatalf("no error when just name provided")
    77  	}
    78  
    79  	// Try with just the password. Error expected.
    80  	err = runCfg(&config{
    81  		RPCPass: "somepass",
    82  	})
    83  	if err == nil {
    84  		t.Fatalf("no error when just password provided")
    85  	}
    86  
    87  	// Give both name and password. This should not be an error.
    88  	err = runCfg(&config{
    89  		RPCUser: "somename",
    90  		RPCPass: "somepass",
    91  	})
    92  	if err != nil {
    93  		t.Fatalf("unexpected error when both name and password provided: %v", err)
    94  	}
    95  	if parsedCfg.RPCListen != defaultMainnet {
    96  		t.Fatalf("unexpected default rpc address. wanted %s, got %s", defaultMainnet, cfg.RPCListen)
    97  	}
    98  	// sanity check for name and password match
    99  	if parsedCfg.RPCUser != cfg.RPCUser {
   100  		t.Fatalf("name mismatch")
   101  	}
   102  	if parsedCfg.RPCPass != cfg.RPCPass {
   103  		t.Fatalf("password mismatch")
   104  	}
   105  	if parsedCfg.RPCCert != defaultRPCCert {
   106  		t.Errorf("RPCCert not set implicitly")
   107  	}
   108  	err = runCfg(&config{
   109  		RPCUser:   "abc",
   110  		RPCPass:   "def",
   111  		RPCListen: "123",
   112  		RPCCert:   "456",
   113  	})
   114  	if err != nil {
   115  		t.Errorf("unexpected error when settings RPCListen/RPCCert explicitly: %v", err)
   116  	}
   117  	if cfg.RPCListen != "123" {
   118  		t.Errorf("RPCListen not set to provided value")
   119  	}
   120  	if cfg.RPCCert != "456" {
   121  		t.Errorf("RPCCert not set to provided value")
   122  	}
   123  }
   124  
   125  // The remaining tests use the testBlockchain which is a stub for
   126  // rpcclient.Client. UTXOs, transactions and blocks are added to the blockchain
   127  // as jsonrpc types to be requested by the Backend.
   128  //
   129  // General formula for testing
   130  // 1. Create a Backend with the node field set to a testNode
   131  // 2. Create a fake UTXO and all of the associated jsonrpc-type blocks and
   132  //    transactions and add the to the test blockchain.
   133  // 3. Verify the Backend and UTXO methods are returning whatever is expected.
   134  // 4. Optionally add more blocks and/or transactions to the blockchain and check
   135  //    return values again, as things near the top of the chain can change.
   136  
   137  func randomBytes(len int) []byte {
   138  	bytes := make([]byte, len)
   139  	rand.Read(bytes)
   140  	return bytes
   141  }
   142  
   143  func randomHash() *chainhash.Hash {
   144  	hash := new(chainhash.Hash)
   145  	err := hash.SetBytes(randomBytes(32))
   146  	if err != nil {
   147  		fmt.Printf("chainhash.Hash.SetBytes error: %v", err)
   148  	}
   149  	return hash
   150  }
   151  
   152  // A fake "blockchain" to be used for RPC calls by the dcrNode.
   153  type testBlockChain struct {
   154  	txOuts map[string]*chainjson.GetTxOutResult
   155  	txRaws map[chainhash.Hash]*chainjson.TxRawResult
   156  	blocks map[chainhash.Hash]*chainjson.GetBlockVerboseResult
   157  	hashes map[int64]*chainhash.Hash
   158  }
   159  
   160  // The testChain is a "blockchain" to store RPC responses for the Backend
   161  // node stub to request.
   162  var testChainMtx sync.RWMutex
   163  var testChain testBlockChain
   164  
   165  // This must be called before using the testNode, and should be called
   166  // in-between independent tests.
   167  func cleanTestChain() {
   168  	testChainMtx.Lock()
   169  	testChain = testBlockChain{
   170  		txOuts: make(map[string]*chainjson.GetTxOutResult),
   171  		txRaws: make(map[chainhash.Hash]*chainjson.TxRawResult),
   172  		blocks: make(map[chainhash.Hash]*chainjson.GetBlockVerboseResult),
   173  		hashes: make(map[int64]*chainhash.Hash),
   174  	}
   175  	testChainMtx.Unlock()
   176  }
   177  
   178  // A stub to replace rpcclient.Client for offline testing.
   179  type testNode struct {
   180  	blockchainInfo    *chainjson.GetBlockChainInfoResult
   181  	blockchainInfoErr error
   182  }
   183  
   184  // Store utxo info as a concatenated string hash:vout.
   185  func txOutID(txHash *chainhash.Hash, index uint32) string {
   186  	return txHash.String() + ":" + strconv.Itoa(int(index))
   187  }
   188  
   189  const optimalFeeRate uint64 = 22
   190  
   191  func (*testNode) EstimateSmartFee(_ context.Context, confirmations int64, mode chainjson.EstimateSmartFeeMode) (*chainjson.EstimateSmartFeeResult, error) {
   192  	optimalRate := float64(optimalFeeRate) * 1e-5 // optimalFeeRate: 22 atoms/byte = 0.00022 DCR/KB * 1e8 atoms/DCR * 1e-3 KB/Byte
   193  	// fmt.Println((float64(optimalFeeRate)*1e-5)-0.00022)
   194  	return &chainjson.EstimateSmartFeeResult{FeeRate: optimalRate}, nil
   195  }
   196  
   197  // Part of the dcrNode interface.
   198  func (*testNode) GetTxOut(_ context.Context, txHash *chainhash.Hash, index uint32, tree int8, _ bool) (*chainjson.GetTxOutResult, error) {
   199  	outID := txOutID(txHash, index)
   200  	testChainMtx.RLock()
   201  	defer testChainMtx.RUnlock()
   202  	out := testChain.txOuts[outID]
   203  	// Unfound is not an error for GetTxOut.
   204  	return out, nil
   205  }
   206  
   207  func (*testNode) SendRawTransaction(ctx context.Context, tx *wire.MsgTx, allowHighFees bool) (*chainhash.Hash, error) {
   208  	hash := tx.TxHash()
   209  	return &hash, nil
   210  }
   211  
   212  // Part of the dcrNode interface.
   213  func (*testNode) GetRawTransactionVerbose(_ context.Context, txHash *chainhash.Hash) (*chainjson.TxRawResult, error) {
   214  	testChainMtx.RLock()
   215  	defer testChainMtx.RUnlock()
   216  	tx, found := testChain.txRaws[*txHash]
   217  	if !found {
   218  		return nil, fmt.Errorf("test transaction not found")
   219  	}
   220  	return tx, nil
   221  }
   222  
   223  // Part of the dcrNode interface.
   224  func (*testNode) GetBlockVerbose(_ context.Context, blockHash *chainhash.Hash, verboseTx bool) (*chainjson.GetBlockVerboseResult, error) {
   225  	testChainMtx.RLock()
   226  	defer testChainMtx.RUnlock()
   227  	block, found := testChain.blocks[*blockHash]
   228  	if !found {
   229  		return nil, fmt.Errorf("test block not found")
   230  	}
   231  	return block, nil
   232  }
   233  
   234  // Part of the dcrNode interface.
   235  func (*testNode) GetBlockHash(_ context.Context, blockHeight int64) (*chainhash.Hash, error) {
   236  	testChainMtx.RLock()
   237  	defer testChainMtx.RUnlock()
   238  	hash, found := testChain.hashes[blockHeight]
   239  	if !found {
   240  		return nil, fmt.Errorf("test hash not found")
   241  	}
   242  	return hash, nil
   243  }
   244  
   245  // Part of the dcrNode interface.
   246  func (*testNode) GetBestBlockHash(context.Context) (*chainhash.Hash, error) {
   247  	testChainMtx.RLock()
   248  	defer testChainMtx.RUnlock()
   249  	if len(testChain.hashes) == 0 {
   250  		return nil, fmt.Errorf("no blocks in testChain")
   251  	}
   252  	var bestHeight int64
   253  	for height := range testChain.hashes {
   254  		if height > bestHeight {
   255  			bestHeight = height
   256  		}
   257  	}
   258  	return testChain.hashes[bestHeight], nil
   259  }
   260  
   261  func (t *testNode) GetBlockChainInfo(context.Context) (*chainjson.GetBlockChainInfoResult, error) {
   262  	if t.blockchainInfoErr != nil {
   263  		return nil, t.blockchainInfoErr
   264  	}
   265  	return t.blockchainInfo, nil
   266  }
   267  
   268  // Part of the dcrNode interface.
   269  func (testNode) GetRawTransaction(_ context.Context, txHash *chainhash.Hash) (*dcrutil.Tx, error) {
   270  	return nil, nil
   271  }
   272  
   273  // Create a chainjson.GetTxOutResult such as is returned from GetTxOut.
   274  func testGetTxOut(confirmations int64, pkScript []byte) *chainjson.GetTxOutResult {
   275  	return &chainjson.GetTxOutResult{
   276  		Confirmations: confirmations,
   277  		ScriptPubKey: chainjson.ScriptPubKeyResult{
   278  			Hex: hex.EncodeToString(pkScript),
   279  		},
   280  	}
   281  }
   282  
   283  // Create a *chainjson.TxRawResult such as is returned by
   284  // GetRawTransactionVerbose, kinda, no Vin and other things.
   285  func testRawTransactionVerbose(msgTx *wire.MsgTx, txid, blockHash *chainhash.Hash, blockHeight,
   286  	confirmations int64) *chainjson.TxRawResult {
   287  
   288  	var hash string
   289  	if blockHash != nil {
   290  		hash = blockHash.String()
   291  	}
   292  	hexTx, err := msgTx.Bytes()
   293  	if err != nil {
   294  		fmt.Printf("error encoding MsgTx")
   295  	}
   296  
   297  	vouts := make([]chainjson.Vout, 0, len(msgTx.TxOut))
   298  	for i, vout := range msgTx.TxOut {
   299  		disbuf, _ := txscript.DisasmString(vout.PkScript)
   300  		sc, addrs := stdscript.ExtractAddrs(vout.Version, vout.PkScript, chainParams)
   301  		reqSigs := stdscript.DetermineRequiredSigs(vout.Version, vout.PkScript)
   302  
   303  		addrStrs := make([]string, len(addrs))
   304  		for i, addr := range addrs {
   305  			addrStrs[i] = addr.String()
   306  		}
   307  
   308  		vouts = append(vouts, chainjson.Vout{
   309  			Value:   dcrutil.Amount(vout.Value).ToCoin(),
   310  			N:       uint32(i),
   311  			Version: vout.Version,
   312  			ScriptPubKey: chainjson.ScriptPubKeyResult{
   313  				Asm:       disbuf,
   314  				Hex:       hex.EncodeToString(vout.PkScript),
   315  				ReqSigs:   int32(reqSigs),
   316  				Type:      sc.String(),
   317  				Addresses: addrStrs,
   318  			},
   319  		})
   320  	}
   321  
   322  	return &chainjson.TxRawResult{
   323  		Hex:           hex.EncodeToString(hexTx),
   324  		Txid:          txid.String(),
   325  		BlockHash:     hash,
   326  		BlockHeight:   blockHeight,
   327  		Confirmations: confirmations,
   328  		Vout:          vouts,
   329  	}
   330  }
   331  
   332  // Add a transaction output and it's getrawtransaction data.
   333  func testAddTxOut(msgTx *wire.MsgTx, vout uint32, txHash, blockHash *chainhash.Hash, blockHeight, confirmations int64) *chainjson.GetTxOutResult {
   334  	txOut := testGetTxOut(confirmations, msgTx.TxOut[vout].PkScript)
   335  	testChainMtx.Lock()
   336  	testChain.txOuts[txOutID(txHash, vout)] = txOut
   337  	testChainMtx.Unlock()
   338  	testAddTxVerbose(msgTx, txHash, blockHash, blockHeight, confirmations)
   339  	return txOut
   340  }
   341  
   342  // Add a chainjson.TxRawResult to the blockchain.
   343  func testAddTxVerbose(msgTx *wire.MsgTx, txHash, blockHash *chainhash.Hash, blockHeight, confirmations int64) *chainjson.TxRawResult {
   344  	tx := testRawTransactionVerbose(msgTx, txHash, blockHash, blockHeight, confirmations)
   345  	testChainMtx.Lock()
   346  	defer testChainMtx.Unlock()
   347  	testChain.txRaws[*txHash] = tx
   348  	return tx
   349  }
   350  
   351  // Add a GetBlockVerboseResult to the blockchain.
   352  func testAddBlockVerbose(blockHash *chainhash.Hash, confirmations int64, height uint32, voteBits uint16) *chainhash.Hash {
   353  	if blockHash == nil {
   354  		blockHash = randomHash()
   355  	}
   356  	testChainMtx.Lock()
   357  	defer testChainMtx.Unlock()
   358  	if voteBits&1 != 0 {
   359  		testChain.hashes[int64(height)] = blockHash
   360  	}
   361  	testChain.blocks[*blockHash] = &chainjson.GetBlockVerboseResult{
   362  		Hash:          blockHash.String(),
   363  		Confirmations: confirmations,
   364  		Height:        int64(height),
   365  		VoteBits:      voteBits,
   366  	}
   367  	return blockHash
   368  }
   369  
   370  // An element of the TxRawResult vout array.
   371  func testVout(value float64, pkScript []byte) chainjson.Vout {
   372  	return chainjson.Vout{
   373  		Value: value,
   374  		ScriptPubKey: chainjson.ScriptPubKeyResult{
   375  			Hex: hex.EncodeToString(pkScript),
   376  		},
   377  	}
   378  }
   379  
   380  // An element of the TxRawResult vin array.
   381  func testVin(txHash *chainhash.Hash, vout uint32) chainjson.Vin {
   382  	return chainjson.Vin{
   383  		Txid: txHash.String(),
   384  		Vout: vout,
   385  	}
   386  }
   387  
   388  type testAuth struct {
   389  	pubkey []byte
   390  	pkHash []byte
   391  	msg    []byte
   392  	sig    []byte
   393  }
   394  
   395  type testMsgTx struct {
   396  	tx     *wire.MsgTx
   397  	auth   *testAuth
   398  	vout   uint32
   399  	script []byte
   400  }
   401  
   402  // Generate a public key on the secp256k1 curve.
   403  func genPubkey() ([]byte, []byte) {
   404  	priv, err := secp256k1.GeneratePrivateKey()
   405  	if err != nil {
   406  		panic(err.Error())
   407  	}
   408  	pub := priv.PubKey()
   409  	pubkey := pub.SerializeCompressed()
   410  	pkHash := dcrutil.Hash160(pubkey)
   411  	return pubkey, pkHash
   412  }
   413  
   414  func s256Auth(msg []byte) *testAuth {
   415  	priv, err := secp256k1.GeneratePrivateKey()
   416  	if err != nil {
   417  		fmt.Printf("s256Auth error: %v\n", err)
   418  	}
   419  	pubkey := priv.PubKey().SerializeCompressed()
   420  	if msg == nil {
   421  		msg = randomBytes(32)
   422  	}
   423  	hash := chainhash.HashB(msg)
   424  	sig := ecdsa.Sign(priv, hash)
   425  	return &testAuth{
   426  		pubkey: pubkey,
   427  		pkHash: dcrutil.Hash160(pubkey),
   428  		msg:    msg,
   429  		sig:    sig.Serialize(),
   430  	}
   431  }
   432  
   433  func edwardsAuth(msg []byte) *testAuth {
   434  	priv, err := edwards.GeneratePrivateKey()
   435  	if err != nil {
   436  		fmt.Printf("edwardsAuth error: %v\n", err)
   437  	}
   438  	pubkey := priv.PubKey().Serialize()
   439  	if msg == nil {
   440  		msg = randomBytes(32)
   441  	}
   442  	hash := chainhash.HashB(msg)
   443  	sig, err := priv.Sign(hash)
   444  	if err != nil {
   445  		fmt.Printf("edwardsAuth sign error: %v\n", err)
   446  	}
   447  	return &testAuth{
   448  		pubkey: pubkey,
   449  		pkHash: dcrutil.Hash160(pubkey),
   450  		msg:    msg,
   451  		sig:    sig.Serialize(),
   452  	}
   453  }
   454  
   455  func schnorrAuth(msg []byte) *testAuth {
   456  	priv, err := secp256k1.GeneratePrivateKey()
   457  	if err != nil {
   458  		fmt.Printf("schnorrAuth error: %v\n", err)
   459  	}
   460  	pubkey := priv.PubKey().SerializeCompressed()
   461  	if msg == nil {
   462  		msg = randomBytes(32)
   463  	}
   464  	hash := chainhash.HashB(msg)
   465  	sig, err := schnorr.Sign(priv, hash)
   466  	if err != nil {
   467  		fmt.Printf("schnorrAuth sign error: %v\n", err)
   468  	}
   469  	return &testAuth{
   470  		pubkey: pubkey,
   471  		pkHash: dcrutil.Hash160(pubkey),
   472  		msg:    msg,
   473  		sig:    sig.Serialize(),
   474  	}
   475  }
   476  
   477  // A pay-to-script-hash pubkey script.
   478  func newP2PKHScript(sigType dcrec.SignatureType) ([]byte, *testAuth) {
   479  	var auth *testAuth
   480  	var addr stdaddr.Address
   481  	var err error
   482  	switch sigType {
   483  	case dcrec.STEcdsaSecp256k1:
   484  		auth = s256Auth(nil)
   485  		addr, err = stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(auth.pkHash, chainParams)
   486  	case dcrec.STEd25519:
   487  		auth = edwardsAuth(nil)
   488  		addr, err = stdaddr.NewAddressPubKeyHashEd25519V0(auth.pkHash, chainParams)
   489  	case dcrec.STSchnorrSecp256k1:
   490  		auth = schnorrAuth(nil)
   491  		addr, err = stdaddr.NewAddressPubKeyHashSchnorrSecp256k1V0(auth.pkHash, chainParams)
   492  	default:
   493  		fmt.Printf("newP2PKHScript unknown sigType")
   494  		return nil, nil
   495  	}
   496  	if err != nil {
   497  		fmt.Printf("NewAddressPubKeyHash error: %v\n", err)
   498  		return nil, nil
   499  	}
   500  	_, pkScript := addr.PaymentScript()
   501  	if err != nil {
   502  		fmt.Printf("addr PaymentScript error: %v\n", err)
   503  	}
   504  	return pkScript, auth
   505  }
   506  
   507  // A pay-to-script-hash pubkey script, with a prepended stake-tree indicator
   508  // byte.
   509  func newStakeP2PKHScript(opcode byte) ([]byte, *testAuth) {
   510  	script, auth := newP2PKHScript(dcrec.STEcdsaSecp256k1)
   511  	stakeScript := make([]byte, 0, len(script)+1)
   512  	stakeScript = append(stakeScript, opcode)
   513  	stakeScript = append(stakeScript, script...)
   514  	return stakeScript, auth
   515  }
   516  
   517  // A MsgTx for a regular transaction with a single output. No inputs, so it's
   518  // not really a valid transaction, but that's okay on testBlockchain and
   519  // irrelevant to Backend.
   520  func testMsgTxRegular(sigType dcrec.SignatureType) *testMsgTx {
   521  	pkScript, auth := newP2PKHScript(sigType)
   522  	msgTx := wire.NewMsgTx()
   523  	msgTx.AddTxOut(wire.NewTxOut(1, pkScript))
   524  	return &testMsgTx{
   525  		tx:   msgTx,
   526  		auth: auth,
   527  	}
   528  }
   529  
   530  // Information about a swap contract.
   531  type testMsgTxSwap struct {
   532  	tx        *wire.MsgTx
   533  	contract  []byte
   534  	recipient stdaddr.Address
   535  	refund    stdaddr.Address
   536  }
   537  
   538  // Create a swap (initialization) contract with random pubkeys and return the
   539  // pubkey script and addresses.
   540  func testSwapContract() ([]byte, stdaddr.Address, stdaddr.Address) {
   541  	lockTime := time.Now().Add(time.Hour * 8).Unix()
   542  	secretKey := randomBytes(32)
   543  	_, receiverPKH := genPubkey()
   544  	_, senderPKH := genPubkey()
   545  	contract, err := txscript.NewScriptBuilder().
   546  		AddOps([]byte{
   547  			txscript.OP_IF,
   548  			txscript.OP_SIZE,
   549  		}).AddInt64(32).
   550  		AddOps([]byte{
   551  			txscript.OP_EQUALVERIFY,
   552  			txscript.OP_SHA256,
   553  		}).AddData(secretKey).
   554  		AddOps([]byte{
   555  			txscript.OP_EQUALVERIFY,
   556  			txscript.OP_DUP,
   557  			txscript.OP_HASH160,
   558  		}).AddData(receiverPKH).
   559  		AddOp(txscript.OP_ELSE).
   560  		AddInt64(lockTime).AddOps([]byte{
   561  		txscript.OP_CHECKLOCKTIMEVERIFY,
   562  		txscript.OP_DROP,
   563  		txscript.OP_DUP,
   564  		txscript.OP_HASH160,
   565  	}).AddData(senderPKH).
   566  		AddOps([]byte{
   567  			txscript.OP_ENDIF,
   568  			txscript.OP_EQUALVERIFY,
   569  			txscript.OP_CHECKSIG,
   570  		}).Script()
   571  	if err != nil {
   572  		fmt.Printf("testSwapContract error: %v\n", err)
   573  	}
   574  	receiverAddr, _ := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(receiverPKH, chainParams)
   575  	refundAddr, _ := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(senderPKH, chainParams)
   576  	return contract, receiverAddr, refundAddr
   577  }
   578  
   579  // Create a transaction with a P2SH swap output at vout 0.
   580  func testMsgTxSwapInit(val int64) *testMsgTxSwap {
   581  	msgTx := wire.NewMsgTx()
   582  	contract, recipient, refund := testSwapContract()
   583  	scriptHash := stdaddr.Hash160(contract)
   584  	pkScript, err := txscript.NewScriptBuilder().
   585  		AddOp(txscript.OP_HASH160).
   586  		AddData(scriptHash).
   587  		AddOp(txscript.OP_EQUAL).
   588  		Script()
   589  	if err != nil {
   590  		fmt.Printf("script building error in testMsgTxSwapInit: %v", err)
   591  	}
   592  	msgTx.AddTxOut(wire.NewTxOut(val, pkScript))
   593  	return &testMsgTxSwap{
   594  		tx:        msgTx,
   595  		contract:  contract,
   596  		recipient: recipient,
   597  		refund:    refund,
   598  	}
   599  }
   600  
   601  // A MsgTx for a vote. Votes have a stricter set of requirements to pass
   602  // txscript.IsSSGen, so some extra inputs and outputs must be constructed.
   603  func testMsgTxVote() *testMsgTx {
   604  	msgTx := wire.NewMsgTx()
   605  	stakeBase := wire.NewTxIn(wire.NewOutPoint(&zeroHash, math.MaxUint32, 0), 0, nil)
   606  	stakeBase.BlockHeight = wire.NullBlockHeight
   607  	stakeBase.BlockIndex = wire.NullBlockIndex
   608  	msgTx.AddTxIn(stakeBase)
   609  	// Second outpoint needs to be stake tree
   610  	msgTx.AddTxIn(wire.NewTxIn(wire.NewOutPoint(&zeroHash, 0, 1), 0, nil))
   611  	// First output must have OP_RETURN script
   612  	script1, err := txscript.NewScriptBuilder().
   613  		AddOp(txscript.OP_RETURN).AddData(randomBytes(36)).Script()
   614  	if err != nil {
   615  		fmt.Printf("script1 building error in testMsgTxVote: %v", err)
   616  	}
   617  	msgTx.AddTxOut(wire.NewTxOut(0, script1))
   618  	// output 2
   619  	script2, err := txscript.NewScriptBuilder().
   620  		AddOp(txscript.OP_RETURN).AddData(randomBytes(2)).Script()
   621  	if err != nil {
   622  		fmt.Printf("script2 building error in testMsgTxVote: %v", err)
   623  	}
   624  	msgTx.AddTxOut(wire.NewTxOut(1, script2))
   625  	// Now just a P2PKH script with a prepended OP_SSGEN
   626  	script3, auth := newStakeP2PKHScript(txscript.OP_SSGEN)
   627  	msgTx.AddTxOut(wire.NewTxOut(2, script3))
   628  	return &testMsgTx{
   629  		tx:   msgTx,
   630  		auth: auth,
   631  		vout: 2,
   632  	}
   633  }
   634  
   635  type testMultiSigAuth struct {
   636  	pubkeys  [][]byte
   637  	pkHashes [][]byte
   638  	msg      []byte
   639  	sigs     [][]byte
   640  }
   641  
   642  // Information about a transaction with a P2SH output.
   643  type testMsgTxP2SH struct {
   644  	tx     *wire.MsgTx
   645  	auth   *testMultiSigAuth
   646  	vout   uint32
   647  	script []byte
   648  	n      int
   649  	m      int
   650  }
   651  
   652  func multiSigScript(pubkeys []*stdaddr.AddressPubKeyEcdsaSecp256k1V0, numReq int64) ([]byte, error) {
   653  	builder := txscript.NewScriptBuilder().AddInt64(numReq)
   654  	for _, key := range pubkeys {
   655  		builder.AddData(key.SerializedPubKey())
   656  	}
   657  	builder.AddInt64(int64(len(pubkeys))).AddOp(txscript.OP_CHECKMULTISIG)
   658  	return builder.Script()
   659  }
   660  
   661  // An M-of-N mutli-sig script. This script is pay-to-pubkey.
   662  func testMultiSigScriptMofN(m, n int) ([]byte, *testMultiSigAuth) {
   663  	// serialized compressed pubkey used for multisig
   664  	addrs := make([]*stdaddr.AddressPubKeyEcdsaSecp256k1V0, 0, n)
   665  	auth := &testMultiSigAuth{
   666  		msg: randomBytes(32),
   667  	}
   668  
   669  	for i := 0; i < m; i++ {
   670  		a := s256Auth(auth.msg)
   671  		auth.pubkeys = append(auth.pubkeys, a.pubkey)
   672  		auth.pkHashes = append(auth.pkHashes, a.pkHash)
   673  		auth.sigs = append(auth.sigs, a.sig)
   674  		addr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0Raw(a.pubkey, chainParams)
   675  		if err != nil {
   676  			fmt.Printf("error creating AddressSecpPubKey: %v", err)
   677  			return nil, nil
   678  		}
   679  		addrs = append(addrs, addr)
   680  	}
   681  	script, err := multiSigScript(addrs, int64(m))
   682  	if err != nil {
   683  		fmt.Printf("error creating MultiSigScript: %v", err)
   684  		return nil, nil
   685  	}
   686  	return script, auth
   687  }
   688  
   689  // A pay-to-script-hash M-of-N multi-sig MsgTx.
   690  func testMsgTxP2SHMofN(m, n int) *testMsgTxP2SH {
   691  	script, auth := testMultiSigScriptMofN(m, n)
   692  	pkScript := make([]byte, 0, 23)
   693  	pkScript = append(pkScript, txscript.OP_HASH160)
   694  	pkScript = append(pkScript, txscript.OP_DATA_20)
   695  	scriptHash := dcrutil.Hash160(script)
   696  	pkScript = append(pkScript, scriptHash...)
   697  	pkScript = append(pkScript, txscript.OP_EQUAL)
   698  	msgTx := wire.NewMsgTx()
   699  	msgTx.AddTxOut(wire.NewTxOut(1, pkScript))
   700  	return &testMsgTxP2SH{
   701  		tx:     msgTx,
   702  		auth:   auth,
   703  		script: script,
   704  		vout:   0,
   705  		n:      n,
   706  		m:      m,
   707  	}
   708  }
   709  
   710  // A pay-to-script hash with a P2SH output. I'm fairly certain this would be an
   711  // uncommon choice in practice, but valid nonetheless.
   712  func testMsgTxP2SHVote() *testMsgTx {
   713  	// Need to pull a little switcharoo, taking the pk script as the redeem script
   714  	// and subbing in a p2sh script.
   715  	msg := testMsgTxVote()
   716  	ogScript := msg.tx.TxOut[msg.vout].PkScript
   717  	pkScript := make([]byte, 0, 24)
   718  	pkScript = append(pkScript, txscript.OP_SSGEN)
   719  	pkScript = append(pkScript, txscript.OP_HASH160)
   720  	pkScript = append(pkScript, txscript.OP_DATA_20)
   721  	scriptHash := stdaddr.Hash160(ogScript)
   722  	pkScript = append(pkScript, scriptHash...)
   723  	pkScript = append(pkScript, txscript.OP_EQUAL)
   724  	msg.tx.TxOut[msg.vout].PkScript = pkScript
   725  	msg.script = ogScript
   726  	return msg
   727  }
   728  
   729  // A revocation MsgTx.
   730  func testMsgTxRevocation() *testMsgTx {
   731  	msgTx := wire.NewMsgTx()
   732  	// Need a single input from stake tree
   733  	msgTx.AddTxIn(wire.NewTxIn(wire.NewOutPoint(&zeroHash, 0, 1), 0, nil))
   734  	// All outputs must have OP_SSRTX prefix.
   735  	script, auth := newStakeP2PKHScript(txscript.OP_SSRTX)
   736  	msgTx.AddTxOut(wire.NewTxOut(1, script))
   737  	return &testMsgTx{
   738  		tx:   msgTx,
   739  		auth: auth,
   740  	}
   741  }
   742  
   743  // Make a backend that logs to stdout.
   744  func testBackend() (*Backend, func()) {
   745  	dcr := unconnectedDCR(&asset.BackendConfig{Logger: tLogger}, nil) // never actually Connect, so no rpc config
   746  	dcr.node = &testNode{}
   747  
   748  	ctx, cancel := context.WithCancel(context.Background())
   749  	dcr.ctx = ctx
   750  
   751  	var wg sync.WaitGroup
   752  	shutdown := func() {
   753  		cancel()
   754  		wg.Wait()
   755  	}
   756  	wg.Add(1)
   757  	go func() {
   758  		dcr.run(ctx)
   759  		wg.Done()
   760  	}()
   761  	return dcr, shutdown
   762  }
   763  
   764  // TestUTXOs tests all UTXO related paths.
   765  func TestUTXOs(t *testing.T) {
   766  	// The various UTXO types to check:
   767  	// 1. A valid UTXO in a mempool transaction
   768  	// 2. A valid UTXO in a mined block. All three signature types
   769  	// 3. A UTXO that is invalid because it is non-existent. This case covers
   770  	//    other important cases, as dcrd will only return a result from
   771  	//    GetTxOut if the utxo is valid and ready to spend.
   772  	// 4. A UTXO that is invalid because it has the wrong script type
   773  	// 5. A UTXO that is invalidated because it is a regular tree tx from a
   774  	//    stakeholder invalidated block
   775  	// 6. A UTXO that is valid even though it is from a stakeholder invalidated
   776  	//    block, because it is a stake tree tx
   777  	// 7. A UTXO that becomes invalid in a reorg
   778  	// 8. A UTXO that is in an orphaned block, but also included in a new
   779  	//     mainchain block, so is still valid.
   780  	// 9. A UTXO with a pay-to-script-hash for a 1-of-2 multisig redeem script
   781  	// 10. A UTXO with a pay-to-script-hash for a 2-of-2 multisig redeem script
   782  	// 11. A UTXO with a pay-to-script-hash for a P2PKH redeem script.
   783  	// 12. A revocation.
   784  
   785  	// Create a Backend with the test node.
   786  	dcr, shutdown := testBackend()
   787  	defer shutdown()
   788  	ctx := dcr.ctx // for the functions then take a context, canceled on shutdown()
   789  
   790  	// The vout will be randomized during reset.
   791  	txHeight := uint32(32)
   792  
   793  	// A general reset function that clears the testBlockchain and the blockCache.
   794  	reset := func() {
   795  		cleanTestChain()
   796  		blockCache := newBlockCache(dcr.log)
   797  		dcr.blockCache.mtx.Lock()
   798  		dcr.blockCache.blocks = blockCache.blocks
   799  		dcr.blockCache.mainchain = blockCache.mainchain
   800  		dcr.blockCache.best = blockCache.best
   801  		dcr.blockCache.mtx.Unlock()
   802  	}
   803  
   804  	// CASE 1: A valid UTXO in a mempool transaction. This UTXO will have zero
   805  	// confirmations, a valid pkScript and will not be marked as coinbase. Then
   806  	// add a block that includes the transaction, and check that Confirmations
   807  	// updates correctly.
   808  	reset()
   809  	txHash := randomHash()
   810  	blockHash := randomHash()
   811  	msg := testMsgTxRegular(dcrec.STEcdsaSecp256k1)
   812  	// For a regular test tx, the output is at output index 0. Pass nil for the
   813  	// block hash and 0 for the block height and confirmations for a mempool tx.
   814  	txout := testAddTxOut(msg.tx, msg.vout, txHash, nil, 0, 0)
   815  	// Set the value of this one.
   816  	txout.Value = 2.0
   817  	testAddTxVerbose(msg.tx, txHash, nil, 0, 0)
   818  	// There is no block info to add, since this is a mempool transaction
   819  	utxo, err := dcr.utxo(ctx, txHash, msg.vout, nil)
   820  	if err != nil {
   821  		t.Fatalf("case 1 - unexpected error: %v", err)
   822  	}
   823  	// While we're here, check the spend size and value are correct.
   824  	spendSize := utxo.SpendSize()
   825  	wantSpendSize := uint32(dexdcr.TxInOverhead + 1 + dexdcr.P2PKHSigScriptSize)
   826  	if spendSize != wantSpendSize {
   827  		t.Fatalf("case 1 - unexpected spend script size reported. expected %d, got %d", wantSpendSize, spendSize)
   828  	}
   829  	if utxo.Value() != 200_000_000 {
   830  		t.Fatalf("case 1 - unexpected output value. expected 200,000,000, got %d", utxo.Value())
   831  	}
   832  	// Now "mine" the transaction.
   833  	testAddBlockVerbose(blockHash, 1, txHeight, 1)
   834  	// Overwrite the test blockchain transaction details.
   835  	testAddTxOut(msg.tx, 0, txHash, blockHash, int64(txHeight), 1)
   836  	testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), 1)
   837  	confs, err := utxo.Confirmations(ctx)
   838  	if err != nil {
   839  		t.Fatalf("case 1 - error retrieving confirmations after transaction \"mined\": %v", err)
   840  	}
   841  	if confs != 1 {
   842  		// The confirmation count is not taken from the txOut.Confirmations, so
   843  		// need to check that it is correct.
   844  		t.Fatalf("case 1 - expected 1 confirmation after mining transaction, found %d", confs)
   845  	}
   846  	// Make sure the pubkey spends the output.
   847  	err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
   848  	if err != nil {
   849  		t.Fatalf("case 1 - Auth error: %v", err)
   850  	}
   851  
   852  	// CASE 2: A valid UTXO in a mined block. This UTXO will have non-zero
   853  	// confirmations, a valid pkScipt. Test all three signature types.
   854  	for _, sigType := range []dcrec.SignatureType{dcrec.STEcdsaSecp256k1, dcrec.STEd25519, dcrec.STSchnorrSecp256k1} {
   855  		reset()
   856  		blockHash = testAddBlockVerbose(nil, 1, txHeight, 1)
   857  		txHash = randomHash()
   858  		msg = testMsgTxRegular(sigType)
   859  		testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), 1)
   860  		testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), 1)
   861  		utxo, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   862  		if err != nil {
   863  			t.Fatalf("case 2 - unexpected error for sig type %d: %v", int(sigType), err)
   864  		}
   865  		err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
   866  		if err != nil {
   867  			t.Fatalf("case 2 - Auth error with sig type %d: %v", int(sigType), err)
   868  		}
   869  	}
   870  
   871  	// CASE 3: A UTXO that is invalid because it is non-existent
   872  	reset()
   873  	_, err = dcr.utxo(ctx, randomHash(), 0, nil)
   874  	if err == nil {
   875  		t.Fatalf("case 3 - received no error for a non-existent UTXO")
   876  	}
   877  
   878  	// CASE 4: A UTXO that is invalid because it has the wrong script type.
   879  	reset()
   880  	blockHash = testAddBlockVerbose(nil, 1, txHeight, 1)
   881  	txHash = randomHash()
   882  	msg = testMsgTxRegular(dcrec.STEcdsaSecp256k1)
   883  	// make the script nonsense.
   884  	msg.tx.TxOut[0].PkScript = []byte{0x00, 0x01, 0x02, 0x03}
   885  	testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), 1)
   886  	testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), 1)
   887  	_, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   888  	if err == nil {
   889  		t.Fatalf("case 4 - received no error for a UTXO with wrong script type")
   890  	}
   891  
   892  	// CASE 5: A UTXO that is invalid because it is a regular tree tx from a
   893  	// stakeholder invalidated block. The transaction is valid when it has 1
   894  	// confirmation, but is invalidated by the next block.
   895  	reset()
   896  	blockHash = testAddBlockVerbose(nil, 1, txHeight, 1)
   897  	txHash = randomHash()
   898  	msg = testMsgTxRegular(dcrec.STEcdsaSecp256k1)
   899  	testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), 1)
   900  	testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), 1)
   901  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   902  	if err != nil {
   903  		t.Fatalf("case 5 - unexpected error: %v", err)
   904  	}
   905  	// Now reject the block. First update the confirmations.
   906  	testAddBlockVerbose(blockHash, 2, txHeight, 1)
   907  	rejectingHash := testAddBlockVerbose(nil, 1, txHeight+1, 0)
   908  	rejectingBlock := testChain.blocks[*rejectingHash]
   909  	_, err = dcr.blockCache.add(rejectingBlock)
   910  	if err != nil {
   911  		t.Fatalf("case 5 - error adding to block cache: %v", err)
   912  	}
   913  	_, err = utxo.Confirmations(ctx)
   914  	if err == nil {
   915  		t.Fatalf("case 5 - block not detected as invalid after stakeholder invalidation")
   916  	}
   917  
   918  	// CASE 6: A UTXO that is valid even though it is from a stakeholder
   919  	// invalidated block, because it is a stake tree tx. First try with an
   920  	// immature vote output, then add a maturing block and try again.
   921  	reset()
   922  	txHash = randomHash()
   923  	immatureHash := testAddBlockVerbose(nil, 2, txHeight, 1)
   924  	msg = testMsgTxVote()
   925  	testAddTxOut(msg.tx, msg.vout, txHash, immatureHash, int64(txHeight), 1)
   926  	testAddTxVerbose(msg.tx, txHash, immatureHash, int64(txHeight), 1)
   927  	_, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   928  	if err == nil {
   929  		t.Fatalf("case 6 - no error for immature transaction")
   930  	}
   931  	// Now reject the block, but mature the transaction. It should still be
   932  	// accepted since it is a stake tree transaction.
   933  	rejectingHash = testAddBlockVerbose(nil, 1, txHeight+1, 0)
   934  	rejectingBlock = testChain.blocks[*rejectingHash]
   935  	_, err = dcr.blockCache.add(rejectingBlock)
   936  	if err != nil {
   937  		t.Fatalf("case 6 - error adding to rejecting block cache: %v", err)
   938  	}
   939  	maturity := int64(chainParams.CoinbaseMaturity)
   940  	testAddBlockVerbose(blockHash, maturity, txHeight, 1)
   941  	testAddBlockVerbose(rejectingHash, maturity-1, txHeight, 1)
   942  	maturingHash := testAddBlockVerbose(nil, 1, txHeight+uint32(maturity)-1, 1)
   943  	maturingBlock := testChain.blocks[*maturingHash]
   944  	_, err = dcr.blockCache.add(maturingBlock)
   945  	if err != nil {
   946  		t.Fatalf("case 6 - error adding to maturing block cache: %v", err)
   947  	}
   948  	testAddTxOut(msg.tx, msg.vout, txHash, immatureHash, int64(txHeight), int64(txHeight)+maturity-1)
   949  	testAddTxVerbose(msg.tx, txHash, immatureHash, int64(txHeight), int64(txHeight)+maturity-1)
   950  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   951  	if err != nil {
   952  		t.Fatalf("case 6 - unexpected error after maturing block: %v", err)
   953  	}
   954  	// Since this is our first stake transaction, let's check the pubkey
   955  	err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
   956  	if err != nil {
   957  		t.Fatalf("case 6 - Auth error: %v", err)
   958  	}
   959  
   960  	// CASE 7: A UTXO that becomes invalid in a reorg
   961  	reset()
   962  	txHash = randomHash()
   963  	blockHash = testAddBlockVerbose(nil, 1, txHeight, 1)
   964  	msg = testMsgTxRegular(dcrec.STEcdsaSecp256k1)
   965  	testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), 1)
   966  	testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), 1)
   967  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   968  	if err != nil {
   969  		t.Fatalf("case 7 - received error for utxo")
   970  	}
   971  	_, err = utxo.Confirmations(ctx)
   972  	if err != nil {
   973  		t.Fatalf("case 7 - received error before reorg")
   974  	}
   975  	betterHash := testAddBlockVerbose(nil, 1, txHeight, 1)
   976  	dcr.blockCache.add(testChain.blocks[*betterHash])
   977  	dcr.blockCache.reorg(int64(txHeight))
   978  	// Remove the txout from the blockchain, since dcrd would no longer return it.
   979  	testChainMtx.Lock()
   980  	delete(testChain.txOuts, txOutID(txHash, msg.vout))
   981  	testChainMtx.Unlock()
   982  	_, err = utxo.Confirmations(ctx)
   983  	if err == nil {
   984  		t.Fatalf("case 7 - received no error for orphaned transaction")
   985  	}
   986  
   987  	// CASE 8: A UTXO that is in an orphaned block, but also included in a new
   988  	// mainchain block, so is still valid.
   989  	reset()
   990  	txHash = randomHash()
   991  	orphanHash := testAddBlockVerbose(nil, 1, txHeight, 1)
   992  	msg = testMsgTxRegular(dcrec.STEcdsaSecp256k1)
   993  	testAddTxOut(msg.tx, msg.vout, txHash, orphanHash, int64(txHeight), 1)
   994  	testAddTxVerbose(msg.tx, txHash, orphanHash, int64(txHeight), 1)
   995  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, nil)
   996  	if err != nil {
   997  		t.Fatalf("case 8 - received error for utxo")
   998  	}
   999  	// Now orphan the block, by doing a reorg.
  1000  	betterHash = testAddBlockVerbose(nil, 1, txHeight, 1)
  1001  	dcr.blockCache.reorg(int64(txHeight))
  1002  	dcr.blockCache.add(testChain.blocks[*betterHash])
  1003  	testAddTxOut(msg.tx, msg.vout, txHash, betterHash, int64(txHeight), 1)
  1004  	testAddTxVerbose(msg.tx, txHash, betterHash, int64(txHeight), 1)
  1005  	_, err = utxo.Confirmations(ctx)
  1006  	if err != nil {
  1007  		t.Fatalf("case 8 - unexpected error after reorg: %v", err)
  1008  	}
  1009  	if utxo.blockHash != *betterHash {
  1010  		t.Fatalf("case 8 - unexpected hash for utxo after reorg")
  1011  	}
  1012  	// Do it again, but this time, put the utxo into mempool.
  1013  	evenBetter := testAddBlockVerbose(nil, 1, txHeight, 1)
  1014  	dcr.blockCache.reorg(int64(txHeight))
  1015  	dcr.blockCache.add(testChain.blocks[*evenBetter])
  1016  	testAddTxOut(msg.tx, msg.vout, txHash, evenBetter, 0, 0)
  1017  	testAddTxVerbose(msg.tx, txHash, evenBetter, 0, 0)
  1018  	_, err = utxo.Confirmations(ctx)
  1019  	if err != nil {
  1020  		t.Fatalf("case 8 - unexpected error for mempool tx after reorg")
  1021  	}
  1022  	if utxo.height != 0 {
  1023  		t.Fatalf("case 8 - unexpected height %d after dumping into mempool", utxo.height)
  1024  	}
  1025  
  1026  	// CASE 9: A UTXO with a pay-to-script-hash for a 1-of-2 multisig redeem
  1027  	// script
  1028  	reset()
  1029  	txHash = randomHash()
  1030  	blockHash = testAddBlockVerbose(nil, 1, txHeight, 1)
  1031  	msgMultiSig := testMsgTxP2SHMofN(1, 2)
  1032  	testAddTxOut(msgMultiSig.tx, msgMultiSig.vout, txHash, blockHash, int64(txHeight), 1)
  1033  	testAddTxVerbose(msgMultiSig.tx, txHash, blockHash, int64(txHeight), 1)
  1034  	// First try to get the UTXO without providing the raw script.
  1035  	_, err = dcr.utxo(ctx, txHash, msgMultiSig.vout, nil)
  1036  	if err == nil {
  1037  		t.Fatalf("no error thrown for p2sh utxo when no script was provided")
  1038  	}
  1039  	// Now provide the script.
  1040  	utxo, err = dcr.utxo(ctx, txHash, msgMultiSig.vout, msgMultiSig.script)
  1041  	if err != nil {
  1042  		t.Fatalf("case 9 - received error for utxo: %v", err)
  1043  	}
  1044  	confs, err = utxo.Confirmations(ctx)
  1045  	if err != nil {
  1046  		t.Fatalf("case 9 - error getting confirmations: %v", err)
  1047  	}
  1048  	if confs != 1 {
  1049  		t.Fatalf("case 9 - expected 1 confirmation, got %d", confs)
  1050  	}
  1051  	err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg)
  1052  	if err != nil {
  1053  		t.Fatalf("case 9 - Auth error: %v", err)
  1054  	}
  1055  
  1056  	// CASE 10: A UTXO with a pay-to-script-hash for a 2-of-2 multisig redeem
  1057  	// script
  1058  	reset()
  1059  	txHash = randomHash()
  1060  	blockHash = testAddBlockVerbose(nil, 1, txHeight, 1)
  1061  	msgMultiSig = testMsgTxP2SHMofN(2, 2)
  1062  	testAddTxOut(msgMultiSig.tx, msgMultiSig.vout, txHash, blockHash, int64(txHeight), 1)
  1063  	testAddTxVerbose(msgMultiSig.tx, txHash, blockHash, int64(txHeight), 1)
  1064  	utxo, err = dcr.utxo(ctx, txHash, msgMultiSig.vout, msgMultiSig.script)
  1065  	if err != nil {
  1066  		t.Fatalf("case 10 - received error for utxo: %v", err)
  1067  	}
  1068  	// Try to get by with just one of the pubkeys.
  1069  	err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg)
  1070  	if err == nil {
  1071  		t.Fatalf("case 10 - no Auth error when only provided one of two required pubkeys")
  1072  	}
  1073  	// Now do both.
  1074  	err = utxo.Auth(msgMultiSig.auth.pubkeys, msgMultiSig.auth.sigs, msgMultiSig.auth.msg)
  1075  	if err != nil {
  1076  		t.Fatalf("case 10 - Auth error: %v", err)
  1077  	}
  1078  	// Try with a duplicate pubkey and signature.
  1079  	dupeKeys := [][]byte{msgMultiSig.auth.pubkeys[0], msgMultiSig.auth.pubkeys[0]}
  1080  	dupeSigs := [][]byte{msgMultiSig.auth.sigs[0], msgMultiSig.auth.sigs[0]}
  1081  	err = utxo.Auth(dupeKeys, dupeSigs, msgMultiSig.auth.msg)
  1082  	if err == nil {
  1083  		t.Fatalf("case 10 - no Auth error with duplicate keys/sigs")
  1084  	}
  1085  
  1086  	// CASE 11: A UTXO with a pay-to-script-hash for a P2PKH redeem script.
  1087  	reset()
  1088  	txHash = randomHash()
  1089  	blockHash = testAddBlockVerbose(nil, maturity, txHeight, 1)
  1090  	msg = testMsgTxP2SHVote()
  1091  	testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), maturity)
  1092  	testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), maturity)
  1093  	// mature the vote
  1094  	testAddBlockVerbose(nil, 1, txHeight+uint32(maturity)-1, 1)
  1095  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, msg.script)
  1096  	if err != nil {
  1097  		t.Fatalf("case 11 - received error for utxo: %v", err)
  1098  	}
  1099  	// Make sure it's marked as stake.
  1100  	if !utxo.scriptType.IsStake() {
  1101  		t.Fatalf("case 11 - stake p2sh not marked as stake")
  1102  	}
  1103  	// Give it nonsense.
  1104  	err = utxo.Auth([][]byte{randomBytes(33)}, [][]byte{randomBytes(33)}, randomBytes(32))
  1105  	if err == nil {
  1106  		t.Fatalf("case 11 - no Auth error when providing nonsense pubkey")
  1107  	}
  1108  	// Now give it the right one.
  1109  	err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
  1110  	if err != nil {
  1111  		t.Fatalf("case 11 - Auth error: %v", err)
  1112  	}
  1113  
  1114  	// CASE 12: A revocation.
  1115  	reset()
  1116  	txHash = randomHash()
  1117  	blockHash = testAddBlockVerbose(nil, maturity, txHeight, 1)
  1118  	msg = testMsgTxRevocation()
  1119  	testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), maturity)
  1120  	testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), maturity)
  1121  	// mature the revocation
  1122  	testAddBlockVerbose(nil, 1, txHeight+uint32(maturity)-1, 1)
  1123  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, msg.script)
  1124  	if err != nil {
  1125  		t.Fatalf("case 12 - received error for utxo: %v", err)
  1126  	}
  1127  	// Make sure it's marked as stake.
  1128  	if !utxo.scriptType.IsStake() {
  1129  		t.Fatalf("case 12 - stake p2sh not marked as stake")
  1130  	}
  1131  	// Check the pubkey.
  1132  	err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
  1133  	if err != nil {
  1134  		t.Fatalf("case 12 - Auth error: %v", err)
  1135  	}
  1136  
  1137  	// CASE 13: A swap contract
  1138  	val := uint64(5)
  1139  	cleanTestChain()
  1140  	txHash = randomHash()
  1141  	blockHash = randomHash()
  1142  	swap := testMsgTxSwapInit(int64(val))
  1143  	testAddBlockVerbose(blockHash, 1, txHeight, 1)
  1144  	testAddTxOut(swap.tx, 0, txHash, blockHash, int64(txHeight), 1).Value = float64(val) / 1e8
  1145  	testAddTxVerbose(swap.tx, txHash, blockHash, int64(txHeight), 1)
  1146  	verboseTx := testChain.txRaws[*txHash]
  1147  	spentTx := randomHash()
  1148  	spentVout := rand.Uint32()
  1149  	verboseTx.Vin = append(verboseTx.Vin, testVin(spentTx, spentVout))
  1150  	txOut := swap.tx.TxOut[0]
  1151  	verboseTx.Vout = append(verboseTx.Vout, testVout(float64(txOut.Value)/1e8, txOut.PkScript))
  1152  	utxo, err = dcr.utxo(ctx, txHash, 0, swap.contract)
  1153  	if err != nil {
  1154  		t.Fatalf("case 13 - received error for utxo: %v", err)
  1155  	}
  1156  
  1157  	// Now try again with the correct vout.
  1158  	contract, err := auditContract(utxo.Output) // sets refund and swap addresses
  1159  	if err != nil {
  1160  		t.Fatalf("case 13 - unexpected error auditing contract: %v", err)
  1161  	}
  1162  	if contract.SwapAddress != swap.recipient.String() {
  1163  		t.Fatalf("case 13 - wrong recipient. wanted '%s' got '%s'", contract.SwapAddress, swap.recipient.String())
  1164  	}
  1165  	if contract.Value() != val {
  1166  		t.Fatalf("case 13 - unexpected output value. wanted 5, got %d", contract.Value())
  1167  	}
  1168  
  1169  }
  1170  
  1171  func TestRedemption(t *testing.T) {
  1172  	dcr, shutdown := testBackend()
  1173  	defer shutdown()
  1174  	ctx := dcr.ctx // for the functions then take a context, canceled on shutdown()
  1175  
  1176  	// The vout will be randomized during reset.
  1177  	txHeight := uint32(32)
  1178  	cleanTestChain()
  1179  	txHash := randomHash()
  1180  	redemptionID := toCoinID(txHash, 0)
  1181  	// blockHash := randomHash()
  1182  	spentHash := randomHash()
  1183  	spentVout := uint32(5)
  1184  	spentID := toCoinID(spentHash, spentVout)
  1185  	msg := testMsgTxRegular(dcrec.STEcdsaSecp256k1)
  1186  	vin := chainjson.Vin{
  1187  		Txid: spentHash.String(),
  1188  		Vout: spentVout,
  1189  	}
  1190  
  1191  	// A valid mempool redemption.
  1192  	verboseTx := testAddTxVerbose(msg.tx, txHash, nil, 0, 0)
  1193  	verboseTx.Vin = append(verboseTx.Vin, vin)
  1194  	redemption, err := dcr.Redemption(redemptionID, spentID, nil)
  1195  	if err != nil {
  1196  		t.Fatalf("Redemption error: %v", err)
  1197  	}
  1198  	confs, err := redemption.Confirmations(ctx)
  1199  	if err != nil {
  1200  		t.Fatalf("redemption Confirmations error: %v", err)
  1201  	}
  1202  	if confs != 0 {
  1203  		t.Fatalf("expected 0 confirmations, got %d", confs)
  1204  	}
  1205  
  1206  	// Missing transaction
  1207  	testChainMtx.Lock()
  1208  	delete(testChain.txRaws, *txHash)
  1209  	testChainMtx.Unlock()
  1210  	_, err = dcr.Redemption(redemptionID, spentID, nil)
  1211  	if err == nil {
  1212  		t.Fatalf("No error for missing transaction")
  1213  	}
  1214  
  1215  	// Doesn't spend transaction.
  1216  	verboseTx = testAddTxVerbose(msg.tx, txHash, nil, 0, 0)
  1217  	verboseTx.Vin = append(verboseTx.Vin, chainjson.Vin{
  1218  		Txid: randomHash().String(),
  1219  	})
  1220  	_, err = dcr.Redemption(redemptionID, spentID, nil)
  1221  	if err == nil {
  1222  		t.Fatalf("No error for wrong previous outpoint")
  1223  	}
  1224  
  1225  	// Mined transaction.
  1226  	blockHash := randomHash()
  1227  	blockHeight := txHeight - 1
  1228  	verboseTx = testAddTxVerbose(msg.tx, txHash, blockHash, int64(blockHeight), 1)
  1229  	verboseTx.Vin = append(verboseTx.Vin, vin)
  1230  	testAddBlockVerbose(blockHash, 1, blockHeight, 1)
  1231  	redemption, err = dcr.Redemption(redemptionID, spentID, nil)
  1232  	if err != nil {
  1233  		t.Fatalf("Redemption with confs error: %v", err)
  1234  	}
  1235  	confs, err = redemption.Confirmations(ctx)
  1236  	if err != nil {
  1237  		t.Fatalf("redemption with confs Confirmations error: %v", err)
  1238  	}
  1239  	if confs != 1 {
  1240  		t.Fatalf("expected 1 confirmation, got %d", confs)
  1241  	}
  1242  }
  1243  
  1244  // TestReorg sends a reorganization-causing block through the anyQ channel, and
  1245  // checks that the cache is responding as expected.
  1246  func TestReorg(t *testing.T) {
  1247  	// Create a Backend with the test node.
  1248  	dcr, shutdown := testBackend()
  1249  	defer shutdown()
  1250  	ctx := dcr.ctx // for the functions then take a context, canceled on shutdown()
  1251  
  1252  	// A general reset function that clears the testBlockchain and the blockCache.
  1253  	var tipHeight uint32 = 10
  1254  	var tipHash *chainhash.Hash
  1255  	reset := func() {
  1256  		cleanTestChain()
  1257  		dcr.blockCache = newBlockCache(dcr.log)
  1258  		for h := uint32(0); h <= tipHeight; h++ {
  1259  			blockHash := testAddBlockVerbose(nil, int64(tipHeight-h+1), h, 1)
  1260  			// force dcr to get and cache the block
  1261  			_, err := dcr.getDcrBlock(ctx, blockHash)
  1262  			if err != nil {
  1263  				t.Fatalf("getDcrBlock: %v", err)
  1264  			}
  1265  		}
  1266  		// Check that the tip is at the expected height and the block is mainchain.
  1267  		block, found := dcr.blockCache.atHeight(tipHeight)
  1268  		if !found {
  1269  			t.Fatalf("tip block not found in cache mainchain")
  1270  		}
  1271  		if block.orphaned {
  1272  			t.Fatalf("block unexpectedly orphaned before reorg")
  1273  		}
  1274  		_, found = dcr.blockCache.block(&block.hash)
  1275  		if !found {
  1276  			t.Fatalf("block not found with block method before reorg")
  1277  		}
  1278  		tipHash = &block.hash
  1279  	}
  1280  
  1281  	ensureOrphaned := func(hash *chainhash.Hash, height uint32) {
  1282  		// Make sure mainchain is empty at the tip height.
  1283  		block, found := dcr.blockCache.block(hash)
  1284  		if !found {
  1285  			t.Fatalf("orphaned block from height %d not found after reorg", height)
  1286  		}
  1287  		if !block.orphaned {
  1288  			t.Fatalf("reorged block from height %d (%s) not marked as orphaned", height, hash)
  1289  		}
  1290  	}
  1291  
  1292  	// A one-block reorg.
  1293  	reset()
  1294  	// Add a replacement blocks
  1295  	newHash := testAddBlockVerbose(nil, 1, tipHeight, 1)
  1296  	// Passing the hash to anyQ triggers the reorganization.
  1297  	dcr.blockCache.reorg(int64(tipHeight))
  1298  	dcr.blockCache.add(testChain.blocks[*newHash])
  1299  	ensureOrphaned(tipHash, tipHeight)
  1300  	newTip, found := dcr.blockCache.atHeight(tipHeight)
  1301  	if !found {
  1302  		t.Fatalf("3-deep reorg-causing new tip block not found on mainchain")
  1303  	}
  1304  	if newTip.hash != *newHash {
  1305  		t.Fatalf("tip hash mismatch after 1-block reorg")
  1306  	}
  1307  
  1308  	// A 3-block reorg
  1309  	reset()
  1310  	tip, found1 := dcr.blockCache.atHeight(tipHeight)
  1311  	oneDeep, found2 := dcr.blockCache.atHeight(tipHeight - 1)
  1312  	twoDeep, found3 := dcr.blockCache.atHeight(tipHeight - 2)
  1313  	if !found1 || !found2 || !found3 {
  1314  		t.Fatalf("not all block found for 3-block reorg (%t, %t, %t)", found1, found2, found3)
  1315  	}
  1316  	newHash = testAddBlockVerbose(nil, 1, tipHeight-2, 1)
  1317  	dcr.blockCache.reorg(int64(tipHeight - 2))
  1318  	dcr.blockCache.add(testChain.blocks[*newHash])
  1319  	ensureOrphaned(&tip.hash, tip.height)
  1320  	ensureOrphaned(&oneDeep.hash, tip.height)
  1321  	ensureOrphaned(&twoDeep.hash, tip.height)
  1322  	newHeight := int64(dcr.blockCache.tipHeight())
  1323  	if newHeight != int64(twoDeep.height) {
  1324  		t.Fatalf("from tip height after 3-block reorg. expected %d, saw %d", twoDeep.height-1, newHeight)
  1325  	}
  1326  	newTip, found = dcr.blockCache.atHeight(uint32(newHeight))
  1327  	if !found {
  1328  		t.Fatalf("3-deep reorg-causing new tip block not found on mainchain")
  1329  	}
  1330  	if newTip.hash != *newHash {
  1331  		t.Fatalf("tip hash mismatch after 3-block reorg")
  1332  	}
  1333  
  1334  	// Create a transaction at the tip, then orphan the block and move the
  1335  	// transaction to mempool.
  1336  	reset()
  1337  	txHash := randomHash()
  1338  	tip, _ = dcr.blockCache.atHeight(tipHeight)
  1339  	msg := testMsgTxRegular(dcrec.STEcdsaSecp256k1)
  1340  
  1341  	testAddTxOut(msg.tx, 0, txHash, &tip.hash, int64(tipHeight), 1)
  1342  
  1343  	utxo, err := dcr.utxo(ctx, txHash, msg.vout, nil)
  1344  	if err != nil {
  1345  		t.Fatalf("utxo error: %v", err)
  1346  	}
  1347  	confs, err := utxo.Confirmations(ctx)
  1348  	if err != nil {
  1349  		t.Fatalf("Confirmations error: %v", err)
  1350  	}
  1351  	if confs != 1 {
  1352  		t.Fatalf("wrong number of confirmations. expected 1, got %d", confs)
  1353  	}
  1354  
  1355  	// Orphan the block and move the transaction to mempool.
  1356  	dcr.blockCache.reorg(int64(tipHeight - 2))
  1357  	testAddTxOut(msg.tx, 0, txHash, nil, 0, 0)
  1358  	confs, err = utxo.Confirmations(ctx)
  1359  	if err != nil {
  1360  		t.Fatalf("Confirmations error after reorg: %v", err)
  1361  	}
  1362  	if confs != 0 {
  1363  		t.Fatalf("Expected zero confirmations after reorg, found %d", confs)
  1364  	}
  1365  
  1366  	// Start over, but put it in a lower block instead.
  1367  	reset()
  1368  	tip, _ = dcr.blockCache.atHeight(tipHeight)
  1369  	testAddBlockVerbose(&tip.hash, 1, tipHeight, 1)
  1370  	testAddTxOut(msg.tx, 0, txHash, &tip.hash, int64(tipHeight), 1)
  1371  	utxo, err = dcr.utxo(ctx, txHash, msg.vout, nil)
  1372  	if err != nil {
  1373  		t.Fatalf("utxo error 2: %v", err)
  1374  	}
  1375  
  1376  	// Reorg and add a single block with the transaction.
  1377  	var reorgHeight uint32 = 5
  1378  	dcr.blockCache.reorg(int64(reorgHeight))
  1379  	newBlockHash := randomHash()
  1380  	testAddTxOut(msg.tx, 0, txHash, newBlockHash, int64(reorgHeight+1), 1)
  1381  	testAddBlockVerbose(newBlockHash, 1, reorgHeight+1, 1)
  1382  	dcr.getDcrBlock(ctx, newBlockHash) // Force blockCache update
  1383  	confs, err = utxo.Confirmations(ctx)
  1384  	if err != nil {
  1385  		t.Fatalf("Confirmations error after reorg to lower block: %v", err)
  1386  	}
  1387  	if confs != 1 {
  1388  		t.Fatalf("Expected zero confirmations after reorg to lower block, found %d", confs)
  1389  	}
  1390  }
  1391  
  1392  // TestAuxiliary checks the UTXO convenience functions like TxHash, Vout, and
  1393  // TxID.
  1394  func TestAuxiliary(t *testing.T) {
  1395  	// Create a Backend with the test node.
  1396  	dcr, shutdown := testBackend()
  1397  	defer shutdown()
  1398  	ctx := dcr.ctx // for the functions then take a context, canceled on shutdown()
  1399  
  1400  	// Add a funding coin and retrieve it. Use a vote, since it has non-zero vout.
  1401  	cleanTestChain()
  1402  	maturity := int64(chainParams.CoinbaseMaturity)
  1403  	msg := testMsgTxVote()
  1404  	txid := hex.EncodeToString(randomBytes(32))
  1405  	txHash, _ := chainhash.NewHashFromStr(txid)
  1406  	txHeight := rand.Uint32()
  1407  	blockHash := testAddBlockVerbose(nil, 1, txHeight, 1)
  1408  	testAddTxOut(msg.tx, msg.vout, txHash, blockHash, int64(txHeight), maturity)
  1409  	coinID := toCoinID(txHash, msg.vout)
  1410  	utxo, err := dcr.FundingCoin(ctx, coinID, nil)
  1411  	if err != nil {
  1412  		t.Fatalf("unexpected error: %v", err)
  1413  	}
  1414  	if txHash.String() != utxo.Coin().TxID() {
  1415  		t.Fatalf("utxo tx hash doesn't match")
  1416  	}
  1417  	if utxo.Coin().TxID() != txid {
  1418  		t.Fatalf("utxo txid doesn't match")
  1419  	}
  1420  
  1421  	// Check that values returned from FeeCoin are as set.
  1422  	cleanTestChain()
  1423  	msg = testMsgTxRegular(dcrec.STEcdsaSecp256k1)
  1424  	msg.tx.TxOut[0].Value = 8e8 // for consistency with fake TxRawResult added below
  1425  
  1426  	scriptClass, scriptAddrs := dexdcr.ExtractScriptAddrs(msg.tx.TxOut[0].Version, msg.tx.TxOut[0].PkScript, chainParams)
  1427  	if scriptClass == dexdcr.ScriptUnsupported {
  1428  		t.Errorf("vote output 0 was non-standard")
  1429  	}
  1430  	addr := scriptAddrs.PkHashes[0].String()
  1431  
  1432  	msgHash := msg.tx.TxHash()
  1433  	txHash = &msgHash
  1434  	confs := int64(3)
  1435  	verboseTx := testAddTxVerbose(msg.tx, txHash, blockHash, int64(txHeight), confs)
  1436  
  1437  	txAddr, v, confs, err := dcr.FeeCoin(toCoinID(txHash, 0))
  1438  	if err != nil {
  1439  		t.Fatalf("FeeCoin error: %v", err)
  1440  	}
  1441  	if txAddr != addr {
  1442  		t.Fatalf("expected address %s, got %s", addr, txAddr)
  1443  	}
  1444  	expVal := toAtoms(8)
  1445  	if v != expVal {
  1446  		t.Fatalf("expected value %d, got %d", expVal, v)
  1447  	}
  1448  	if confs != 3 {
  1449  		t.Fatalf("expected 3 confirmations, got %d", confs)
  1450  	}
  1451  
  1452  	var txHashBad chainhash.Hash
  1453  	copy(txHashBad[:], randomBytes(32))
  1454  	_, _, _, err = dcr.FeeCoin(toCoinID(&txHashBad, 0))
  1455  	if err == nil {
  1456  		t.Fatal("FeeCoin found for non-existent txid")
  1457  	}
  1458  
  1459  	_, _, _, err = dcr.FeeCoin(toCoinID(txHash, 1))
  1460  	if err == nil {
  1461  		t.Fatal("FeeCoin found for non-existent output")
  1462  	}
  1463  
  1464  	// make the output a stake hash
  1465  	stakeScript, _ := newStakeP2PKHScript(txscript.OP_SSGEN)
  1466  	verboseTx.Vout[0].ScriptPubKey.Hex = hex.EncodeToString(stakeScript)
  1467  	_, _, _, err = dcr.FeeCoin(toCoinID(txHash, 0))
  1468  	if err == nil {
  1469  		t.Fatal("FeeCoin accepted a stake output")
  1470  	}
  1471  
  1472  	// make a p2sh that FeeCoin should also accept
  1473  	msgP2SH := testMsgTxP2SHMofN(1, 2)
  1474  	scriptClass, _ = dexdcr.ExtractScriptAddrs(msgP2SH.tx.TxOut[0].Version, msgP2SH.tx.TxOut[0].PkScript, chainParams)
  1475  	if scriptClass == dexdcr.ScriptUnsupported {
  1476  		t.Errorf("output 0 was non-standard")
  1477  	}
  1478  	msgHash = msgP2SH.tx.TxHash()
  1479  	txHash = &msgHash
  1480  	confs = int64(3)
  1481  	testAddTxVerbose(msgP2SH.tx, txHash, blockHash, int64(txHeight), confs)
  1482  	_, _, _, err = dcr.FeeCoin(toCoinID(txHash, 0))
  1483  	if err != nil {
  1484  		t.Fatal("FeeCoin rejected a p2sh output")
  1485  	}
  1486  
  1487  	// make a p2pk that FeeCoin should reject
  1488  	priv, err := secp256k1.GeneratePrivateKey()
  1489  	if err != nil {
  1490  		t.Fatalf("GeneratePrivateKey error: %v", err)
  1491  	}
  1492  	pkAddr, err := stdaddr.NewAddressPubKeyEcdsaSecp256k1V0(priv.PubKey(), chainParams)
  1493  	if err != nil {
  1494  		t.Fatalf("NewAddressPubKeyEcdsaSecp256k1V0 error: %v", err)
  1495  	}
  1496  	msgTxPK := wire.NewMsgTx()
  1497  	_, pkScript := pkAddr.PaymentScript()
  1498  	msgTxPK.AddTxOut(wire.NewTxOut(1, pkScript))
  1499  	chainHash := msgTxPK.TxHash()
  1500  	testAddTxVerbose(msgTxPK, &chainHash, blockHash, int64(txHeight), confs)
  1501  	_, _, _, err = dcr.FeeCoin(toCoinID(txHash, 0))
  1502  	if err != nil {
  1503  		t.Fatal("FeeCoin rejected a p2sh output")
  1504  	}
  1505  }
  1506  
  1507  // TestCheckSwapAddress checks that addresses are parsing or not parsing as
  1508  // expected.
  1509  func TestCheckSwapAddress(t *testing.T) {
  1510  	dcr, shutdown := testBackend()
  1511  	defer shutdown()
  1512  
  1513  	type test struct {
  1514  		addr    string
  1515  		wantErr bool
  1516  	}
  1517  	tests := []test{
  1518  		{"", true},
  1519  		{"DsYXjAK3UiTVN9js8v9G21iRbr2wPty7f12", false},
  1520  		{"DeZcGyCtPq7sTvACZupjT3BC1tsSEsKaYL4", true},                   // valid, but AddressPubKeyHashEd25519V0
  1521  		{"DSo9Qw4FZLTwFL6fg2T9XPoJA8sFoZ4idZ7", true},                   // valid, but AddressPubKeyHashSchnorrSecp256k1V0
  1522  		{"DkM3W1518RharMSnqSiJCCGQ7RikMKCATeRvRwEW8vy1B2fjTd4Xi", true}, // valid, but AddressPubKeyEcdsaSecp256k1V0
  1523  		{"Dce4vLzzENaZT7D2Wq5crRZ4VwfYMDMWkD9", true},                   // valid, but AddressScriptHashV0
  1524  		{"TsYXjAK3UiTVN9js8v9G21iRbr2wPty7f12", true},                   // wrong network
  1525  		{"Dce4vLzzENaZT7D2Wq5crRZ4VwfYMDMWkD0", true},                   // capital letter O not base 58
  1526  		{"Dce4vLzzE", true},
  1527  	}
  1528  	for _, test := range tests {
  1529  		if dcr.CheckSwapAddress(test.addr) != !test.wantErr {
  1530  			t.Fatalf("wantErr = %t, address = %s", test.wantErr, test.addr)
  1531  		}
  1532  	}
  1533  }
  1534  
  1535  func TestValidateXPub(t *testing.T) {
  1536  	seed := randomBytes(hdkeychain.MinSeedBytes)
  1537  	master, err := hdkeychain.NewMaster(seed, chainParams)
  1538  	if err != nil {
  1539  		t.Fatalf("failed to generate master extended key: %v", err)
  1540  	}
  1541  
  1542  	// fail for private key
  1543  	xprivStr := master.String()
  1544  	if err = ValidateXPub(xprivStr); err == nil {
  1545  		t.Errorf("no error for extended private key")
  1546  	}
  1547  
  1548  	// succeed for public key
  1549  	xpub := master.Neuter()
  1550  	xpubStr := xpub.String()
  1551  	if err = ValidateXPub(xpubStr); err != nil {
  1552  		t.Error(err)
  1553  	}
  1554  
  1555  	// fail for invalid key of wrong length
  1556  	if err = ValidateXPub(xpubStr[2:]); err == nil {
  1557  		t.Errorf("no error for invalid key")
  1558  	}
  1559  
  1560  	// fail for wrong network
  1561  	masterTestnet, err := hdkeychain.NewMaster(seed, chaincfg.TestNet3Params())
  1562  	if err != nil {
  1563  		t.Fatalf("failed to generate master extended key: %v", err)
  1564  	}
  1565  	xpubTestnet := masterTestnet.Neuter()
  1566  	if err = ValidateXPub(xpubTestnet.String()); err == nil {
  1567  		t.Errorf("no error for invalid wrong network")
  1568  	}
  1569  }
  1570  
  1571  func TestDriver_DecodeCoinID(t *testing.T) {
  1572  	tests := []struct {
  1573  		name    string
  1574  		coinID  []byte
  1575  		want    string
  1576  		wantErr bool
  1577  	}{
  1578  		{
  1579  			"ok",
  1580  			[]byte{
  1581  				0x16, 0x8f, 0x34, 0x3a, 0xdf, 0x17, 0xe0, 0xc3,
  1582  				0xa2, 0xe8, 0x88, 0x79, 0x8, 0x87, 0x17, 0xb8,
  1583  				0xac, 0x93, 0x47, 0xb9, 0x66, 0xd, 0xa7, 0x4b,
  1584  				0xde, 0x3e, 0x1d, 0x1f, 0x47, 0x94, 0x9f, 0xdf, // 32 byte hash
  1585  				0x0, 0x0, 0x0, 0x1, // 4 byte vout
  1586  			},
  1587  			"df9f94471f1d3ede4ba70d66b94793acb81787087988e8a2c3e017df3a348f16:1",
  1588  			false,
  1589  		},
  1590  		{
  1591  			"bad",
  1592  			[]byte{
  1593  				0x16, 0x8f, 0x34, 0x3a, 0xdf, 0x17, 0xe0, 0xc3,
  1594  				0xa2, 0xe8, 0x88, 0x79, 0x8, 0x87, 0x17, 0xb8,
  1595  				0xac, 0x93, 0x47, 0xb9, 0x66, 0xd, 0xa7, 0x4b,
  1596  				0xde, 0x3e, 0x1d, 0x1f, 0x47, 0x94, 0x9f, // 31 bytes
  1597  				0x0, 0x0, 0x0, 0x1,
  1598  			},
  1599  			"",
  1600  			true,
  1601  		},
  1602  	}
  1603  	for _, tt := range tests {
  1604  		t.Run(tt.name, func(t *testing.T) {
  1605  			d := &Driver{}
  1606  			got, err := d.DecodeCoinID(tt.coinID)
  1607  			if (err != nil) != tt.wantErr {
  1608  				t.Errorf("Driver.DecodeCoinID() error = %v, wantErr %v", err, tt.wantErr)
  1609  				return
  1610  			}
  1611  			if got != tt.want {
  1612  				t.Errorf("Driver.DecodeCoinID() = %v, want %v", got, tt.want)
  1613  			}
  1614  		})
  1615  	}
  1616  
  1617  	// Same tests with ValidateCoinID
  1618  	be := &Backend{}
  1619  	for _, tt := range tests {
  1620  		t.Run(tt.name, func(t *testing.T) {
  1621  			got, err := be.ValidateCoinID(tt.coinID)
  1622  			if (err != nil) != tt.wantErr {
  1623  				t.Errorf("Driver.DecodeCoinID() error = %v, wantErr %v", err, tt.wantErr)
  1624  				return
  1625  			}
  1626  			if got != tt.want {
  1627  				t.Errorf("Driver.DecodeCoinID() = %v, want %v", got, tt.want)
  1628  			}
  1629  		})
  1630  	}
  1631  }
  1632  
  1633  func TestSynced(t *testing.T) {
  1634  	dcr, shutdown := testBackend()
  1635  	defer shutdown()
  1636  	tNode := dcr.node.(*testNode)
  1637  	tNode.blockchainInfo = &chainjson.GetBlockChainInfoResult{
  1638  		Headers: 100,
  1639  		Blocks:  99,
  1640  	}
  1641  	synced, err := dcr.Synced()
  1642  	if err != nil {
  1643  		t.Fatalf("Synced error: %v", err)
  1644  	}
  1645  	if !synced {
  1646  		t.Fatalf("not synced when should be synced")
  1647  	}
  1648  
  1649  	tNode.blockchainInfo = &chainjson.GetBlockChainInfoResult{
  1650  		Headers: 100,
  1651  		Blocks:  50,
  1652  	}
  1653  	synced, err = dcr.Synced()
  1654  	if err != nil {
  1655  		t.Fatalf("Synced error: %v", err)
  1656  	}
  1657  	if synced {
  1658  		t.Fatalf("synced when shouldn't be synced")
  1659  	}
  1660  
  1661  	tNode.blockchainInfoErr = fmt.Errorf("test error")
  1662  	_, err = dcr.Synced()
  1663  	if err == nil {
  1664  		t.Fatalf("getblockchaininfo error not propagated")
  1665  	}
  1666  	tNode.blockchainInfoErr = nil
  1667  }
  1668  
  1669  func TestParseBondTx(t *testing.T) {
  1670  	const bondVer = 0
  1671  	var bondAmt int64 = 1e8
  1672  	txOK := wire.NewMsgTx()
  1673  	txIn := wire.NewTxIn(&wire.OutPoint{}, 1, []byte{2, 2, 3}) // junk input
  1674  	txOK.AddTxIn(txIn)
  1675  
  1676  	bondPriv, err := secp256k1.GeneratePrivateKey()
  1677  	if err != nil {
  1678  		t.Fatalf("GeneratePrivateKey error: %v\n", err)
  1679  	}
  1680  	bondPubKey := bondPriv.PubKey()
  1681  	pkh := stdaddr.Hash160(bondPubKey.SerializeCompressed())
  1682  	lockTime := time.Now().Add(time.Hour)
  1683  	bondRedeemScript, err := dexdcr.MakeBondScript(bondVer, uint32(lockTime.Unix()), pkh)
  1684  	if err != nil {
  1685  		t.Fatalf("failed to build bond output redeem script: %v", err)
  1686  	}
  1687  
  1688  	bondAddr, err := stdaddr.NewAddressScriptHashV0(bondRedeemScript, chainParams)
  1689  	if err != nil {
  1690  		t.Fatalf("NewAddressScriptHashV0 error: %v\n", err)
  1691  	}
  1692  	_, bondPkScript := bondAddr.PaymentScript()
  1693  
  1694  	feeOut := wire.NewTxOut(bondAmt, bondPkScript)
  1695  	txOK.AddTxOut(feeOut)
  1696  
  1697  	priv, err := secp256k1.GeneratePrivateKey()
  1698  	if err != nil {
  1699  		t.Fatalf("GeneratePrivateKey error: %v\n", err)
  1700  	}
  1701  	pubkey := priv.PubKey().SerializeCompressed()
  1702  	acct, err := account.NewAccountFromPubKey(pubkey)
  1703  	if err != nil {
  1704  		t.Fatal(err)
  1705  	}
  1706  
  1707  	pushData := make([]byte, 2+len(acct.ID)+4+20)
  1708  	var offset int
  1709  	binary.BigEndian.PutUint16(pushData[offset:], bondVer)
  1710  	offset += 2
  1711  	copy(pushData[offset:], acct.ID[:])
  1712  	offset += len(acct.ID)
  1713  	binary.BigEndian.PutUint32(pushData[offset:], uint32(lockTime.Unix()))
  1714  	offset += 4
  1715  	copy(pushData[offset:], pkh)
  1716  	commitPkScript, err := txscript.NewScriptBuilder().
  1717  		AddOp(txscript.OP_RETURN).
  1718  		AddData(pushData).
  1719  		Script()
  1720  	if err != nil {
  1721  		t.Fatalf("script building error in testMsgTxSwapInit: %v", err)
  1722  	}
  1723  	acctOut := wire.NewTxOut(0, commitPkScript)
  1724  	txOK.AddTxOut(acctOut)
  1725  
  1726  	txRaw, err := txOK.Bytes()
  1727  	if err != nil {
  1728  		t.Fatal(err)
  1729  	}
  1730  
  1731  	gotCoinID, gotAmt, gotAddr, gotPubKeyHash, gotLockTime, gotAcct, err := ParseBondTx(bondVer, txRaw)
  1732  	if err != nil {
  1733  		t.Fatal(err)
  1734  	}
  1735  
  1736  	wantBondAddr := bondAddr.String()
  1737  	if wantBondAddr != gotAddr {
  1738  		t.Errorf("wrong fee address, wanted %v, got %v", wantBondAddr, gotAmt)
  1739  	}
  1740  
  1741  	if gotAcct != acct.ID {
  1742  		t.Errorf("wrong account, wanted %v, got %v", acct.ID, gotAcct)
  1743  	}
  1744  
  1745  	txHash, vout, err := decodeCoinID(gotCoinID)
  1746  	if err != nil {
  1747  		t.Fatalf("decodeCoinID: %v", err)
  1748  	}
  1749  	if txOK.TxHash() != *txHash {
  1750  		t.Fatalf("got txid %v, expected %v", txOK.TxHash(), txHash)
  1751  	}
  1752  	if vout != 0 {
  1753  		t.Fatalf("wanted vout 0, got %d", vout)
  1754  	}
  1755  	if !bytes.Equal(gotPubKeyHash, pkh) {
  1756  		t.Fatal("pubkey mismatch")
  1757  	}
  1758  	if lockTime.Unix() != gotLockTime {
  1759  		t.Fatalf("got lock time %d, wanted %d", gotLockTime, lockTime.Unix())
  1760  	}
  1761  }