decred.org/dcrdex@v1.0.5/server/swap/swap_test.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package swap
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"crypto/sha256"
    10  	"encoding/binary"
    11  	"encoding/hex"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"math/rand"
    16  	"os"
    17  	"strconv"
    18  	"strings"
    19  	"sync"
    20  	"sync/atomic"
    21  	"testing"
    22  	"time"
    23  
    24  	"decred.org/dcrdex/dex"
    25  	"decred.org/dcrdex/dex/calc"
    26  	"decred.org/dcrdex/dex/encode"
    27  	"decred.org/dcrdex/dex/msgjson"
    28  	"decred.org/dcrdex/dex/order"
    29  	"decred.org/dcrdex/server/account"
    30  	"decred.org/dcrdex/server/asset"
    31  	"decred.org/dcrdex/server/auth"
    32  	"decred.org/dcrdex/server/coinlock"
    33  	"decred.org/dcrdex/server/comms"
    34  	"decred.org/dcrdex/server/db"
    35  	"decred.org/dcrdex/server/matcher"
    36  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    37  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
    38  )
    39  
    40  const (
    41  	ABCID  = 123
    42  	XYZID  = 789
    43  	ACCTID = 456
    44  )
    45  
    46  var (
    47  	testCtx      context.Context
    48  	acctTemplate = account.AccountID{
    49  		0x22, 0x4c, 0xba, 0xaa, 0xfa, 0x80, 0xbf, 0x3b, 0xd1, 0xff, 0x73, 0x15,
    50  		0x90, 0xbc, 0xbd, 0xda, 0x5a, 0x76, 0xf9, 0x1e, 0x60, 0xa1, 0x56, 0x99,
    51  		0x46, 0x34, 0xe9, 0x1c, 0xaa, 0xaa, 0xaa, 0xaa,
    52  	}
    53  	acctCounter      uint32
    54  	dexPrivKey       *secp256k1.PrivateKey
    55  	tBcastTimeout    time.Duration
    56  	txWaitExpiration time.Duration
    57  )
    58  
    59  type tUser struct {
    60  	sig      []byte
    61  	sigHex   string
    62  	acct     account.AccountID
    63  	addr     string
    64  	lbl      string
    65  	matchIDs []order.MatchID
    66  }
    67  
    68  func tickMempool() {
    69  	time.Sleep(fastRecheckInterval * 3 / 2)
    70  }
    71  
    72  func timeOutMempool() {
    73  	time.Sleep(txWaitExpiration * 3 / 2)
    74  }
    75  
    76  func dirtyEncode(s string) []byte {
    77  	b, err := hex.DecodeString(s)
    78  	if err != nil {
    79  		fmt.Printf("dirtyEncode error for input '%s': %v", s, err)
    80  	}
    81  	return b
    82  }
    83  
    84  // A new tUser with a unique account ID, signature, and address.
    85  func tNewUser(lbl string) *tUser {
    86  	intBytes := make([]byte, 4)
    87  	binary.BigEndian.PutUint32(intBytes, acctCounter)
    88  	acctID := account.AccountID{}
    89  	copy(acctID[:], acctTemplate[:])
    90  	copy(acctID[account.HashSize-4:], intBytes)
    91  	addr := strconv.Itoa(int(acctCounter))
    92  	sig := []byte{0xab} // Just to differentiate from the addr.
    93  	sig = append(sig, intBytes...)
    94  	sigHex := hex.EncodeToString(sig)
    95  	acctCounter++
    96  	return &tUser{
    97  		sig:    sig,
    98  		sigHex: sigHex,
    99  		acct:   acctID,
   100  		addr:   addr,
   101  		lbl:    lbl,
   102  	}
   103  }
   104  
   105  type TRequest struct {
   106  	req      *msgjson.Message
   107  	respFunc func(comms.Link, *msgjson.Message)
   108  }
   109  
   110  // This stub satisfies AuthManager.
   111  type TAuthManager struct {
   112  	mtx         sync.Mutex
   113  	authErr     error
   114  	privkey     *secp256k1.PrivateKey
   115  	reqs        map[account.AccountID][]*TRequest
   116  	resps       map[account.AccountID][]*msgjson.Message
   117  	ntfns       map[account.AccountID][]*msgjson.Message
   118  	newNtfn     chan struct{}
   119  	suspensions map[account.AccountID]account.Rule
   120  	newSuspend  chan struct{}
   121  	swapID      uint64
   122  	// Use swapReceived if you need to synchronize error responses to init
   123  	// requests.
   124  	swapReceived chan struct{}
   125  	auditReq     chan struct{}
   126  	redeemID     uint64
   127  	// Use redeemReceived if you need to synchronize error responses to redeem
   128  	// requests.
   129  	redeemReceived chan struct{}
   130  	redemptionReq  chan struct{}
   131  }
   132  
   133  func newTAuthManager() *TAuthManager {
   134  	// Reuse any previously generated dex server private key.
   135  	if dexPrivKey == nil {
   136  		dexPrivKey, _ = secp256k1.GeneratePrivateKey()
   137  	}
   138  	return &TAuthManager{
   139  		privkey:     dexPrivKey,
   140  		reqs:        make(map[account.AccountID][]*TRequest),
   141  		ntfns:       make(map[account.AccountID][]*msgjson.Message),
   142  		resps:       make(map[account.AccountID][]*msgjson.Message),
   143  		suspensions: make(map[account.AccountID]account.Rule),
   144  	}
   145  }
   146  
   147  func (m *TAuthManager) Send(user account.AccountID, msg *msgjson.Message) error {
   148  	m.mtx.Lock()
   149  	defer m.mtx.Unlock()
   150  	l := m.resps[user]
   151  	if l == nil {
   152  		l = make([]*msgjson.Message, 0, 1)
   153  	}
   154  	if msg.Route == "" {
   155  		// response
   156  		m.resps[user] = append(l, msg)
   157  		if m.redeemReceived != nil && msg.ID == m.redeemID {
   158  			m.redeemReceived <- struct{}{}
   159  		}
   160  		if m.swapReceived != nil && msg.ID == m.swapID {
   161  			m.swapReceived <- struct{}{}
   162  		}
   163  	} else {
   164  		// notification
   165  		m.ntfns[user] = append(l, msg)
   166  		if m.newNtfn != nil {
   167  			m.newNtfn <- struct{}{}
   168  		}
   169  	}
   170  	return nil
   171  }
   172  
   173  func (m *TAuthManager) Request(user account.AccountID, msg *msgjson.Message,
   174  	f func(comms.Link, *msgjson.Message)) error {
   175  	return m.RequestWithTimeout(user, msg, f, time.Hour, func() {})
   176  }
   177  
   178  func (m *TAuthManager) RequestWithTimeout(user account.AccountID, msg *msgjson.Message,
   179  	f func(comms.Link, *msgjson.Message), _ time.Duration, _ func()) error {
   180  	m.mtx.Lock()
   181  	defer m.mtx.Unlock()
   182  	tReq := &TRequest{
   183  		req:      msg,
   184  		respFunc: f,
   185  	}
   186  	l := m.reqs[user]
   187  	if l == nil {
   188  		l = make([]*TRequest, 0, 1)
   189  	}
   190  	m.reqs[user] = append(l, tReq)
   191  	switch {
   192  	case m.auditReq != nil && msg.Route == msgjson.AuditRoute:
   193  		m.auditReq <- struct{}{}
   194  	case m.redemptionReq != nil && msg.Route == msgjson.RedemptionRoute:
   195  		m.redemptionReq <- struct{}{}
   196  	}
   197  	return nil
   198  }
   199  func (m *TAuthManager) Sign(signables ...msgjson.Signable) {
   200  	for _, signable := range signables {
   201  		hash := sha256.Sum256(signable.Serialize())
   202  		sig := ecdsa.Sign(m.privkey, hash[:])
   203  		signable.SetSig(sig.Serialize())
   204  	}
   205  }
   206  func (m *TAuthManager) Suspended(user account.AccountID) (found, suspended bool) {
   207  	var rule account.Rule
   208  	rule, found = m.suspensions[user]
   209  	suspended = rule != account.NoRule
   210  	return // TODO: test suspended account handling (no trades, just cancels)
   211  }
   212  func (m *TAuthManager) Auth(user account.AccountID, msg, sig []byte) error {
   213  	return m.authErr
   214  }
   215  func (m *TAuthManager) Route(string,
   216  	func(account.AccountID, *msgjson.Message) *msgjson.Error) {
   217  }
   218  
   219  func (m *TAuthManager) SwapSuccess(id account.AccountID, mmid db.MarketMatchID, value uint64, refTime time.Time) {
   220  }
   221  func (m *TAuthManager) Inaction(id account.AccountID, step auth.NoActionStep, mmid db.MarketMatchID, matchValue uint64, refTime time.Time, oid order.OrderID) {
   222  	m.penalize(id, account.FailureToAct)
   223  }
   224  func (m *TAuthManager) penalize(id account.AccountID, rule account.Rule) {
   225  	m.mtx.Lock()
   226  	defer m.mtx.Unlock()
   227  	m.suspensions[id] = rule
   228  	if m.newSuspend != nil {
   229  		m.newSuspend <- struct{}{}
   230  	}
   231  }
   232  
   233  func (m *TAuthManager) flushPenalty(user account.AccountID) (found bool, rule account.Rule) {
   234  	m.mtx.Lock()
   235  	defer m.mtx.Unlock()
   236  	rule, found = m.suspensions[user]
   237  	if found {
   238  		delete(m.suspensions, user)
   239  	}
   240  	return
   241  }
   242  
   243  // pop front
   244  func (m *TAuthManager) popReq(id account.AccountID) *TRequest {
   245  	m.mtx.Lock()
   246  	defer m.mtx.Unlock()
   247  	reqs := m.reqs[id]
   248  	if len(reqs) == 0 {
   249  		return nil
   250  	}
   251  	req := reqs[0]
   252  	m.reqs[id] = reqs[1:]
   253  	return req
   254  }
   255  
   256  // push front
   257  func (m *TAuthManager) pushReq(id account.AccountID, req *TRequest) {
   258  	m.mtx.Lock()
   259  	defer m.mtx.Unlock()
   260  	m.reqs[id] = append([]*TRequest{req}, m.reqs[id]...)
   261  }
   262  
   263  func (m *TAuthManager) getNtfn(id account.AccountID, route string, payload any) error {
   264  	m.mtx.Lock()
   265  	defer m.mtx.Unlock()
   266  	msgs := m.ntfns[id]
   267  	if len(msgs) == 0 {
   268  		return errors.New("no message")
   269  	}
   270  	msg := msgs[0]
   271  	if msg.Route != route {
   272  		return fmt.Errorf("wrong route: %v", route)
   273  	}
   274  	return msg.Unmarshal(payload)
   275  }
   276  
   277  // push front
   278  func (m *TAuthManager) pushResp(id account.AccountID, msg *msgjson.Message) {
   279  	m.mtx.Lock()
   280  	defer m.mtx.Unlock()
   281  	m.resps[id] = append([]*msgjson.Message{msg}, m.resps[id]...)
   282  }
   283  
   284  // pop front
   285  func (m *TAuthManager) popResp(id account.AccountID) (msg *msgjson.Message, resp *msgjson.ResponsePayload) {
   286  	m.mtx.Lock()
   287  	defer m.mtx.Unlock()
   288  	msgs := m.resps[id]
   289  	if len(msgs) == 0 {
   290  		return
   291  	}
   292  	msg = msgs[0]
   293  	m.resps[id] = msgs[1:]
   294  	resp, _ = msg.Response()
   295  	return
   296  }
   297  
   298  type TStorage struct {
   299  	fatalMtx sync.RWMutex
   300  	fatal    chan struct{}
   301  	fatalErr error
   302  }
   303  
   304  func (ts *TStorage) LastErr() error {
   305  	ts.fatalMtx.RLock()
   306  	defer ts.fatalMtx.RUnlock()
   307  	return ts.fatalErr
   308  }
   309  
   310  func (ts *TStorage) Fatal() <-chan struct{} {
   311  	ts.fatalMtx.RLock()
   312  	defer ts.fatalMtx.RUnlock()
   313  	return ts.fatal
   314  }
   315  
   316  func (ts *TStorage) fatalBackendErr(err error) {
   317  	ts.fatalMtx.Lock()
   318  	if ts.fatal == nil {
   319  		ts.fatal = make(chan struct{})
   320  		close(ts.fatal)
   321  	}
   322  	ts.fatalErr = err // consider slice and append
   323  	ts.fatalMtx.Unlock()
   324  }
   325  
   326  func (ts *TStorage) Order(oid order.OrderID, base, quote uint32) (order.Order, order.OrderStatus, error) {
   327  	return nil, order.OrderStatusUnknown, nil // not loading swaps
   328  }
   329  func (ts *TStorage) CancelOrder(*order.LimitOrder) error      { return nil }
   330  func (ts *TStorage) ActiveSwaps() ([]*db.SwapDataFull, error) { return nil, nil }
   331  func (ts *TStorage) InsertMatch(match *order.Match) error     { return nil }
   332  func (ts *TStorage) SwapData(mid db.MarketMatchID) (order.MatchStatus, *db.SwapData, error) {
   333  	return 0, nil, nil
   334  }
   335  func (ts *TStorage) SaveMatchAckSigA(mid db.MarketMatchID, sig []byte) error { return nil }
   336  func (ts *TStorage) SaveMatchAckSigB(mid db.MarketMatchID, sig []byte) error { return nil }
   337  
   338  // Contract data.
   339  func (ts *TStorage) SaveContractA(mid db.MarketMatchID, contract []byte, coinID []byte, timestamp int64) error {
   340  	return nil
   341  }
   342  func (ts *TStorage) SaveAuditAckSigB(mid db.MarketMatchID, sig []byte) error { return nil }
   343  func (ts *TStorage) SaveContractB(mid db.MarketMatchID, contract []byte, coinID []byte, timestamp int64) error {
   344  	return nil
   345  }
   346  func (ts *TStorage) SaveAuditAckSigA(mid db.MarketMatchID, sig []byte) error { return nil }
   347  
   348  // Redeem data.
   349  func (ts *TStorage) SaveRedeemA(mid db.MarketMatchID, coinID, secret []byte, timestamp int64) error {
   350  	return nil
   351  }
   352  func (ts *TStorage) SaveRedeemAckSigB(mid db.MarketMatchID, sig []byte) error {
   353  	return nil
   354  }
   355  func (ts *TStorage) SaveRedeemB(mid db.MarketMatchID, coinID []byte, timestamp int64) error {
   356  	return nil
   357  }
   358  func (ts *TStorage) SetMatchInactive(mid db.MarketMatchID, forgive bool) error { return nil }
   359  
   360  type redeemKey struct {
   361  	redemptionCoin       string
   362  	counterpartySwapCoin string
   363  }
   364  
   365  // This stub satisfies asset.Backend.
   366  type TBackend struct {
   367  	mtx            sync.RWMutex
   368  	contracts      map[string]*asset.Contract
   369  	contractErr    error
   370  	fundsErr       error
   371  	redemptions    map[redeemKey]asset.Coin
   372  	redemptionErr  error
   373  	bChan          chan *asset.BlockUpdate // to trigger processBlock and eventually (after up to BroadcastTimeout) checkInaction depending on block time
   374  	lbl            string
   375  	invalidFeeRate bool
   376  }
   377  
   378  func newTBackend(lbl string) TBackend {
   379  	return TBackend{
   380  		bChan:       make(chan *asset.BlockUpdate, 5),
   381  		lbl:         lbl,
   382  		contracts:   make(map[string]*asset.Contract),
   383  		redemptions: make(map[redeemKey]asset.Coin),
   384  		fundsErr:    asset.CoinNotFoundError,
   385  	}
   386  }
   387  
   388  func newUTXOBackend(lbl string) *TUTXOBackend {
   389  	return &TUTXOBackend{TBackend: newTBackend(lbl)}
   390  }
   391  
   392  func newAccountBackend(lbl string) *TAccountBackend {
   393  	return &TAccountBackend{newTBackend(lbl)}
   394  }
   395  
   396  func (a *TBackend) Contract(coinID, redeemScript []byte) (*asset.Contract, error) {
   397  	a.mtx.RLock()
   398  	defer a.mtx.RUnlock()
   399  	if a.contractErr != nil {
   400  		return nil, a.contractErr
   401  	}
   402  	contract, found := a.contracts[string(coinID)]
   403  	if !found || contract == nil {
   404  		return nil, asset.CoinNotFoundError
   405  	}
   406  
   407  	return contract, nil
   408  }
   409  func (a *TBackend) Redemption(redemptionID, cpSwapCoinID, contractData []byte) (asset.Coin, error) {
   410  	a.mtx.RLock()
   411  	defer a.mtx.RUnlock()
   412  	if a.redemptionErr != nil {
   413  		return nil, a.redemptionErr
   414  	}
   415  	redeem, found := a.redemptions[redeemKey{string(redemptionID), string(cpSwapCoinID)}]
   416  	if !found || redeem == nil {
   417  		return nil, asset.CoinNotFoundError
   418  	}
   419  	return redeem, nil
   420  }
   421  func (a *TBackend) ValidateCoinID(coinID []byte) (string, error) {
   422  	return "", nil
   423  }
   424  func (a *TBackend) ValidateContract(contract []byte) error {
   425  	return nil
   426  }
   427  func (a *TBackend) BlockChannel(size int) <-chan *asset.BlockUpdate  { return a.bChan }
   428  func (a *TBackend) FeeRate(context.Context) (uint64, error)          { return 10, nil }
   429  func (a *TBackend) CheckSwapAddress(string) bool                     { return true }
   430  func (a *TBackend) Connect(context.Context) (*sync.WaitGroup, error) { return nil, nil }
   431  func (a *TBackend) ValidateSecret(secret, contract []byte) bool      { return true }
   432  func (a *TBackend) Synced() (bool, error)                            { return true, nil }
   433  func (a *TBackend) TxData([]byte) ([]byte, error) {
   434  	return nil, nil
   435  }
   436  
   437  func (a *TBackend) setContractErr(err error) {
   438  	a.mtx.Lock()
   439  	defer a.mtx.Unlock()
   440  	a.contractErr = err
   441  }
   442  func (a *TBackend) setContract(contract *asset.Contract, resetErr bool) {
   443  	a.mtx.Lock()
   444  	a.contracts[string(contract.ID())] = contract
   445  	if resetErr {
   446  		a.contractErr = nil
   447  	}
   448  	a.mtx.Unlock()
   449  }
   450  
   451  func (a *TBackend) setRedemptionErr(err error) {
   452  	a.mtx.Lock()
   453  	defer a.mtx.Unlock()
   454  	a.redemptionErr = err
   455  }
   456  func (a *TBackend) setRedemption(redeem asset.Coin, cpSwap asset.Coin, resetErr bool) {
   457  	a.mtx.Lock()
   458  	a.redemptions[redeemKey{string(redeem.ID()), string(cpSwap.ID())}] = redeem
   459  	if resetErr {
   460  		a.redemptionErr = nil
   461  	}
   462  	a.mtx.Unlock()
   463  }
   464  func (*TBackend) Info() *asset.BackendInfo {
   465  	return &asset.BackendInfo{}
   466  }
   467  func (a *TBackend) ValidateFeeRate(asset.Coin, uint64) bool {
   468  	return !a.invalidFeeRate
   469  }
   470  
   471  type TUTXOBackend struct {
   472  	TBackend
   473  	funds asset.FundingCoin
   474  }
   475  
   476  func (a *TUTXOBackend) FundingCoin(_ context.Context, coinID, redeemScript []byte) (asset.FundingCoin, error) {
   477  	a.mtx.RLock()
   478  	defer a.mtx.RUnlock()
   479  	return a.funds, a.fundsErr
   480  }
   481  
   482  func (a *TUTXOBackend) VerifyUnspentCoin(_ context.Context, coinID []byte) error { return nil }
   483  
   484  type TAccountBackend struct {
   485  	TBackend
   486  }
   487  
   488  var _ asset.AccountBalancer = (*TAccountBackend)(nil)
   489  
   490  func (b *TAccountBackend) AccountBalance(addr string) (uint64, error) {
   491  	return 0, nil
   492  }
   493  
   494  func (b *TAccountBackend) ValidateSignature(addr string, pubkey, msg, sig []byte) error {
   495  	return nil
   496  }
   497  
   498  func (a *TAccountBackend) InitTxSize() uint64 { return 100 }
   499  
   500  func (b *TAccountBackend) RedeemSize() uint64 {
   501  	return 21_000
   502  }
   503  
   504  // This stub satisfies asset.Transaction, used by asset.Backend.
   505  type TCoin struct {
   506  	mtx       sync.RWMutex
   507  	id        []byte
   508  	confs     int64
   509  	confsErr  error
   510  	auditAddr string
   511  	auditVal  uint64
   512  	feeRate   uint64
   513  }
   514  
   515  func (coin *TCoin) Confirmations(context.Context) (int64, error) {
   516  	coin.mtx.RLock()
   517  	defer coin.mtx.RUnlock()
   518  	return coin.confs, coin.confsErr
   519  }
   520  
   521  func (coin *TCoin) Addresses() []string {
   522  	return []string{coin.auditAddr}
   523  }
   524  
   525  func (coin *TCoin) setConfs(confs int64) {
   526  	coin.mtx.Lock()
   527  	defer coin.mtx.Unlock()
   528  	coin.confs = confs
   529  }
   530  
   531  func (coin *TCoin) Auth(pubkeys, sigs [][]byte, msg []byte) error { return nil }
   532  func (coin *TCoin) ID() []byte                                    { return coin.id }
   533  func (coin *TCoin) TxID() string                                  { return hex.EncodeToString(coin.id) }
   534  func (coin *TCoin) Value() uint64                                 { return coin.auditVal }
   535  func (coin *TCoin) SpendSize() uint32                             { return 0 }
   536  func (coin *TCoin) String() string                                { return hex.EncodeToString(coin.id) /* not txid:vout */ }
   537  
   538  func (coin *TCoin) FeeRate() uint64 {
   539  	return coin.feeRate
   540  }
   541  
   542  func TNewAsset(backend asset.Backend, assetID uint32) *asset.BackedAsset {
   543  	return &asset.BackedAsset{
   544  		Backend: backend,
   545  		Asset: dex.Asset{
   546  			ID:         assetID,
   547  			Symbol:     "qwe",
   548  			MaxFeeRate: 120, // not used by Swapper other than prohibiting zero
   549  			SwapConf:   2,
   550  		},
   551  	}
   552  }
   553  
   554  var testMsgID uint64
   555  
   556  func nextID() uint64 {
   557  	return atomic.AddUint64(&testMsgID, 1)
   558  }
   559  
   560  func tNewResponse(id uint64, resp []byte) *msgjson.Message {
   561  	msg, _ := msgjson.NewResponse(id, json.RawMessage(resp), nil)
   562  	return msg
   563  }
   564  
   565  // testRig is the primary test data structure.
   566  type testRig struct {
   567  	abc           *asset.BackedAsset
   568  	abcNode       *TUTXOBackend
   569  	xyz           *asset.BackedAsset
   570  	xyzNode       *TUTXOBackend
   571  	acctAsset     *asset.BackedAsset
   572  	acctNode      *TAccountBackend
   573  	auth          *TAuthManager
   574  	swapper       *Swapper
   575  	swapperWaiter *dex.StartStopWaiter
   576  	storage       *TStorage
   577  	matches       *tMatchSet
   578  	matchInfo     *tMatch
   579  	noResume      bool
   580  }
   581  
   582  func tNewTestRig(matchInfo *tMatch) (*testRig, func()) {
   583  	storage := &TStorage{}
   584  	authMgr := newTAuthManager()
   585  	var noResume bool
   586  
   587  	abcBackend := newUTXOBackend("abc")
   588  	xyzBackend := newUTXOBackend("xyz")
   589  	acctBackend := newAccountBackend("acct")
   590  
   591  	abcAsset := TNewAsset(abcBackend, ABCID)
   592  	abcCoinLocker := coinlock.NewAssetCoinLocker()
   593  
   594  	xyzAsset := TNewAsset(xyzBackend, XYZID)
   595  	xyzCoinLocker := coinlock.NewAssetCoinLocker()
   596  
   597  	acctAsset := TNewAsset(acctBackend, ACCTID)
   598  
   599  	swapper, err := NewSwapper(&Config{
   600  		Assets: map[uint32]*SwapperAsset{
   601  			ABCID:  {abcAsset, abcCoinLocker},
   602  			XYZID:  {xyzAsset, xyzCoinLocker},
   603  			ACCTID: {BackedAsset: acctAsset}, // no coin locker for account based asset.
   604  		},
   605  		Storage:          storage,
   606  		AuthManager:      authMgr,
   607  		BroadcastTimeout: tBcastTimeout,
   608  		TxWaitExpiration: txWaitExpiration,
   609  		LockTimeTaker:    dex.LockTimeTaker(dex.Testnet),
   610  		LockTimeMaker:    dex.LockTimeMaker(dex.Testnet),
   611  		SwapDone:         func(ord order.Order, match *order.Match, fail bool) {},
   612  	})
   613  	if err != nil {
   614  		panic(err.Error())
   615  	}
   616  
   617  	ssw := dex.NewStartStopWaiter(swapper)
   618  	ssw.Start(testCtx)
   619  	cleanup := func() {
   620  		ssw.Stop()
   621  		ssw.WaitForShutdown()
   622  	}
   623  
   624  	return &testRig{
   625  		abc:           abcAsset,
   626  		abcNode:       abcBackend,
   627  		xyz:           xyzAsset,
   628  		xyzNode:       xyzBackend,
   629  		acctAsset:     acctAsset,
   630  		acctNode:      acctBackend,
   631  		auth:          authMgr,
   632  		swapper:       swapper,
   633  		swapperWaiter: ssw,
   634  		storage:       storage,
   635  		matchInfo:     matchInfo,
   636  		noResume:      noResume,
   637  	}, cleanup
   638  }
   639  
   640  func (rig *testRig) getTracker() *matchTracker {
   641  	rig.swapper.matchMtx.Lock()
   642  	defer rig.swapper.matchMtx.Unlock()
   643  	return rig.swapper.matches[rig.matchInfo.matchID]
   644  }
   645  
   646  // waitChans waits on the specified channels sequentially in the order given.
   647  // nil channels are ignored.
   648  func (rig *testRig) waitChans(tag string, chans ...chan struct{}) error {
   649  	for _, c := range chans {
   650  		if c == nil {
   651  			continue
   652  		}
   653  		select {
   654  		case <-c:
   655  		case <-time.After(time.Second):
   656  			return fmt.Errorf("waiting on %q timed out", tag)
   657  		}
   658  	}
   659  	return nil
   660  }
   661  
   662  // Taker: Acknowledge the servers match notification.
   663  func (rig *testRig) ackMatch_maker(checkSig bool) (err error) {
   664  	matchInfo := rig.matchInfo
   665  	err = rig.ackMatch(matchInfo.maker, matchInfo.makerOID, matchInfo.taker.addr)
   666  	if err != nil {
   667  		return err
   668  	}
   669  	if checkSig {
   670  		tracker := rig.getTracker()
   671  		if !bytes.Equal(tracker.Sigs.MakerMatch, matchInfo.maker.sig) {
   672  			return fmt.Errorf("expected maker audit signature '%x', got '%x'", matchInfo.maker.sig, tracker.Sigs.MakerMatch)
   673  		}
   674  	}
   675  	return nil
   676  }
   677  
   678  // Maker: Acknowledge the servers match notification.
   679  func (rig *testRig) ackMatch_taker(checkSig bool) error {
   680  	matchInfo := rig.matchInfo
   681  	err := rig.ackMatch(matchInfo.taker, matchInfo.takerOID, matchInfo.maker.addr)
   682  	if err != nil {
   683  		return err
   684  	}
   685  	if checkSig {
   686  		tracker := rig.getTracker()
   687  		if !bytes.Equal(tracker.Sigs.TakerMatch, matchInfo.taker.sig) {
   688  			return fmt.Errorf("expected taker audit signature '%x', got '%x'", matchInfo.taker.sig, tracker.Sigs.TakerMatch)
   689  		}
   690  	}
   691  	return nil
   692  }
   693  
   694  func (rig *testRig) ackMatch(user *tUser, oid order.OrderID, counterAddr string) error {
   695  	req := rig.auth.popReq(user.acct)
   696  	if req == nil {
   697  		return fmt.Errorf("failed to find match notification for %s", user.lbl)
   698  	}
   699  	if req.req.Route != msgjson.MatchRoute {
   700  		return fmt.Errorf("expected method '%s', got '%s'", msgjson.MatchRoute, req.req.Route)
   701  	}
   702  	err := rig.checkMatchNotification(req.req, oid, counterAddr)
   703  	if err != nil {
   704  		return err
   705  	}
   706  	// The maker and taker would sign the notifications and return a list of
   707  	// authorizations.
   708  	resp := tNewResponse(req.req.ID, tAckArr(user, user.matchIDs))
   709  	req.respFunc(nil, resp) // e.g. processMatchAcks, may Send resp on error
   710  	return nil
   711  }
   712  
   713  // Helper to check the match notifications.
   714  func (rig *testRig) checkMatchNotification(msg *msgjson.Message, oid order.OrderID, counterAddr string) error {
   715  	matchInfo := rig.matchInfo
   716  	var notes []*msgjson.Match
   717  	err := json.Unmarshal(msg.Payload, &notes)
   718  	if err != nil {
   719  		fmt.Printf("checkMatchNotification unmarshal error: %v\n", err)
   720  	}
   721  	var notification *msgjson.Match
   722  	for _, n := range notes {
   723  		if bytes.Equal(n.MatchID, matchInfo.matchID[:]) {
   724  			notification = n
   725  			break
   726  		}
   727  		if err = checkSigS256(n, rig.auth.privkey.PubKey()); err != nil {
   728  			return fmt.Errorf("incorrect server signature: %w", err)
   729  		}
   730  	}
   731  	if notification == nil {
   732  		return fmt.Errorf("did not find match ID %s in match notifications", matchInfo.matchID)
   733  	}
   734  	if notification.OrderID.String() != oid.String() {
   735  		return fmt.Errorf("expected order ID %s, got %s", oid, notification.OrderID)
   736  	}
   737  	if notification.Quantity != matchInfo.qty {
   738  		return fmt.Errorf("expected order quantity %d, got %d", matchInfo.qty, notification.Quantity)
   739  	}
   740  	if notification.Rate != matchInfo.rate {
   741  		return fmt.Errorf("expected match rate %d, got %d", matchInfo.rate, notification.Rate)
   742  	}
   743  	if notification.Address != counterAddr {
   744  		return fmt.Errorf("expected match address %s, got %s", counterAddr, notification.Address)
   745  	}
   746  	return nil
   747  }
   748  
   749  // Helper to check the swap status for specified user.
   750  // Swap counterparty usually gets a request from server notifying that it's time
   751  // for his turn. Synchronize with that before checking status change.
   752  func (rig *testRig) ensureSwapStatus(tag string, wantStatus order.MatchStatus, waitOn ...chan struct{}) error {
   753  	if err := rig.waitChans(tag, waitOn...); err != nil {
   754  		return err
   755  	}
   756  	tracker := rig.getTracker()
   757  	status := tracker.Status
   758  	if status != wantStatus {
   759  		return fmt.Errorf("unexpected swap status %d after maker swap notification, wanted: %s", status, wantStatus)
   760  	}
   761  	return nil
   762  }
   763  
   764  // Can be used to ensure that a non-error response is returned from the swapper.
   765  func (rig *testRig) checkServerResponseSuccess(user *tUser) error {
   766  	msg, resp := rig.auth.popResp(user.acct)
   767  	if msg == nil {
   768  		return fmt.Errorf("unexpected nil response to %s's request", user.lbl)
   769  	}
   770  	if resp.Error != nil {
   771  		return fmt.Errorf("%s swap rpc error. code: %d, msg: %s", user.lbl, resp.Error.Code, resp.Error.Message)
   772  	}
   773  	return nil
   774  }
   775  
   776  // Can be used to ensure that an error response is returned from the swapper.
   777  func (rig *testRig) checkServerResponseFail(user *tUser, code int, grep ...string) error {
   778  	msg, resp := rig.auth.popResp(user.acct)
   779  	if msg == nil {
   780  		return fmt.Errorf("no response for %s", user.lbl)
   781  	}
   782  	if resp.Error == nil {
   783  		return fmt.Errorf("no error for %s", user.lbl)
   784  	}
   785  	if resp.Error.Code != code {
   786  		return fmt.Errorf("wrong error code for %s. expected %d, got %d", user.lbl, code, resp.Error.Code)
   787  	}
   788  	if len(grep) > 0 && !strings.Contains(resp.Error.Message, grep[0]) {
   789  		return fmt.Errorf("error missing the message %q", grep[0])
   790  	}
   791  	return nil
   792  }
   793  
   794  // Maker: Send swap transaction (swap init request).
   795  func (rig *testRig) sendSwap_maker(expectSuccess bool) (err error) {
   796  	matchInfo := rig.matchInfo
   797  	swap, err := rig.sendSwap(matchInfo.maker, matchInfo.makerOID, matchInfo.taker.addr)
   798  	if err != nil {
   799  		return fmt.Errorf("error sending maker swap request: %w", err)
   800  	}
   801  	matchInfo.db.makerSwap = swap
   802  	if expectSuccess {
   803  		err = rig.ensureSwapStatus("server received our swap -> counterparty got audit request",
   804  			order.MakerSwapCast, rig.auth.swapReceived, rig.auth.auditReq)
   805  		if err != nil {
   806  			return fmt.Errorf("ensure swap status: %w", err)
   807  		}
   808  		err = rig.checkServerResponseSuccess(matchInfo.maker)
   809  		if err != nil {
   810  			return fmt.Errorf("check server response success: %w", err)
   811  		}
   812  	}
   813  	return nil
   814  }
   815  
   816  // Taker: Send swap transaction (swap init request)..
   817  func (rig *testRig) sendSwap_taker(expectSuccess bool) (err error) {
   818  	matchInfo := rig.matchInfo
   819  	swap, err := rig.sendSwap(matchInfo.taker, matchInfo.takerOID, matchInfo.maker.addr)
   820  	if err != nil {
   821  		return fmt.Errorf("error sending taker swap request: %w", err)
   822  	}
   823  	matchInfo.db.takerSwap = swap
   824  	if err != nil {
   825  		return err
   826  	}
   827  	if expectSuccess {
   828  		err = rig.ensureSwapStatus("server received our swap -> counterparty got audit request",
   829  			order.TakerSwapCast, rig.auth.swapReceived, rig.auth.auditReq)
   830  		if err != nil {
   831  			return fmt.Errorf("ensure swap status: %w", err)
   832  		}
   833  		err = rig.checkServerResponseSuccess(matchInfo.taker)
   834  		if err != nil {
   835  			return fmt.Errorf("check server response success: %w", err)
   836  		}
   837  	}
   838  	return nil
   839  }
   840  
   841  func (rig *testRig) sendSwap(user *tUser, oid order.OrderID, recipient string) (*tSwap, error) {
   842  	matchInfo := rig.matchInfo
   843  	swap := tNewSwap(matchInfo, oid, recipient, user)
   844  	if isQuoteSwap(user, matchInfo.match) {
   845  		rig.xyzNode.setContract(swap.coin, false)
   846  	} else {
   847  		rig.abcNode.setContract(swap.coin, false)
   848  	}
   849  	rig.auth.swapID = swap.req.ID
   850  	rpcErr := rig.swapper.handleInit(user.acct, swap.req)
   851  	if rpcErr != nil {
   852  		resp, _ := msgjson.NewResponse(swap.req.ID, nil, rpcErr)
   853  		_ = rig.auth.Send(user.acct, resp)
   854  		return nil, fmt.Errorf("%s swap rpc error. code: %d, msg: %s", user.lbl, rpcErr.Code, rpcErr.Message)
   855  	}
   856  	return swap, nil
   857  }
   858  
   859  // Taker: Process the 'audit' request from the swapper. The request should be
   860  // acknowledged separately with ackAudit_taker.
   861  func (rig *testRig) auditSwap_taker() error {
   862  	matchInfo := rig.matchInfo
   863  	req := rig.auth.popReq(matchInfo.taker.acct)
   864  	matchInfo.db.takerAudit = req
   865  	if req == nil {
   866  		return fmt.Errorf("failed to find audit request for taker after maker's init")
   867  	}
   868  	return rig.auditSwap(req.req, matchInfo.takerOID, matchInfo.db.makerSwap, "taker", matchInfo.taker)
   869  }
   870  
   871  // Maker: Process the 'audit' request from the swapper. The request should be
   872  // acknowledged separately with ackAudit_maker.
   873  func (rig *testRig) auditSwap_maker() error {
   874  	matchInfo := rig.matchInfo
   875  	req := rig.auth.popReq(matchInfo.maker.acct)
   876  	matchInfo.db.makerAudit = req
   877  	if req == nil {
   878  		return fmt.Errorf("failed to find audit request for maker after taker's init")
   879  	}
   880  	return rig.auditSwap(req.req, matchInfo.makerOID, matchInfo.db.takerSwap, "maker", matchInfo.maker)
   881  }
   882  
   883  // checkSigS256 checks that the message's signature was created with the
   884  // private key for the provided secp256k1 public key.
   885  func checkSigS256(msg msgjson.Signable, pubKey *secp256k1.PublicKey) error {
   886  	signature, err := ecdsa.ParseDERSignature(msg.SigBytes())
   887  	if err != nil {
   888  		return fmt.Errorf("error decoding secp256k1 Signature from bytes: %w", err)
   889  	}
   890  	msgB := msg.Serialize()
   891  	hash := sha256.Sum256(msgB)
   892  	if !signature.Verify(hash[:], pubKey) {
   893  		return fmt.Errorf("secp256k1 signature verification failed")
   894  	}
   895  	return nil
   896  }
   897  
   898  func (rig *testRig) auditSwap(msg *msgjson.Message, oid order.OrderID, swap *tSwap, tag string, user *tUser) error {
   899  	if msg == nil {
   900  		return fmt.Errorf("no %s 'audit' request from DEX", user.lbl)
   901  	}
   902  
   903  	if msg.Route != msgjson.AuditRoute {
   904  		return fmt.Errorf("expected method '%s', got '%s'", msgjson.AuditRoute, msg.Route)
   905  	}
   906  	var params *msgjson.Audit
   907  	err := json.Unmarshal(msg.Payload, &params)
   908  	if err != nil {
   909  		return fmt.Errorf("error unmarshaling audit params: %w", err)
   910  	}
   911  	if err = checkSigS256(params, rig.auth.privkey.PubKey()); err != nil {
   912  		return fmt.Errorf("incorrect server signature: %w", err)
   913  	}
   914  	if params.OrderID.String() != oid.String() {
   915  		return fmt.Errorf("%s : incorrect order ID in auditSwap, expected '%s', got '%s'", tag, oid, params.OrderID)
   916  	}
   917  	matchID := rig.matchInfo.matchID
   918  	if params.MatchID.String() != matchID.String() {
   919  		return fmt.Errorf("%s : incorrect match ID in auditSwap, expected '%s', got '%s'", tag, matchID, params.MatchID)
   920  	}
   921  	if params.Contract.String() != swap.contract {
   922  		return fmt.Errorf("%s : incorrect contract. expected '%s', got '%s'", tag, swap.contract, params.Contract)
   923  	}
   924  	if !bytes.Equal(params.TxData, swap.coin.TxData) {
   925  		return fmt.Errorf("%s : incorrect tx data. expected '%s', got '%s'", tag, swap.coin.TxData, params.TxData)
   926  	}
   927  	return nil
   928  }
   929  
   930  // Maker: Acknowledge the DEX 'audit' request.
   931  func (rig *testRig) ackAudit_maker(checkSig bool) error {
   932  	maker := rig.matchInfo.maker
   933  	err := rig.ackAudit(maker, rig.matchInfo.db.makerAudit)
   934  	if err != nil {
   935  		return err
   936  	}
   937  	if checkSig {
   938  		tracker := rig.getTracker()
   939  		if !bytes.Equal(tracker.Sigs.MakerAudit, maker.sig) {
   940  			return fmt.Errorf("expected taker audit signature '%x', got '%x'", maker.sig, tracker.Sigs.MakerAudit)
   941  		}
   942  	}
   943  	return nil
   944  }
   945  
   946  // Taker: Acknowledge the DEX 'audit' request.
   947  func (rig *testRig) ackAudit_taker(checkSig bool) error {
   948  	taker := rig.matchInfo.taker
   949  	err := rig.ackAudit(taker, rig.matchInfo.db.takerAudit)
   950  	if err != nil {
   951  		return err
   952  	}
   953  	if checkSig {
   954  		tracker := rig.getTracker()
   955  		if !bytes.Equal(tracker.Sigs.TakerAudit, taker.sig) {
   956  			return fmt.Errorf("expected taker audit signature '%x', got '%x'", taker.sig, tracker.Sigs.TakerAudit)
   957  		}
   958  	}
   959  	return nil
   960  }
   961  
   962  func (rig *testRig) ackAudit(user *tUser, req *TRequest) error {
   963  	if req == nil {
   964  		return fmt.Errorf("no %s 'audit' request from DEX", user.lbl)
   965  	}
   966  	req.respFunc(nil, tNewResponse(req.req.ID, tAck(user, rig.matchInfo.matchID)))
   967  	return nil
   968  }
   969  
   970  // Maker: Redeem taker's swap transaction.
   971  func (rig *testRig) redeem_maker(expectSuccess bool) error {
   972  	matchInfo := rig.matchInfo
   973  	redeem, err := rig.redeem(matchInfo.maker, matchInfo.makerOID)
   974  	if err != nil {
   975  		return fmt.Errorf("error sending maker redeem request: %w", err)
   976  	}
   977  	matchInfo.db.makerRedeem = redeem
   978  	if expectSuccess {
   979  		err = rig.ensureSwapStatus("server received our redeem -> counterparty got redemption request",
   980  			order.MakerRedeemed, rig.auth.redeemReceived, rig.auth.redemptionReq)
   981  		if err != nil {
   982  			return fmt.Errorf("ensure swap status: %w", err)
   983  		}
   984  		err = rig.checkServerResponseSuccess(matchInfo.maker)
   985  		if err != nil {
   986  			return fmt.Errorf("check server response success: %w", err)
   987  		}
   988  	}
   989  	return nil
   990  }
   991  
   992  // Taker: Redeem maker's swap transaction.
   993  func (rig *testRig) redeem_taker(expectSuccess bool) error {
   994  	matchInfo := rig.matchInfo
   995  	redeem, err := rig.redeem(matchInfo.taker, matchInfo.takerOID)
   996  	if err != nil {
   997  		return fmt.Errorf("error sending taker redeem request: %w", err)
   998  	}
   999  	matchInfo.db.takerRedeem = redeem
  1000  	if expectSuccess {
  1001  		if err := rig.waitChans("server received our redeem and we got a redemption note", rig.auth.redeemReceived, rig.auth.redemptionReq); err != nil {
  1002  			return err
  1003  		}
  1004  		tracker := rig.getTracker()
  1005  		if tracker.Status != order.MatchComplete {
  1006  			return fmt.Errorf("unexpected swap status %d after taker redeem notification", tracker.Status)
  1007  		}
  1008  		err = rig.checkServerResponseSuccess(matchInfo.taker)
  1009  		if err != nil {
  1010  			return fmt.Errorf("check server response success: %w", err)
  1011  		}
  1012  	}
  1013  	return nil
  1014  }
  1015  
  1016  func (rig *testRig) redeem(user *tUser, oid order.OrderID) (*tRedeem, error) {
  1017  	matchInfo := rig.matchInfo
  1018  	redeem := tNewRedeem(matchInfo, oid, user)
  1019  	if isQuoteSwap(user, matchInfo.match) {
  1020  		// do not clear redemptionErr yet
  1021  		rig.abcNode.setRedemption(redeem.coin, redeem.cpSwapCoin, false)
  1022  	} else {
  1023  		rig.xyzNode.setRedemption(redeem.coin, redeem.cpSwapCoin, false)
  1024  	}
  1025  	rig.auth.redeemID = redeem.req.ID
  1026  	rpcErr := rig.swapper.handleRedeem(user.acct, redeem.req)
  1027  	if rpcErr != nil {
  1028  		resp, _ := msgjson.NewResponse(redeem.req.ID, nil, rpcErr)
  1029  		_ = rig.auth.Send(user.acct, resp)
  1030  		return nil, fmt.Errorf("%s swap rpc error. code: %d, msg: %s", user.lbl, rpcErr.Code, rpcErr.Message)
  1031  	}
  1032  	return redeem, nil
  1033  }
  1034  
  1035  // Taker: Acknowledge the DEX 'redemption' request.
  1036  func (rig *testRig) ackRedemption_taker(checkSig bool) error {
  1037  	matchInfo := rig.matchInfo
  1038  	err := rig.ackRedemption(matchInfo.taker, matchInfo.takerOID, matchInfo.db.makerRedeem)
  1039  	if err != nil {
  1040  		return err
  1041  	}
  1042  	if checkSig {
  1043  		tracker := rig.getTracker()
  1044  		if !bytes.Equal(tracker.Sigs.TakerRedeem, matchInfo.taker.sig) {
  1045  			return fmt.Errorf("expected taker redemption signature '%x', got '%x'", matchInfo.taker.sig, tracker.Sigs.TakerRedeem)
  1046  		}
  1047  	}
  1048  	return nil
  1049  }
  1050  
  1051  // Maker: Acknowledge the DEX 'redemption' request.
  1052  func (rig *testRig) ackRedemption_maker(checkSig bool) error {
  1053  	matchInfo := rig.matchInfo
  1054  	err := rig.ackRedemption(matchInfo.maker, matchInfo.makerOID, matchInfo.db.takerRedeem)
  1055  	if err != nil {
  1056  		return err
  1057  	}
  1058  	if checkSig {
  1059  		tracker := rig.getTracker()
  1060  		if tracker != nil {
  1061  			return fmt.Errorf("expected match to be removed, found it, in status %v", tracker.Status)
  1062  		}
  1063  	}
  1064  	return nil
  1065  }
  1066  
  1067  func (rig *testRig) ackRedemption(user *tUser, oid order.OrderID, redeem *tRedeem) error {
  1068  	if redeem == nil {
  1069  		return fmt.Errorf("nil redeem info")
  1070  	}
  1071  	req := rig.auth.popReq(user.acct)
  1072  	if req == nil {
  1073  		return fmt.Errorf("failed to find redemption request for %s", user.lbl)
  1074  	}
  1075  	err := rig.checkRedeem(req.req, oid, redeem.coin.ID(), user.lbl)
  1076  	if err != nil {
  1077  		return err
  1078  	}
  1079  	req.respFunc(nil, tNewResponse(req.req.ID, tAck(user, rig.matchInfo.matchID)))
  1080  	return nil
  1081  }
  1082  
  1083  func (rig *testRig) checkRedeem(msg *msgjson.Message, oid order.OrderID, coinID []byte, tag string) error {
  1084  	var params *msgjson.Redemption
  1085  	err := json.Unmarshal(msg.Payload, &params)
  1086  	if err != nil {
  1087  		return fmt.Errorf("error unmarshaling redeem params: %w", err)
  1088  	}
  1089  	if err = checkSigS256(params, rig.auth.privkey.PubKey()); err != nil {
  1090  		return fmt.Errorf("incorrect server signature: %w", err)
  1091  	}
  1092  	if params.OrderID.String() != oid.String() {
  1093  		return fmt.Errorf("%s : incorrect order ID in checkRedeem, expected '%s', got '%s'", tag, oid, params.OrderID)
  1094  	}
  1095  	matchID := rig.matchInfo.matchID
  1096  	if params.MatchID.String() != matchID.String() {
  1097  		return fmt.Errorf("%s : incorrect match ID in checkRedeem, expected '%s', got '%s'", tag, matchID, params.MatchID)
  1098  	}
  1099  	if !bytes.Equal(params.CoinID, coinID) {
  1100  		return fmt.Errorf("%s : incorrect coinID in checkRedeem. expected '%x', got '%x'", tag, coinID, params.CoinID)
  1101  	}
  1102  	return nil
  1103  }
  1104  
  1105  func makeCancelOrder(limitOrder *order.LimitOrder, user *tUser) *order.CancelOrder {
  1106  	return &order.CancelOrder{
  1107  		P: order.Prefix{
  1108  			AccountID:  user.acct,
  1109  			BaseAsset:  limitOrder.BaseAsset,
  1110  			QuoteAsset: limitOrder.QuoteAsset,
  1111  			OrderType:  order.CancelOrderType,
  1112  			ClientTime: unixMsNow(),
  1113  			ServerTime: unixMsNow(),
  1114  		},
  1115  		TargetOrderID: limitOrder.ID(),
  1116  	}
  1117  }
  1118  
  1119  func makeLimitOrder(qty, rate uint64, user *tUser, makerSell bool) *order.LimitOrder {
  1120  	return &order.LimitOrder{
  1121  		P: order.Prefix{
  1122  			AccountID:  user.acct,
  1123  			BaseAsset:  ABCID,
  1124  			QuoteAsset: XYZID,
  1125  			OrderType:  order.LimitOrderType,
  1126  			ClientTime: time.UnixMilli(1566497654000),
  1127  			ServerTime: time.UnixMilli(1566497655000),
  1128  		},
  1129  		T: order.Trade{
  1130  			Sell:     makerSell,
  1131  			Quantity: qty,
  1132  			Address:  user.addr,
  1133  		},
  1134  		Rate: rate,
  1135  	}
  1136  }
  1137  
  1138  func makeMarketOrder(qty uint64, user *tUser, makerSell bool) *order.MarketOrder {
  1139  	return &order.MarketOrder{
  1140  		P: order.Prefix{
  1141  			AccountID:  user.acct,
  1142  			BaseAsset:  ABCID,
  1143  			QuoteAsset: XYZID,
  1144  			OrderType:  order.LimitOrderType,
  1145  			ClientTime: time.UnixMilli(1566497654000),
  1146  			ServerTime: time.UnixMilli(1566497655000),
  1147  		},
  1148  		T: order.Trade{
  1149  			Sell:     makerSell,
  1150  			Quantity: qty,
  1151  			Address:  user.addr,
  1152  		},
  1153  	}
  1154  }
  1155  
  1156  func limitLimitPair(makerQty, takerQty, makerRate, takerRate uint64, maker, taker *tUser, makerSell bool) (*order.LimitOrder, *order.LimitOrder) {
  1157  	return makeLimitOrder(makerQty, makerRate, maker, makerSell), makeLimitOrder(takerQty, takerRate, taker, !makerSell)
  1158  }
  1159  
  1160  func marketLimitPair(makerQty, takerQty, rate uint64, maker, taker *tUser, makerSell bool) (*order.LimitOrder, *order.MarketOrder) {
  1161  	return makeLimitOrder(makerQty, rate, maker, makerSell), makeMarketOrder(takerQty, taker, !makerSell)
  1162  }
  1163  
  1164  func tLimitPair(makerQty, takerQty, matchQty, makerRate, takerRate uint64, makerSell bool) *tMatchSet {
  1165  	set := new(tMatchSet)
  1166  	maker, taker := tNewUser("maker"), tNewUser("taker")
  1167  	makerOrder, takerOrder := limitLimitPair(makerQty, takerQty, makerRate, takerRate, maker, taker, makerSell)
  1168  	return set.add(tMatchInfo(maker, taker, matchQty, makerRate, makerOrder, takerOrder))
  1169  }
  1170  
  1171  func tPerfectLimitLimit(qty, rate uint64, makerSell bool) *tMatchSet {
  1172  	return tLimitPair(qty, qty, qty, rate, rate, makerSell)
  1173  }
  1174  
  1175  func tMarketPair(makerQty, takerQty, rate uint64, makerSell bool) *tMatchSet {
  1176  	set := new(tMatchSet)
  1177  	maker, taker := tNewUser("maker"), tNewUser("taker")
  1178  	makerOrder, takerOrder := marketLimitPair(makerQty, takerQty, rate, maker, taker, makerSell)
  1179  	return set.add(tMatchInfo(maker, taker, makerQty, rate, makerOrder, takerOrder))
  1180  }
  1181  
  1182  func tPerfectLimitMarket(qty, rate uint64, makerSell bool) *tMatchSet {
  1183  	return tMarketPair(qty, qty, rate, makerSell)
  1184  }
  1185  
  1186  func tCancelPair() *tMatchSet {
  1187  	set := new(tMatchSet)
  1188  	user := tNewUser("user")
  1189  	qty := uint64(1e8)
  1190  	rate := uint64(1e8)
  1191  	makerOrder := makeLimitOrder(qty, rate, user, true)
  1192  	cancelOrder := makeCancelOrder(makerOrder, user)
  1193  	return set.add(tMatchInfo(user, user, qty, rate, makerOrder, cancelOrder))
  1194  }
  1195  
  1196  // tMatch is the match info for a single match. A tMatch is typically created
  1197  // with tMatchInfo.
  1198  type tMatch struct {
  1199  	match    *order.Match
  1200  	matchID  order.MatchID
  1201  	makerOID order.OrderID
  1202  	takerOID order.OrderID
  1203  	qty      uint64
  1204  	rate     uint64
  1205  	maker    *tUser
  1206  	taker    *tUser
  1207  	db       struct {
  1208  		makerRedeem *tRedeem
  1209  		takerRedeem *tRedeem
  1210  		makerSwap   *tSwap
  1211  		takerSwap   *tSwap
  1212  		makerAudit  *TRequest
  1213  		takerAudit  *TRequest
  1214  	}
  1215  }
  1216  
  1217  func makeAck(mid order.MatchID, sig []byte) msgjson.Acknowledgement {
  1218  	return msgjson.Acknowledgement{
  1219  		MatchID: mid[:],
  1220  		Sig:     sig,
  1221  	}
  1222  }
  1223  
  1224  func tAck(user *tUser, matchID order.MatchID) []byte {
  1225  	b, _ := json.Marshal(makeAck(matchID, user.sig))
  1226  	return b
  1227  }
  1228  
  1229  func tAckArr(user *tUser, matchIDs []order.MatchID) []byte {
  1230  	ackArr := make([]msgjson.Acknowledgement, 0, len(matchIDs))
  1231  	for _, matchID := range matchIDs {
  1232  		ackArr = append(ackArr, makeAck(matchID, user.sig))
  1233  	}
  1234  	b, _ := json.Marshal(ackArr)
  1235  	return b
  1236  }
  1237  
  1238  func tMatchInfo(maker, taker *tUser, matchQty, matchRate uint64, makerOrder *order.LimitOrder, takerOrder order.Order) *tMatch {
  1239  	match := &order.Match{
  1240  		Taker:        takerOrder,
  1241  		Maker:        makerOrder,
  1242  		Quantity:     matchQty,
  1243  		Rate:         matchRate,
  1244  		FeeRateBase:  42,
  1245  		FeeRateQuote: 62,
  1246  		Epoch: order.EpochID{ // make Epoch.End() be now, Idx and Dur not important per se
  1247  			Idx: uint64(time.Now().UnixMilli()),
  1248  			Dur: 1,
  1249  		}, // Need Epoch set for lock time
  1250  	}
  1251  	mid := match.ID()
  1252  	maker.matchIDs = append(maker.matchIDs, mid)
  1253  	taker.matchIDs = append(taker.matchIDs, mid)
  1254  	return &tMatch{
  1255  		match:    match,
  1256  		qty:      matchQty,
  1257  		rate:     matchRate,
  1258  		matchID:  mid,
  1259  		makerOID: makerOrder.ID(),
  1260  		takerOID: takerOrder.ID(),
  1261  		maker:    maker,
  1262  		taker:    taker,
  1263  	}
  1264  }
  1265  
  1266  // Matches are submitted to the swapper in small batches, one for each taker.
  1267  type tMatchSet struct {
  1268  	matchSet   *order.MatchSet
  1269  	matchInfos []*tMatch
  1270  }
  1271  
  1272  // Add a new match to the tMatchSet.
  1273  func (set *tMatchSet) add(matchInfo *tMatch) *tMatchSet {
  1274  	match := matchInfo.match
  1275  	if set.matchSet == nil {
  1276  		set.matchSet = &order.MatchSet{Taker: match.Taker}
  1277  	}
  1278  	if set.matchSet.Taker.User() != match.Taker.User() {
  1279  		fmt.Println("!!!tMatchSet taker mismatch!!!")
  1280  	}
  1281  	ms := set.matchSet
  1282  	ms.Epoch = matchInfo.match.Epoch
  1283  	ms.Makers = append(ms.Makers, match.Maker)
  1284  	ms.Amounts = append(ms.Amounts, matchInfo.qty)
  1285  	ms.Rates = append(ms.Rates, matchInfo.rate)
  1286  	ms.Total += matchInfo.qty
  1287  	// In practice, a MatchSet's fee rate is used to set the individual match
  1288  	// fee rates via (*MatchSet).Matches in Negotiate > readMatches.
  1289  	ms.FeeRateBase = matchInfo.match.FeeRateBase
  1290  	ms.FeeRateQuote = matchInfo.match.FeeRateQuote
  1291  	set.matchInfos = append(set.matchInfos, matchInfo)
  1292  	return set
  1293  }
  1294  
  1295  // Either a market or limit order taker, and any number of makers.
  1296  func tMultiMatchSet(matchQtys, rates []uint64, makerSell bool, isMarket bool) *tMatchSet {
  1297  	var sum uint64
  1298  	for _, v := range matchQtys {
  1299  		sum += v
  1300  	}
  1301  	// Taker order can be > sum of match amounts
  1302  	taker := tNewUser("taker")
  1303  	var takerOrder order.Order
  1304  	if isMarket {
  1305  		takerOrder = makeMarketOrder(sum*5/4, taker, !makerSell)
  1306  	} else {
  1307  		takerOrder = makeLimitOrder(sum*5/4, rates[0], taker, !makerSell)
  1308  	}
  1309  	set := new(tMatchSet)
  1310  	now := uint64(time.Now().UnixMilli())
  1311  	for i, v := range matchQtys {
  1312  		maker := tNewUser("maker" + strconv.Itoa(i))
  1313  		// Alternate market and limit orders
  1314  		makerOrder := makeLimitOrder(v, rates[i], maker, makerSell)
  1315  		matchInfo := tMatchInfo(maker, taker, v, rates[i], makerOrder, takerOrder)
  1316  		matchInfo.match.Epoch.Idx = now
  1317  		matchInfo.match.Epoch.Dur = 1
  1318  		set.add(matchInfo)
  1319  	}
  1320  	return set
  1321  }
  1322  
  1323  // tSwap is the information needed for spoofing a swap transaction.
  1324  type tSwap struct {
  1325  	coin     *asset.Contract
  1326  	req      *msgjson.Message
  1327  	contract string
  1328  }
  1329  
  1330  var (
  1331  	tConfsSpoofer     int64
  1332  	tValSpoofer       uint64 = 1
  1333  	tRecipientSpoofer        = ""
  1334  	tLockTimeSpoofer  time.Time
  1335  )
  1336  
  1337  func tNewSwap(matchInfo *tMatch, oid order.OrderID, recipient string, user *tUser) *tSwap {
  1338  	auditVal := matchInfo.qty
  1339  	if isQuoteSwap(user, matchInfo.match) {
  1340  		auditVal = matcher.BaseToQuote(matchInfo.rate, matchInfo.qty)
  1341  	}
  1342  	coinID := randBytes(36)
  1343  	coin := &TCoin{
  1344  		feeRate:   1,
  1345  		confs:     tConfsSpoofer,
  1346  		auditAddr: recipient + tRecipientSpoofer,
  1347  		auditVal:  auditVal * tValSpoofer,
  1348  		id:        coinID,
  1349  	}
  1350  
  1351  	contract := &asset.Contract{
  1352  		Coin:        coin,
  1353  		SwapAddress: recipient + tRecipientSpoofer,
  1354  		TxData:      encode.RandomBytes(100),
  1355  	}
  1356  
  1357  	contract.LockTime = encode.DropMilliseconds(matchInfo.match.Epoch.End().Add(dex.LockTimeTaker(dex.Testnet)))
  1358  	if user == matchInfo.maker {
  1359  		contract.LockTime = encode.DropMilliseconds(matchInfo.match.Epoch.End().Add(dex.LockTimeMaker(dex.Testnet)))
  1360  	}
  1361  
  1362  	if !tLockTimeSpoofer.IsZero() {
  1363  		contract.LockTime = tLockTimeSpoofer
  1364  	}
  1365  
  1366  	script := "01234567" + user.sigHex
  1367  	req, _ := msgjson.NewRequest(nextID(), msgjson.InitRoute, &msgjson.Init{
  1368  		OrderID: oid[:],
  1369  		MatchID: matchInfo.matchID[:],
  1370  		// We control what the backend returns, so the txid doesn't matter right now.
  1371  		CoinID: coinID,
  1372  		//Time:     uint64(time.Now().UnixMilli()),
  1373  		Contract: dirtyEncode(script),
  1374  	})
  1375  
  1376  	return &tSwap{
  1377  		coin:     contract,
  1378  		req:      req,
  1379  		contract: script,
  1380  	}
  1381  }
  1382  
  1383  func isQuoteSwap(user *tUser, match *order.Match) bool {
  1384  	makerSell := match.Maker.Sell
  1385  	isMaker := user.acct == match.Maker.User()
  1386  	return (isMaker && !makerSell) || (!isMaker && makerSell)
  1387  }
  1388  
  1389  func randBytes(len int) []byte {
  1390  	bytes := make([]byte, len)
  1391  	rand.Read(bytes)
  1392  	return bytes
  1393  }
  1394  
  1395  // tRedeem is the information needed to spoof a redemption transaction.
  1396  type tRedeem struct {
  1397  	req        *msgjson.Message
  1398  	coin       *TCoin
  1399  	cpSwapCoin *asset.Contract
  1400  }
  1401  
  1402  func tNewRedeem(matchInfo *tMatch, oid order.OrderID, user *tUser) *tRedeem {
  1403  	coinID := randBytes(36)
  1404  	req, _ := msgjson.NewRequest(nextID(), msgjson.RedeemRoute, &msgjson.Redeem{
  1405  		OrderID: oid[:],
  1406  		MatchID: matchInfo.matchID[:],
  1407  		CoinID:  coinID,
  1408  		//Time:    uint64(time.Now().UnixMilli()),
  1409  	})
  1410  	var cpSwapCoin *asset.Contract
  1411  	switch user.acct {
  1412  	case matchInfo.maker.acct:
  1413  		cpSwapCoin = matchInfo.db.takerSwap.coin
  1414  	case matchInfo.taker.acct:
  1415  		cpSwapCoin = matchInfo.db.makerSwap.coin
  1416  	default:
  1417  		panic("wrong user")
  1418  	}
  1419  	return &tRedeem{
  1420  		req:        req,
  1421  		coin:       &TCoin{id: coinID},
  1422  		cpSwapCoin: cpSwapCoin,
  1423  	}
  1424  }
  1425  
  1426  // Create a closure that will call t.Fatal if an error is non-nil.
  1427  func makeEnsureNilErr(t *testing.T) func(error) {
  1428  	return func(err error) {
  1429  		t.Helper()
  1430  		if err != nil {
  1431  			t.Fatal(err)
  1432  		}
  1433  	}
  1434  }
  1435  
  1436  func TestMain(m *testing.M) {
  1437  	fastRecheckInterval = time.Millisecond * 40
  1438  	taperedRecheckInterval = time.Millisecond * 40
  1439  	txWaitExpiration = time.Millisecond * 200
  1440  	tBcastTimeout = txWaitExpiration * 5
  1441  	minBlockPeriod = tBcastTimeout / 3
  1442  	logger := dex.StdOutLogger("TEST", dex.LevelTrace)
  1443  	UseLogger(logger)
  1444  	db.UseLogger(logger)
  1445  	matcher.UseLogger(logger)
  1446  	comms.UseLogger(logger)
  1447  	var shutdown func()
  1448  	testCtx, shutdown = context.WithCancel(context.Background())
  1449  	code := m.Run()
  1450  	shutdown()
  1451  	os.Exit(code)
  1452  }
  1453  
  1454  func TestFatalStorageErr(t *testing.T) {
  1455  	rig, cleanup := tNewTestRig(nil)
  1456  	defer cleanup()
  1457  
  1458  	// Test a fatal storage backend error that causes the main loop to return
  1459  	// first, the anomalous shutdown route.
  1460  	rig.storage.fatalBackendErr(errors.New("backend error"))
  1461  
  1462  	rig.swapperWaiter.WaitForShutdown()
  1463  }
  1464  
  1465  func testSwap(t *testing.T, rig *testRig) {
  1466  	t.Helper()
  1467  	ensureNilErr := makeEnsureNilErr(t)
  1468  
  1469  	sendBlock := func(node *TBackend) {
  1470  		node.bChan <- &asset.BlockUpdate{Err: nil}
  1471  	}
  1472  
  1473  	// Step through the negotiation process. No errors should be generated.
  1474  	var takerAcked bool
  1475  	for _, matchInfo := range rig.matches.matchInfos {
  1476  		rig.matchInfo = matchInfo
  1477  		ensureNilErr(rig.ackMatch_maker(true))
  1478  		if !takerAcked {
  1479  			ensureNilErr(rig.ackMatch_taker(true))
  1480  			takerAcked = true
  1481  		}
  1482  
  1483  		// Assuming market's base asset is abc, quote is xyz
  1484  		makerSwapAsset, takerSwapAsset := rig.xyz, rig.abc
  1485  		if matchInfo.match.Maker.Sell {
  1486  			makerSwapAsset, takerSwapAsset = rig.abc, rig.xyz
  1487  		}
  1488  
  1489  		ensureNilErr(rig.sendSwap_maker(true))
  1490  		ensureNilErr(rig.auditSwap_taker())
  1491  		ensureNilErr(rig.ackAudit_taker(true))
  1492  		matchInfo.db.makerSwap.coin.Coin.(*TCoin).setConfs(int64(makerSwapAsset.SwapConf))
  1493  		sendBlock(&makerSwapAsset.Backend.(*TUTXOBackend).TBackend)
  1494  		ensureNilErr(rig.sendSwap_taker(true))
  1495  		ensureNilErr(rig.auditSwap_maker())
  1496  		ensureNilErr(rig.ackAudit_maker(true))
  1497  		matchInfo.db.takerSwap.coin.Coin.(*TCoin).setConfs(int64(takerSwapAsset.SwapConf))
  1498  		sendBlock(&takerSwapAsset.Backend.(*TUTXOBackend).TBackend)
  1499  		ensureNilErr(rig.redeem_maker(true))
  1500  		ensureNilErr(rig.ackRedemption_taker(true))
  1501  		ensureNilErr(rig.redeem_taker(true))
  1502  		ensureNilErr(rig.ackRedemption_maker(true))
  1503  	}
  1504  }
  1505  
  1506  func TestSwaps(t *testing.T) {
  1507  	rig, cleanup := tNewTestRig(nil)
  1508  	defer cleanup()
  1509  
  1510  	rig.auth.auditReq = make(chan struct{}, 1)
  1511  	rig.auth.redeemReceived = make(chan struct{}, 2)
  1512  	rig.auth.redemptionReq = make(chan struct{}, 2)
  1513  
  1514  	for _, makerSell := range []bool{true, false} {
  1515  		sellStr := " buy"
  1516  		if makerSell {
  1517  			sellStr = " sell"
  1518  		}
  1519  		t.Run("perfect limit-limit match"+sellStr, func(t *testing.T) {
  1520  			rig.matches = tPerfectLimitLimit(uint64(1e8), uint64(1e8), makerSell)
  1521  			rig.swapper.Negotiate([]*order.MatchSet{rig.matches.matchSet})
  1522  			testSwap(t, rig)
  1523  		})
  1524  		t.Run("perfect limit-market match"+sellStr, func(t *testing.T) {
  1525  			rig.matches = tPerfectLimitMarket(uint64(1e8), uint64(1e8), makerSell)
  1526  			rig.swapper.Negotiate([]*order.MatchSet{rig.matches.matchSet})
  1527  			testSwap(t, rig)
  1528  		})
  1529  		t.Run("imperfect limit-market match"+sellStr, func(t *testing.T) {
  1530  			// only requirement is that maker val > taker val.
  1531  			rig.matches = tMarketPair(uint64(10e8), uint64(2e8), uint64(5e8), makerSell)
  1532  			rig.swapper.Negotiate([]*order.MatchSet{rig.matches.matchSet})
  1533  			testSwap(t, rig)
  1534  		})
  1535  		t.Run("imperfect limit-limit match"+sellStr, func(t *testing.T) {
  1536  			rig.matches = tLimitPair(uint64(10e8), uint64(2e8), uint64(2e8), uint64(5e8), uint64(5e8), makerSell)
  1537  			rig.swapper.Negotiate([]*order.MatchSet{rig.matches.matchSet})
  1538  			testSwap(t, rig)
  1539  		})
  1540  		for _, isMarket := range []bool{true, false} {
  1541  			marketStr := " limit"
  1542  			if isMarket {
  1543  				marketStr = " market"
  1544  			}
  1545  			t.Run("three match set"+sellStr+marketStr, func(t *testing.T) {
  1546  				matchQtys := []uint64{uint64(1e8), uint64(9e8), uint64(3e8)}
  1547  				rates := []uint64{uint64(10e8), uint64(11e8), uint64(12e8)}
  1548  				// one taker, 3 makers => 4 'match' requests
  1549  				rig.matches = tMultiMatchSet(matchQtys, rates, makerSell, isMarket)
  1550  
  1551  				rig.swapper.Negotiate([]*order.MatchSet{rig.matches.matchSet})
  1552  				testSwap(t, rig)
  1553  			})
  1554  		}
  1555  	}
  1556  }
  1557  
  1558  func TestInvalidFeeRate(t *testing.T) {
  1559  	set := tPerfectLimitLimit(uint64(1e8), uint64(1e8), true)
  1560  	matchInfo := set.matchInfos[0]
  1561  	rig, cleanup := tNewTestRig(matchInfo)
  1562  	defer cleanup()
  1563  
  1564  	rig.auth.swapReceived = make(chan struct{}, 1)
  1565  
  1566  	rig.swapper.Negotiate([]*order.MatchSet{set.matchSet})
  1567  
  1568  	rig.abcNode.invalidFeeRate = true
  1569  
  1570  	// The error will be generated by the chainWaiter thread, so will need to
  1571  	// check the response.
  1572  	if err := rig.sendSwap_maker(false); err != nil {
  1573  		t.Fatal(err)
  1574  	}
  1575  	timeOutMempool()
  1576  	if err := rig.waitChans("invalid fee rate", rig.auth.swapReceived); err != nil {
  1577  		t.Fatalf("error waiting for response: %v", err)
  1578  	}
  1579  	// Should have an rpc error.
  1580  	msg, resp := rig.auth.popResp(matchInfo.maker.acct)
  1581  	if msg == nil {
  1582  		t.Fatalf("no response for missing tx after timeout")
  1583  	}
  1584  	if resp.Error == nil {
  1585  		t.Fatalf("no rpc error for erroneous maker swap %v", resp.Error)
  1586  	}
  1587  }
  1588  
  1589  func TestTxWaiters(t *testing.T) {
  1590  	set := tPerfectLimitLimit(uint64(1e8), uint64(1e8), true)
  1591  	matchInfo := set.matchInfos[0]
  1592  	rig, cleanup := tNewTestRig(matchInfo)
  1593  	defer cleanup()
  1594  
  1595  	rig.auth.auditReq = make(chan struct{}, 1)
  1596  	rig.auth.redeemReceived = make(chan struct{}, 1)
  1597  	rig.auth.redemptionReq = make(chan struct{}, 1)
  1598  	rig.auth.swapReceived = make(chan struct{}, 1)
  1599  
  1600  	ensureNilErr := makeEnsureNilErr(t)
  1601  	dummyError := fmt.Errorf("test error")
  1602  
  1603  	sendBlock := func(node *TBackend) {
  1604  		node.bChan <- &asset.BlockUpdate{Err: nil}
  1605  	}
  1606  
  1607  	rig.swapper.Negotiate([]*order.MatchSet{set.matchSet})
  1608  
  1609  	// Get the MatchNotifications that the swapper sent to the clients and check
  1610  	// the match notification length, content, IDs, etc.
  1611  	if err := rig.ackMatch_maker(true); err != nil {
  1612  		t.Fatal(err)
  1613  	}
  1614  	if err := rig.ackMatch_taker(true); err != nil {
  1615  		t.Fatal(err)
  1616  	}
  1617  
  1618  	// Set a non-latency error.
  1619  	rig.abcNode.setContractErr(dummyError)
  1620  	rig.sendSwap_maker(false)
  1621  	if err := rig.waitChans("maker contract error", rig.auth.swapReceived); err != nil {
  1622  		t.Fatalf("error waiting for maker swap error response: %v", err)
  1623  	}
  1624  	msg, _ := rig.auth.popResp(matchInfo.maker.acct)
  1625  	if msg == nil {
  1626  		t.Fatalf("no response for erroneous maker swap")
  1627  	}
  1628  
  1629  	// Set an error for the maker's swap asset
  1630  	rig.abcNode.setContractErr(asset.CoinNotFoundError)
  1631  	// The error will be generated by the chainWaiter thread, so will need to
  1632  	// check the response.
  1633  	if err := rig.sendSwap_maker(false); err != nil {
  1634  		t.Fatal(err)
  1635  	}
  1636  
  1637  	// Duplicate init request should get rejected.
  1638  	dupSwapReq := *matchInfo.db.makerSwap.req
  1639  	dupSwapReq.ID = nextID() // same content but new request ID
  1640  	rpcErr := rig.swapper.handleInit(matchInfo.maker.acct, &dupSwapReq)
  1641  	if rpcErr == nil {
  1642  		t.Fatal("should have rejected the duplicate init request")
  1643  	}
  1644  	if rpcErr.Code != msgjson.DuplicateRequestError {
  1645  		t.Errorf("duplicate init request expected code %d, got %d",
  1646  			msgjson.DuplicateRequestError, rpcErr.Code)
  1647  	}
  1648  
  1649  	// Now timeout the initial search.
  1650  	timeOutMempool()
  1651  	if err := rig.waitChans("maker mempool timeout error", rig.auth.swapReceived); err != nil {
  1652  		t.Fatalf("error waiting for maker swap error response: %v", err)
  1653  	}
  1654  	// Should have an rpc error.
  1655  	msg, resp := rig.auth.popResp(matchInfo.maker.acct)
  1656  	if msg == nil {
  1657  		t.Fatalf("no response for missing tx after timeout")
  1658  	}
  1659  	if resp.Error == nil {
  1660  		t.Fatalf("no rpc error for erroneous maker swap")
  1661  	}
  1662  
  1663  	rig.abcNode.setContractErr(nil)
  1664  	// Everything should work now.
  1665  	if err := rig.sendSwap_maker(true); err != nil {
  1666  		t.Fatal(err)
  1667  	}
  1668  	matchInfo.db.makerSwap.coin.Coin.(*TCoin).setConfs(int64(rig.abc.SwapConf))
  1669  	sendBlock(&rig.abc.Backend.(*TUTXOBackend).TBackend)
  1670  	if err := rig.auditSwap_taker(); err != nil {
  1671  		t.Fatal(err)
  1672  	}
  1673  	if err := rig.ackAudit_taker(true); err != nil {
  1674  		t.Fatal(err)
  1675  	}
  1676  	// Non-latency error.
  1677  	rig.xyzNode.setContractErr(dummyError)
  1678  	rig.sendSwap_taker(false)
  1679  	if err := rig.waitChans("taker contract error", rig.auth.swapReceived); err != nil {
  1680  		t.Fatalf("error waiting for taker swap error response: %v", err)
  1681  	}
  1682  	msg, _ = rig.auth.popResp(matchInfo.taker.acct)
  1683  	if msg == nil {
  1684  		t.Fatalf("no response for erroneous taker swap")
  1685  	}
  1686  	// For the taker swap, simulate latency.
  1687  	rig.xyzNode.setContractErr(asset.CoinNotFoundError)
  1688  	ensureNilErr(rig.sendSwap_taker(false))
  1689  	// Wait a tick
  1690  	tickMempool()
  1691  	// There should not be a response yet.
  1692  	msg, _ = rig.auth.popResp(matchInfo.taker.acct)
  1693  	if msg != nil {
  1694  		t.Fatalf("unexpected response for latent taker swap")
  1695  	}
  1696  	// Clear the error.
  1697  	rig.xyzNode.setContractErr(nil)
  1698  	tickMempool()
  1699  	if err := rig.waitChans("taker mempool timeout error", rig.auth.swapReceived); err != nil {
  1700  		t.Fatalf("error waiting for taker timeout error response: %v", err)
  1701  	}
  1702  
  1703  	msg, resp = rig.auth.popResp(matchInfo.taker.acct)
  1704  	if msg == nil {
  1705  		t.Fatalf("no response for ok taker swap")
  1706  	}
  1707  	if resp.Error != nil {
  1708  		t.Fatalf("unexpected rpc error for ok taker swap. code: %d, msg: %s",
  1709  			resp.Error.Code, resp.Error.Message)
  1710  	}
  1711  	matchInfo.db.takerSwap.coin.Coin.(*TCoin).setConfs(int64(rig.xyz.SwapConf))
  1712  	sendBlock(&rig.xyz.Backend.(*TUTXOBackend).TBackend)
  1713  
  1714  	ensureNilErr(rig.auditSwap_maker())
  1715  	ensureNilErr(rig.ackAudit_maker(true))
  1716  
  1717  	// Set a transaction error for the maker's redemption.
  1718  	rig.xyzNode.setRedemptionErr(asset.CoinNotFoundError)
  1719  
  1720  	ensureNilErr(rig.redeem_maker(false))
  1721  	tickMempool()
  1722  	tickMempool()
  1723  	msg, _ = rig.auth.popResp(matchInfo.maker.acct)
  1724  	if msg != nil {
  1725  		t.Fatalf("unexpected response for latent maker redeem")
  1726  	}
  1727  	// Clear the error.
  1728  	rig.xyzNode.setRedemptionErr(nil)
  1729  	tickMempool()
  1730  	if err := rig.waitChans("maker redemption error", rig.auth.redeemReceived); err != nil {
  1731  		t.Fatalf("error waiting for taker timeout error response: %v", err)
  1732  	}
  1733  	msg, resp = rig.auth.popResp(matchInfo.maker.acct)
  1734  	if msg == nil {
  1735  		t.Fatalf("no response for erroneous maker redeem")
  1736  	}
  1737  	if resp.Error != nil {
  1738  		t.Fatalf("unexpected rpc error for erroneous maker redeem. code: %d, msg: %s",
  1739  			resp.Error.Code, resp.Error.Message)
  1740  	}
  1741  	// Back to the taker, but let it timeout first, and then rebroadcast.
  1742  	// Get the tracker now, since it will be removed from the match dict if
  1743  	// everything goes right
  1744  	tracker := rig.getTracker()
  1745  	ensureNilErr(rig.ackRedemption_taker(true))
  1746  	rig.abcNode.setRedemptionErr(asset.CoinNotFoundError)
  1747  	ensureNilErr(rig.redeem_taker(false))
  1748  	timeOutMempool()
  1749  	if err := rig.waitChans("taker redemption timeout", rig.auth.redeemReceived); err != nil {
  1750  		t.Fatalf("error waiting for taker timeout error response: %v", err)
  1751  	}
  1752  	msg, _ = rig.auth.popResp(matchInfo.taker.acct)
  1753  	if msg == nil {
  1754  		t.Fatalf("no response for erroneous taker redeem")
  1755  	}
  1756  	rig.abcNode.setRedemptionErr(nil)
  1757  	ensureNilErr(rig.redeem_taker(true))
  1758  	tickMempool()
  1759  	tickMempool()
  1760  	ensureNilErr(rig.ackRedemption_maker(true))
  1761  	// Set the number of confirmations on the redemptions.
  1762  	matchInfo.db.makerRedeem.coin.setConfs(int64(rig.xyz.SwapConf))
  1763  	matchInfo.db.takerRedeem.coin.setConfs(int64(rig.abc.SwapConf))
  1764  	// send a block through for either chain to trigger a completion check.
  1765  	rig.xyzNode.bChan <- &asset.BlockUpdate{Err: nil}
  1766  	tickMempool()
  1767  	if tracker.Status != order.MatchComplete {
  1768  		t.Fatalf("match not marked as complete: %d", tracker.Status)
  1769  	}
  1770  	// Make sure that the tracker is removed from swappers match map.
  1771  	if rig.getTracker() != nil {
  1772  		t.Fatalf("matchTracker not removed from swapper's match map")
  1773  	}
  1774  }
  1775  
  1776  func TestBroadcastTimeouts(t *testing.T) {
  1777  	rig, cleanup := tNewTestRig(nil)
  1778  	defer cleanup()
  1779  
  1780  	rig.auth.newSuspend = make(chan struct{}, 1)
  1781  	rig.auth.auditReq = make(chan struct{}, 1)
  1782  	rig.auth.redemptionReq = make(chan struct{}, 1)
  1783  	rig.auth.newNtfn = make(chan struct{}, 2) // 2 revoke_match requests
  1784  
  1785  	ensureNilErr := makeEnsureNilErr(t)
  1786  	sendBlock := func(node *TBackend) {
  1787  		node.bChan <- &asset.BlockUpdate{Err: nil}
  1788  	}
  1789  
  1790  	ntfnWait := func(timeout time.Duration) {
  1791  		t.Helper()
  1792  		select {
  1793  		case <-rig.auth.newNtfn:
  1794  		case <-time.After(timeout):
  1795  			t.Fatalf("no notification received")
  1796  		}
  1797  	}
  1798  
  1799  	checkRevokeMatch := func(user *tUser, i int) {
  1800  		t.Helper()
  1801  		rev := new(msgjson.RevokeMatch)
  1802  		err := rig.auth.getNtfn(user.acct, msgjson.RevokeMatchRoute, rev)
  1803  		if err != nil {
  1804  			t.Fatalf("failed to get revoke_match ntfn: %v", err)
  1805  		}
  1806  		if err = checkSigS256(rev, rig.auth.privkey.PubKey()); err != nil {
  1807  			t.Fatalf("incorrect server signature: %v", err)
  1808  		}
  1809  		if !bytes.Equal(rev.MatchID, rig.matchInfo.matchID[:]) {
  1810  			t.Fatalf("unexpected revocation match ID for %s at step %d. expected %s, got %s",
  1811  				user.lbl, i, rig.matchInfo.matchID, rev.MatchID)
  1812  		}
  1813  
  1814  		// TODO: expect revoke order for at-fault user
  1815  	}
  1816  
  1817  	// tryExpire will sleep for the duration of a BroadcastTimeout, and then
  1818  	// check that a penalty was assigned to the appropriate user, and that a
  1819  	// revoke_match message is sent to both users.
  1820  	tryExpire := func(i, j int, step order.MatchStatus, jerk, victim *tUser, node *TBackend) bool {
  1821  		t.Helper()
  1822  		if i != j {
  1823  			return false
  1824  		}
  1825  		// Sending a block through should schedule an inaction check after duration
  1826  		// BroadcastTimeout.
  1827  		sendBlock(node)
  1828  		select {
  1829  		case <-rig.auth.newSuspend:
  1830  		case <-time.After(rig.swapper.bTimeout * 2):
  1831  			t.Fatalf("no penalization happened")
  1832  		}
  1833  		found, rule := rig.auth.flushPenalty(jerk.acct)
  1834  		if !found {
  1835  			t.Fatalf("failed to penalize user at step %d", i)
  1836  		}
  1837  		if rule == account.NoRule {
  1838  			t.Fatalf("no penalty at step %d (status %v)", i, step)
  1839  		}
  1840  		// Make sure the specified user has a cancellation for this order
  1841  		ntfnWait(rig.swapper.bTimeout * 3) // wait for both revoke requests, no particular order
  1842  		ntfnWait(rig.swapper.bTimeout * 3)
  1843  		checkRevokeMatch(jerk, i)
  1844  		checkRevokeMatch(victim, i)
  1845  		return true
  1846  	}
  1847  	// Run a timeout test after every important step.
  1848  	for i := 0; i <= 3; i++ {
  1849  		set := tPerfectLimitLimit(uint64(1e8), uint64(1e8), true) // same orders, different users
  1850  		matchInfo := set.matchInfos[0]
  1851  		rig.matchInfo = matchInfo
  1852  		rig.swapper.Negotiate([]*order.MatchSet{set.matchSet})
  1853  		// Step through the negotiation process. No errors should be generated.
  1854  		// TODO: timeout each match ack, not block based inaction.
  1855  
  1856  		ensureNilErr(rig.ackMatch_maker(true))
  1857  		ensureNilErr(rig.ackMatch_taker(true))
  1858  
  1859  		// Timeout waiting for maker swap.
  1860  		if tryExpire(i, 0, order.NewlyMatched, matchInfo.maker, matchInfo.taker, &rig.abcNode.TBackend) {
  1861  			continue
  1862  		}
  1863  
  1864  		ensureNilErr(rig.sendSwap_maker(true))
  1865  
  1866  		// Pull the server's 'audit' request from the comms queue.
  1867  		ensureNilErr(rig.auditSwap_taker())
  1868  
  1869  		// NOTE: timeout on the taker's audit ack response itself does not cause
  1870  		// a revocation. The taker not broadcasting their swap when maker's swap
  1871  		// reaches swapconf plus bTimeout is the trigger.
  1872  		ensureNilErr(rig.ackAudit_taker(true))
  1873  
  1874  		// Maker's swap reaches swapConf.
  1875  		matchInfo.db.makerSwap.coin.Coin.(*TCoin).setConfs(int64(rig.abc.SwapConf))
  1876  		sendBlock(&rig.abcNode.TBackend) // tryConfirmSwap
  1877  		// With maker swap confirmed, inaction happens bTimeout after
  1878  		// swapConfirmed time.
  1879  		if tryExpire(i, 1, order.MakerSwapCast, matchInfo.taker, matchInfo.maker, &rig.xyzNode.TBackend) {
  1880  			continue
  1881  		}
  1882  
  1883  		ensureNilErr(rig.sendSwap_taker(true))
  1884  
  1885  		// Pull the server's 'audit' request from the comms queue
  1886  		ensureNilErr(rig.auditSwap_maker())
  1887  
  1888  		// NOTE: timeout on the maker's audit ack response itself does not cause
  1889  		// a revocation. The maker not broadcasting their redeem when taker's
  1890  		// swap reaches swapconf plus bTimeout is the trigger.
  1891  		ensureNilErr(rig.ackAudit_maker(true))
  1892  
  1893  		// Taker's swap reaches swapConf.
  1894  		matchInfo.db.takerSwap.coin.Coin.(*TCoin).setConfs(int64(rig.xyz.SwapConf))
  1895  		sendBlock(&rig.xyzNode.TBackend)
  1896  		// With taker swap confirmed, inaction happens bTimeout after
  1897  		// swapConfirmed time.
  1898  		if tryExpire(i, 2, order.TakerSwapCast, matchInfo.maker, matchInfo.taker, &rig.xyzNode.TBackend) {
  1899  			continue
  1900  		}
  1901  
  1902  		ensureNilErr(rig.redeem_maker(true))
  1903  
  1904  		// Pull the server's 'redemption' request from the comms queue
  1905  		ensureNilErr(rig.ackRedemption_taker(true))
  1906  
  1907  		// Maker's redeem reaches swapConf. Not necessary for taker redeem.
  1908  		// matchInfo.db.makerRedeem.coin.setConfs(int64(rig.xyz.SwapConf))
  1909  		// sendBlock(rig.xyzNode)
  1910  		if tryExpire(i, 3, order.MakerRedeemed, matchInfo.taker, matchInfo.maker, &rig.abcNode.TBackend) {
  1911  			continue
  1912  		}
  1913  
  1914  		// Next is redeem_taker... not a block-based inaction.
  1915  
  1916  		return
  1917  	}
  1918  }
  1919  
  1920  func TestSigErrors(t *testing.T) {
  1921  	dummyError := fmt.Errorf("test error")
  1922  	set := tPerfectLimitLimit(uint64(1e8), uint64(1e8), true)
  1923  	matchInfo := set.matchInfos[0]
  1924  	rig, cleanup := tNewTestRig(matchInfo)
  1925  	defer cleanup()
  1926  
  1927  	rig.auth.auditReq = make(chan struct{}, 1)
  1928  	rig.auth.redemptionReq = make(chan struct{}, 1)
  1929  	rig.auth.redeemReceived = make(chan struct{}, 1)
  1930  	rig.auth.swapReceived = make(chan struct{}, 1)
  1931  
  1932  	rig.swapper.Negotiate([]*order.MatchSet{set.matchSet}) // pushes a new req to m.reqs
  1933  	ensureNilErr := makeEnsureNilErr(t)
  1934  
  1935  	// We need a way to restore the state of the queue after testing an auth error.
  1936  	var tReq *TRequest
  1937  	var msg *msgjson.Message
  1938  	apply := func(user *tUser) {
  1939  		if msg != nil {
  1940  			rig.auth.pushResp(user.acct, msg)
  1941  		}
  1942  		if tReq != nil {
  1943  			rig.auth.pushReq(user.acct, tReq)
  1944  		}
  1945  	}
  1946  	stash := func(user *tUser) {
  1947  		msg, _ = rig.auth.popResp(user.acct)
  1948  		tReq = rig.auth.popReq(user.acct)
  1949  		apply(user) // put them back now that we have a copy
  1950  	}
  1951  	testAction := func(stepFunc func(bool) error, user *tUser, failChans ...chan struct{}) {
  1952  		t.Helper()
  1953  		// First do it with an auth error.
  1954  		rig.auth.authErr = dummyError
  1955  		stash(user) // make a copy of this user's next req/resp pair
  1956  		// The error will be pulled from the auth manager.
  1957  		_ = stepFunc(false) // popReq => req.respFunc(..., tNewResponse()) => send error to client (m.resps)
  1958  		if err := rig.waitChans("testAction", failChans...); err != nil {
  1959  			t.Fatalf("testAction failChan wait error: %v", err)
  1960  		}
  1961  		// ensureSigErr makes sure that the specified user has a signature error
  1962  		// response from the swapper.
  1963  		ensureNilErr(rig.checkServerResponseFail(user, msgjson.SignatureError))
  1964  		// Again with no auth error to go to the next step.
  1965  		rig.auth.authErr = nil
  1966  		apply(user) // restore the initial live request
  1967  		ensureNilErr(stepFunc(true))
  1968  	}
  1969  	maker, taker := matchInfo.maker, matchInfo.taker
  1970  	// 1 live match, 2 pending client acks
  1971  	testAction(rig.ackMatch_maker, maker)
  1972  	testAction(rig.ackMatch_taker, taker)
  1973  	testAction(rig.sendSwap_maker, maker, rig.auth.swapReceived)
  1974  	ensureNilErr(rig.auditSwap_taker())
  1975  	testAction(rig.ackAudit_taker, taker)
  1976  	testAction(rig.sendSwap_taker, taker, rig.auth.swapReceived)
  1977  	ensureNilErr(rig.auditSwap_maker())
  1978  	testAction(rig.ackAudit_maker, maker)
  1979  	testAction(rig.redeem_maker, maker, rig.auth.redeemReceived)
  1980  	testAction(rig.ackRedemption_taker, taker)
  1981  	testAction(rig.redeem_taker, taker, rig.auth.redeemReceived)
  1982  	testAction(rig.ackRedemption_maker, maker)
  1983  }
  1984  
  1985  func TestMalformedSwap(t *testing.T) {
  1986  	set := tPerfectLimitLimit(uint64(1e8), uint64(1e8), true)
  1987  	matchInfo := set.matchInfos[0]
  1988  	rig, cleanup := tNewTestRig(matchInfo)
  1989  	defer cleanup()
  1990  
  1991  	rig.auth.swapReceived = make(chan struct{}, 1)
  1992  
  1993  	rig.swapper.Negotiate([]*order.MatchSet{set.matchSet})
  1994  	ensureNilErr := makeEnsureNilErr(t)
  1995  
  1996  	ensureNilErr(rig.ackMatch_maker(true))
  1997  	ensureNilErr(rig.ackMatch_taker(true))
  1998  
  1999  	ensureErr := func(tag string) {
  2000  		t.Helper()
  2001  		ensureNilErr(rig.sendSwap_maker(false))
  2002  		ensureNilErr(rig.waitChans(tag, rig.auth.swapReceived))
  2003  	}
  2004  
  2005  	// Bad contract value
  2006  	tValSpoofer = 2
  2007  	ensureErr("bad maker contract")
  2008  	ensureNilErr(rig.checkServerResponseFail(matchInfo.maker, msgjson.ContractError))
  2009  	tValSpoofer = 1
  2010  	// Bad contract recipient
  2011  	tRecipientSpoofer = "2"
  2012  	ensureErr("bad recipient")
  2013  	ensureNilErr(rig.checkServerResponseFail(matchInfo.maker, msgjson.ContractError))
  2014  	tRecipientSpoofer = ""
  2015  	// Bad locktime
  2016  	tLockTimeSpoofer = time.Unix(1, 0)
  2017  	ensureErr("bad locktime")
  2018  	ensureNilErr(rig.checkServerResponseFail(matchInfo.maker, msgjson.ContractError))
  2019  	tLockTimeSpoofer = time.Time{}
  2020  
  2021  	// Low fee, unconfirmed
  2022  	rig.abcNode.invalidFeeRate = true
  2023  	ensureErr("low fee")
  2024  	ensureNilErr(rig.checkServerResponseFail(matchInfo.maker, msgjson.ContractError))
  2025  
  2026  	// Works with low fee, but now a confirmation.
  2027  	tConfsSpoofer = 1
  2028  	ensureNilErr(rig.sendSwap_maker(true))
  2029  }
  2030  
  2031  func TestRetriesDuringSwap(t *testing.T) {
  2032  	rig, cleanup := tNewTestRig(nil)
  2033  	defer cleanup()
  2034  
  2035  	ensureNilErr := makeEnsureNilErr(t)
  2036  	// retryUntilSuccess will retry action until success or timeout.
  2037  	retryUntilSuccess := func(action func() error, onSuccess, onFail func()) {
  2038  		startWaitingTime := time.Now()
  2039  		for {
  2040  			err := action()
  2041  			if err == nil {
  2042  				// We are done, finally got a retry attempt that worked.
  2043  				onSuccess()
  2044  				break
  2045  			}
  2046  			onFail()
  2047  
  2048  			if time.Since(startWaitingTime) > 10*time.Second {
  2049  				t.Fatalf("timed out retrying, err: %v", err)
  2050  			}
  2051  			time.Sleep(100 * time.Millisecond)
  2052  		}
  2053  	}
  2054  
  2055  	match := tPerfectLimitLimit(uint64(1e8), uint64(1e8), false)
  2056  	rig.matches = match
  2057  	rig.matchInfo = match.matchInfos[0]
  2058  	rig.auth.swapReceived = make(chan struct{}, 1)
  2059  	rig.auth.auditReq = make(chan struct{}, 1)
  2060  	rig.auth.redeemReceived = make(chan struct{}, 1)
  2061  	rig.auth.redemptionReq = make(chan struct{}, 1)
  2062  	rig.swapper.Negotiate([]*order.MatchSet{rig.matches.matchSet})
  2063  
  2064  	ensureNilErr(rig.ackMatch_maker(true))
  2065  	ensureNilErr(rig.ackMatch_taker(true))
  2066  
  2067  	ensureNilErr(rig.sendSwap_maker(true))
  2068  	ensureNilErr(rig.auditSwap_taker())
  2069  	tracker := rig.getTracker()
  2070  	// We're "rewinding time" back NewlyMatched status to be able to retry sending
  2071  	// the same init request again.
  2072  	tracker.Status = order.NewlyMatched
  2073  	retryUntilSuccess(func() error {
  2074  		// We might get a couple of duplicate init request errors here, in that case
  2075  		// we simply retry request later.
  2076  		// We need to wait for swapSearching semaphore (it coordinates init request
  2077  		// handling) to clear, so our retry request should eventually work (it has
  2078  		// adequate fee and 0 confs).
  2079  		return rig.sendSwap_maker(false)
  2080  	}, func() {
  2081  		// On success, executes once.
  2082  		ensureNilErr(rig.ensureSwapStatus("server received our swap -> counterparty got audit request",
  2083  			order.MakerSwapCast, rig.auth.swapReceived, rig.auth.auditReq))
  2084  		ensureNilErr(rig.checkServerResponseSuccess(rig.matchInfo.maker))
  2085  		ensureNilErr(rig.auditSwap_taker())
  2086  	}, func() {
  2087  		// On every failure.
  2088  		ensureNilErr(rig.ensureSwapStatus("server received our swap",
  2089  			order.NewlyMatched, rig.auth.swapReceived))
  2090  		ensureNilErr(rig.checkServerResponseFail(rig.matchInfo.maker, msgjson.DuplicateRequestError))
  2091  	})
  2092  
  2093  	ensureNilErr(rig.ackAudit_taker(true))
  2094  	ensureNilErr(rig.sendSwap_taker(true))
  2095  	ensureNilErr(rig.auditSwap_maker())
  2096  	tracker = rig.getTracker()
  2097  	// We're "rewinding time" back MakerSwapCast status to be able to retry sending
  2098  	// the same init request again.
  2099  	tracker.Status = order.MakerSwapCast
  2100  	retryUntilSuccess(func() error {
  2101  		// We might get a couple of duplicate init request errors here, in that case
  2102  		// we simply retry request later.
  2103  		// We need to wait for swapSearching semaphore (it coordinates init request
  2104  		// handling) to clear, so our retry request should eventually work (it has
  2105  		// adequate fee and 0 confs).
  2106  		return rig.sendSwap_taker(false)
  2107  	}, func() {
  2108  		// On success, executes once.
  2109  		ensureNilErr(rig.ensureSwapStatus("server received our swap -> counterparty got audit request",
  2110  			order.TakerSwapCast, rig.auth.swapReceived, rig.auth.auditReq))
  2111  		ensureNilErr(rig.checkServerResponseSuccess(rig.matchInfo.taker))
  2112  		ensureNilErr(rig.auditSwap_maker())
  2113  	}, func() {
  2114  		// On every failure.
  2115  		ensureNilErr(rig.ensureSwapStatus("server received our swap",
  2116  			order.MakerSwapCast, rig.auth.swapReceived))
  2117  		ensureNilErr(rig.checkServerResponseFail(rig.matchInfo.taker, msgjson.DuplicateRequestError))
  2118  	})
  2119  
  2120  	ensureNilErr(rig.ackAudit_maker(true))
  2121  
  2122  	ensureNilErr(rig.redeem_maker(true))
  2123  	ensureNilErr(rig.ackRedemption_taker(true))
  2124  	tracker = rig.getTracker()
  2125  	// We're "rewinding time" back TakerSwapCast status to be able to retry sending
  2126  	// the same redeem request again.
  2127  	tracker.Status = order.TakerSwapCast
  2128  	retryUntilSuccess(func() error {
  2129  		// We might get a couple of duplicate redeem request errors here, in that case
  2130  		// we simply retry request later.
  2131  		// We need to wait for redeemSearching semaphore (it coordinates redeem request
  2132  		// handling) to clear, so our retry request should eventually work.
  2133  		return rig.redeem_maker(false)
  2134  	}, func() {
  2135  		// On success, executes once.
  2136  		ensureNilErr(rig.ensureSwapStatus("server received our redeem -> counterparty got redemption request",
  2137  			order.MakerRedeemed, rig.auth.redeemReceived, rig.auth.redemptionReq))
  2138  		ensureNilErr(rig.checkServerResponseSuccess(rig.matchInfo.maker))
  2139  		ensureNilErr(rig.ackRedemption_taker(true))
  2140  	}, func() {
  2141  		// On every failure.
  2142  		ensureNilErr(rig.ensureSwapStatus("server received our redeem",
  2143  			order.TakerSwapCast, rig.auth.redeemReceived))
  2144  		ensureNilErr(rig.checkServerResponseFail(rig.matchInfo.maker, msgjson.DuplicateRequestError))
  2145  	})
  2146  
  2147  	ensureNilErr(rig.redeem_taker(true))
  2148  	// Check that any subsequent redeem request retry will just return a
  2149  	// settlement sequence error, and after the redemption ack, an "unknown
  2150  	// match" error.
  2151  	err := rig.redeem_taker(false)
  2152  	if err == nil {
  2153  		t.Fatalf("expected 2nd redeem request to fail after 1st one succeeded")
  2154  	}
  2155  	ensureNilErr(rig.waitChans("server received our redeem", rig.auth.redeemReceived))
  2156  	tracker = rig.getTracker()
  2157  	if tracker == nil {
  2158  		t.Fatal("missing match tracker after taker redeem")
  2159  	}
  2160  	if tracker.Status != order.MatchComplete {
  2161  		t.Fatalf("unexpected swap status %d after taker redeem notification", tracker.Status)
  2162  	}
  2163  	ensureNilErr(rig.checkServerResponseFail(rig.matchInfo.taker, msgjson.SettlementSequenceError))
  2164  
  2165  	tickMempool()
  2166  	tickMempool()
  2167  	ensureNilErr(rig.ackRedemption_maker(true)) // will cause match to be deleted
  2168  
  2169  	tracker = rig.getTracker()
  2170  	if tracker != nil {
  2171  		t.Fatalf("expected match to be removed, found it, in status %v", tracker.Status)
  2172  	}
  2173  
  2174  	// Now it will fail with "unknown match".
  2175  	err = rig.redeem_taker(false)
  2176  	if err == nil {
  2177  		t.Fatalf("expected 2nd redeem request to fail after 1st one succeeded")
  2178  	}
  2179  
  2180  	ensureNilErr(rig.checkServerResponseFail(rig.matchInfo.taker, msgjson.RPCUnknownMatch))
  2181  }
  2182  
  2183  func TestBadParams(t *testing.T) {
  2184  	set := tPerfectLimitLimit(uint64(1e8), uint64(1e8), true)
  2185  	matchInfo := set.matchInfos[0]
  2186  	rig, cleanup := tNewTestRig(matchInfo)
  2187  	defer cleanup()
  2188  	rig.swapper.Negotiate([]*order.MatchSet{set.matchSet})
  2189  	swapper := rig.swapper
  2190  	match := rig.getTracker()
  2191  	user := matchInfo.maker
  2192  	acker := &messageAcker{
  2193  		user:    user.acct,
  2194  		match:   match,
  2195  		isMaker: true,
  2196  		isAudit: true,
  2197  	}
  2198  	ensureNilErr := makeEnsureNilErr(t)
  2199  
  2200  	ackArr := make([]*msgjson.Acknowledgement, 0)
  2201  	matches := []*messageAcker{
  2202  		{match: rig.getTracker()},
  2203  	}
  2204  
  2205  	encodedAckArray := func() json.RawMessage {
  2206  		b, _ := json.Marshal(ackArr)
  2207  		return json.RawMessage(b)
  2208  	}
  2209  
  2210  	// Invalid result.
  2211  	msg, _ := msgjson.NewResponse(1, nil, nil)
  2212  	msg.Payload = json.RawMessage(`{"result":?}`)
  2213  	swapper.processAck(msg, acker)
  2214  	ensureNilErr(rig.checkServerResponseFail(user, msgjson.RPCParseError))
  2215  	swapper.processMatchAcks(user.acct, msg, []*messageAcker{})
  2216  	ensureNilErr(rig.checkServerResponseFail(user, msgjson.RPCParseError))
  2217  
  2218  	msg, _ = msgjson.NewResponse(1, encodedAckArray(), nil)
  2219  	swapper.processMatchAcks(user.acct, msg, matches)
  2220  	ensureNilErr(rig.checkServerResponseFail(user, msgjson.AckCountError))
  2221  }
  2222  
  2223  func TestCancel(t *testing.T) {
  2224  	set := tCancelPair()
  2225  	matchInfo := set.matchInfos[0]
  2226  	rig, cleanup := tNewTestRig(matchInfo)
  2227  	defer cleanup()
  2228  	rig.swapper.Negotiate([]*order.MatchSet{set.matchSet})
  2229  	// There should be no matchTracker
  2230  	if rig.getTracker() != nil {
  2231  		t.Fatalf("found matchTracker for a cancellation")
  2232  	}
  2233  	// The user should have two match requests.
  2234  	user := matchInfo.maker
  2235  	req := rig.auth.popReq(user.acct)
  2236  	if req == nil {
  2237  		t.Fatalf("no request sent from Negotiate")
  2238  	}
  2239  	matchNotes := make([]*msgjson.Match, 0)
  2240  	err := json.Unmarshal(req.req.Payload, &matchNotes)
  2241  	if err != nil {
  2242  		t.Fatalf("unmarshal error: %v", err)
  2243  	}
  2244  	if len(matchNotes) != 2 {
  2245  		t.Fatalf("expected 2 match notification, got %d", len(matchNotes))
  2246  	}
  2247  	for _, match := range matchNotes {
  2248  		if err = checkSigS256(match, rig.auth.privkey.PubKey()); err != nil {
  2249  			t.Fatalf("incorrect server signature: %v", err)
  2250  		}
  2251  	}
  2252  	makerNote, takerNote := matchNotes[0], matchNotes[1]
  2253  	if makerNote.OrderID.String() != matchInfo.makerOID.String() {
  2254  		t.Fatalf("expected maker ID %s, got %s", matchInfo.makerOID, makerNote.OrderID)
  2255  	}
  2256  	if takerNote.OrderID.String() != matchInfo.takerOID.String() {
  2257  		t.Fatalf("expected taker ID %s, got %s", matchInfo.takerOID, takerNote.OrderID)
  2258  	}
  2259  	if makerNote.MatchID.String() != takerNote.MatchID.String() {
  2260  		t.Fatalf("match ID mismatch. %s != %s", makerNote.MatchID, takerNote.MatchID)
  2261  	}
  2262  }
  2263  
  2264  func TestAccountTracking(t *testing.T) {
  2265  	const lotSize uint64 = 1e8
  2266  	const rate = 2e10
  2267  	const lots = 5
  2268  	const qty = lotSize * lots
  2269  	makerAddr := "maker"
  2270  	takerAddr := "taker"
  2271  
  2272  	trackedSet := tPerfectLimitLimit(qty, rate, true)
  2273  	matchInfo := trackedSet.matchInfos[0]
  2274  
  2275  	rig, cleanup := tNewTestRig(matchInfo)
  2276  	defer cleanup()
  2277  
  2278  	var baseAsset, quoteAsset uint32 = ACCTID, XYZID
  2279  
  2280  	addOrder := func(matchSet *order.MatchSet, makerSell bool) {
  2281  		maker, taker := matchSet.Makers[0], matchSet.Taker
  2282  		taker.Prefix().BaseAsset = baseAsset
  2283  		maker.BaseAsset = baseAsset
  2284  		taker.Prefix().QuoteAsset = quoteAsset
  2285  		maker.QuoteAsset = quoteAsset
  2286  		maker.Coins = []order.CoinID{[]byte(makerAddr)}
  2287  		taker.Trade().Coins = []order.CoinID{[]byte(takerAddr)}
  2288  		taker.Trade().Address = takerAddr
  2289  		maker.Address = makerAddr
  2290  		if makerSell {
  2291  			taker.Trade().Sell = false
  2292  			maker.Sell = true
  2293  		} else {
  2294  			taker.Trade().Sell = true
  2295  			maker.Sell = false
  2296  		}
  2297  		rig.swapper.Negotiate([]*order.MatchSet{matchSet})
  2298  	}
  2299  
  2300  	checkStats := func(addr string, expQty, expSwaps uint64, expRedeems int) {
  2301  		t.Helper()
  2302  		q, s, r := rig.swapper.AccountStats(addr, ACCTID)
  2303  		if q != expQty {
  2304  			t.Fatalf("wrong quantity. wanted %d, got %d", expQty, q)
  2305  		}
  2306  		if s != expSwaps {
  2307  			t.Fatalf("wrong swaps. wanted %d, got %d", expSwaps, s)
  2308  		}
  2309  		if r != expRedeems {
  2310  			t.Fatalf("wrong redeems. wanted %d, got %d", expRedeems, r)
  2311  		}
  2312  	}
  2313  
  2314  	addOrder(trackedSet.matchSet, true)
  2315  	checkStats(makerAddr, qty, 1, 0)
  2316  	checkStats(takerAddr, 0, 0, 1)
  2317  
  2318  	tracker := rig.getTracker()
  2319  	tracker.Status = order.MakerSwapCast
  2320  	checkStats(makerAddr, 0, 0, 0)
  2321  	checkStats(takerAddr, 0, 0, 1)
  2322  
  2323  	tracker.Status = order.MatchComplete
  2324  	checkStats(takerAddr, 0, 0, 0)
  2325  
  2326  	rig.swapper.matchMtx.Lock()
  2327  	rig.swapper.deleteMatch(tracker)
  2328  	rig.swapper.matchMtx.Unlock()
  2329  	if len(rig.swapper.acctMatches[ACCTID]) > 0 {
  2330  		t.Fatalf("account tracking not deleted for removed match")
  2331  	}
  2332  
  2333  	// Do 3 maker buys, 2 maker sells, and check the numbers.
  2334  	for i := 0; i < 5; i++ {
  2335  		set := tPerfectLimitLimit(qty, rate, i > 2)
  2336  		addOrder(set.matchSet, i > 2)
  2337  	}
  2338  
  2339  	checkStats(makerAddr, qty*2, 2, 3)
  2340  	checkStats(takerAddr, qty*3, 3, 2)
  2341  
  2342  	quoteAsset, baseAsset = baseAsset, quoteAsset
  2343  	set := tPerfectLimitLimit(qty, rate, false)
  2344  
  2345  	addOrder(set.matchSet, false)
  2346  	checkStats(makerAddr, qty*2+calc.BaseToQuote(rate, qty), 3, 3)
  2347  	checkStats(takerAddr, qty*3, 3, 3)
  2348  }
  2349  
  2350  // TODO: TestSwapper_restoreActiveSwaps? It would be almost entirely driven by
  2351  // stubbed out asset backend and storage.