decred.org/dcrdex@v1.0.5/client/db/bolt/db_test.go (about) 1 package bolt 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "flag" 8 "fmt" 9 "math/rand" 10 "os" 11 "path/filepath" 12 "sync" 13 "testing" 14 "time" 15 16 "decred.org/dcrdex/client/db" 17 dbtest "decred.org/dcrdex/client/db/test" 18 "decred.org/dcrdex/dex" 19 "decred.org/dcrdex/dex/order" 20 ordertest "decred.org/dcrdex/dex/order/test" 21 "go.etcd.io/bbolt" 22 ) 23 24 var ( 25 tLogger = dex.StdOutLogger("db_TEST", dex.LevelTrace) 26 withLongTests bool 27 ) 28 29 func newTestDB(t *testing.T, opts ...Opts) (*BoltDB, func()) { 30 t.Helper() 31 dbPath := filepath.Join(t.TempDir(), "db.db") 32 dbi, err := NewDB(dbPath, tLogger, opts...) 33 if err != nil { 34 t.Fatalf("error creating dB: %v", err) 35 } 36 ctx, cancel := context.WithCancel(context.Background()) 37 var wg sync.WaitGroup 38 wg.Add(1) 39 go func() { 40 defer wg.Done() 41 dbi.Run(ctx) 42 }() 43 db, ok := dbi.(*BoltDB) 44 if !ok { 45 t.Fatalf("DB is not a *BoltDB") 46 } 47 shutdown := func() { 48 cancel() 49 wg.Wait() 50 } 51 return db, shutdown 52 } 53 54 func TestMain(m *testing.M) { 55 flag.BoolVar(&withLongTests, "withlongtests", false, "include tests that take a long time to run") 56 flag.Parse() 57 58 defer os.Stdout.Sync() 59 os.Exit(m.Run()) 60 } 61 62 func TestBackup(t *testing.T) { 63 db, shutdown := newTestDB(t) 64 defer shutdown() 65 66 // Backup the database. 67 err := db.Backup() 68 if err != nil { 69 t.Fatalf("unable to backup database: %v", err) 70 } 71 72 // Ensure the backup exists. 73 path := filepath.Join(filepath.Dir(db.Path()), backupDir, filepath.Base(db.Path())) 74 if _, err := os.Stat(path); os.IsNotExist(err) { 75 t.Fatalf("backup file does not exist: %v", err) 76 } 77 78 // Overwrite the backup. 79 err = db.Backup() 80 if err != nil { 81 t.Fatalf("unable to overwrite backup: %v", err) 82 } 83 } 84 85 func TestBackupTo(t *testing.T) { 86 db, shutdown := newTestDB(t) 87 defer shutdown() 88 89 // Backup the database. 90 testBackup := "asdf.db" 91 err := db.BackupTo(testBackup, false, false) 92 if err != nil { 93 t.Fatalf("unable to backup database: %v", err) 94 } 95 96 // Ensure the backup exists. 97 path := filepath.Join(filepath.Dir(db.Path()), testBackup) 98 if _, err := os.Stat(path); os.IsNotExist(err) { 99 t.Fatalf("backup file does not exist: %v", err) 100 } 101 102 // Don't overwrite existing. 103 same := db.Path() 104 err = db.BackupTo(same, false, false) 105 if err == nil { 106 t.Fatalf("overwrote file!") 107 } 108 err = db.BackupTo(testBackup, false, false) 109 if err == nil { 110 t.Fatalf("overwrote file!") 111 } 112 // Allow overwrite 113 err = db.BackupTo(testBackup, true, false) // no compact 114 if err != nil { 115 t.Fatalf("unable to backup database: %v", err) 116 } 117 err = db.BackupTo(testBackup, true, true) // compact 118 if err != nil { 119 t.Fatalf("unable to backup database: %v", err) 120 } 121 } 122 123 func TestStorePrimaryCredentials(t *testing.T) { 124 boltdb, shutdown := newTestDB(t) 125 defer shutdown() 126 127 // Trying to fetch credentials before storing should be an error. 128 _, err := boltdb.PrimaryCredentials() 129 if err == nil { 130 t.Fatalf("no error for missing credentials: %v", err) 131 } 132 133 newCreds := func(seed, inner, innerParams, outerParams bool) *db.PrimaryCredentials { 134 creds := &db.PrimaryCredentials{} 135 if seed { 136 creds.EncSeed = []byte("EncSeed") 137 } 138 if inner { 139 creds.EncInnerKey = []byte("EncInnerKey") 140 } 141 if innerParams { 142 creds.InnerKeyParams = []byte("InnerKeyParams") 143 } 144 if outerParams { 145 creds.OuterKeyParams = []byte("OuterKeyParams") 146 } 147 creds.Birthday = time.Now().Truncate(time.Second) 148 return creds 149 } 150 151 ensureErr := func(tag string, creds *db.PrimaryCredentials) { 152 if err := boltdb.SetPrimaryCredentials(creds); err == nil { 153 t.Fatalf("%s: no error", tag) 154 } 155 } 156 157 ensureErr("no EncSeed", newCreds(false, true, true, true)) 158 ensureErr("no EncInnerKey", newCreds(true, false, true, true)) 159 ensureErr("no InnerKeyParams", newCreds(true, true, false, true)) 160 ensureErr("no OuterKeyParams", newCreds(true, true, true, false)) 161 162 // Success 163 goodCreds := newCreds(true, true, true, true) 164 err = boltdb.SetPrimaryCredentials(goodCreds) 165 if err != nil { 166 t.Fatalf("SetPrimaryCredentials error: %v", err) 167 } 168 169 // Retrieve the credentials and check for consistency. 170 reCreds, err := boltdb.PrimaryCredentials() 171 if err != nil { 172 t.Fatalf("PrimaryCredentials error: %v", err) 173 } 174 175 if !bytes.Equal(reCreds.EncSeed, goodCreds.EncSeed) { 176 t.Fatalf("EncSeed wrong, wanted %x, got %x", goodCreds.EncSeed, reCreds.EncSeed) 177 } 178 if !bytes.Equal(reCreds.EncInnerKey, goodCreds.EncInnerKey) { 179 t.Fatalf("EncInnerKey wrong, wanted %x, got %x", goodCreds.EncInnerKey, reCreds.EncInnerKey) 180 } 181 if !bytes.Equal(reCreds.InnerKeyParams, goodCreds.InnerKeyParams) { 182 t.Fatalf("InnerKeyParams wrong, wanted %x, got %x", goodCreds.InnerKeyParams, reCreds.InnerKeyParams) 183 } 184 if !bytes.Equal(reCreds.OuterKeyParams, goodCreds.OuterKeyParams) { 185 t.Fatalf("OuterKeyParams wrong, wanted %x, got %x", goodCreds.OuterKeyParams, reCreds.OuterKeyParams) 186 } 187 if !reCreds.Birthday.Equal(goodCreds.Birthday) { 188 t.Fatalf("Birthday wrong. wanted %s, got %s", goodCreds.Birthday, reCreds.Birthday) 189 } 190 } 191 192 func TestAccounts(t *testing.T) { 193 boltdb, shutdown := newTestDB(t) 194 defer shutdown() 195 196 dexURLs, err := boltdb.ListAccounts() 197 if err != nil { 198 t.Fatalf("error listing accounts: %v", err) 199 } 200 if len(dexURLs) != 0 { 201 t.Fatalf("unexpected non-empty accounts in fresh DB") 202 } 203 // Create and insert 1,000 accounts. 204 numToDo := 1000 205 if testing.Short() { 206 numToDo = 50 207 } 208 accts := make([]*db.AccountInfo, 0, numToDo) 209 acctMap := make(map[string]*db.AccountInfo) 210 nTimes(numToDo, func(int) { 211 acct := dbtest.RandomAccountInfo() 212 accts = append(accts, acct) 213 acctMap[acct.Host] = acct 214 }) 215 tStart := time.Now() 216 nTimes(numToDo, func(i int) { 217 boltdb.CreateAccount(accts[i]) 218 }) 219 t.Logf("%d milliseconds to insert %d AccountInfo", time.Since(tStart)/time.Millisecond, numToDo) 220 221 tStart = time.Now() 222 nTimes(numToDo, func(i int) { 223 ai := accts[i] 224 reAI, err := boltdb.Account(ai.Host) 225 if err != nil { 226 t.Fatalf("error fetching AccountInfo: %v", err) 227 } 228 dbtest.MustCompareAccountInfo(t, ai, reAI) 229 }) 230 t.Logf("%d milliseconds to read and compare %d account names", time.Since(tStart)/time.Millisecond, numToDo) 231 232 tStart = time.Now() 233 readAccts, err := boltdb.Accounts() 234 if err != nil { 235 t.Fatalf("Accounts error: %v", err) 236 } 237 nTimes(numToDo, func(i int) { 238 reAI := readAccts[i] 239 ai, found := acctMap[reAI.Host] 240 if !found { 241 t.Fatalf("no account found in map for %s", reAI.Host) 242 } 243 dbtest.MustCompareAccountInfo(t, ai, reAI) 244 }) 245 t.Logf("%d milliseconds to batch read and compare %d AccountInfo", time.Since(tStart)/time.Millisecond, numToDo) 246 247 dexURLs, err = boltdb.ListAccounts() 248 if err != nil { 249 t.Fatalf("error listing accounts: %v", err) 250 } 251 if len(dexURLs) != numToDo { 252 t.Fatalf("expected %d accounts, found %d", numToDo, len(dexURLs)) 253 } 254 255 acct := dbtest.RandomAccountInfo() 256 ensureErr := func(tag string) { 257 err := boltdb.CreateAccount(acct) 258 if err == nil { 259 t.Fatalf("no error for %s", tag) 260 } 261 } 262 263 host := acct.Host 264 acct.Host = "" 265 ensureErr("Host") 266 acct.Host = host 267 268 dexKey := acct.DEXPubKey 269 acct.DEXPubKey = nil 270 ensureErr("DEX key") 271 acct.DEXPubKey = dexKey 272 } 273 274 func TestToggleAccountStatus(t *testing.T) { 275 boltdb, shutdown := newTestDB(t) 276 defer shutdown() 277 278 acct := dbtest.RandomAccountInfo() 279 host := acct.Host 280 err := boltdb.CreateAccount(acct) 281 if err != nil { 282 t.Fatalf("Unexpected CreateAccount error: %v", err) 283 } 284 285 accounts, err := boltdb.Accounts() 286 if err != nil { 287 t.Fatalf("Unexpected boltdb.Accounts error: %v", err) 288 } 289 if len(accounts) != 1 { 290 t.Fatalf("Expected 1 account but got %d", len(accounts)) 291 } 292 293 // Test disable account 294 err = boltdb.ToggleAccountStatus(host, true) 295 if err != nil { 296 t.Fatalf("Unexpected ToggleAccountStatus error: %v", err) 297 } 298 299 actualAcct, err := boltdb.Account(host) 300 if err != nil { 301 t.Fatalf("Unexpected boltdb.Account error: %v", err) 302 } 303 304 if !actualAcct.Disabled { 305 t.Fatalf("Expected a disabled account.") 306 } 307 308 // Test enable account 309 err = boltdb.ToggleAccountStatus(host, false) 310 if err != nil { 311 t.Fatalf("Unexpected ToggleAccountStatus error: %v", err) 312 } 313 314 actualAcct, err = boltdb.Account(host) 315 if err != nil { 316 t.Fatalf("Unexpected boltdb.Account error: %v", err) 317 } 318 319 if actualAcct.Disabled { 320 t.Fatalf("Expected an active account.") 321 } 322 } 323 324 func TestWallets(t *testing.T) { 325 boltdb, shutdown := newTestDB(t) 326 defer shutdown() 327 328 wallets, err := boltdb.Wallets() 329 if err != nil { 330 t.Fatalf("error listing wallets from empty DB: %v", err) 331 } 332 if len(wallets) != 0 { 333 t.Fatalf("unexpected non-empty wallets in fresh DB") 334 } 335 // Create and insert 1,000 wallets. 336 numToDo := 1000 337 if testing.Short() { 338 numToDo = 50 339 } 340 wallets = make([]*db.Wallet, 0, numToDo) 341 walletMap := make(map[string]*db.Wallet) 342 tStart := time.Now() 343 nTimes(numToDo, func(int) { 344 w := dbtest.RandomWallet() 345 wallets = append(wallets, w) 346 walletMap[w.SID()] = w 347 boltdb.UpdateWallet(w) 348 }) 349 t.Logf("%d milliseconds to insert %d Wallet", time.Since(tStart)/time.Millisecond, numToDo) 350 351 tStart = time.Now() 352 reWallets, err := boltdb.Wallets() 353 if err != nil { 354 t.Fatalf("wallets retrieval error: %v", err) 355 } 356 if len(reWallets) != numToDo { 357 t.Fatalf("expected %d wallets, got %d", numToDo, len(reWallets)) 358 } 359 for _, reW := range reWallets { 360 wid := reW.SID() 361 ogWallet, found := walletMap[wid] 362 if !found { 363 t.Fatalf("wallet %s not found after retrieval", wid) 364 } 365 dbtest.MustCompareWallets(t, reW, ogWallet) 366 } 367 // Test changing the balance 368 w := reWallets[0] 369 newBal := *w.Balance 370 newBal.Available += 1e8 371 newBal.Locked += 2e8 372 newBal.Immature += 3e8 373 newBal.Stamp = newBal.Stamp.Add(time.Second) 374 boltdb.UpdateBalance(w.ID(), &newBal) 375 reW, err := boltdb.Wallet(w.ID()) 376 if err != nil { 377 t.Fatalf("failed to retrieve wallet for balance check") 378 } 379 dbtest.MustCompareBalances(t, reW.Balance, &newBal) 380 if !reW.Balance.Stamp.After(w.Balance.Stamp) { 381 t.Fatalf("update time can't be right: %s > %s", reW.Balance.Stamp, w.Balance.Stamp) 382 } 383 384 // Test changing the password. 385 newPW := randBytes(32) 386 boltdb.SetWalletPassword(w.ID(), newPW) 387 reW, err = boltdb.Wallet(w.ID()) 388 if err != nil { 389 t.Fatalf("failed to retrieve wallet for new password check") 390 } 391 if !bytes.Equal(newPW, reW.EncryptedPW) { 392 t.Fatalf("failed to set password. wanted %x, got %x", newPW, reW.EncryptedPW) 393 } 394 t.Logf("%d milliseconds to read and compare %d Wallet", time.Since(tStart)/time.Millisecond, numToDo) 395 396 } 397 398 func randOrderForMarket(base, quote uint32) order.Order { 399 switch rand.Intn(3) { 400 case 0: 401 o, _ := ordertest.RandomCancelOrder() 402 o.BaseAsset = base 403 o.QuoteAsset = quote 404 return o 405 case 1: 406 o, _ := ordertest.RandomMarketOrder() 407 o.BaseAsset = base 408 o.QuoteAsset = quote 409 return o 410 default: 411 o, _ := ordertest.RandomLimitOrder() 412 o.BaseAsset = base 413 o.QuoteAsset = quote 414 return o 415 } 416 } 417 418 func mustContainOrder(t *testing.T, os []*db.MetaOrder, o *db.MetaOrder) { 419 t.Helper() 420 oid := o.Order.ID() 421 for _, mord := range os { 422 if mord.Order.ID() == oid { 423 ordertest.MustCompareOrders(t, mord.Order, o.Order) 424 return 425 } 426 } 427 t.Fatalf("order %x not contained in list", oid[:]) 428 } 429 430 func TestOrders(t *testing.T) { 431 boltdb, shutdown := newTestDB(t) 432 defer shutdown() 433 434 // Create an account to use. 435 acct1 := dbtest.RandomAccountInfo() 436 acct2 := dbtest.RandomAccountInfo() 437 err := boltdb.CreateAccount(acct1) 438 err1 := boltdb.CreateAccount(acct2) 439 if err != nil || err1 != nil { 440 t.Fatalf("CreateAccount error: %v : %v", err, err1) 441 } 442 base1, quote1 := randU32(), randU32() 443 base2, quote2 := randU32(), randU32() 444 445 numToDo := 1008 // must be a multiple of 16 446 numActive := 100 447 if testing.Short() { 448 numToDo = 48 449 numActive = 10 450 } 451 orders := make(map[int]*db.MetaOrder, numToDo) 452 orderIndex := make(map[order.OrderID]order.Order) 453 nTimes(numToDo, func(i int) { 454 // statuses 3, 4, and 5 considered inactive orders 455 status := order.OrderStatus(rand.Intn(3) + 3) // inactive 456 if i < numActive { 457 // Technically, this is putting even cancel and market orders in the 458 // booked state half the time, which should be impossible. The DB does not 459 // check for this, and will recognize the order as active. 460 // statuses 1 and 2 considered inactive orders. 461 status = order.OrderStatus(rand.Intn(2) + 1) 462 } 463 acct := acct1 464 base, quote := base1, quote1 465 if i%2 == 1 { 466 acct = acct2 467 base, quote = base2, quote2 468 } 469 ord := randOrderForMarket(base, quote) 470 471 orders[i] = &db.MetaOrder{ 472 MetaData: &db.OrderMetaData{ 473 Status: status, 474 Host: acct.Host, 475 Proof: db.OrderProof{DEXSig: randBytes(73)}, 476 SwapFeesPaid: rand.Uint64(), 477 RedemptionFeesPaid: rand.Uint64(), 478 MaxFeeRate: rand.Uint64(), 479 }, 480 Order: ord, 481 } 482 orderIndex[ord.ID()] = ord 483 }) 484 485 tStart := time.Now() 486 // Grab a timestamp halfway through. 487 var tMid uint64 488 iMid := numToDo / 2 489 nTimes(numToDo, func(i int) { 490 time.Sleep(time.Millisecond) 491 if i == iMid { 492 tMid = timeNow() 493 } 494 err := boltdb.UpdateOrder(orders[i]) 495 if err != nil { 496 t.Fatalf("error inserting order: %v", err) 497 } 498 }) 499 t.Logf("~ %d milliseconds to insert %d MetaOrder", int(time.Since(tStart)/time.Millisecond)-numToDo, numToDo) 500 tStart = time.Now() 501 502 // Grab an order by ID. 503 504 firstOrd := orders[0] 505 mord, err := boltdb.Order(firstOrd.Order.ID()) 506 if err != nil { 507 t.Fatalf("unable to retrieve order by id") 508 } 509 ordertest.MustCompareOrders(t, firstOrd.Order, mord.Order) 510 if firstOrd.MetaData.SwapFeesPaid != mord.MetaData.SwapFeesPaid { 511 t.Fatalf("wrong SwapFeesPaid. wanted %d, got %d", firstOrd.MetaData.SwapFeesPaid, mord.MetaData.SwapFeesPaid) 512 } 513 if firstOrd.MetaData.RedemptionFeesPaid != mord.MetaData.RedemptionFeesPaid { 514 t.Fatalf("wrong RedemptionFeesPaid. wanted %d, got %d", firstOrd.MetaData.RedemptionFeesPaid, mord.MetaData.RedemptionFeesPaid) 515 } 516 if firstOrd.MetaData.MaxFeeRate != mord.MetaData.MaxFeeRate { 517 t.Fatalf("wrong MaxFeeRate. wanted %d, got %d", firstOrd.MetaData.MaxFeeRate, mord.MetaData.MaxFeeRate) 518 } 519 520 // Check the active orders. 521 activeOrders, err := boltdb.ActiveOrders() 522 if err != nil { 523 t.Fatalf("error retrieving active orders: %v", err) 524 } 525 if len(activeOrders) != numActive { 526 t.Fatalf("expected %d active orders, got %d", numActive, len(activeOrders)) 527 } 528 for _, m := range activeOrders { 529 ord := orderIndex[m.Order.ID()] 530 ordertest.MustCompareOrders(t, m.Order, ord) 531 } 532 t.Logf("%d milliseconds to read and compare %d active MetaOrder", time.Since(tStart)/time.Millisecond, numActive) 533 534 // Get the orders for account 1. 535 tStart = time.Now() 536 acctOrders, err := boltdb.AccountOrders(acct1.Host, 0, 0) 537 if err != nil { 538 t.Fatalf("error fetching account orders: %v", err) 539 } 540 if len(acctOrders) != numToDo/2 { 541 t.Fatalf("expected %d account orders, got %d", numToDo/2, len(acctOrders)) 542 } 543 for _, m := range acctOrders { 544 ord := orderIndex[m.Order.ID()] 545 ordertest.MustCompareOrders(t, m.Order, ord) 546 } 547 t.Logf("%d milliseconds to read and compare %d account MetaOrder", time.Since(tStart)/time.Millisecond, numToDo/2) 548 549 // Filter the account's first half of orders by timestamp. 550 tStart = time.Now() 551 sinceOrders, err := boltdb.AccountOrders(acct1.Host, 0, tMid) 552 if err != nil { 553 t.Fatalf("error retrieve account's since orders: %v", err) 554 } 555 if len(sinceOrders) != numToDo/4 { 556 t.Fatalf("expected %d orders for account with since time, got %d", numToDo/4, len(sinceOrders)) 557 } 558 for _, mord := range sinceOrders { 559 mustContainOrder(t, acctOrders, mord) 560 } 561 t.Logf("%d milliseconds to read %d time-filtered MetaOrders for account", time.Since(tStart)/time.Millisecond, numToDo/4) 562 563 // Get the orders for the specified market. 564 tStart = time.Now() 565 mktOrders, err := boltdb.MarketOrders(acct1.Host, base1, quote1, 0, 0) 566 if err != nil { 567 t.Fatalf("error retrieving orders for market: %v", err) 568 } 569 if len(mktOrders) != numToDo/2 { 570 t.Fatalf("expected %d orders for market, got %d", numToDo/2, len(mktOrders)) 571 } 572 t.Logf("%d milliseconds to read and compare %d MetaOrder for market", time.Since(tStart)/time.Millisecond, numToDo/2) 573 574 // Filter the market's first half out by timestamp. 575 tStart = time.Now() 576 sinceOrders, err = boltdb.MarketOrders(acct1.Host, base1, quote1, 0, tMid) 577 if err != nil { 578 t.Fatalf("error retrieving market's since orders: %v", err) 579 } 580 if len(sinceOrders) != numToDo/4 { 581 t.Fatalf("expected %d orders for market with since time, got %d", numToDo/4, len(sinceOrders)) 582 } 583 for _, mord := range sinceOrders { 584 mustContainOrder(t, acctOrders, mord) 585 } 586 t.Logf("%d milliseconds to read %d time-filtered MetaOrders for market", time.Since(tStart)/time.Millisecond, numToDo/4) 587 588 // Same thing, but only last half 589 halfSince := len(sinceOrders) / 2 590 nOrders, err := boltdb.MarketOrders(acct1.Host, base1, quote1, halfSince, tMid) 591 if err != nil { 592 t.Fatalf("error returning n orders: %v", err) 593 } 594 if len(nOrders) != halfSince { 595 t.Fatalf("requested %d orders, got %d", halfSince, len(nOrders)) 596 } 597 // Should match exactly with first half of sinceOrders. 598 for i := 0; i < halfSince; i++ { 599 ordertest.MustCompareOrders(t, nOrders[i].Order, sinceOrders[i].Order) 600 } 601 602 // Make a MetaOrder and check insertion errors. 603 m := &db.MetaOrder{ 604 MetaData: &db.OrderMetaData{ 605 Status: order.OrderStatusExecuted, 606 Host: acct1.Host, 607 Proof: db.OrderProof{DEXSig: randBytes(73)}, 608 }, 609 Order: randOrderForMarket(base1, quote1), 610 } 611 612 host := m.MetaData.Host 613 m.MetaData.Host = "" 614 err = boltdb.UpdateOrder(m) 615 if err == nil { 616 t.Fatalf("no error for empty DEX") 617 } 618 m.MetaData.Host = host 619 620 sig := m.MetaData.Proof.DEXSig 621 m.MetaData.Proof.DEXSig = nil 622 err = boltdb.UpdateOrder(m) 623 if err == nil { 624 t.Fatalf("no error for empty DEX signature") 625 } 626 m.MetaData.Proof.DEXSig = sig 627 628 err = boltdb.UpdateOrder(m) 629 if err != nil { 630 t.Fatalf("error after order fixed: %v", err) 631 } 632 633 // Set the change coin for an order. 634 activeOrder := activeOrders[0].Order 635 err = boltdb.UpdateOrderStatus(activeOrder.ID(), order.OrderStatusExecuted) 636 if err != nil { 637 t.Fatalf("error setting order status: %v", err) 638 } 639 mord, err = boltdb.Order(activeOrder.ID()) 640 if mord.MetaData.Status != order.OrderStatusExecuted { 641 t.Fatalf("failed to update order status") 642 } 643 if err != nil { 644 t.Fatalf("failed to load order: %v", err) 645 } 646 // random id should be an error 647 err = boltdb.UpdateOrderStatus(ordertest.RandomOrderID(), order.OrderStatusExecuted) 648 if err == nil { 649 t.Fatalf("no error encountered for updating unknown order's status") 650 } 651 } 652 653 func TestOrderFilters(t *testing.T) { 654 boltdb, shutdown := newTestDB(t) 655 defer shutdown() 656 657 makeOrder := func(host string, base, quote uint32, stamp int64, status order.OrderStatus) *db.MetaOrder { 658 mord := &db.MetaOrder{ 659 MetaData: &db.OrderMetaData{ 660 Status: status, 661 Host: host, 662 Proof: db.OrderProof{DEXSig: randBytes(73)}, 663 }, 664 Order: &order.LimitOrder{ 665 P: order.Prefix{ 666 BaseAsset: base, 667 QuoteAsset: quote, 668 ServerTime: time.UnixMilli(stamp), 669 }, 670 }, 671 } 672 oid := mord.Order.ID() 673 674 err := boltdb.UpdateOrder(mord) 675 if err != nil { 676 t.Fatalf("error inserting order: %v", err) 677 } 678 679 // Set the update time. 680 boltdb.ordersUpdate(func(aob, eob *bbolt.Bucket) error { 681 oBkt := aob.Bucket(oid[:]) 682 if oBkt == nil { 683 oBkt = eob.Bucket(oid[:]) 684 } 685 if oBkt == nil { 686 t.Fatalf("order %s not found", oid) 687 } 688 oBkt.Put(updateTimeKey, uint64Bytes(uint64(stamp))) 689 return nil 690 }) 691 692 return mord 693 } 694 695 var start int64 696 host1 := "somehost.co" 697 host2 := "anotherhost.org" 698 var asset1 uint32 = 1 699 var asset2 uint32 = 2 700 var asset3 uint32 = 3 701 orders := []*db.MetaOrder{ 702 makeOrder(host1, asset1, asset2, start, order.OrderStatusExecuted), // 0, oid: 95318d1d4d1d19348d96f260e6d54eca942ce9bf0760f43edc7afa2c0173a401 703 makeOrder(host2, asset2, asset3, start+1, order.OrderStatusRevoked), // 1, oid: 58148721dd3109647fd912fb1b3e29be7c72e72cf589dcbe5d0697735e1df8bb 704 makeOrder(host1, asset3, asset1, start+2, order.OrderStatusCanceled), // 2, oid: feea1852996c042174a40a88e74175f95009ce72b31d51402f452b28f2618a13 705 makeOrder(host2, asset1, asset3, start+3, order.OrderStatusEpoch), // 3, oid: 8707caf2e70bc615845673cf30e44c67dec972064ab137a321d9ee98e8c96fe3 706 makeOrder(host1, asset2, asset3, start+4, order.OrderStatusBooked), // 4, oid: e2fe7b28eae9a4511013ecb35be58e8031e5f7ec9f3a0e2f6411ec58efd4464a 707 makeOrder(host2, asset3, asset1, start+4, order.OrderStatusExecuted), // 5, oid: c76d4dfbc4ea8e0e8065e809f9c3ebfca98a1053a42f464e7632f79126f752d0 708 } 709 orderCount := len(orders) 710 711 for i, ord := range orders { 712 fmt.Println(i, ord.Order.ID().String()) 713 } 714 715 tests := []struct { 716 name string 717 filter *db.OrderFilter 718 expected []int 719 }{ 720 { 721 name: "zero-filter", 722 filter: &db.OrderFilter{ 723 N: orderCount, 724 }, 725 expected: []int{4, 5, 3, 2, 1, 0}, 726 }, 727 { 728 name: "all-hosts", 729 filter: &db.OrderFilter{ 730 N: orderCount, 731 Hosts: []string{host1, host2}, 732 }, 733 expected: []int{4, 5, 3, 2, 1, 0}, 734 }, 735 { 736 name: "host1", 737 filter: &db.OrderFilter{ 738 N: orderCount, 739 Hosts: []string{host1}, 740 }, 741 expected: []int{4, 2, 0}, 742 }, 743 { 744 name: "host1 + asset1", 745 filter: &db.OrderFilter{ 746 N: orderCount, 747 Hosts: []string{host1}, 748 Assets: []uint32{asset1}, 749 }, 750 expected: []int{2, 0}, 751 }, 752 { 753 name: "asset1", 754 filter: &db.OrderFilter{ 755 N: orderCount, 756 Assets: []uint32{asset1}, 757 }, 758 expected: []int{5, 3, 2, 0}, 759 }, 760 // Open filter with last order as Offset should return all but that 761 // order, since order 5 is lexicographically after order 4. 762 { 763 name: "offset", 764 filter: &db.OrderFilter{ 765 N: orderCount, 766 Offset: orders[5].Order.ID(), 767 }, 768 expected: []int{4, 3, 2, 1, 0}, 769 }, 770 { 771 name: "epoch & booked", 772 filter: &db.OrderFilter{ 773 N: orderCount, 774 Statuses: []order.OrderStatus{order.OrderStatusEpoch, order.OrderStatusBooked}, 775 }, 776 expected: []int{4, 3}, 777 }, 778 } 779 780 for _, test := range tests { 781 ords, err := boltdb.Orders(test.filter) 782 if err != nil { 783 t.Fatalf("%s: Orders error: %v", test.name, err) 784 } 785 if len(ords) != len(test.expected) { 786 t.Fatalf("%s: wrong number of orders. wanted %d, got %d", test.name, len(test.expected), len(ords)) 787 } 788 for i, j := range test.expected { 789 if ords[i].Order.ID() != orders[j].Order.ID() { 790 t.Fatalf("%s: index %d wrong ID. wanted %s, got %s", test.name, i, orders[j].Order.ID(), ords[i].Order.ID()) 791 } 792 } 793 } 794 } 795 796 func TestOrderChange(t *testing.T) { 797 boltdb, shutdown := newTestDB(t) 798 defer shutdown() 799 800 // Create an account to use. 801 acct := dbtest.RandomAccountInfo() 802 err := boltdb.CreateAccount(acct) 803 if err != nil { 804 t.Fatalf("CreateAccount error: %v", err) 805 } 806 807 ord := randOrderForMarket(randU32(), randU32()) 808 mordIn := &db.MetaOrder{ 809 MetaData: &db.OrderMetaData{ 810 Status: 3, 811 Host: acct.Host, 812 Proof: db.OrderProof{DEXSig: randBytes(73)}, 813 }, 814 Order: ord, 815 } 816 // fmt.Printf("%#v\n", mordIn.MetaData.ChangeCoin) // order.CoinID(nil) 817 818 err = boltdb.UpdateOrder(mordIn) 819 if err != nil { 820 t.Fatalf("error inserting order: %v", err) 821 } 822 823 mord, err := boltdb.Order(ord.ID()) 824 if err != nil { 825 t.Fatalf("unable to retrieve order by id") 826 } 827 if mord.MetaData.ChangeCoin != nil { 828 t.Errorf("ChangeCoin was not nil: %#v", mord.MetaData.ChangeCoin) 829 } 830 831 // non-nil empty loads as nil too 832 mord.MetaData.ChangeCoin = []byte{} 833 err = boltdb.UpdateOrderMetaData(ord.ID(), mord.MetaData) 834 if err != nil { 835 t.Fatalf("error setting change coin: %v", err) 836 } 837 mord, err = boltdb.Order(ord.ID()) 838 if err != nil { 839 t.Fatalf("failed to load order: %v", err) 840 } 841 if mord.MetaData.ChangeCoin != nil { 842 t.Fatalf("failed to set change coin, got %#v, want <nil>", mord.MetaData.ChangeCoin) 843 } 844 845 // now some data 846 someChange := []byte{1, 2, 3} 847 mord.MetaData.ChangeCoin = someChange 848 err = boltdb.UpdateOrderMetaData(ord.ID(), mord.MetaData) 849 if err != nil { 850 t.Fatalf("error setting change coin: %v", err) 851 } 852 // Add a linked order ID. 853 linkedID := ordertest.RandomOrderID() 854 err = boltdb.LinkOrder(ord.ID(), linkedID) 855 if err != nil { 856 t.Fatalf("error setting linked order: %v", err) 857 } 858 mord, err = boltdb.Order(ord.ID()) 859 if err != nil { 860 t.Fatalf("failed to load order: %v", err) 861 } 862 if !bytes.Equal(someChange, mord.MetaData.ChangeCoin) { 863 t.Fatalf("failed to set change coin, got %#v, want %#v", mord.MetaData.ChangeCoin, someChange) 864 } 865 if mord.MetaData.LinkedOrder != linkedID { 866 t.Fatalf("wrong linked ID. expected %s, got %s", linkedID, mord.MetaData.LinkedOrder) 867 } 868 869 // random id should be an error 870 err = boltdb.UpdateOrderMetaData(ordertest.RandomOrderID(), mord.MetaData) 871 if err == nil { 872 t.Fatalf("no error encountered for updating unknown order change coin") 873 } 874 } 875 876 func TestMatches(t *testing.T) { 877 boltdb, shutdown := newTestDB(t) 878 defer shutdown() 879 880 base, quote := randU32(), randU32() 881 acct := dbtest.RandomAccountInfo() 882 883 numToDo := 1000 // must be quadruply a multiple of 8. 884 numActive := 100 885 if testing.Short() { 886 numToDo = 24 887 numActive = 8 888 } 889 metaMatches := make([]*db.MetaMatch, 0, numToDo) 890 matchIndex := make(map[order.MatchID]*db.MetaMatch, numToDo) 891 nTimes(numToDo, func(i int) { 892 m := &db.MetaMatch{ 893 MetaData: &db.MatchMetaData{ 894 Proof: *dbtest.RandomMatchProof(0.5), 895 DEX: acct.Host, 896 Base: base, 897 Quote: quote, 898 Stamp: rand.Uint64(), 899 }, 900 UserMatch: ordertest.RandomUserMatch(), 901 } 902 if i < numActive { 903 m.Status = order.MatchStatus(rand.Intn(4)) 904 } else { 905 m.Status = order.MatchConfirmed // inactive 906 m.MetaData.Proof.Auth.RedeemSig = []byte{0} // redeemSig required for MatchComplete to be considered inactive 907 } 908 matchIndex[m.MatchID] = m 909 metaMatches = append(metaMatches, m) 910 }) 911 tStart := time.Now() 912 nTimes(numToDo, func(i int) { 913 err := boltdb.UpdateMatch(metaMatches[i]) 914 if err != nil { 915 t.Fatalf("update error: %v", err) 916 } 917 }) 918 t.Logf("%d milliseconds to insert %d account MetaMatch", time.Since(tStart)/time.Millisecond, numToDo) 919 920 tStart = time.Now() 921 activeMatches, err := boltdb.ActiveMatches() 922 if err != nil { 923 t.Fatalf("error getting active matches: %v", err) 924 } 925 if len(activeMatches) != numActive { 926 t.Fatalf("expected %d active matches, got %d", numActive, len(activeMatches)) 927 } 928 activeOrders := make(map[order.OrderID]bool) 929 for _, m1 := range activeMatches { 930 activeOrders[m1.OrderID] = true 931 m2 := matchIndex[m1.MatchID] 932 ordertest.MustCompareUserMatch(t, m1.UserMatch, m2.UserMatch) 933 dbtest.MustCompareMatchMetaData(t, m1.MetaData, m2.MetaData) 934 } 935 t.Logf("%d milliseconds to retrieve and compare %d active MetaMatch", time.Since(tStart)/time.Millisecond, numActive) 936 937 activeMatchOrders, err := boltdb.DEXOrdersWithActiveMatches(acct.Host) 938 if err != nil { 939 t.Fatalf("error retrieving active match orders: %v", err) 940 } 941 if len(activeMatchOrders) != len(activeOrders) { 942 t.Fatalf("wrong number of DEXOrdersWithActiveMatches returned. expected %d, got %d", len(activeOrders), len(activeMatchOrders)) 943 } 944 for _, oid := range activeMatchOrders { 945 if !activeOrders[oid] { 946 t.Fatalf("active match order ID mismatch") 947 } 948 } 949 950 m := &db.MetaMatch{ 951 MetaData: &db.MatchMetaData{ 952 Proof: *dbtest.RandomMatchProof(0.5), 953 DEX: acct.Host, 954 Base: base, 955 Quote: quote, 956 Stamp: rand.Uint64(), 957 }, 958 UserMatch: ordertest.RandomUserMatch(), 959 } 960 m.Status = order.NewlyMatched 961 962 m.MetaData.DEX = "" 963 err = boltdb.UpdateMatch(m) 964 if err == nil { 965 t.Fatalf("no error on empty DEX") 966 } 967 m.MetaData.DEX = acct.Host 968 969 m.MetaData.Base, m.MetaData.Quote = 0, 0 970 err = boltdb.UpdateMatch(m) 971 if err == nil { 972 t.Fatalf("no error on same base and quote") 973 } 974 m.MetaData.Base, m.MetaData.Quote = base, quote 975 976 err = boltdb.UpdateMatch(m) 977 if err != nil { 978 t.Fatalf("error after fixing match: %v", err) 979 } 980 } 981 982 var randU32 = func() uint32 { return uint32(rand.Int31()) } 983 984 func nTimes(n int, f func(int)) { 985 for i := 0; i < n; i++ { 986 f(i) 987 } 988 } 989 990 func randBytes(l int) []byte { 991 b := make([]byte, l) 992 rand.Read(b) 993 return b 994 } 995 996 func TestNotifications(t *testing.T) { 997 boltdb, shutdown := newTestDB(t) 998 defer shutdown() 999 1000 numToDo := 1000 1001 numToFetch := 200 1002 if testing.Short() { 1003 numToDo = 10 1004 numToFetch = 2 1005 } 1006 newest := uint64(rand.Int63()) - uint64(numToFetch) 1007 1008 notes := make([]*db.Notification, 0, numToDo) 1009 nTimes(numToDo, func(int) { 1010 notes = append(notes, dbtest.RandomNotification(newest)) 1011 }) 1012 1013 fetches := make([]*db.Notification, numToFetch) 1014 for i := numToFetch - 1; i >= 0; i-- { 1015 newest++ 1016 notes[i].TimeStamp = newest 1017 fetches[i] = notes[i] 1018 } 1019 1020 tStart := time.Now() 1021 nTimes(numToDo, func(i int) { 1022 err := boltdb.SaveNotification(notes[i]) 1023 if err != nil { 1024 t.Fatalf("SaveNotification error: %v", err) 1025 } 1026 }) 1027 t.Logf("%d milliseconds to insert %d active Notification", time.Since(tStart)/time.Millisecond, numToDo) 1028 1029 tStart = time.Now() 1030 fetched, err := boltdb.NotificationsN(numToFetch) 1031 if err != nil { 1032 t.Fatalf("fetch error: %v", err) 1033 } 1034 t.Logf("%d milliseconds to fetch %d sorted Notification", time.Since(tStart)/time.Millisecond, numToFetch) 1035 if len(fetched) != numToFetch { 1036 t.Fatalf("fetched wrong number of notifications. %d != %d", len(fetched), numToFetch) 1037 } 1038 1039 for i, note := range fetched { 1040 dbtest.MustCompareNotifications(t, note, fetches[i]) 1041 boltdb.AckNotification(note.ID()) 1042 } 1043 1044 fetched, err = boltdb.NotificationsN(numToFetch) 1045 if err != nil { 1046 t.Fatalf("fetch error after acks: %v", err) 1047 } 1048 1049 for _, note := range fetched { 1050 if !note.Ack { 1051 t.Fatalf("order acknowledgement not recorded") 1052 } 1053 } 1054 } 1055 1056 type tCrypter struct { 1057 enc []byte 1058 } 1059 1060 func (c *tCrypter) Encrypt(b []byte) ([]byte, error) { 1061 return c.enc, nil 1062 } 1063 1064 func (c *tCrypter) Decrypt(b []byte) ([]byte, error) { 1065 return b, nil 1066 } 1067 1068 func (c *tCrypter) Serialize() []byte { 1069 return nil 1070 } 1071 1072 func (c *tCrypter) Close() {} 1073 1074 func TestRecrypt(t *testing.T) { 1075 boltdb, shutdown := newTestDB(t) 1076 defer shutdown() 1077 1078 tester := func(walletID []byte, host string, creds *db.PrimaryCredentials) error { 1079 encPW := randBytes(5) 1080 oldCrypter, newCrypter := &tCrypter{}, &tCrypter{encPW} 1081 walletUpdates, acctUpdates, err := boltdb.Recrypt(creds, oldCrypter, newCrypter) 1082 if err != nil { 1083 return err 1084 } 1085 1086 if len(walletUpdates) != 1 { 1087 return fmt.Errorf("expected 1 wallet update, got %d", len(walletUpdates)) 1088 } 1089 for _, newEncPW := range walletUpdates { 1090 if len(newEncPW) == 0 { 1091 return errors.New("no updated key") 1092 } 1093 if !bytes.Equal(newEncPW, encPW) { 1094 return fmt.Errorf("wrong encrypted password. wanted %x, got %x", encPW, newEncPW) 1095 } 1096 } 1097 1098 w, err := boltdb.Wallet(walletID) 1099 if err != nil { 1100 return fmt.Errorf("error retrieving wallet: %v", err) 1101 } 1102 if !bytes.Equal(w.EncryptedPW, encPW) { 1103 return fmt.Errorf("wallet not wallet updated") 1104 } 1105 1106 if len(acctUpdates) != 1 { 1107 return fmt.Errorf("expected 1 account update, got %d", len(acctUpdates)) 1108 } 1109 newEncPW := acctUpdates[host] 1110 if len(newEncPW) == 0 { 1111 return fmt.Errorf("no account update") 1112 } 1113 if !bytes.Equal(newEncPW, encPW) { 1114 return fmt.Errorf("wrong encrypted account password") 1115 } 1116 1117 acct, err := boltdb.Account(host) 1118 if err != nil { 1119 return fmt.Errorf("error retrieving account: %v", err) 1120 } 1121 if !bytes.Equal(acct.LegacyEncKey, encPW) { 1122 return fmt.Errorf("account not updated") 1123 } 1124 1125 return nil 1126 } 1127 testCredentialsUpdate(t, boltdb, tester) 1128 } 1129 1130 func testCredentialsUpdate(t *testing.T, boltdb *BoltDB, tester func([]byte, string, *db.PrimaryCredentials) error) { 1131 t.Helper() 1132 w := dbtest.RandomWallet() 1133 w.EncryptedPW = randBytes(6) 1134 walletID := w.ID() 1135 boltdb.UpdateWallet(w) 1136 1137 acct := dbtest.RandomAccountInfo() 1138 acct.LegacyEncKey = acct.EncKeyV2 1139 acct.EncKeyV2 = nil 1140 boltdb.CreateAccount(acct) 1141 host := acct.Host 1142 1143 clearCreds := func() { 1144 boltdb.Update(func(tx *bbolt.Tx) error { 1145 credsBkt := tx.Bucket(credentialsBucket) 1146 credsBkt.Delete(encSeedKey) 1147 credsBkt.Delete(encInnerKeyKey) 1148 credsBkt.Delete(innerKeyParamsKey) 1149 credsBkt.Delete(outerKeyParamsKey) 1150 return nil 1151 }) 1152 } 1153 1154 numToDo := 100 1155 if testing.Short() { 1156 numToDo = 5 1157 } 1158 1159 nTimes(numToDo, func(int) { 1160 clearCreds() 1161 creds := dbtest.RandomPrimaryCredentials() 1162 err := tester(walletID, host, creds) 1163 if err != nil { 1164 t.Fatalf("%T error: %v", tester, err) 1165 } 1166 }) 1167 } 1168 1169 func TestDeleteInactiveMatches(t *testing.T) { 1170 // TODO: This test takes way too long to run. Why? 1171 if !withLongTests { 1172 return 1173 } 1174 boltdb, shutdown := newTestDB(t) 1175 defer shutdown() 1176 1177 ctx, cancel := context.WithCancel(context.Background()) 1178 defer cancel() 1179 1180 // Create an account to use. 1181 acct1 := dbtest.RandomAccountInfo() 1182 acct2 := dbtest.RandomAccountInfo() 1183 err := boltdb.CreateAccount(acct1) 1184 err1 := boltdb.CreateAccount(acct2) 1185 if err != nil || err1 != nil { 1186 t.Fatalf("CreateAccount error: %v : %v", err, err1) 1187 } 1188 base1, quote1 := randU32(), randU32() 1189 base2, quote2 := randU32(), randU32() 1190 1191 numToDoO := 1000 1192 numActiveO := 500 1193 numActiveOrdWithMatch := 481 1194 1195 if testing.Short() { 1196 numToDoO = 24 1197 numActiveO = 11 1198 numActiveOrdWithMatch = 6 1199 } 1200 1201 activeOrdsWithMatch := make([]order.OrderID, numActiveOrdWithMatch) 1202 var anInactiveOrder order.OrderID 1203 orders := make(map[int]*db.MetaOrder, numToDoO) 1204 nTimes(numToDoO, func(i int) { 1205 // statuses 3, 4, and 5 considered inactive orders 1206 status := order.OrderStatus(rand.Intn(3) + 3) // inactive 1207 if i < numActiveO { 1208 // Technically, this is putting even cancel and market orders in the 1209 // booked state half the time, which should be impossible. The DB does not 1210 // check for this, and will recognize the order as active. 1211 // statuses 1 and 2 considered inactive orders. 1212 status = order.OrderStatus(rand.Intn(2) + 1) 1213 } 1214 acct := acct1 1215 base, quote := base1, quote1 1216 if i%2 == 1 { 1217 acct = acct2 1218 base, quote = base2, quote2 1219 } 1220 ord := randOrderForMarket(base, quote) 1221 1222 orders[i] = &db.MetaOrder{ 1223 MetaData: &db.OrderMetaData{ 1224 Status: status, 1225 Host: acct.Host, 1226 Proof: db.OrderProof{DEXSig: randBytes(73)}, 1227 SwapFeesPaid: rand.Uint64(), 1228 RedemptionFeesPaid: rand.Uint64(), 1229 MaxFeeRate: rand.Uint64(), 1230 }, 1231 Order: ord, 1232 } 1233 // One order saved so that orderSide does not fail. 1234 if i == numActiveO { 1235 anInactiveOrder = ord.ID() 1236 } 1237 if i < numActiveOrdWithMatch { 1238 activeOrdsWithMatch[i] = ord.ID() 1239 } 1240 }) 1241 1242 tStart := time.Now() 1243 nTimes(numToDoO, func(i int) { 1244 err := boltdb.UpdateOrder(orders[i]) 1245 if err != nil { 1246 t.Fatalf("error inserting order: %v", err) 1247 } 1248 }) 1249 t.Logf("~ %d milliseconds to insert %d MetaOrder", int(time.Since(tStart)/time.Millisecond)-numToDoO, numToDoO) 1250 1251 numToDo := 10000 1252 numActive := 100 1253 numNewer := 1111 1254 if testing.Short() { 1255 numToDo = 24 1256 numActive = 8 1257 numNewer = 3 1258 } 1259 stamp := rand.Uint64() - 1 1260 metaMatches := make([]*db.MetaMatch, 0, numToDo) 1261 matchIndex := make(map[order.MatchID]*db.MetaMatch, numToDo) 1262 nTimes(numToDo, func(i int) { 1263 s := stamp 1264 // numNewer archived matches are deleted. 1265 if i >= numActive && i < numActive+numNewer { 1266 s += 1 1267 } 1268 m := &db.MetaMatch{ 1269 MetaData: &db.MatchMetaData{ 1270 Proof: *dbtest.RandomMatchProof(0.5), 1271 DEX: acct1.Host, 1272 Base: base1, 1273 Quote: quote1, 1274 Stamp: s, 1275 }, 1276 UserMatch: ordertest.RandomUserMatch(), 1277 } 1278 if i < numActive { 1279 m.Status = order.MatchStatus(rand.Intn(4)) 1280 } else { 1281 // Some matches will not be deleted if they have an 1282 // active order. 1283 if i >= numNewer+numActive && i < numActive+numNewer+numActiveOrdWithMatch { 1284 m.OrderID = activeOrdsWithMatch[i-numActive-numNewer] 1285 } else { 1286 m.OrderID = anInactiveOrder 1287 } 1288 m.Status = order.MatchConfirmed // inactive 1289 m.MetaData.Proof.Auth.RedeemSig = []byte{0} // redeemSig required for MatchComplete to be considered inactive 1290 } 1291 matchIndex[m.MatchID] = m 1292 metaMatches = append(metaMatches, m) 1293 }) 1294 1295 tStart = time.Now() 1296 nTimes(numToDo, func(i int) { 1297 err := boltdb.UpdateMatch(metaMatches[i]) 1298 if err != nil { 1299 t.Fatalf("update error: %v", err) 1300 } 1301 }) 1302 t.Logf("%d milliseconds to insert %d account MetaMatch", time.Since(tStart)/time.Millisecond, numToDo) 1303 1304 var ( 1305 olderThan = time.UnixMilli(int64(stamp)) 1306 matchN = 0 1307 perMatchFn = func(m *db.MetaMatch, isSell bool) error { 1308 matchN++ 1309 if matchN%100 == 0 { 1310 fmt.Printf("Deleted %d'th match %v with isSell %v\n", matchN, m.MatchID, isSell) 1311 } 1312 return nil 1313 } 1314 ) 1315 nMatchesDeleted, err := boltdb.DeleteInactiveMatches(ctx, &olderThan, perMatchFn) 1316 if err != nil { 1317 t.Fatalf("unable to delete inactive matches: %v", err) 1318 } 1319 if nMatchesDeleted != matchN { 1320 t.Fatalf("expected %d archived matches to be deleted got %d", matchN, nMatchesDeleted) 1321 } 1322 1323 // Active matches should be untouched. 1324 activeMatches, err := boltdb.ActiveMatches() 1325 if err != nil { 1326 t.Fatalf("error getting active matches: %v", err) 1327 } 1328 if len(activeMatches) != numActive { 1329 t.Fatalf("expected %d active matches, got %d", numActive, len(activeMatches)) 1330 } 1331 1332 // Matches occurring after stamp or with active orders should be 1333 // untouched. 1334 numArchivedMatches := 0 1335 if err = boltdb.View(func(tx *bbolt.Tx) error { 1336 archivedMB := tx.Bucket(archivedMatchesBucket) 1337 if archivedMB == nil { 1338 return fmt.Errorf("failed to open %s bucket", string(archivedMatchesBucket)) 1339 } 1340 numArchivedMatches = archivedMB.Stats().BucketN - 1 1341 return nil 1342 }); err != nil { 1343 t.Fatalf("unable to count archived matches: %v", err) 1344 } 1345 numLeft := numNewer + numActiveOrdWithMatch 1346 if numArchivedMatches != numLeft { 1347 t.Fatalf("expected %d archived matches left after deletion but got %d", numLeft, numArchivedMatches) 1348 } 1349 } 1350 1351 func TestDeleteInactiveOrders(t *testing.T) { 1352 // TODO: This test takes way too long to run. Why? 1353 if !withLongTests { 1354 return 1355 } 1356 boltdb, shutdown := newTestDB(t) 1357 defer shutdown() 1358 1359 ctx, cancel := context.WithCancel(context.Background()) 1360 defer cancel() 1361 1362 // Create an account to use. 1363 acct1 := dbtest.RandomAccountInfo() 1364 acct2 := dbtest.RandomAccountInfo() 1365 err := boltdb.CreateAccount(acct1) 1366 err1 := boltdb.CreateAccount(acct2) 1367 if err != nil || err1 != nil { 1368 t.Fatalf("CreateAccount error: %v : %v", err, err1) 1369 } 1370 base1, quote1 := randU32(), randU32() 1371 base2, quote2 := randU32(), randU32() 1372 1373 numToDo := 10008 1374 numActive := 1000 1375 numNewer := 1111 1376 numActiveMatchWithOrd := 481 1377 if testing.Short() { 1378 numToDo = 48 1379 numActive = 10 1380 numNewer = 4 1381 numActiveMatchWithOrd = 5 1382 } 1383 activeMatchsWithOrd := make([]order.OrderID, numActiveMatchWithOrd) 1384 orders := make(map[int]*db.MetaOrder, numToDo) 1385 orderIndex := make(map[order.OrderID]order.Order) 1386 nTimes(numToDo, func(i int) { 1387 // statuses 3, 4, and 5 considered inactive orders 1388 status := order.OrderStatus(rand.Intn(3) + 3) // inactive 1389 if i < numActive { 1390 // Technically, this is putting even cancel and market orders in the 1391 // booked state half the time, which should be impossible. The DB does not 1392 // check for this, and will recognize the order as active. 1393 // statuses 1 and 2 considered active orders. 1394 status = order.OrderStatus(rand.Intn(2) + 1) 1395 } 1396 acct := acct1 1397 base, quote := base1, quote1 1398 if i%2 == 1 { 1399 acct = acct2 1400 base, quote = base2, quote2 1401 } 1402 ord := randOrderForMarket(base, quote) 1403 1404 // These orders will be inserted into active matches. 1405 if i > numToDo/2 && i <= numToDo/2+numActiveMatchWithOrd { 1406 activeMatchsWithOrd[i-numToDo/2-1] = ord.ID() 1407 } 1408 1409 orders[i] = &db.MetaOrder{ 1410 MetaData: &db.OrderMetaData{ 1411 Status: status, 1412 Host: acct.Host, 1413 Proof: db.OrderProof{DEXSig: randBytes(73)}, 1414 SwapFeesPaid: rand.Uint64(), 1415 RedemptionFeesPaid: rand.Uint64(), 1416 MaxFeeRate: rand.Uint64(), 1417 }, 1418 Order: ord, 1419 } 1420 orderIndex[ord.ID()] = ord 1421 }) 1422 1423 var olderThan time.Time 1424 tStart := time.Now() 1425 nTimes(numToDo, func(i int) { 1426 if numToDo-i == numNewer { 1427 olderThan = time.Now() 1428 time.Sleep(time.Millisecond * 100) 1429 } 1430 err := boltdb.UpdateOrder(orders[i]) 1431 if err != nil { 1432 t.Fatalf("error inserting order: %v", err) 1433 } 1434 }) 1435 t.Logf("~ %d milliseconds to insert %d MetaOrder", int(time.Since(tStart)/time.Millisecond)-numToDo, numToDo) 1436 1437 numToDoM := 1000 1438 numActiveM := 500 // must be more than numActiveMatchWithOrd 1439 if testing.Short() { 1440 numToDoM = 24 1441 numActiveM = 8 1442 } 1443 metaMatches := make([]*db.MetaMatch, 0, numToDoM) 1444 matchIndex := make(map[order.MatchID]*db.MetaMatch, numToDoM) 1445 nTimes(numToDoM, func(i int) { 1446 m := &db.MetaMatch{ 1447 MetaData: &db.MatchMetaData{ 1448 Proof: *dbtest.RandomMatchProof(0.5), 1449 DEX: acct1.Host, 1450 Base: base1, 1451 Quote: quote1, 1452 Stamp: rand.Uint64(), 1453 }, 1454 UserMatch: ordertest.RandomUserMatch(), 1455 } 1456 // Insert orders in active matches to be ignored. 1457 if i < numActiveMatchWithOrd { 1458 m.OrderID = activeMatchsWithOrd[i] 1459 } 1460 if i < numActiveM { 1461 m.Status = order.MatchStatus(rand.Intn(4)) 1462 } else { 1463 m.Status = order.MatchComplete // inactive 1464 m.MetaData.Proof.Auth.RedeemSig = []byte{0} // redeemSig required for MatchComplete to be considered inactive 1465 } 1466 matchIndex[m.MatchID] = m 1467 metaMatches = append(metaMatches, m) 1468 }) 1469 tStart = time.Now() 1470 nTimes(numToDoM, func(i int) { 1471 err := boltdb.UpdateMatch(metaMatches[i]) 1472 if err != nil { 1473 t.Fatalf("update error: %v", err) 1474 } 1475 }) 1476 t.Logf("%d milliseconds to insert %d account MetaMatch", time.Since(tStart)/time.Millisecond, numToDoM) 1477 1478 var ( 1479 orderN int 1480 perOrderFn = func(ord *db.MetaOrder) error { 1481 orderN++ 1482 if orderN%100 == 0 { 1483 fmt.Printf("Deleted %d'th order %v\n", orderN, ord.Order.ID()) 1484 } 1485 return nil 1486 } 1487 ) 1488 nOrdersDeleted, err := boltdb.DeleteInactiveOrders(ctx, &olderThan, perOrderFn) 1489 if err != nil { 1490 t.Fatalf("unable to delete inactive matches: %v", err) 1491 } 1492 if nOrdersDeleted != orderN { 1493 t.Fatalf("expected %d deleted archived orders, got %d", orderN, nOrdersDeleted) 1494 } 1495 1496 // Active orders should be untouched. 1497 activeOrders, err := boltdb.ActiveOrders() 1498 if err != nil { 1499 t.Fatalf("error getting active orders: %v", err) 1500 } 1501 if len(activeOrders) != numActive { 1502 t.Fatalf("expected %d active orders, got %d", numActive, len(activeOrders)) 1503 } 1504 1505 // Matches occurring after stamp or part of an active match should be 1506 // untouched. 1507 numArchivedOrders := 0 1508 if err = boltdb.View(func(tx *bbolt.Tx) error { 1509 archivedOB := tx.Bucket(archivedOrdersBucket) 1510 if archivedOB == nil { 1511 return fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket)) 1512 } 1513 numArchivedOrders = archivedOB.Stats().BucketN - 1 1514 return nil 1515 }); err != nil { 1516 t.Fatalf("unable to count archived orders: %v", err) 1517 } 1518 numUntouched := numNewer + numActiveMatchWithOrd 1519 if numArchivedOrders != numUntouched { 1520 t.Fatalf("expected %d archived orders left after deletion but got %d", numUntouched, numArchivedOrders) 1521 } 1522 } 1523 1524 func TestOrderSide(t *testing.T) { 1525 boltdb, shutdown := newTestDB(t) 1526 defer shutdown() 1527 1528 // Create an account to use. 1529 acct1 := dbtest.RandomAccountInfo() 1530 acct2 := dbtest.RandomAccountInfo() 1531 err := boltdb.CreateAccount(acct1) 1532 err1 := boltdb.CreateAccount(acct2) 1533 if err != nil || err1 != nil { 1534 t.Fatalf("CreateAccount error: %v : %v", err, err1) 1535 } 1536 base1, quote1 := randU32(), randU32() 1537 base2, quote2 := randU32(), randU32() 1538 1539 numToDoO := 2 1540 1541 orders := make(map[int]*db.MetaOrder, numToDoO) 1542 nTimes(numToDoO, func(i int) { 1543 status := order.OrderStatus(rand.Intn(5) + 1) 1544 acct := acct1 1545 base, quote := base1, quote1 1546 if i%2 == 1 { 1547 acct = acct2 1548 base, quote = base2, quote2 1549 } 1550 ord, _ := ordertest.RandomLimitOrder() 1551 ord.BaseAsset = base 1552 ord.QuoteAsset = quote 1553 ord.Trade().Sell = i == 0 // true then false 1554 1555 orders[i] = &db.MetaOrder{ 1556 MetaData: &db.OrderMetaData{ 1557 Status: status, 1558 Host: acct.Host, 1559 Proof: db.OrderProof{DEXSig: randBytes(73)}, 1560 SwapFeesPaid: rand.Uint64(), 1561 RedemptionFeesPaid: rand.Uint64(), 1562 MaxFeeRate: rand.Uint64(), 1563 }, 1564 Order: ord, 1565 } 1566 }) 1567 1568 tStart := time.Now() 1569 nTimes(numToDoO, func(i int) { 1570 err := boltdb.UpdateOrder(orders[i]) 1571 if err != nil { 1572 t.Fatalf("error inserting order: %v", err) 1573 } 1574 }) 1575 t.Logf("~ %d milliseconds to insert %d MetaOrder", int(time.Since(tStart)/time.Millisecond)-numToDoO, numToDoO) 1576 1577 if err := boltdb.View(func(tx *bbolt.Tx) error { 1578 for _, ord := range orders { 1579 side, err := orderSide(tx, ord.Order.ID()) 1580 if err != nil { 1581 return err 1582 } 1583 want := ord.Order.Trade().Sell 1584 if side != want { 1585 t.Fatalf("wanted isSell %v but got %v", want, side) 1586 } 1587 } 1588 return nil 1589 }); err != nil { 1590 t.Fatal(err) 1591 } 1592 } 1593 1594 func TestPokes(t *testing.T) { 1595 boltdb, shutdown := newTestDB(t) 1596 defer shutdown() 1597 1598 pokes := []*db.Notification{ 1599 dbtest.RandomNotification(1), dbtest.RandomNotification(2), dbtest.RandomNotification(3), 1600 dbtest.RandomNotification(6), dbtest.RandomNotification(5), dbtest.RandomNotification(4), 1601 dbtest.RandomNotification(7), 1602 } 1603 1604 if err := boltdb.SavePokes(pokes); err != nil { 1605 t.Fatalf("SavePokes error: %v", err) 1606 } 1607 1608 rePokes, err := boltdb.LoadPokes() 1609 if err != nil { 1610 t.Fatalf("LoadPokes error: %v", err) 1611 } 1612 1613 if len(rePokes) != len(pokes) { 1614 t.Fatalf("Expected to load %d pokes. Loaded %d instead.", len(pokes), len(rePokes)) 1615 } 1616 1617 for i, poke := range pokes { 1618 dbtest.MustCompareNotifications(t, poke, rePokes[i]) 1619 } 1620 1621 noPokes, err := boltdb.LoadPokes() 1622 if err != nil { 1623 t.Fatalf("Second LoadPokes error: %v", err) 1624 } 1625 if len(noPokes) != 0 { 1626 t.Fatal("Result from second LoadPokes wasn't empty") 1627 } 1628 } 1629 1630 func TestPruneArchivedOrders(t *testing.T) { 1631 const host = "blah" 1632 const prunedSize = 5 1633 boltdb, shutdown := newTestDB(t) 1634 defer shutdown() 1635 1636 archivedOrdersN := func() (n int) { 1637 boltdb.View(func(tx *bbolt.Tx) error { 1638 n = tx.Bucket(archivedOrdersBucket).Stats().BucketN - 1 /* BucketN includes top bucket */ 1639 return nil 1640 }) 1641 return n 1642 } 1643 1644 var ordStampI uint64 1645 addOrder := func(stamp uint64) order.OrderID { 1646 ord, _ := ordertest.RandomLimitOrder() 1647 if stamp == 0 { 1648 stamp = ordStampI 1649 ordStampI++ 1650 } 1651 boltdb.UpdateOrder(&db.MetaOrder{ 1652 MetaData: &db.OrderMetaData{ 1653 Status: order.OrderStatusExecuted, 1654 Host: host, 1655 Proof: db.OrderProof{DEXSig: []byte{0xa}}, 1656 }, 1657 Order: ord, 1658 }) 1659 oid := ord.ID() 1660 boltdb.ordersUpdate(func(ob, archivedOB *bbolt.Bucket) error { 1661 archivedOB.Bucket(oid[:]).Put(updateTimeKey, uint64Bytes(stamp)) 1662 return nil 1663 }) 1664 return oid 1665 } 1666 for i := 0; i < prunedSize*2; i++ { 1667 addOrder(0) 1668 } 1669 1670 if n := archivedOrdersN(); n != prunedSize*2 { 1671 t.Fatalf("Expected %d archived orders after intitialization, saw %d", prunedSize*2, n) 1672 } 1673 1674 if err := boltdb.pruneArchivedOrders(prunedSize); err != nil { 1675 t.Fatalf("pruneArchivedOrders error: %v", err) 1676 } 1677 1678 if n := archivedOrdersN(); n != prunedSize { 1679 t.Fatalf("Expected %d archived orders after first pruning, saw %d", prunedSize, n) 1680 } 1681 1682 // Make sure we pruned the first 5. 1683 if err := boltdb.View(func(tx *bbolt.Tx) error { 1684 bkt := tx.Bucket(archivedOrdersBucket) 1685 return bkt.ForEach(func(oidB, _ []byte) error { 1686 if stamp := intCoder.Uint64(bkt.Bucket(oidB).Get(updateTimeKey)); stamp < prunedSize { 1687 return fmt.Errorf("order stamp %d should have been pruned", stamp) 1688 } 1689 return nil 1690 }) 1691 }); err != nil { 1692 t.Fatal(err) 1693 } 1694 1695 // Add an order with an early stamp and an active match 1696 oid := addOrder(1) 1697 m := &db.MetaMatch{ 1698 MetaData: &db.MatchMetaData{ 1699 DEX: host, 1700 Base: 1, 1701 }, 1702 UserMatch: ordertest.RandomUserMatch(), 1703 } 1704 m.OrderID = oid 1705 m.Status = order.NewlyMatched 1706 if err := boltdb.UpdateMatch(m); err != nil { 1707 t.Fatal(err) 1708 } 1709 1710 if err := boltdb.pruneArchivedOrders(prunedSize); err != nil { 1711 t.Fatalf("Error pruning orders when one has an active match: %v", err) 1712 } 1713 1714 if n := archivedOrdersN(); n != prunedSize { 1715 t.Fatalf("Expected %d archived orders after pruning with active match order in place, saw %d", prunedSize, n) 1716 } 1717 1718 // Our active match order should still be available 1719 if _, err := boltdb.Order(oid); err != nil { 1720 t.Fatalf("Error retrieving unpruned active match order: %v", err) 1721 } 1722 1723 // Retire the active match order 1724 m.Status = order.MatchComplete 1725 if err := boltdb.UpdateMatch(m); err != nil { 1726 t.Fatal(err) 1727 } 1728 // Add an order to push the now retirable older order out 1729 addOrder(0) 1730 if err := boltdb.pruneArchivedOrders(prunedSize); err != nil { 1731 t.Fatalf("Error pruning orders after retiring match: %v", err) 1732 } 1733 if n := archivedOrdersN(); n != prunedSize { 1734 t.Fatalf("Expected %d archived orders after pruning with retired match, saw %d", prunedSize, n) 1735 } 1736 // Match should not be archived any longer. 1737 metaID := m.MatchOrderUniqueID() 1738 if err := boltdb.matchesView(func(mb, archivedMB *bbolt.Bucket) error { 1739 if mb.Bucket(metaID) != nil || archivedMB.Bucket(metaID) != nil { 1740 return errors.New("still found bucket for retired match of pruned order") 1741 } 1742 return nil 1743 }); err != nil { 1744 t.Fatal(err) 1745 } 1746 }