decred.org/dcrdex@v1.0.5/client/core/account_test.go (about)

     1  //go:build !harness && !botlive
     2  
     3  package core
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/hex"
     8  	"errors"
     9  	"testing"
    10  
    11  	"decred.org/dcrdex/client/db"
    12  	"decred.org/dcrdex/dex/encode"
    13  	"decred.org/dcrdex/dex/msgjson"
    14  	"decred.org/dcrdex/dex/order"
    15  	"decred.org/dcrdex/server/account"
    16  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    17  )
    18  
    19  /* TODO: rework TestAccountExport
    20  func TestAccountExport(t *testing.T) {
    21  	rig := newTestRig()
    22  	tCore := rig.core
    23  	host := tCore.conns[tDexHost].acct.host
    24  	tCore.conns[tDexHost].acct.isPaid = true
    25  
    26  	setupRigAccountProof(host, rig)
    27  
    28  	accountResponse, _ , err := tCore.AccountExport(tPW, host)
    29  	if err != nil {
    30  		t.Fatalf("account keys error: %v", err)
    31  	}
    32  	if accountResponse == nil {
    33  		t.Fatalf("accountResponse is nil")
    34  	}
    35  	if host != accountResponse.Host {
    36  		t.Fatalf("host key not equal to account host")
    37  	}
    38  	if accountResponse.AccountID != rig.acct.id.String() {
    39  		t.Fatal("unexpected AccountID")
    40  	}
    41  	if accountResponse.DEXPubKey != hex.EncodeToString(rig.acct.dexPubKey.SerializeCompressed()) {
    42  		t.Fatal("unexpected DEXPubKey")
    43  	}
    44  	if accountResponse.PrivKey != hex.EncodeToString(rig.acct.privKey.Serialize()) {
    45  		t.Fatal("unexpected PrivKey")
    46  	}
    47  	if accountResponse.FeeCoin != hex.EncodeToString(rig.acct.feeCoin) {
    48  		t.Fatal("unexpected FeeCoin")
    49  	}
    50  	if accountResponse.Cert != hex.EncodeToString(rig.acct.cert) {
    51  		t.Fatal("unexpected Cert")
    52  	}
    53  	if accountResponse.FeeProofSig != hex.EncodeToString(rig.db.accountProof.Sig) {
    54  		t.Fatal("unexpected FeeProofSig")
    55  	}
    56  	if accountResponse.FeeProofStamp != rig.db.accountProof.Stamp {
    57  		t.Fatal("unexpected FeeProofStamp")
    58  	}
    59  }
    60  */
    61  
    62  func TestToggleAccountStatus(t *testing.T) {
    63  	activeTrades := map[order.OrderID]*trackedTrade{
    64  		{}: {metaData: &db.OrderMetaData{Status: order.OrderStatusBooked}},
    65  	}
    66  
    67  	tests := []struct {
    68  		name, host                                   string
    69  		recryptErr, acctErr, disableAcctErr          error
    70  		wantErr, wantErrCode, loseConns, wantDisable bool
    71  		activeTrades                                 map[order.OrderID]*trackedTrade
    72  		errCode                                      int
    73  	}{{
    74  		name:        "ok: disable account",
    75  		host:        tDexHost,
    76  		wantDisable: true,
    77  	}, {
    78  		name:        "ok: enable account",
    79  		host:        tDexHost,
    80  		wantDisable: false,
    81  	}, {
    82  		name:        "password error",
    83  		host:        tDexHost,
    84  		recryptErr:  tErr,
    85  		wantErr:     true,
    86  		errCode:     passwordErr,
    87  		wantDisable: true,
    88  	}, {
    89  		name:        "host error",
    90  		host:        ":bad:",
    91  		wantErr:     true,
    92  		wantErrCode: true,
    93  		errCode:     unknownDEXErr,
    94  		wantDisable: true,
    95  	}, {
    96  		name:        "dex not in conns",
    97  		host:        tDexHost,
    98  		loseConns:   true,
    99  		wantErr:     true,
   100  		wantErrCode: true,
   101  		errCode:     unknownDEXErr,
   102  		wantDisable: true,
   103  	}, {
   104  		name:         "has active orders",
   105  		host:         tDexHost,
   106  		activeTrades: activeTrades,
   107  		wantErr:      true,
   108  		wantDisable:  true,
   109  	}, {
   110  		name:           "disable account error",
   111  		host:           tDexHost,
   112  		disableAcctErr: errors.New(""),
   113  		wantErr:        true,
   114  		wantErrCode:    true,
   115  		errCode:        accountStatusUpdateErr,
   116  		wantDisable:    true,
   117  	}}
   118  
   119  	for _, test := range tests {
   120  		rig := newTestRig()
   121  		defer rig.shutdown()
   122  		tCore := rig.core
   123  		rig.crypter.(*tCrypter).recryptErr = test.recryptErr
   124  		rig.db.disabledHost = nil
   125  		rig.db.disableAccountErr = test.disableAcctErr
   126  		tCore.connMtx.Lock()
   127  		tCore.conns[tDexHost].trades = test.activeTrades
   128  
   129  		if test.loseConns {
   130  			// Lose the dexConnection
   131  			delete(tCore.conns, tDexHost)
   132  		}
   133  		tCore.connMtx.Unlock()
   134  
   135  		err := tCore.ToggleAccountStatus(tPW, test.host, test.wantDisable)
   136  		if test.wantErr {
   137  			if err == nil {
   138  				t.Fatalf("expected error for test %v", test.name)
   139  			}
   140  			if test.wantErrCode && !errorHasCode(err, test.errCode) {
   141  				t.Fatalf("wanted errCode %v but got %v for test %v", test.errCode, err, test.name)
   142  			}
   143  			continue
   144  		}
   145  		if err != nil {
   146  			t.Fatalf("unexpected error for test %v: %v", test.name, err)
   147  		}
   148  		if test.wantDisable {
   149  			if dc, found := tCore.conns[test.host]; found && !dc.acct.isDisabled() {
   150  				t.Fatal("expected disabled dex account")
   151  			}
   152  			if rig.db.disabledHost == nil {
   153  				t.Fatal("expected a disable dex server host")
   154  			}
   155  			if *rig.db.disabledHost != test.host {
   156  				t.Fatalf("expected db account to match test host, want: %v"+
   157  					" got: %v", test.host, *rig.db.disabledHost)
   158  			}
   159  		} else {
   160  			if dc, found := tCore.conns[test.host]; found && dc.acct.isDisabled() {
   161  				t.Fatal("expected enabled dex account")
   162  			}
   163  		}
   164  	}
   165  }
   166  
   167  func TestUpdateCert(t *testing.T) {
   168  	rig := newTestRig()
   169  	tCore := rig.core
   170  	rig.db.acct.LegacyFeePaid = true
   171  	rig.db.acct.LegacyFeeCoin = encode.RandomBytes(32)
   172  
   173  	tests := []struct {
   174  		name                 string
   175  		host                 string
   176  		acctErr              bool
   177  		updateAccountInfoErr bool
   178  		queueConfig          bool
   179  		expectError          bool
   180  	}{
   181  		{
   182  			name:        "ok",
   183  			host:        rig.db.acct.Host,
   184  			queueConfig: true,
   185  		},
   186  		{
   187  			name:        "connect error",
   188  			host:        rig.db.acct.Host,
   189  			queueConfig: false,
   190  			expectError: true,
   191  		},
   192  		{
   193  			name:        "db get account error",
   194  			host:        rig.db.acct.Host,
   195  			queueConfig: true,
   196  			acctErr:     true,
   197  			expectError: true,
   198  		},
   199  		{
   200  			name:                 "db update account err",
   201  			host:                 rig.db.acct.Host,
   202  			queueConfig:          true,
   203  			updateAccountInfoErr: true,
   204  			expectError:          true,
   205  		},
   206  	}
   207  
   208  	for _, test := range tests {
   209  		rig.db.verifyUpdateAccountInfo = false
   210  		if test.updateAccountInfoErr {
   211  			rig.db.updateAccountInfoErr = errors.New("")
   212  		} else {
   213  			rig.db.updateAccountInfoErr = nil
   214  		}
   215  		if test.acctErr {
   216  			rig.db.acctErr = errors.New("")
   217  		} else {
   218  			rig.db.acctErr = nil
   219  		}
   220  		randomCert := encode.RandomBytes(32)
   221  		if test.queueConfig {
   222  			rig.queueConfig()
   223  		}
   224  		err := tCore.UpdateCert(test.host, randomCert)
   225  		if test.expectError {
   226  			if err == nil {
   227  				t.Fatalf("%s: expected error but did not get", test.name)
   228  			}
   229  			continue
   230  		}
   231  		if err != nil {
   232  			t.Fatalf("%s: unexpected error: %v", test.name, err)
   233  		}
   234  		if !rig.db.verifyUpdateAccountInfo {
   235  			t.Fatalf("%s: expected update account to be called but it was not", test.name)
   236  		}
   237  		if !bytes.Equal(randomCert, rig.db.acct.Cert) {
   238  			t.Fatalf("%s: expected account to be updated with cert but it was not", test.name)
   239  		}
   240  	}
   241  }
   242  
   243  func TestUpdateDEXHost(t *testing.T) {
   244  	newPrivKey, _ := secp256k1.GeneratePrivateKey()
   245  	newPubKey := newPrivKey.PubKey()
   246  	newHost := "newhost.com:123"
   247  
   248  	tests := []struct {
   249  		name        string
   250  		oldHost     string
   251  		feePending  bool
   252  		expectError bool
   253  		newPubKey   *secp256k1.PublicKey
   254  	}{
   255  		{
   256  			name:      "ok",
   257  			oldHost:   tDexHost,
   258  			newPubKey: tDexKey,
   259  		},
   260  		{
   261  			name:        "new host has different pub key",
   262  			oldHost:     tDexHost,
   263  			newPubKey:   newPubKey,
   264  			expectError: true,
   265  		},
   266  		{
   267  			name:        "trying to update host that doesn't exist",
   268  			oldHost:     "hostdoesntexist.com:123",
   269  			newPubKey:   tDexKey,
   270  			expectError: true,
   271  		},
   272  	}
   273  
   274  	for _, test := range tests {
   275  		rig := newTestRig()
   276  		tCore := rig.core
   277  		rig.db.acct.LegacyFeePaid = true
   278  		rig.db.acct.LegacyFeeCoin = encode.RandomBytes(32)
   279  		rig.db.acct.Host = tDexHost
   280  
   281  		tCore.addDexConnection(rig.dc)
   282  
   283  		rig.queueConfig()
   284  		rig.queueConnect(nil, []*msgjson.Match{}, []*msgjson.OrderStatus{})
   285  		rig.dc.cfg.DEXPubKey = test.newPubKey.SerializeCompressed()
   286  
   287  		_, err := tCore.UpdateDEXHost(test.oldHost, newHost, tPW, []byte{11, 11})
   288  		if test.expectError {
   289  			if err == nil {
   290  				t.Fatalf("%s: expected error but did not get", test.name)
   291  			}
   292  			continue
   293  		}
   294  		if err != nil {
   295  			t.Fatalf("%s: unepected error: %v", test.name, err)
   296  		}
   297  
   298  		if len(tCore.conns) != 1 {
   299  			t.Fatalf("%s: expected conns map to have 1 entry but got %d", test.name, len(tCore.conns))
   300  		}
   301  
   302  		if _, ok := tCore.conns[newHost]; !ok {
   303  			t.Fatalf("%s: new host was not added to connections map", test.name)
   304  		}
   305  
   306  		if rig.db.disabledHost == nil {
   307  			t.Fatalf("%s: expected execution of db.DisableAccount", test.name)
   308  		}
   309  		if *rig.db.disabledHost != rig.acct.host {
   310  			t.Fatalf("%s: expected db disabled account to match test host, want: %v"+
   311  				" got: %v", test.name, rig.acct.host, rig.db.disabledHost)
   312  		}
   313  
   314  		if rig.db.acct.Host != newHost {
   315  			t.Fatalf("%s: expected newly create host %v to match test host %v",
   316  				test.name, rig.db.acct.Host, newHost)
   317  		}
   318  	}
   319  }
   320  
   321  func TestAccountExportPasswordError(t *testing.T) {
   322  	rig := newTestRig()
   323  	defer rig.shutdown()
   324  	tCore := rig.core
   325  	host := tCore.conns[tDexHost].acct.host
   326  	rig.crypter.(*tCrypter).recryptErr = tErr
   327  	_, _, err := tCore.AccountExport(tPW, host)
   328  	if !errorHasCode(err, passwordErr) {
   329  		t.Fatalf("expected password error, actual error: '%v'", err)
   330  	}
   331  }
   332  
   333  func TestAccountExportAddressError(t *testing.T) {
   334  	rig := newTestRig()
   335  	defer rig.shutdown()
   336  	tCore := rig.core
   337  	host := ":bad:"
   338  	_, _, err := tCore.AccountExport(tPW, host)
   339  	if !errorHasCode(err, addressParseErr) {
   340  		t.Fatalf("expected address parse error, actual error: '%v'", err)
   341  	}
   342  }
   343  
   344  func TestAccountExportUnknownDEX(t *testing.T) {
   345  	rig := newTestRig()
   346  	defer rig.shutdown()
   347  	rig.db.acct.Host = "different"
   348  	// Test the db Account look up failing.
   349  	rig.db.acctErr = errors.New("acct retrieve error")
   350  	defer func() { rig.db.acctErr = nil }()
   351  	tCore := rig.core
   352  	_, _, err := tCore.AccountExport(tPW, rig.db.acct.Host) // any valid host is fine
   353  	if !errorHasCode(err, unknownDEXErr) {
   354  		t.Fatalf("expected unknown DEX error, actual error: '%v'", err)
   355  	}
   356  }
   357  
   358  func TestAccountExportAccountKeyError(t *testing.T) {
   359  	rig := newTestRig()
   360  	defer rig.shutdown()
   361  	tCore := rig.core
   362  	host := tCore.conns[tDexHost].acct.host
   363  	rig.crypter.(*tCrypter).decryptErr = tErr
   364  	_, _, err := tCore.AccountExport(tPW, host)
   365  	if !errorHasCode(err, passwordErr) {
   366  		t.Fatalf("expected password error, actual error: '%v'", err)
   367  	}
   368  }
   369  
   370  func TestAccountExportAccountProofError(t *testing.T) {
   371  	rig := newTestRig()
   372  	defer rig.shutdown()
   373  	tCore := rig.core
   374  	host := tCore.conns[tDexHost].acct.host
   375  	rig.db.acct.LegacyFeePaid = true
   376  	rig.db.acctErr = tErr
   377  	_, _, err := tCore.AccountExport(tPW, host)
   378  	if !errorHasCode(err, unknownDEXErr) {
   379  		t.Fatalf("expected unknown dex error, actual error: '%v'", err)
   380  	}
   381  }
   382  
   383  func buildTestAccount(host string) *Account {
   384  	privKey, _ := secp256k1.GeneratePrivateKey()
   385  	return &Account{
   386  		Host:      host,
   387  		AccountID: account.NewID(privKey.PubKey().SerializeCompressed()).String(), // can be anything though
   388  		PrivKey:   hex.EncodeToString(privKey.Serialize()),
   389  		DEXPubKey: hex.EncodeToString(tDexKey.SerializeCompressed()),
   390  		Cert:      hex.EncodeToString([]byte{0x1}),
   391  	}
   392  }
   393  
   394  func TestAccountImportPasswordError(t *testing.T) {
   395  	rig := newTestRig()
   396  	rig.db.acctErr = db.ErrAcctNotFound
   397  	defer rig.shutdown()
   398  	tCore := rig.core
   399  	delete(tCore.conns, tDexHost)
   400  	account := buildTestAccount(tDexHost)
   401  	rig.queueConfig()
   402  	rig.crypter.(*tCrypter).recryptErr = tErr
   403  	err := tCore.AccountImport(tPW, account, nil)
   404  	if !errorHasCode(err, passwordErr) {
   405  		t.Fatalf("expected password error, actual error: '%v'", err)
   406  	}
   407  }
   408  
   409  func TestAccountImportAddressError(t *testing.T) {
   410  	rig := newTestRig()
   411  	rig.db.acctErr = db.ErrAcctNotFound
   412  	defer rig.shutdown()
   413  	tCore := rig.core
   414  	delete(tCore.conns, tDexHost)
   415  	account := buildTestAccount(":bad:")
   416  	rig.queueConfig()
   417  	err := tCore.AccountImport(tPW, account, nil)
   418  	if !errorHasCode(err, addressParseErr) {
   419  		t.Fatalf("expected address parse error, actual error: '%v'", err)
   420  	}
   421  }
   422  
   423  func TestAccountImportDecodePubKeyError(t *testing.T) {
   424  	rig := newTestRig()
   425  	rig.db.acctErr = db.ErrAcctNotFound
   426  	defer rig.shutdown()
   427  	tCore := rig.core
   428  	delete(tCore.conns, tDexHost)
   429  	account := buildTestAccount(tDexHost)
   430  	account.DEXPubKey = "bad"
   431  	rig.queueConfig()
   432  	err := tCore.AccountImport(tPW, account, nil)
   433  	if !errorHasCode(err, decodeErr) {
   434  		t.Fatalf("expected decode error, actual error: '%v'", err)
   435  	}
   436  }
   437  
   438  func TestAccountImportParsePubKeyError(t *testing.T) {
   439  	rig := newTestRig()
   440  	rig.db.acctErr = db.ErrAcctNotFound
   441  	defer rig.shutdown()
   442  	tCore := rig.core
   443  	delete(tCore.conns, tDexHost)
   444  	account := buildTestAccount(tDexHost)
   445  	account.DEXPubKey = hex.EncodeToString([]byte("bad"))
   446  	rig.queueConfig()
   447  	err := tCore.AccountImport(tPW, account, nil)
   448  	if !errorHasCode(err, parseKeyErr) {
   449  		t.Fatalf("expected parse key error, actual error: '%v'", err)
   450  	}
   451  }
   452  
   453  func TestAccountImportDecodeCertError(t *testing.T) {
   454  	rig := newTestRig()
   455  	rig.db.acctErr = db.ErrAcctNotFound
   456  	defer rig.shutdown()
   457  	tCore := rig.core
   458  	delete(tCore.conns, tDexHost)
   459  	account := buildTestAccount(tDexHost)
   460  	account.Cert = "bad"
   461  	rig.queueConfig()
   462  	err := tCore.AccountImport(tPW, account, nil)
   463  	if !errorHasCode(err, decodeErr) {
   464  		t.Fatalf("expected decode error, actual error: '%v'", err)
   465  	}
   466  }
   467  
   468  func TestAccountImportDecodePrivKeyError(t *testing.T) {
   469  	rig := newTestRig()
   470  	rig.db.acctErr = db.ErrAcctNotFound
   471  	defer rig.shutdown()
   472  	tCore := rig.core
   473  	delete(tCore.conns, tDexHost)
   474  	account := buildTestAccount(tDexHost)
   475  	account.PrivKey = "bad"
   476  	rig.queueConfig()
   477  	err := tCore.AccountImport(tPW, account, nil)
   478  	if !errorHasCode(err, decodeErr) {
   479  		t.Fatalf("expected decode error, actual error: '%v'", err)
   480  	}
   481  }
   482  
   483  func TestAccountImportEncryptPrivKeyError(t *testing.T) {
   484  	rig := newTestRig()
   485  	rig.db.acctErr = db.ErrAcctNotFound
   486  	defer rig.shutdown()
   487  	tCore := rig.core
   488  	delete(tCore.conns, tDexHost)
   489  	account := buildTestAccount(tDexHost)
   490  	rig.crypter.(*tCrypter).encryptErr = tErr
   491  	rig.queueConfig()
   492  	err := tCore.AccountImport(tPW, account, nil)
   493  	if !errorHasCode(err, encryptionErr) {
   494  		t.Fatalf("expected encryption error, actual error: '%v'", err)
   495  	}
   496  }
   497  
   498  func TestAccountImportAccountCreateAccountError(t *testing.T) {
   499  	rig := newTestRig()
   500  	rig.db.acctErr = db.ErrAcctNotFound
   501  	defer rig.shutdown()
   502  	tCore := rig.core
   503  	delete(tCore.conns, tDexHost)
   504  	account := buildTestAccount(tDexHost)
   505  	rig.queueConfig()
   506  	rig.db.createAccountErr = tErr
   507  	err := tCore.AccountImport(tPW, account, nil)
   508  	if !errorHasCode(err, dbErr) {
   509  		t.Fatalf("expected db error, actual error: '%v'", err)
   510  	}
   511  }