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 }