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  }