decred.org/dcrdex@v1.0.5/client/db/bolt/db.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package bolt
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io/fs"
    13  	"os"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"time"
    18  
    19  	"decred.org/dcrdex/client/db"
    20  	dexdb "decred.org/dcrdex/client/db"
    21  	"decred.org/dcrdex/dex"
    22  	"decred.org/dcrdex/dex/config"
    23  	"decred.org/dcrdex/dex/encode"
    24  	"decred.org/dcrdex/dex/encrypt"
    25  	"decred.org/dcrdex/dex/order"
    26  	"go.etcd.io/bbolt"
    27  )
    28  
    29  // getCopy returns a copy of the value for the given key and provided bucket. If
    30  // the key is not found or if an empty slice was loaded, nil is returned. Thus,
    31  // use bkt.Get(key) == nil directly to test for existence of the key. This
    32  // function should be used instead of bbolt.(*Bucket).Get when the read value
    33  // needs to be kept after the transaction, at which time the buffer from Get is
    34  // no longer safe to use.
    35  func getCopy(bkt *bbolt.Bucket, key []byte) []byte {
    36  	b := bkt.Get(key)
    37  	if len(b) == 0 {
    38  		return nil
    39  	}
    40  	val := make([]byte, len(b))
    41  	copy(val, b)
    42  	return val
    43  }
    44  
    45  // Short names for some commonly used imported functions.
    46  var (
    47  	intCoder    = encode.IntCoder
    48  	bEqual      = bytes.Equal
    49  	uint16Bytes = encode.Uint16Bytes
    50  	uint32Bytes = encode.Uint32Bytes
    51  	uint64Bytes = encode.Uint64Bytes
    52  )
    53  
    54  // Bolt works on []byte keys and values. These are some commonly used key and
    55  // value encodings.
    56  var (
    57  	// bucket keys
    58  	appBucket             = []byte("appBucket")
    59  	accountsBucket        = []byte("accounts")
    60  	bondIndexesBucket     = []byte("bondIndexes")
    61  	bondsSubBucket        = []byte("bonds") // sub bucket of accounts
    62  	activeOrdersBucket    = []byte("activeOrders")
    63  	archivedOrdersBucket  = []byte("orders")
    64  	activeMatchesBucket   = []byte("activeMatches")
    65  	archivedMatchesBucket = []byte("matches")
    66  	botProgramsBucket     = []byte("botPrograms")
    67  	walletsBucket         = []byte("wallets")
    68  	notesBucket           = []byte("notes")
    69  	pokesBucket           = []byte("pokes")
    70  	credentialsBucket     = []byte("credentials")
    71  
    72  	// value keys
    73  	versionKey            = []byte("version")
    74  	linkedKey             = []byte("linked")
    75  	feeProofKey           = []byte("feecoin")
    76  	statusKey             = []byte("status")
    77  	baseKey               = []byte("base")
    78  	quoteKey              = []byte("quote")
    79  	orderKey              = []byte("order")
    80  	matchKey              = []byte("match")
    81  	orderIDKey            = []byte("orderID")
    82  	matchIDKey            = []byte("matchID")
    83  	proofKey              = []byte("proof")
    84  	activeKey             = []byte("active")
    85  	bondKey               = []byte("bond")
    86  	confirmedKey          = []byte("confirmed")
    87  	refundedKey           = []byte("refunded")
    88  	lockTimeKey           = []byte("lockTime")
    89  	dexKey                = []byte("dex")
    90  	updateTimeKey         = []byte("utime")
    91  	accountKey            = []byte("account")
    92  	balanceKey            = []byte("balance")
    93  	walletKey             = []byte("wallet")
    94  	changeKey             = []byte("change")
    95  	noteKey               = []byte("note")
    96  	pokesKey              = []byte("pokesKey")
    97  	stampKey              = []byte("stamp")
    98  	severityKey           = []byte("severity")
    99  	ackKey                = []byte("ack")
   100  	swapFeesKey           = []byte("swapFees")
   101  	maxFeeRateKey         = []byte("maxFeeRate")
   102  	redeemMaxFeeRateKey   = []byte("redeemMaxFeeRate")
   103  	redemptionFeesKey     = []byte("redeemFees")
   104  	fundingFeesKey        = []byte("fundingFees")
   105  	accelerationsKey      = []byte("accelerations")
   106  	typeKey               = []byte("type")
   107  	seedGenTimeKey        = []byte("seedGenTime")
   108  	encSeedKey            = []byte("encSeed")
   109  	encInnerKeyKey        = []byte("encInnerKey")
   110  	innerKeyParamsKey     = []byte("innerKeyParams")
   111  	outerKeyParamsKey     = []byte("outerKeyParams")
   112  	birthdayKey           = []byte("birthday")
   113  	credsVersionKey       = []byte("credsVersion")
   114  	legacyKeyParamsKey    = []byte("keyParams")
   115  	epochDurKey           = []byte("epochDur")
   116  	fromVersionKey        = []byte("fromVersion")
   117  	toVersionKey          = []byte("toVersion")
   118  	fromSwapConfKey       = []byte("fromSwapConf")
   119  	toSwapConfKey         = []byte("toSwapConf")
   120  	optionsKey            = []byte("options")
   121  	redemptionReservesKey = []byte("redemptionReservesKey")
   122  	refundReservesKey     = []byte("refundReservesKey")
   123  	disabledRateSourceKey = []byte("disabledRateSources")
   124  	walletDisabledKey     = []byte("walletDisabled")
   125  	programKey            = []byte("program")
   126  	langKey               = []byte("lang")
   127  
   128  	// values
   129  	byteTrue   = encode.ByteTrue
   130  	byteFalse  = encode.ByteFalse
   131  	byteEpoch  = uint16Bytes(uint16(order.OrderStatusEpoch))
   132  	byteBooked = uint16Bytes(uint16(order.OrderStatusBooked))
   133  
   134  	backupDir = "backup"
   135  )
   136  
   137  // Opts is a set of options for the DB.
   138  type Opts struct {
   139  	BackupOnShutdown bool // default is true
   140  	PruneArchive     uint64
   141  }
   142  
   143  var defaultOpts = Opts{
   144  	BackupOnShutdown: true,
   145  }
   146  
   147  // BoltDB is a bbolt-based database backend for Bison Wallet. BoltDB satisfies
   148  // the db.DB interface defined at decred.org/dcrdex/client/db.
   149  type BoltDB struct {
   150  	*bbolt.DB
   151  	opts Opts
   152  	log  dex.Logger
   153  }
   154  
   155  // Check that BoltDB satisfies the db.DB interface.
   156  var _ dexdb.DB = (*BoltDB)(nil)
   157  
   158  // NewDB is a constructor for a *BoltDB.
   159  func NewDB(dbPath string, logger dex.Logger, opts ...Opts) (dexdb.DB, error) {
   160  	_, err := os.Stat(dbPath)
   161  	isNew := os.IsNotExist(err)
   162  
   163  	db, err := bbolt.Open(dbPath, 0600, &bbolt.Options{Timeout: 3 * time.Second})
   164  	if err != nil {
   165  		if errors.Is(err, bbolt.ErrTimeout) {
   166  			err = fmt.Errorf("%w, could happen when database is already being used by another process", err)
   167  		}
   168  		return nil, err
   169  	}
   170  
   171  	// Release the file lock on exit.
   172  	bdb := &BoltDB{
   173  		DB:   db,
   174  		opts: defaultOpts,
   175  		log:  logger,
   176  	}
   177  	if len(opts) > 0 {
   178  		bdb.opts = opts[0]
   179  	}
   180  
   181  	if err = bdb.makeTopLevelBuckets([][]byte{
   182  		appBucket, accountsBucket, bondIndexesBucket,
   183  		activeOrdersBucket, archivedOrdersBucket,
   184  		activeMatchesBucket, archivedMatchesBucket,
   185  		walletsBucket, notesBucket, credentialsBucket,
   186  		botProgramsBucket, pokesBucket,
   187  	}); err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	// If the db is a new one, initialize it with the current DB version.
   192  	if isNew {
   193  		err := bdb.DB.Update(func(dbTx *bbolt.Tx) error {
   194  			bkt := dbTx.Bucket(appBucket)
   195  			if bkt == nil {
   196  				return fmt.Errorf("app bucket not found")
   197  			}
   198  
   199  			versionB := encode.Uint32Bytes(DBVersion)
   200  			err := bkt.Put(versionKey, versionB)
   201  			if err != nil {
   202  				return err
   203  			}
   204  
   205  			return nil
   206  		})
   207  		if err != nil {
   208  			return nil, err
   209  		}
   210  
   211  		bdb.log.Infof("Created and started database (version = %d, file = %s)", DBVersion, dbPath)
   212  
   213  		return bdb, nil
   214  	}
   215  
   216  	err = bdb.upgradeDB()
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	if bdb.opts.PruneArchive > 0 {
   222  		bdb.log.Info("Pruning the order archive")
   223  		bdb.pruneArchivedOrders(bdb.opts.PruneArchive)
   224  	}
   225  
   226  	bdb.log.Infof("Started database (version = %d, file = %s)", DBVersion, dbPath)
   227  
   228  	return bdb, nil
   229  }
   230  
   231  func (db *BoltDB) fileSize(path string) int64 {
   232  	stat, err := os.Stat(path)
   233  	if err != nil {
   234  		db.log.Errorf("Failed to stat %v: %v", path, err)
   235  		return 0
   236  	}
   237  	return stat.Size()
   238  }
   239  
   240  // Run waits for context cancellation and closes the database.
   241  func (db *BoltDB) Run(ctx context.Context) {
   242  	<-ctx.Done() // wait for shutdown to backup and compact
   243  
   244  	// Create a backup in the backups folder.
   245  	if db.opts.BackupOnShutdown {
   246  		db.log.Infof("Backing up database...")
   247  		if err := db.Backup(); err != nil {
   248  			db.log.Errorf("Unable to backup database: %v", err)
   249  		}
   250  	}
   251  
   252  	// Only compact the current DB file if there is excessive free space, in
   253  	// terms of bytes AND relative to total DB size.
   254  	const byteThresh = 1 << 18 // 256 KiB free
   255  	const pctThresh = 0.05     // 5% free
   256  	var dbSize int64
   257  	_ = db.View(func(tx *bbolt.Tx) error {
   258  		dbSize = tx.Size() // db size including free bytes
   259  		return nil
   260  	})
   261  	dbStats := db.Stats()
   262  	// FreeAlloc is (FreePageN + PendingPageN) * db.Info().PageSize
   263  	// FreelistInuse seems to be page header overhead (much smaller). Add them.
   264  	// https://github.com/etcd-io/bbolt/blob/020684ea1eb7b5574a8007c8d69605b1de8d9ec4/tx.go#L308-L309
   265  	freeBytes := int64(dbStats.FreeAlloc + dbStats.FreelistInuse)
   266  	pctFree := float64(freeBytes) / float64(dbSize)
   267  	db.log.Debugf("Total DB size %d bytes, %d bytes unused (%.2f%%)",
   268  		dbSize, freeBytes, 100*pctFree)
   269  	// Only compact if free space is at least the byte threshold AND that fee
   270  	// space accounts for a significant percent of the file.
   271  	if freeBytes < byteThresh || pctFree < pctThresh {
   272  		db.Close()
   273  		return
   274  	}
   275  
   276  	// TODO: If we never see trouble with the compacted DB files when bisonw
   277  	// starts up, we can just compact to the backups folder and copy that backup
   278  	// file back on top of the main DB file after it is closed.  For now, the
   279  	// backup above is a direct (not compacted) copy.
   280  
   281  	// Compact the database by writing into a temporary file, closing the source
   282  	// DB, and overwriting the original with the compacted temporary file.
   283  	db.log.Infof("Compacting database to reclaim at least %d bytes...", freeBytes)
   284  	srcPath := db.Path()                     // before db.Close
   285  	compFile := srcPath + ".tmp"             // deterministic on *same fs*
   286  	err := db.BackupTo(compFile, true, true) // overwrite and compact
   287  	if err != nil {
   288  		db.Close()
   289  		db.log.Errorf("Unable to compact database: %v", err)
   290  		return
   291  	}
   292  
   293  	db.Close() // close db file at srcPath
   294  
   295  	initSize, compSize := db.fileSize(srcPath), db.fileSize(compFile)
   296  	db.log.Infof("Compacted database file from %v => %v bytes (%.2f%% reduction)",
   297  		initSize, compSize, 100*float64(initSize-compSize)/float64(initSize))
   298  
   299  	err = os.Rename(compFile, srcPath) // compFile => srcPath
   300  	if err != nil {
   301  		db.log.Errorf("Unable to switch to compacted database: %v", err)
   302  		return
   303  	}
   304  }
   305  
   306  // Recrypt re-encrypts the wallet passwords and account private keys. As a
   307  // convenience, the provided *PrimaryCredentials are stored under the same
   308  // transaction.
   309  func (db *BoltDB) Recrypt(creds *dexdb.PrimaryCredentials, oldCrypter, newCrypter encrypt.Crypter) (walletUpdates map[uint32][]byte, acctUpdates map[string][]byte, err error) {
   310  	if err := validateCreds(creds); err != nil {
   311  		return nil, nil, err
   312  	}
   313  
   314  	walletUpdates = make(map[uint32][]byte)
   315  	acctUpdates = make(map[string][]byte)
   316  
   317  	return walletUpdates, acctUpdates, db.Update(func(tx *bbolt.Tx) error {
   318  		// Updates accounts and wallets.
   319  		wallets := tx.Bucket(walletsBucket)
   320  		if wallets == nil {
   321  			return fmt.Errorf("no wallets bucket")
   322  		}
   323  
   324  		accounts := tx.Bucket(accountsBucket)
   325  		if accounts == nil {
   326  			return fmt.Errorf("no accounts bucket")
   327  		}
   328  
   329  		if err := wallets.ForEach(func(wid, _ []byte) error {
   330  			wBkt := wallets.Bucket(wid)
   331  			w, err := makeWallet(wBkt)
   332  			if err != nil {
   333  				return err
   334  			}
   335  			if len(w.EncryptedPW) == 0 {
   336  				return nil
   337  			}
   338  
   339  			pw, err := oldCrypter.Decrypt(w.EncryptedPW)
   340  			if err != nil {
   341  				return fmt.Errorf("Decrypt error: %w", err)
   342  			}
   343  			w.EncryptedPW, err = newCrypter.Encrypt(pw)
   344  			if err != nil {
   345  				return fmt.Errorf("Encrypt error: %w", err)
   346  			}
   347  			err = wBkt.Put(walletKey, w.Encode())
   348  			if err != nil {
   349  				return err
   350  			}
   351  			walletUpdates[w.AssetID] = w.EncryptedPW
   352  			return nil
   353  
   354  		}); err != nil {
   355  			return fmt.Errorf("wallets update error: %w", err)
   356  		}
   357  
   358  		err = accounts.ForEach(func(hostB, _ []byte) error {
   359  			acct := accounts.Bucket(hostB)
   360  			if acct == nil {
   361  				return fmt.Errorf("account bucket %s value not a nested bucket", string(hostB))
   362  			}
   363  			acctB := getCopy(acct, accountKey)
   364  			if acctB == nil {
   365  				return fmt.Errorf("empty account found for %s", string(hostB))
   366  			}
   367  			acctInfo, err := dexdb.DecodeAccountInfo(acctB)
   368  			if err != nil {
   369  				return err
   370  			}
   371  			if len(acctInfo.LegacyEncKey) != 0 {
   372  				privB, err := oldCrypter.Decrypt(acctInfo.LegacyEncKey)
   373  				if err != nil {
   374  					return err
   375  				}
   376  
   377  				acctInfo.LegacyEncKey, err = newCrypter.Encrypt(privB)
   378  				if err != nil {
   379  					return err
   380  				}
   381  				acctUpdates[acctInfo.Host] = acctInfo.LegacyEncKey
   382  			} else if len(acctInfo.EncKeyV2) > 0 {
   383  				privB, err := oldCrypter.Decrypt(acctInfo.EncKeyV2)
   384  				if err != nil {
   385  					return err
   386  				}
   387  				acctInfo.EncKeyV2, err = newCrypter.Encrypt(privB)
   388  				if err != nil {
   389  					return err
   390  				}
   391  				acctUpdates[acctInfo.Host] = acctInfo.EncKeyV2
   392  			}
   393  
   394  			return acct.Put(accountKey, acctInfo.Encode())
   395  		})
   396  		if err != nil {
   397  			return fmt.Errorf("accounts update error: %w", err)
   398  		}
   399  
   400  		// Store the new credentials.
   401  		return db.setCreds(tx, creds)
   402  	})
   403  }
   404  
   405  // SetPrimaryCredentials validates and stores the PrimaryCredentials.
   406  func (db *BoltDB) SetPrimaryCredentials(creds *dexdb.PrimaryCredentials) error {
   407  	if err := validateCreds(creds); err != nil {
   408  		return err
   409  	}
   410  
   411  	return db.Update(func(tx *bbolt.Tx) error {
   412  		return db.setCreds(tx, creds)
   413  	})
   414  }
   415  
   416  func (db *BoltDB) pruneArchivedOrders(prunedSize uint64) error {
   417  
   418  	return db.Update(func(tx *bbolt.Tx) error {
   419  		archivedOB := tx.Bucket(archivedOrdersBucket)
   420  		if archivedOB == nil {
   421  			return fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket))
   422  		}
   423  
   424  		nOrds := uint64(archivedOB.Stats().BucketN - 1 /* BucketN includes top bucket */)
   425  		if nOrds <= prunedSize {
   426  			return nil
   427  		}
   428  
   429  		// We won't delete any orders with active matches.
   430  		activeMatches := tx.Bucket(activeMatchesBucket)
   431  		if activeMatches == nil {
   432  			return fmt.Errorf("failed to open %s bucket", string(activeMatchesBucket))
   433  		}
   434  		oidsWithActiveMatches := make(map[order.OrderID]struct{}, 0)
   435  		if err := activeMatches.ForEach(func(k, _ []byte) error {
   436  			mBkt := activeMatches.Bucket(k)
   437  			if mBkt == nil {
   438  				return fmt.Errorf("error getting match bucket %x", k)
   439  			}
   440  			var oid order.OrderID
   441  			copy(oid[:], mBkt.Get(orderIDKey))
   442  			oidsWithActiveMatches[oid] = struct{}{}
   443  			return nil
   444  		}); err != nil {
   445  			return fmt.Errorf("error building active match order ID index: %w", err)
   446  		}
   447  
   448  		toClear := int(nOrds - prunedSize)
   449  
   450  		type orderStamp struct {
   451  			oid   []byte
   452  			stamp uint64
   453  		}
   454  		deletes := make([]*orderStamp, 0, toClear)
   455  		sortDeletes := func() {
   456  			sort.Slice(deletes, func(i, j int) bool {
   457  				return deletes[i].stamp < deletes[j].stamp
   458  			})
   459  		}
   460  		var sortedAtCapacity bool
   461  		if err := archivedOB.ForEach(func(oidB, v []byte) error {
   462  			var oid order.OrderID
   463  			copy(oid[:], oidB)
   464  			if _, found := oidsWithActiveMatches[oid]; found {
   465  				return nil
   466  			}
   467  			oBkt := archivedOB.Bucket(oidB)
   468  			if oBkt == nil {
   469  				return fmt.Errorf("no order bucket iterated order %x", oidB)
   470  			}
   471  			stampB := oBkt.Get(updateTimeKey)
   472  			if stampB == nil {
   473  				// Highly improbable.
   474  				stampB = make([]byte, 8)
   475  			}
   476  			stamp := intCoder.Uint64(stampB)
   477  			if len(deletes) < toClear {
   478  				deletes = append(deletes, &orderStamp{
   479  					stamp: stamp,
   480  					oid:   oidB,
   481  				})
   482  				return nil
   483  			}
   484  			if !sortedAtCapacity {
   485  				// Make sure the last element is the newest one once we hit
   486  				// capacity.
   487  				sortDeletes()
   488  				sortedAtCapacity = true
   489  			}
   490  			if stamp > deletes[len(deletes)-1].stamp {
   491  				return nil
   492  			}
   493  			deletes[len(deletes)-1] = &orderStamp{
   494  				stamp: stamp,
   495  				oid:   oidB,
   496  			}
   497  			sortDeletes()
   498  			return nil
   499  		}); err != nil {
   500  			return fmt.Errorf("archive iteration error: %v", err)
   501  		}
   502  
   503  		deletedOrders := make(map[order.OrderID]struct{})
   504  		for _, del := range deletes {
   505  			var oid order.OrderID
   506  			copy(oid[:], del.oid)
   507  			deletedOrders[oid] = struct{}{}
   508  			if err := archivedOB.DeleteBucket(del.oid); err != nil {
   509  				return fmt.Errorf("error deleting archived order %q: %v", del.oid, err)
   510  			}
   511  		}
   512  
   513  		matchesToDelete := make([][]byte, 0, prunedSize /* just avoid some allocs if we can */)
   514  		archivedMatches := tx.Bucket(archivedMatchesBucket)
   515  		if archivedMatches == nil {
   516  			return errors.New("no archived match bucket")
   517  		}
   518  		if err := archivedMatches.ForEach(func(k, _ []byte) error {
   519  			matchBkt := archivedMatches.Bucket(k)
   520  			if matchBkt == nil {
   521  				return fmt.Errorf("no bucket found for %x during iteration", k)
   522  			}
   523  			var oid order.OrderID
   524  			copy(oid[:], matchBkt.Get(orderIDKey))
   525  			if _, found := deletedOrders[oid]; found {
   526  				matchesToDelete = append(matchesToDelete, k)
   527  			}
   528  			return nil
   529  		}); err != nil {
   530  			return fmt.Errorf("error finding matches to prune: %w", err)
   531  		}
   532  		for i := range matchesToDelete {
   533  			if err := archivedMatches.DeleteBucket(matchesToDelete[i]); err != nil {
   534  				return fmt.Errorf("error deleting pruned match %x: %w", matchesToDelete[i], err)
   535  			}
   536  		}
   537  		return nil
   538  	})
   539  }
   540  
   541  // validateCreds checks that the PrimaryCredentials fields are properly
   542  // populated.
   543  func validateCreds(creds *dexdb.PrimaryCredentials) error {
   544  	if len(creds.EncSeed) == 0 {
   545  		return errors.New("EncSeed not set")
   546  	}
   547  	if len(creds.EncInnerKey) == 0 {
   548  		return errors.New("EncInnerKey not set")
   549  	}
   550  	if len(creds.InnerKeyParams) == 0 {
   551  		return errors.New("InnerKeyParams not set")
   552  	}
   553  	if len(creds.OuterKeyParams) == 0 {
   554  		return errors.New("OuterKeyParams not set")
   555  	}
   556  	return nil
   557  }
   558  
   559  // primaryCreds reconstructs the *PrimaryCredentials.
   560  func (db *BoltDB) primaryCreds() (creds *dexdb.PrimaryCredentials, err error) {
   561  	return creds, db.View(func(tx *bbolt.Tx) error {
   562  		bkt := tx.Bucket(credentialsBucket)
   563  		if bkt == nil {
   564  			return errors.New("no credentials bucket")
   565  		}
   566  		if bkt.Stats().KeyN == 0 {
   567  			return dexdb.ErrNoCredentials
   568  		}
   569  
   570  		versionB := getCopy(bkt, credsVersionKey)
   571  		if len(versionB) != 2 {
   572  			versionB = []byte{0x00, 0x00}
   573  		}
   574  
   575  		bdayB := getCopy(bkt, birthdayKey)
   576  		bday := time.Time{}
   577  		if len(bdayB) > 0 {
   578  			bday = time.Unix(int64(intCoder.Uint64(bdayB)), 0)
   579  		}
   580  
   581  		creds = &dexdb.PrimaryCredentials{
   582  			EncSeed:        getCopy(bkt, encSeedKey),
   583  			EncInnerKey:    getCopy(bkt, encInnerKeyKey),
   584  			InnerKeyParams: getCopy(bkt, innerKeyParamsKey),
   585  			OuterKeyParams: getCopy(bkt, outerKeyParamsKey),
   586  			Birthday:       bday,
   587  			Version:        intCoder.Uint16(versionB),
   588  		}
   589  		return nil
   590  	})
   591  }
   592  
   593  // setCreds stores the *PrimaryCredentials.
   594  func (db *BoltDB) setCreds(tx *bbolt.Tx, creds *dexdb.PrimaryCredentials) error {
   595  	bkt := tx.Bucket(credentialsBucket)
   596  	if bkt == nil {
   597  		return errors.New("no credentials bucket")
   598  	}
   599  	return newBucketPutter(bkt).
   600  		put(encSeedKey, creds.EncSeed).
   601  		put(encInnerKeyKey, creds.EncInnerKey).
   602  		put(innerKeyParamsKey, creds.InnerKeyParams).
   603  		put(outerKeyParamsKey, creds.OuterKeyParams).
   604  		put(birthdayKey, uint64Bytes(uint64(creds.Birthday.Unix()))).
   605  		put(credsVersionKey, uint16Bytes(creds.Version)).
   606  		err()
   607  }
   608  
   609  // PrimaryCredentials retrieves the *PrimaryCredentials, if they are stored. It
   610  // is an error if none have been stored.
   611  func (db *BoltDB) PrimaryCredentials() (creds *dexdb.PrimaryCredentials, err error) {
   612  	return db.primaryCreds()
   613  }
   614  
   615  // SetSeedGenerationTime stores the time the app seed was generated.
   616  func (db *BoltDB) SetSeedGenerationTime(time uint64) error {
   617  	return db.Update(func(tx *bbolt.Tx) error {
   618  		bkt := tx.Bucket(credentialsBucket)
   619  		if bkt == nil {
   620  			return errors.New("no credentials bucket")
   621  		}
   622  
   623  		return newBucketPutter(bkt).
   624  			put(seedGenTimeKey, uint64Bytes(time)).
   625  			err()
   626  	})
   627  }
   628  
   629  // SeedGenerationTime returns the time the app seed was generated, if it was
   630  // stored. It returns dexdb.ErrNoSeedGenTime if it was not stored.
   631  func (db *BoltDB) SeedGenerationTime() (uint64, error) {
   632  	var seedGenTime uint64
   633  	return seedGenTime, db.View(func(tx *bbolt.Tx) error {
   634  		bkt := tx.Bucket(credentialsBucket)
   635  		if bkt == nil {
   636  			return errors.New("no credentials bucket")
   637  		}
   638  
   639  		seedGenTimeBytes := bkt.Get(seedGenTimeKey)
   640  		if seedGenTimeBytes == nil {
   641  			return dexdb.ErrNoSeedGenTime
   642  		}
   643  		if len(seedGenTimeBytes) != 8 {
   644  			return fmt.Errorf("seed generation time length %v, expected 8", len(seedGenTimeBytes))
   645  		}
   646  
   647  		seedGenTime = intCoder.Uint64(seedGenTimeBytes)
   648  		return nil
   649  	})
   650  }
   651  
   652  // ListAccounts returns a list of DEX URLs. The DB is designed to have a single
   653  // account per DEX, so the account itself is identified by the DEX URL.
   654  func (db *BoltDB) ListAccounts() ([]string, error) {
   655  	var urls []string
   656  	return urls, db.acctsView(func(accts *bbolt.Bucket) error {
   657  		c := accts.Cursor()
   658  		for acct, _ := c.First(); acct != nil; acct, _ = c.Next() {
   659  			acctBkt := accts.Bucket(acct)
   660  			if acctBkt == nil {
   661  				return fmt.Errorf("account bucket %s value not a nested bucket", string(acct))
   662  			}
   663  			if bEqual(acctBkt.Get(activeKey), byteTrue) {
   664  				urls = append(urls, string(acct))
   665  			}
   666  		}
   667  		return nil
   668  	})
   669  }
   670  
   671  func loadAccountInfo(acct *bbolt.Bucket, log dex.Logger) (*db.AccountInfo, error) {
   672  	acctB := getCopy(acct, accountKey)
   673  	if acctB == nil {
   674  		return nil, fmt.Errorf("empty account")
   675  	}
   676  	acctInfo, err := dexdb.DecodeAccountInfo(acctB)
   677  	if err != nil {
   678  		return nil, err
   679  	}
   680  
   681  	acctInfo.Disabled = bytes.Equal(acct.Get(activeKey), byteFalse)
   682  
   683  	bondsBkt := acct.Bucket(bondsSubBucket)
   684  	if bondsBkt == nil {
   685  		return acctInfo, nil // no bonds, OK for legacy account
   686  	}
   687  
   688  	c := bondsBkt.Cursor()
   689  	for bondUID, _ := c.First(); bondUID != nil; bondUID, _ = c.Next() {
   690  		bond := bondsBkt.Bucket(bondUID)
   691  		if acct == nil {
   692  			return nil, fmt.Errorf("bond sub-bucket %x not a nested bucket", bondUID)
   693  		}
   694  		dbBond, err := dexdb.DecodeBond(getCopy(bond, bondKey))
   695  		if err != nil {
   696  			log.Errorf("Invalid bond data encoding: %v", err)
   697  			continue
   698  		}
   699  		dbBond.Confirmed = bEqual(bond.Get(confirmedKey), byteTrue)
   700  		dbBond.Refunded = bEqual(bond.Get(refundedKey), byteTrue)
   701  		acctInfo.Bonds = append(acctInfo.Bonds, dbBond)
   702  	}
   703  
   704  	return acctInfo, nil
   705  }
   706  
   707  // Accounts returns a list of DEX Accounts. The DB is designed to have a single
   708  // account per DEX, so the account itself is identified by the DEX host. TODO:
   709  // allow bonds filter based on lockTime.
   710  func (db *BoltDB) Accounts() ([]*dexdb.AccountInfo, error) {
   711  	var accounts []*dexdb.AccountInfo
   712  	return accounts, db.acctsView(func(accts *bbolt.Bucket) error {
   713  		c := accts.Cursor()
   714  		for acctKey, _ := c.First(); acctKey != nil; acctKey, _ = c.Next() {
   715  			acct := accts.Bucket(acctKey)
   716  			if acct == nil {
   717  				return fmt.Errorf("account bucket %s value not a nested bucket", string(acctKey))
   718  			}
   719  			acctInfo, err := loadAccountInfo(acct, db.log)
   720  			if err != nil {
   721  				return err
   722  			}
   723  			accounts = append(accounts, acctInfo)
   724  		}
   725  		return nil
   726  	})
   727  }
   728  
   729  // Account gets the AccountInfo associated with the specified DEX address.
   730  func (db *BoltDB) Account(url string) (*dexdb.AccountInfo, error) {
   731  	var acctInfo *dexdb.AccountInfo
   732  	acctKey := []byte(url)
   733  	return acctInfo, db.acctsView(func(accts *bbolt.Bucket) (err error) {
   734  		acct := accts.Bucket(acctKey)
   735  		if acct == nil {
   736  			return dexdb.ErrAcctNotFound
   737  		}
   738  		acctInfo, err = loadAccountInfo(acct, db.log)
   739  		return
   740  	})
   741  }
   742  
   743  // CreateAccount saves the AccountInfo. If an account already exists for this
   744  // DEX, it will return an error.
   745  func (db *BoltDB) CreateAccount(ai *dexdb.AccountInfo) error {
   746  	if ai.Host == "" {
   747  		return fmt.Errorf("empty host not allowed")
   748  	}
   749  	if ai.DEXPubKey == nil {
   750  		return fmt.Errorf("nil DEXPubKey not allowed")
   751  	}
   752  	return db.acctsUpdate(func(accts *bbolt.Bucket) error {
   753  		acct, err := accts.CreateBucket([]byte(ai.Host))
   754  		if err != nil {
   755  			return fmt.Errorf("failed to create account bucket: %w", err)
   756  		}
   757  
   758  		err = acct.Put(accountKey, ai.Encode())
   759  		if err != nil {
   760  			return fmt.Errorf("accountKey put error: %w", err)
   761  		}
   762  		err = acct.Put(activeKey, byteTrue)
   763  		if err != nil {
   764  			return fmt.Errorf("activeKey put error: %w", err)
   765  		}
   766  
   767  		bonds, err := acct.CreateBucket(bondsSubBucket)
   768  		if err != nil {
   769  			return fmt.Errorf("unable to create bonds sub-bucket for account for %s: %w", ai.Host, err)
   770  		}
   771  
   772  		for _, bond := range ai.Bonds {
   773  			bondUID := bond.UniqueID()
   774  			bondBkt, err := bonds.CreateBucketIfNotExists(bondUID)
   775  			if err != nil {
   776  				return fmt.Errorf("failed to create bond %x bucket: %w", bondUID, err)
   777  			}
   778  
   779  			err = db.storeBond(bondBkt, bond)
   780  			if err != nil {
   781  				return err
   782  			}
   783  		}
   784  
   785  		return nil
   786  	})
   787  }
   788  
   789  // NextBondKeyIndex returns the next bond key index and increments the stored
   790  // value so that subsequent calls will always return a higher index.
   791  func (db *BoltDB) NextBondKeyIndex(assetID uint32) (uint32, error) {
   792  	var bondIndex uint32
   793  	return bondIndex, db.Update(func(tx *bbolt.Tx) error {
   794  		bkt := tx.Bucket(bondIndexesBucket)
   795  		if bkt == nil {
   796  			return errors.New("no bond indexes bucket")
   797  		}
   798  
   799  		thisBondIdxKey := uint32Bytes(assetID)
   800  		bondIndexB := bkt.Get(thisBondIdxKey)
   801  		if len(bondIndexB) != 0 {
   802  			bondIndex = intCoder.Uint32(bondIndexB)
   803  		}
   804  		return bkt.Put(thisBondIdxKey, uint32Bytes(bondIndex+1))
   805  	})
   806  }
   807  
   808  // UpdateAccountInfo updates the account info for an existing account with
   809  // the same Host as the parameter. If no account exists with this host,
   810  // an error is returned.
   811  func (db *BoltDB) UpdateAccountInfo(ai *dexdb.AccountInfo) error {
   812  	return db.acctsUpdate(func(accts *bbolt.Bucket) error {
   813  		acct := accts.Bucket([]byte(ai.Host))
   814  		if acct == nil {
   815  			return fmt.Errorf("account not found for %s", ai.Host)
   816  		}
   817  
   818  		err := acct.Put(accountKey, ai.Encode())
   819  		if err != nil {
   820  			return fmt.Errorf("accountKey put error: %w", err)
   821  		}
   822  
   823  		bonds, err := acct.CreateBucketIfNotExists(bondsSubBucket)
   824  		if err != nil {
   825  			return fmt.Errorf("unable to create bonds sub-bucket for account for %s: %w", ai.Host, err)
   826  		}
   827  
   828  		for _, bond := range ai.Bonds {
   829  			bondUID := bond.UniqueID()
   830  			bondBkt, err := bonds.CreateBucketIfNotExists(bondUID)
   831  			if err != nil {
   832  				return fmt.Errorf("failed to create bond %x bucket: %w", bondUID, err)
   833  			}
   834  
   835  			err = db.storeBond(bondBkt, bond)
   836  			if err != nil {
   837  				return err
   838  			}
   839  		}
   840  
   841  		return nil
   842  	})
   843  }
   844  
   845  // ToggleAccountStatus enables or disables the account associated with the given
   846  // host.
   847  func (db *BoltDB) ToggleAccountStatus(host string, disable bool) error {
   848  	return db.acctsUpdate(func(accts *bbolt.Bucket) error {
   849  		acct := accts.Bucket([]byte(host))
   850  		if acct == nil {
   851  			return fmt.Errorf("account not found for %s", host)
   852  		}
   853  
   854  		newStatus := byteTrue
   855  		if disable {
   856  			newStatus = byteFalse
   857  		}
   858  
   859  		if bytes.Equal(acct.Get(activeKey), newStatus) {
   860  			msg := "account is already enabled"
   861  			if disable {
   862  				msg = "account is already disabled"
   863  			}
   864  			return errors.New(msg)
   865  		}
   866  
   867  		err := acct.Put(activeKey, newStatus)
   868  		if err != nil {
   869  			return fmt.Errorf("accountKey put error: %w", err)
   870  		}
   871  		return nil
   872  	})
   873  }
   874  
   875  // acctsView is a convenience function for reading from the account bucket.
   876  func (db *BoltDB) acctsView(f bucketFunc) error {
   877  	return db.withBucket(accountsBucket, db.View, f)
   878  }
   879  
   880  // acctsUpdate is a convenience function for updating the account bucket.
   881  func (db *BoltDB) acctsUpdate(f bucketFunc) error {
   882  	return db.withBucket(accountsBucket, db.Update, f)
   883  }
   884  
   885  func (db *BoltDB) storeBond(bondBkt *bbolt.Bucket, bond *db.Bond) error {
   886  	err := bondBkt.Put(bondKey, bond.Encode())
   887  	if err != nil {
   888  		return fmt.Errorf("bondKey put error: %w", err)
   889  	}
   890  
   891  	confirmed := encode.ByteFalse
   892  	if bond.Confirmed {
   893  		confirmed = encode.ByteTrue
   894  	}
   895  	err = bondBkt.Put(confirmedKey, confirmed)
   896  	if err != nil {
   897  		return fmt.Errorf("confirmedKey put error: %w", err)
   898  	}
   899  
   900  	refunded := encode.ByteFalse
   901  	if bond.Refunded {
   902  		refunded = encode.ByteTrue
   903  	}
   904  	err = bondBkt.Put(refundedKey, refunded)
   905  	if err != nil {
   906  		return fmt.Errorf("refundedKey put error: %w", err)
   907  	}
   908  
   909  	err = bondBkt.Put(lockTimeKey, uint64Bytes(bond.LockTime)) // also in bond encoding
   910  	if err != nil {
   911  		return fmt.Errorf("lockTimeKey put error: %w", err)
   912  	}
   913  
   914  	return nil
   915  }
   916  
   917  // AddBond saves a new Bond or updates an existing bond for an existing DEX
   918  // account.
   919  func (db *BoltDB) AddBond(host string, bond *db.Bond) error {
   920  	acctKey := []byte(host)
   921  	return db.acctsUpdate(func(accts *bbolt.Bucket) error {
   922  		acct := accts.Bucket(acctKey)
   923  		if acct == nil {
   924  			return fmt.Errorf("account not found for %s", host)
   925  		}
   926  
   927  		bonds, err := acct.CreateBucketIfNotExists(bondsSubBucket)
   928  		if err != nil {
   929  			return fmt.Errorf("unable to access bonds sub-bucket for account for %s: %w", host, err)
   930  		}
   931  
   932  		bondUID := bond.UniqueID()
   933  		bondBkt, err := bonds.CreateBucketIfNotExists(bondUID)
   934  		if err != nil {
   935  			return fmt.Errorf("failed to create bond %x bucket: %w", bondUID, err)
   936  		}
   937  
   938  		return db.storeBond(bondBkt, bond)
   939  	})
   940  }
   941  
   942  func (db *BoltDB) setBondFlag(host string, assetID uint32, bondCoinID []byte, flagKey []byte) error {
   943  	acctKey := []byte(host)
   944  	return db.acctsUpdate(func(accts *bbolt.Bucket) error {
   945  		acct := accts.Bucket(acctKey)
   946  		if acct == nil {
   947  			return fmt.Errorf("account not found for %s", host)
   948  		}
   949  
   950  		bonds := acct.Bucket(bondsSubBucket)
   951  		if bonds == nil {
   952  			return fmt.Errorf("bonds sub-bucket not found for account for %s", host)
   953  		}
   954  
   955  		bondUID := dexdb.BondUID(assetID, bondCoinID)
   956  		bondBkt := bonds.Bucket(bondUID)
   957  		if bondBkt == nil {
   958  			return fmt.Errorf("bond bucket does not exist: %x", bondUID)
   959  		}
   960  
   961  		return bondBkt.Put(flagKey, byteTrue)
   962  	})
   963  }
   964  
   965  // ConfirmBond marks a DEX account bond as confirmed by the DEX.
   966  func (db *BoltDB) ConfirmBond(host string, assetID uint32, bondCoinID []byte) error {
   967  	return db.setBondFlag(host, assetID, bondCoinID, confirmedKey)
   968  }
   969  
   970  // BondRefunded marks a DEX account bond as refunded by the client wallet.
   971  func (db *BoltDB) BondRefunded(host string, assetID uint32, bondCoinID []byte) error {
   972  	return db.setBondFlag(host, assetID, bondCoinID, refundedKey)
   973  }
   974  
   975  // UpdateOrder saves the order information in the database. Any existing order
   976  // info for the same order ID will be overwritten without indication.
   977  func (db *BoltDB) UpdateOrder(m *dexdb.MetaOrder) error {
   978  	ord, md := m.Order, m.MetaData
   979  	if md.Status == order.OrderStatusUnknown {
   980  		return fmt.Errorf("cannot set order %s status to unknown", ord.ID())
   981  	}
   982  	if md.Host == "" {
   983  		return fmt.Errorf("empty DEX not allowed")
   984  	}
   985  	if len(md.Proof.DEXSig) == 0 {
   986  		return fmt.Errorf("cannot save order without DEX signature")
   987  	}
   988  	return db.ordersUpdate(func(ob, archivedOB *bbolt.Bucket) error {
   989  		oid := ord.ID()
   990  
   991  		// Create or move an order bucket based on order status. Active
   992  		// orders go in the activeOrdersBucket. Inactive orders go in the
   993  		// archivedOrdersBucket.
   994  		bkt := ob
   995  		whichBkt := "active"
   996  		inactive := !md.Status.IsActive()
   997  		if inactive {
   998  			// No order means that it was never added to the active
   999  			// orders bucket, or UpdateOrder was already called on
  1000  			// this order after it became inactive.
  1001  			if ob.Bucket(oid[:]) != nil {
  1002  				// This order now belongs in the archived orders
  1003  				// bucket. Delete it from active orders.
  1004  				if err := ob.DeleteBucket(oid[:]); err != nil {
  1005  					return fmt.Errorf("archived order bucket delete error: %w", err)
  1006  				}
  1007  			}
  1008  			bkt = archivedOB
  1009  			whichBkt = "archived"
  1010  		}
  1011  		oBkt, err := bkt.CreateBucketIfNotExists(oid[:])
  1012  		if err != nil {
  1013  			return fmt.Errorf("%s bucket error: %w", whichBkt, err)
  1014  		}
  1015  
  1016  		err = newBucketPutter(oBkt).
  1017  			put(baseKey, uint32Bytes(ord.Base())).
  1018  			put(quoteKey, uint32Bytes(ord.Quote())).
  1019  			put(dexKey, []byte(md.Host)).
  1020  			put(typeKey, []byte{byte(ord.Type())}).
  1021  			put(orderKey, order.EncodeOrder(ord)).
  1022  			put(epochDurKey, uint64Bytes(md.EpochDur)).
  1023  			put(fromVersionKey, uint32Bytes(md.FromVersion)).
  1024  			put(toVersionKey, uint32Bytes(md.ToVersion)).
  1025  			put(fromSwapConfKey, uint32Bytes(md.FromSwapConf)).
  1026  			put(toSwapConfKey, uint32Bytes(md.ToSwapConf)).
  1027  			put(redeemMaxFeeRateKey, uint64Bytes(md.RedeemMaxFeeRate)).
  1028  			put(maxFeeRateKey, uint64Bytes(md.MaxFeeRate)).
  1029  			err()
  1030  
  1031  		if err != nil {
  1032  			return err
  1033  		}
  1034  
  1035  		return updateOrderMetaData(oBkt, md)
  1036  	})
  1037  }
  1038  
  1039  // ActiveOrders retrieves all orders which appear to be in an active state,
  1040  // which is either in the epoch queue or in the order book.
  1041  func (db *BoltDB) ActiveOrders() ([]*dexdb.MetaOrder, error) {
  1042  	return db.filteredOrders(func(_ *bbolt.Bucket) bool {
  1043  		return true
  1044  	}, false)
  1045  }
  1046  
  1047  // AccountOrders retrieves all orders associated with the specified DEX. n = 0
  1048  // applies no limit on number of orders returned. since = 0 is equivalent to
  1049  // disabling the time filter, since no orders were created before 1970.
  1050  func (db *BoltDB) AccountOrders(dex string, n int, since uint64) ([]*dexdb.MetaOrder, error) {
  1051  	dexB := []byte(dex)
  1052  	if n == 0 && since == 0 {
  1053  		return db.filteredOrders(func(oBkt *bbolt.Bucket) bool {
  1054  			return bEqual(dexB, oBkt.Get(dexKey))
  1055  		}, true)
  1056  	}
  1057  	sinceB := uint64Bytes(since)
  1058  	return db.newestOrders(n, func(_ []byte, oBkt *bbolt.Bucket) bool {
  1059  		timeB := oBkt.Get(updateTimeKey)
  1060  		return bEqual(dexB, oBkt.Get(dexKey)) && bytes.Compare(timeB, sinceB) >= 0
  1061  	}, true)
  1062  }
  1063  
  1064  // MarketOrders retrieves all orders for the specified DEX and market. n = 0
  1065  // applies no limit on number of orders returned. since = 0 is equivalent to
  1066  // disabling the time filter, since no orders were created before 1970.
  1067  func (db *BoltDB) MarketOrders(dex string, base, quote uint32, n int, since uint64) ([]*dexdb.MetaOrder, error) {
  1068  	dexB := []byte(dex)
  1069  	baseB := uint32Bytes(base)
  1070  	quoteB := uint32Bytes(quote)
  1071  	if n == 0 && since == 0 {
  1072  		return db.marketOrdersAll(dexB, baseB, quoteB)
  1073  	}
  1074  	return db.marketOrdersSince(dexB, baseB, quoteB, n, since)
  1075  }
  1076  
  1077  // ActiveDEXOrders retrieves all orders for the specified DEX.
  1078  func (db *BoltDB) ActiveDEXOrders(dex string) ([]*dexdb.MetaOrder, error) {
  1079  	dexB := []byte(dex)
  1080  	return db.filteredOrders(func(oBkt *bbolt.Bucket) bool {
  1081  		return bEqual(dexB, oBkt.Get(dexKey))
  1082  	}, false)
  1083  }
  1084  
  1085  // marketOrdersAll retrieves all orders for the specified DEX and market.
  1086  func (db *BoltDB) marketOrdersAll(dexB, baseB, quoteB []byte) ([]*dexdb.MetaOrder, error) {
  1087  	return db.filteredOrders(func(oBkt *bbolt.Bucket) bool {
  1088  		return bEqual(dexB, oBkt.Get(dexKey)) && bEqual(baseB, oBkt.Get(baseKey)) &&
  1089  			bEqual(quoteB, oBkt.Get(quoteKey))
  1090  	}, true)
  1091  }
  1092  
  1093  // marketOrdersSince grabs market orders with optional filters for maximum count
  1094  // and age. The sort order is always newest first. If n is 0, there is no limit
  1095  // to the number of orders returned. If using with both n = 0, and since = 0,
  1096  // use marketOrdersAll instead.
  1097  func (db *BoltDB) marketOrdersSince(dexB, baseB, quoteB []byte, n int, since uint64) ([]*dexdb.MetaOrder, error) {
  1098  	sinceB := uint64Bytes(since)
  1099  	return db.newestOrders(n, func(_ []byte, oBkt *bbolt.Bucket) bool {
  1100  		timeB := oBkt.Get(updateTimeKey)
  1101  		return bEqual(dexB, oBkt.Get(dexKey)) && bEqual(baseB, oBkt.Get(baseKey)) &&
  1102  			bEqual(quoteB, oBkt.Get(quoteKey)) && bytes.Compare(timeB, sinceB) >= 0
  1103  	}, true)
  1104  }
  1105  
  1106  // newestOrders returns the n newest orders, filtered with a supplied filter
  1107  // function. Each order's bucket is provided to the filter, and a boolean true
  1108  // return value indicates the order should is eligible to be decoded and
  1109  // returned.
  1110  func (db *BoltDB) newestOrders(n int, filter func([]byte, *bbolt.Bucket) bool, includeArchived bool) ([]*dexdb.MetaOrder, error) {
  1111  	orders := make([]*dexdb.MetaOrder, 0, n)
  1112  	return orders, db.ordersView(func(ob, archivedOB *bbolt.Bucket) error {
  1113  		buckets := []*bbolt.Bucket{ob}
  1114  		if includeArchived {
  1115  			buckets = append(buckets, archivedOB)
  1116  		}
  1117  		trios := newestBuckets(buckets, n, updateTimeKey, filter)
  1118  		for _, trio := range trios {
  1119  			o, err := decodeOrderBucket(trio.k, trio.b)
  1120  			if err != nil {
  1121  				return err
  1122  			}
  1123  			orders = append(orders, o)
  1124  		}
  1125  		return nil
  1126  	})
  1127  }
  1128  
  1129  // filteredOrders gets all orders that pass the provided filter function. Each
  1130  // order's bucket is provided to the filter, and a boolean true return value
  1131  // indicates the order should be decoded and returned.
  1132  func (db *BoltDB) filteredOrders(filter func(*bbolt.Bucket) bool, includeArchived bool) ([]*dexdb.MetaOrder, error) {
  1133  	var orders []*dexdb.MetaOrder
  1134  	return orders, db.ordersView(func(ob, archivedOB *bbolt.Bucket) error {
  1135  		buckets := []*bbolt.Bucket{ob}
  1136  		if includeArchived {
  1137  			buckets = append(buckets, archivedOB)
  1138  		}
  1139  		for _, master := range buckets {
  1140  			err := master.ForEach(func(oid, _ []byte) error {
  1141  				oBkt := master.Bucket(oid)
  1142  				if oBkt == nil {
  1143  					return fmt.Errorf("order %x bucket is not a bucket", oid)
  1144  				}
  1145  				if filter(oBkt) {
  1146  					o, err := decodeOrderBucket(oid, oBkt)
  1147  					if err != nil {
  1148  						return err
  1149  					}
  1150  					orders = append(orders, o)
  1151  				}
  1152  				return nil
  1153  			})
  1154  			if err != nil {
  1155  				return err
  1156  			}
  1157  		}
  1158  		return nil
  1159  	})
  1160  }
  1161  
  1162  // Order fetches a MetaOrder by order ID.
  1163  func (db *BoltDB) Order(oid order.OrderID) (mord *dexdb.MetaOrder, err error) {
  1164  	oidB := oid[:]
  1165  	err = db.ordersView(func(ob, archivedOB *bbolt.Bucket) error {
  1166  		oBkt := ob.Bucket(oidB)
  1167  		// If the order is not in the active bucket, check the archived
  1168  		// orders bucket.
  1169  		if oBkt == nil {
  1170  			oBkt = archivedOB.Bucket(oidB)
  1171  		}
  1172  		if oBkt == nil {
  1173  			return fmt.Errorf("order %s not found", oid)
  1174  		}
  1175  		var err error
  1176  		mord, err = decodeOrderBucket(oidB, oBkt)
  1177  		return err
  1178  	})
  1179  	return mord, err
  1180  }
  1181  
  1182  // filterSet is a set of bucket filtering functions. Each function takes a
  1183  // bucket key and the associated bucket, and should return true if the bucket
  1184  // passes the filter.
  1185  type filterSet []func(oidB []byte, oBkt *bbolt.Bucket) bool
  1186  
  1187  // check runs the bucket through all filters, and will return false if the
  1188  // bucket fails to pass any one filter.
  1189  func (fs filterSet) check(oidB []byte, oBkt *bbolt.Bucket) bool {
  1190  	for _, f := range fs {
  1191  		if !f(oidB, oBkt) {
  1192  			return false
  1193  		}
  1194  	}
  1195  	return true
  1196  }
  1197  
  1198  // Orders fetches a slice of orders, sorted by descending time, and filtered
  1199  // with the provided OrderFilter. Orders does not return cancel orders.
  1200  func (db *BoltDB) Orders(orderFilter *dexdb.OrderFilter) (ords []*dexdb.MetaOrder, err error) {
  1201  	// Default filter is just to exclude cancel orders.
  1202  	filters := filterSet{
  1203  		func(oidB []byte, oBkt *bbolt.Bucket) bool {
  1204  			oTypeB := oBkt.Get(typeKey)
  1205  			if len(oTypeB) != 1 {
  1206  				db.log.Error("encountered order type encoded with wrong number of bytes = %d for order %x", len(oTypeB), oidB)
  1207  				return false
  1208  			}
  1209  			oType := order.OrderType(oTypeB[0])
  1210  			return oType != order.CancelOrderType
  1211  		},
  1212  	}
  1213  
  1214  	if len(orderFilter.Hosts) > 0 {
  1215  		hosts := make(map[string]bool, len(orderFilter.Hosts))
  1216  		for _, host := range orderFilter.Hosts {
  1217  			hosts[host] = true
  1218  		}
  1219  		filters = append(filters, func(_ []byte, oBkt *bbolt.Bucket) bool {
  1220  			return hosts[string(oBkt.Get(dexKey))]
  1221  		})
  1222  	}
  1223  
  1224  	if len(orderFilter.Assets) > 0 {
  1225  		assetIDs := make(map[uint32]bool, len(orderFilter.Assets))
  1226  		for _, assetID := range orderFilter.Assets {
  1227  			assetIDs[assetID] = true
  1228  		}
  1229  		filters = append(filters, func(_ []byte, oBkt *bbolt.Bucket) bool {
  1230  			return assetIDs[intCoder.Uint32(oBkt.Get(baseKey))] || assetIDs[intCoder.Uint32(oBkt.Get(quoteKey))]
  1231  		})
  1232  	}
  1233  
  1234  	includeArchived := true
  1235  	if len(orderFilter.Statuses) > 0 {
  1236  		filters = append(filters, func(_ []byte, oBkt *bbolt.Bucket) bool {
  1237  			status := order.OrderStatus(intCoder.Uint16(oBkt.Get(statusKey)))
  1238  			for _, acceptable := range orderFilter.Statuses {
  1239  				if status == acceptable {
  1240  					return true
  1241  				}
  1242  			}
  1243  			return false
  1244  		})
  1245  		includeArchived = false
  1246  		for _, status := range orderFilter.Statuses {
  1247  			if !status.IsActive() {
  1248  				includeArchived = true
  1249  				break
  1250  			}
  1251  		}
  1252  	}
  1253  
  1254  	if orderFilter.Market != nil {
  1255  		filters = append(filters, func(_ []byte, oBkt *bbolt.Bucket) bool {
  1256  			baseID, quoteID := intCoder.Uint32(oBkt.Get(baseKey)), intCoder.Uint32(oBkt.Get(quoteKey))
  1257  			return orderFilter.Market.Base == baseID && orderFilter.Market.Quote == quoteID
  1258  		})
  1259  	}
  1260  
  1261  	if !orderFilter.Offset.IsZero() {
  1262  		offsetOID := orderFilter.Offset
  1263  		var stampB []byte
  1264  		err := db.ordersView(func(ob, archivedOB *bbolt.Bucket) error {
  1265  			offsetBucket := ob.Bucket(offsetOID[:])
  1266  			// If the order is not in the active bucket, check the
  1267  			// archived orders bucket.
  1268  			if offsetBucket == nil {
  1269  				offsetBucket = archivedOB.Bucket(offsetOID[:])
  1270  			}
  1271  			if offsetBucket == nil {
  1272  				return fmt.Errorf("order %s not found", offsetOID)
  1273  			}
  1274  			stampB = getCopy(offsetBucket, updateTimeKey)
  1275  			return nil
  1276  		})
  1277  		if err != nil {
  1278  			return nil, err
  1279  		}
  1280  
  1281  		filters = append(filters, func(oidB []byte, oBkt *bbolt.Bucket) bool {
  1282  			comp := bytes.Compare(oBkt.Get(updateTimeKey), stampB)
  1283  			return comp < 0 || (comp == 0 && bytes.Compare(offsetOID[:], oidB) < 0)
  1284  		})
  1285  	}
  1286  
  1287  	return db.newestOrders(orderFilter.N, filters.check, includeArchived)
  1288  }
  1289  
  1290  // decodeOrderBucket decodes the order's *bbolt.Bucket into a *MetaOrder.
  1291  func decodeOrderBucket(oid []byte, oBkt *bbolt.Bucket) (*dexdb.MetaOrder, error) {
  1292  	orderB := getCopy(oBkt, orderKey)
  1293  	if orderB == nil {
  1294  		return nil, fmt.Errorf("nil order bytes for order %x", oid)
  1295  	}
  1296  	ord, err := order.DecodeOrder(orderB)
  1297  	if err != nil {
  1298  		return nil, fmt.Errorf("error decoding order %x: %w", oid, err)
  1299  	}
  1300  	proofB := getCopy(oBkt, proofKey)
  1301  	if proofB == nil {
  1302  		return nil, fmt.Errorf("nil proof for order %x", oid)
  1303  	}
  1304  	proof, err := dexdb.DecodeOrderProof(proofB)
  1305  	if err != nil {
  1306  		return nil, fmt.Errorf("error decoding order proof for %x: %w", oid, err)
  1307  	}
  1308  
  1309  	var redemptionReserves uint64
  1310  	redemptionReservesB := oBkt.Get(redemptionReservesKey)
  1311  	if len(redemptionReservesB) == 8 {
  1312  		redemptionReserves = intCoder.Uint64(redemptionReservesB)
  1313  	}
  1314  
  1315  	var refundReserves uint64
  1316  	refundReservesB := oBkt.Get(refundReservesKey)
  1317  	if len(refundReservesB) == 8 {
  1318  		refundReserves = intCoder.Uint64(refundReservesB)
  1319  	}
  1320  
  1321  	var linkedID order.OrderID
  1322  	copy(linkedID[:], oBkt.Get(linkedKey))
  1323  
  1324  	// Old cancel orders may not have a maxFeeRate set since the v2 upgrade
  1325  	// doesn't set it for cancel orders.
  1326  	var maxFeeRate uint64
  1327  	if maxFeeRateB := oBkt.Get(maxFeeRateKey); len(maxFeeRateB) == 8 {
  1328  		maxFeeRate = intCoder.Uint64(maxFeeRateB)
  1329  	} else if ord.Type() != order.CancelOrderType {
  1330  		// Cancel orders should use zero, but trades need a non-zero value.
  1331  		maxFeeRate = ^uint64(0) // should not happen for trade orders after v2 upgrade
  1332  	}
  1333  
  1334  	var redeemMaxFeeRate uint64
  1335  	if redeemMaxFeeRateB := oBkt.Get(redeemMaxFeeRateKey); len(redeemMaxFeeRateB) == 8 {
  1336  		redeemMaxFeeRate = intCoder.Uint64(redeemMaxFeeRateB)
  1337  	}
  1338  
  1339  	var fromVersion, toVersion uint32
  1340  	fromVersionB, toVersionB := oBkt.Get(fromVersionKey), oBkt.Get(toVersionKey)
  1341  	if len(fromVersionB) == 4 {
  1342  		fromVersion = intCoder.Uint32(fromVersionB)
  1343  	}
  1344  	if len(toVersionB) == 4 {
  1345  		toVersion = intCoder.Uint32(toVersionB)
  1346  	}
  1347  
  1348  	var fromSwapConf, toSwapConf uint32
  1349  	fromSwapConfB, toSwapConfB := oBkt.Get(fromSwapConfKey), oBkt.Get(toSwapConfKey)
  1350  	if len(fromSwapConfB) == 4 {
  1351  		fromSwapConf = intCoder.Uint32(fromSwapConfB)
  1352  	}
  1353  	if len(toSwapConfB) == 4 {
  1354  		toSwapConf = intCoder.Uint32(toSwapConfB)
  1355  	}
  1356  
  1357  	var epochDur uint64
  1358  	if epochDurB := oBkt.Get(epochDurKey); len(epochDurB) == 8 {
  1359  		epochDur = intCoder.Uint64(epochDurB)
  1360  	}
  1361  
  1362  	optionsB := oBkt.Get(optionsKey)
  1363  	options, err := config.Parse(optionsB)
  1364  	if err != nil {
  1365  		return nil, fmt.Errorf("unable to decode order options")
  1366  	}
  1367  
  1368  	var accelerationCoinIDs []order.CoinID
  1369  	accelerationsB := getCopy(oBkt, accelerationsKey)
  1370  	if len(accelerationsB) > 0 {
  1371  		_, coinIDs, err := encode.DecodeBlob(accelerationsB)
  1372  		if err != nil {
  1373  			return nil, fmt.Errorf("unable to decode accelerations")
  1374  		}
  1375  		for _, coinID := range coinIDs {
  1376  			accelerationCoinIDs = append(accelerationCoinIDs, order.CoinID(coinID))
  1377  		}
  1378  	}
  1379  
  1380  	var fundingFeesPaid uint64
  1381  	if fundingFeesB := oBkt.Get(fundingFeesKey); len(fundingFeesB) == 8 {
  1382  		fundingFeesPaid = intCoder.Uint64(fundingFeesB)
  1383  	}
  1384  
  1385  	return &dexdb.MetaOrder{
  1386  		MetaData: &dexdb.OrderMetaData{
  1387  			Proof:              *proof,
  1388  			Status:             order.OrderStatus(intCoder.Uint16(oBkt.Get(statusKey))),
  1389  			Host:               string(getCopy(oBkt, dexKey)),
  1390  			ChangeCoin:         getCopy(oBkt, changeKey),
  1391  			LinkedOrder:        linkedID,
  1392  			SwapFeesPaid:       intCoder.Uint64(oBkt.Get(swapFeesKey)),
  1393  			EpochDur:           epochDur,
  1394  			MaxFeeRate:         maxFeeRate,
  1395  			RedeemMaxFeeRate:   redeemMaxFeeRate,
  1396  			RedemptionFeesPaid: intCoder.Uint64(oBkt.Get(redemptionFeesKey)),
  1397  			FromSwapConf:       fromSwapConf,
  1398  			ToSwapConf:         toSwapConf,
  1399  			FromVersion:        fromVersion,
  1400  			ToVersion:          toVersion,
  1401  			Options:            options,
  1402  			RedemptionReserves: redemptionReserves,
  1403  			RefundReserves:     refundReserves,
  1404  			AccelerationCoins:  accelerationCoinIDs,
  1405  			FundingFeesPaid:    fundingFeesPaid,
  1406  		},
  1407  		Order: ord,
  1408  	}, nil
  1409  }
  1410  
  1411  // updateOrderBucket expects an oid to exist in either the orders or archived
  1412  // order buckets. If status is not active, it first checks active orders. If
  1413  // found it moves the order to the archived bucket and returns that order
  1414  // bucket. If status is less than or equal to booked, it expects the order
  1415  // to already be in active orders and returns that bucket.
  1416  func updateOrderBucket(ob, archivedOB *bbolt.Bucket, oid order.OrderID, status order.OrderStatus) (*bbolt.Bucket, error) {
  1417  	if status == order.OrderStatusUnknown {
  1418  		return nil, fmt.Errorf("cannot set order %s status to unknown", oid)
  1419  	}
  1420  	inactive := !status.IsActive()
  1421  	if inactive {
  1422  		if bkt := ob.Bucket(oid[:]); bkt != nil {
  1423  			// It's in the active bucket. Move it to the archived bucket.
  1424  			oBkt, err := archivedOB.CreateBucket(oid[:])
  1425  			if err != nil {
  1426  				return nil, fmt.Errorf("unable to create archived order bucket: %v", err)
  1427  			}
  1428  			// Assume the order bucket contains only values, no
  1429  			// sub-buckets
  1430  			if err := bkt.ForEach(func(k, v []byte) error {
  1431  				return oBkt.Put(k, v)
  1432  			}); err != nil {
  1433  				return nil, fmt.Errorf("unable to copy active order bucket: %v", err)
  1434  			}
  1435  			if err := ob.DeleteBucket(oid[:]); err != nil {
  1436  				return nil, fmt.Errorf("unable to delete active order bucket: %v", err)
  1437  			}
  1438  			return oBkt, nil
  1439  		}
  1440  		// It's not in the active bucket, check archived.
  1441  		oBkt := archivedOB.Bucket(oid[:])
  1442  		if oBkt == nil {
  1443  			return nil, fmt.Errorf("archived order %s not found", oid)
  1444  		}
  1445  		return oBkt, nil
  1446  	}
  1447  	// Active status should be in the active bucket.
  1448  	oBkt := ob.Bucket(oid[:])
  1449  	if oBkt == nil {
  1450  		return nil, fmt.Errorf("active order %s not found", oid)
  1451  	}
  1452  	return oBkt, nil
  1453  }
  1454  
  1455  // UpdateOrderMetaData updates the order metadata, not including the Host.
  1456  func (db *BoltDB) UpdateOrderMetaData(oid order.OrderID, md *dexdb.OrderMetaData) error {
  1457  	return db.ordersUpdate(func(ob, archivedOB *bbolt.Bucket) error {
  1458  		oBkt, err := updateOrderBucket(ob, archivedOB, oid, md.Status)
  1459  		if err != nil {
  1460  			return fmt.Errorf("UpdateOrderMetaData: %w", err)
  1461  		}
  1462  
  1463  		return updateOrderMetaData(oBkt, md)
  1464  	})
  1465  }
  1466  
  1467  func updateOrderMetaData(bkt *bbolt.Bucket, md *dexdb.OrderMetaData) error {
  1468  	var linkedB []byte
  1469  	if !md.LinkedOrder.IsZero() {
  1470  		linkedB = md.LinkedOrder[:]
  1471  	}
  1472  
  1473  	var accelerationsB encode.BuildyBytes
  1474  	if len(md.AccelerationCoins) > 0 {
  1475  		accelerationsB = encode.BuildyBytes{0}
  1476  		for _, acceleration := range md.AccelerationCoins {
  1477  			accelerationsB = accelerationsB.AddData(acceleration)
  1478  		}
  1479  	}
  1480  
  1481  	return newBucketPutter(bkt).
  1482  		put(statusKey, uint16Bytes(uint16(md.Status))).
  1483  		put(updateTimeKey, uint64Bytes(timeNow())).
  1484  		put(proofKey, md.Proof.Encode()).
  1485  		put(changeKey, md.ChangeCoin).
  1486  		put(linkedKey, linkedB).
  1487  		put(swapFeesKey, uint64Bytes(md.SwapFeesPaid)).
  1488  		put(redemptionFeesKey, uint64Bytes(md.RedemptionFeesPaid)).
  1489  		put(optionsKey, config.Data(md.Options)).
  1490  		put(redemptionReservesKey, uint64Bytes(md.RedemptionReserves)).
  1491  		put(refundReservesKey, uint64Bytes(md.RefundReserves)).
  1492  		put(accelerationsKey, accelerationsB).
  1493  		put(fundingFeesKey, uint64Bytes(md.FundingFeesPaid)).
  1494  		err()
  1495  }
  1496  
  1497  // UpdateOrderStatus sets the order status for an order.
  1498  func (db *BoltDB) UpdateOrderStatus(oid order.OrderID, status order.OrderStatus) error {
  1499  	return db.ordersUpdate(func(ob, archivedOB *bbolt.Bucket) error {
  1500  		oBkt, err := updateOrderBucket(ob, archivedOB, oid, status)
  1501  		if err != nil {
  1502  			return fmt.Errorf("UpdateOrderStatus: %w", err)
  1503  		}
  1504  		return oBkt.Put(statusKey, uint16Bytes(uint16(status)))
  1505  	})
  1506  }
  1507  
  1508  // LinkOrder sets the linked order.
  1509  func (db *BoltDB) LinkOrder(oid, linkedID order.OrderID) error {
  1510  	return db.ordersUpdate(func(ob, archivedOB *bbolt.Bucket) error {
  1511  		oBkt := ob.Bucket(oid[:])
  1512  		// If the order is not in the active bucket, check the archived
  1513  		// orders bucket.
  1514  		if oBkt == nil {
  1515  			oBkt = archivedOB.Bucket(oid[:])
  1516  		}
  1517  		if oBkt == nil {
  1518  			return fmt.Errorf("LinkOrder - order %s not found", oid)
  1519  		}
  1520  		linkedB := linkedID[:]
  1521  		if linkedID.IsZero() {
  1522  			linkedB = nil
  1523  		}
  1524  		return oBkt.Put(linkedKey, linkedB)
  1525  	})
  1526  }
  1527  
  1528  // ordersView is a convenience function for reading from the order buckets.
  1529  // Orders are spread over two buckets to make searching active orders faster.
  1530  // Any reads of the order buckets should be done in the same transaction, as
  1531  // orders may move from active to archived at any time.
  1532  func (db *BoltDB) ordersView(f func(ob, archivedOB *bbolt.Bucket) error) error {
  1533  	return db.View(func(tx *bbolt.Tx) error {
  1534  		ob := tx.Bucket(activeOrdersBucket)
  1535  		if ob == nil {
  1536  			return fmt.Errorf("failed to open %s bucket", string(activeOrdersBucket))
  1537  		}
  1538  		archivedOB := tx.Bucket(archivedOrdersBucket)
  1539  		if archivedOB == nil {
  1540  			return fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket))
  1541  		}
  1542  		return f(ob, archivedOB)
  1543  	})
  1544  }
  1545  
  1546  // ordersUpdate is a convenience function for updating the order buckets.
  1547  // Orders are spread over two buckets to make searching active orders faster.
  1548  // Any writes of the order buckets should be done in the same transaction to
  1549  // ensure that reads can be kept concurrent.
  1550  func (db *BoltDB) ordersUpdate(f func(ob, archivedOB *bbolt.Bucket) error) error {
  1551  	return db.Update(func(tx *bbolt.Tx) error {
  1552  		ob := tx.Bucket(activeOrdersBucket)
  1553  		if ob == nil {
  1554  			return fmt.Errorf("failed to open %s bucket", string(activeOrdersBucket))
  1555  		}
  1556  		archivedOB := tx.Bucket(archivedOrdersBucket)
  1557  		if archivedOB == nil {
  1558  			return fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket))
  1559  		}
  1560  		return f(ob, archivedOB)
  1561  	})
  1562  }
  1563  
  1564  // matchBucket gets a match's bucket in either the active or archived matches
  1565  // bucket. It will be created if it is not in either matches bucket. If an
  1566  // inactive match is found in the active bucket, it is moved to the archived
  1567  // matches bucket.
  1568  func matchBucket(mb, archivedMB *bbolt.Bucket, metaID []byte, active bool) (*bbolt.Bucket, error) {
  1569  	if active {
  1570  		// Active match would be in active bucket if it was previously inserted.
  1571  		return mb.CreateBucketIfNotExists(metaID) // might exist already
  1572  	}
  1573  
  1574  	// Archived match may currently be in either active or archived bucket.
  1575  	mBkt := mb.Bucket(metaID) // try the active bucket first
  1576  	if mBkt == nil {
  1577  		// It's not in the active bucket, check/create in archived.
  1578  		return archivedMB.CreateBucketIfNotExists(metaID) // might exist already
  1579  	}
  1580  
  1581  	// It's in the active bucket, but no longer active. Move it to the archived
  1582  	// bucket.
  1583  	newBkt, err := archivedMB.CreateBucketIfNotExists(metaID) // should not exist in both buckets though!
  1584  	if err != nil {
  1585  		return nil, fmt.Errorf("unable to create archived match bucket: %w", err)
  1586  	}
  1587  	// Assume the order bucket contains only values, no sub-buckets.
  1588  	if err := mBkt.ForEach(func(k, v []byte) error {
  1589  		return newBkt.Put(k, v)
  1590  	}); err != nil {
  1591  		return nil, fmt.Errorf("unable to copy active matches bucket: %w", err)
  1592  	}
  1593  	if err := mb.DeleteBucket(metaID); err != nil {
  1594  		return nil, fmt.Errorf("unable to delete active match bucket: %w", err)
  1595  	}
  1596  	return newBkt, nil
  1597  }
  1598  
  1599  // UpdateMatch updates the match information in the database. Any existing
  1600  // entry for the same match ID will be overwritten without indication.
  1601  func (db *BoltDB) UpdateMatch(m *dexdb.MetaMatch) error {
  1602  	match, md := m.UserMatch, m.MetaData
  1603  	if md.Quote == md.Base {
  1604  		return fmt.Errorf("quote and base asset cannot be the same")
  1605  	}
  1606  	if md.DEX == "" {
  1607  		return fmt.Errorf("empty DEX not allowed")
  1608  	}
  1609  	return db.matchesUpdate(func(mb, archivedMB *bbolt.Bucket) error {
  1610  		metaID := m.MatchOrderUniqueID()
  1611  		active := dexdb.MatchIsActive(m.UserMatch, &m.MetaData.Proof)
  1612  		mBkt, err := matchBucket(mb, archivedMB, metaID, active)
  1613  		if err != nil {
  1614  			return err
  1615  		}
  1616  
  1617  		return newBucketPutter(mBkt).
  1618  			put(baseKey, uint32Bytes(md.Base)).
  1619  			put(quoteKey, uint32Bytes(md.Quote)).
  1620  			put(statusKey, []byte{byte(match.Status)}).
  1621  			put(dexKey, []byte(md.DEX)).
  1622  			put(updateTimeKey, uint64Bytes(timeNow())).
  1623  			put(proofKey, md.Proof.Encode()).
  1624  			put(orderIDKey, match.OrderID[:]).
  1625  			put(matchIDKey, match.MatchID[:]).
  1626  			put(matchKey, order.EncodeMatch(match)).
  1627  			put(stampKey, uint64Bytes(md.Stamp)).
  1628  			err()
  1629  	})
  1630  }
  1631  
  1632  // ActiveMatches retrieves the matches that are in an active state, which is
  1633  // any match that is still active.
  1634  func (db *BoltDB) ActiveMatches() ([]*dexdb.MetaMatch, error) {
  1635  	return db.filteredMatches(
  1636  		func(mBkt *bbolt.Bucket) bool {
  1637  			return true // all matches in the bucket
  1638  		},
  1639  		true,  // don't bother with cancel matches that are never active
  1640  		false, // exclude archived matches
  1641  	)
  1642  }
  1643  
  1644  // DEXOrdersWithActiveMatches retrieves order IDs for any order that has active
  1645  // matches, regardless of whether the order itself is in an active state.
  1646  func (db *BoltDB) DEXOrdersWithActiveMatches(dex string) ([]order.OrderID, error) {
  1647  	dexB := []byte(dex)
  1648  	// For each match for this DEX, pick the active ones.
  1649  	idMap := make(map[order.OrderID]bool)
  1650  	err := db.matchesView(func(ob, _ *bbolt.Bucket) error { // only the active matches bucket is used
  1651  		return ob.ForEach(func(k, _ []byte) error {
  1652  			mBkt := ob.Bucket(k)
  1653  			if mBkt == nil {
  1654  				return fmt.Errorf("match %x bucket is not a bucket", k)
  1655  			}
  1656  			if !bytes.Equal(dexB, mBkt.Get(dexKey)) {
  1657  				return nil
  1658  			}
  1659  
  1660  			oidB := mBkt.Get(orderIDKey)
  1661  			var oid order.OrderID
  1662  			copy(oid[:], oidB)
  1663  			idMap[oid] = true
  1664  			return nil
  1665  		})
  1666  	})
  1667  	if err != nil {
  1668  		return nil, err
  1669  	}
  1670  	ids := make([]order.OrderID, 0, len(idMap))
  1671  	for id := range idMap {
  1672  		ids = append(ids, id)
  1673  	}
  1674  	return ids, nil
  1675  
  1676  }
  1677  
  1678  // MatchesForOrder retrieves the matches for the specified order ID.
  1679  func (db *BoltDB) MatchesForOrder(oid order.OrderID, excludeCancels bool) ([]*dexdb.MetaMatch, error) {
  1680  	oidB := oid[:]
  1681  	return db.filteredMatches(func(mBkt *bbolt.Bucket) bool {
  1682  		oid := mBkt.Get(orderIDKey)
  1683  		return bytes.Equal(oid, oidB)
  1684  	}, excludeCancels, true) // include archived matches
  1685  }
  1686  
  1687  // filteredMatches gets all matches that pass the provided filter function. Each
  1688  // match's bucket is provided to the filter, and a boolean true return value
  1689  // indicates the match should be decoded and returned. Matches with cancel
  1690  // orders may be excluded, a separate option so the filter function does not
  1691  // need to load and decode the matchKey value.
  1692  func (db *BoltDB) filteredMatches(filter func(*bbolt.Bucket) bool, excludeCancels, includeArchived bool) ([]*dexdb.MetaMatch, error) {
  1693  	var matches []*dexdb.MetaMatch
  1694  	return matches, db.matchesView(func(mb, archivedMB *bbolt.Bucket) error {
  1695  		buckets := []*bbolt.Bucket{mb}
  1696  		if includeArchived {
  1697  			buckets = append(buckets, archivedMB)
  1698  		}
  1699  		for _, master := range buckets {
  1700  			err := master.ForEach(func(k, _ []byte) error {
  1701  				mBkt := master.Bucket(k)
  1702  				if mBkt == nil {
  1703  					return fmt.Errorf("match %x bucket is not a bucket", k)
  1704  				}
  1705  				if !filter(mBkt) {
  1706  					return nil
  1707  				}
  1708  				match, err := loadMatchBucket(mBkt, excludeCancels)
  1709  				if err != nil {
  1710  					return fmt.Errorf("loading match %x bucket: %w", k, err)
  1711  				}
  1712  				if match != nil {
  1713  					matches = append(matches, match)
  1714  				}
  1715  				return nil
  1716  			})
  1717  			if err != nil {
  1718  				return err
  1719  			}
  1720  		}
  1721  		return nil
  1722  	})
  1723  }
  1724  
  1725  func loadMatchBucket(mBkt *bbolt.Bucket, excludeCancels bool) (*dexdb.MetaMatch, error) {
  1726  	var proof *dexdb.MatchProof
  1727  	matchB := getCopy(mBkt, matchKey)
  1728  	if matchB == nil {
  1729  		return nil, fmt.Errorf("nil match bytes")
  1730  	}
  1731  	match, matchVer, err := order.DecodeMatch(matchB)
  1732  	if err != nil {
  1733  		return nil, fmt.Errorf("error decoding match: %w", err)
  1734  	}
  1735  	if matchVer == 0 && match.Status == order.MatchComplete {
  1736  		// When v0 matches were written, there was no MatchConfirmed, so we will
  1737  		// "upgrade" this match on the fly to agree with MatchIsActive.
  1738  		match.Status = order.MatchConfirmed
  1739  	}
  1740  	// A cancel match for a maker (trade) order has an empty address.
  1741  	if excludeCancels && match.Address == "" {
  1742  		return nil, nil
  1743  	}
  1744  	proofB := getCopy(mBkt, proofKey)
  1745  	if len(proofB) == 0 {
  1746  		return nil, fmt.Errorf("empty proof")
  1747  	}
  1748  	proof, _, err = dexdb.DecodeMatchProof(proofB)
  1749  	if err != nil {
  1750  		return nil, fmt.Errorf("error decoding proof: %w", err)
  1751  	}
  1752  	// A cancel match for a taker (the cancel) order is complete with no
  1753  	// InitSig. Unfortunately, the trade Address was historically set.
  1754  	if excludeCancels && (len(proof.Auth.InitSig) == 0 && match.Status == order.MatchComplete) {
  1755  		return nil, nil
  1756  	}
  1757  	return &dexdb.MetaMatch{
  1758  		MetaData: &dexdb.MatchMetaData{
  1759  			Proof: *proof,
  1760  			DEX:   string(getCopy(mBkt, dexKey)),
  1761  			Base:  intCoder.Uint32(mBkt.Get(baseKey)),
  1762  			Quote: intCoder.Uint32(mBkt.Get(quoteKey)),
  1763  			Stamp: intCoder.Uint64(mBkt.Get(stampKey)),
  1764  		},
  1765  		UserMatch: match,
  1766  	}, nil
  1767  }
  1768  
  1769  // matchesView is a convenience function for reading from the match bucket.
  1770  func (db *BoltDB) matchesView(f func(mb, archivedMB *bbolt.Bucket) error) error {
  1771  	return db.View(func(tx *bbolt.Tx) error {
  1772  		mb := tx.Bucket(activeMatchesBucket)
  1773  		if mb == nil {
  1774  			return fmt.Errorf("failed to open %s bucket", string(activeMatchesBucket))
  1775  		}
  1776  		archivedMB := tx.Bucket(archivedMatchesBucket)
  1777  		if archivedMB == nil {
  1778  			return fmt.Errorf("failed to open %s bucket", string(archivedMatchesBucket))
  1779  		}
  1780  		return f(mb, archivedMB)
  1781  	})
  1782  }
  1783  
  1784  // matchesUpdate is a convenience function for updating the match bucket.
  1785  func (db *BoltDB) matchesUpdate(f func(mb, archivedMB *bbolt.Bucket) error) error {
  1786  	return db.Update(func(tx *bbolt.Tx) error {
  1787  		mb := tx.Bucket(activeMatchesBucket)
  1788  		if mb == nil {
  1789  			return fmt.Errorf("failed to open %s bucket", string(activeMatchesBucket))
  1790  		}
  1791  		archivedMB := tx.Bucket(archivedMatchesBucket)
  1792  		if archivedMB == nil {
  1793  			return fmt.Errorf("failed to open %s bucket", string(archivedMatchesBucket))
  1794  		}
  1795  		return f(mb, archivedMB)
  1796  	})
  1797  }
  1798  
  1799  // UpdateWallet adds a wallet to the database.
  1800  func (db *BoltDB) UpdateWallet(wallet *dexdb.Wallet) error {
  1801  	if wallet.Balance == nil {
  1802  		return fmt.Errorf("cannot UpdateWallet with nil Balance field")
  1803  	}
  1804  	return db.walletsUpdate(func(master *bbolt.Bucket) error {
  1805  		wBkt, err := master.CreateBucketIfNotExists(wallet.ID())
  1806  		if err != nil {
  1807  			return err
  1808  		}
  1809  		err = wBkt.Put(walletKey, wallet.Encode())
  1810  		if err != nil {
  1811  			return err
  1812  		}
  1813  		return wBkt.Put(balanceKey, wallet.Balance.Encode())
  1814  	})
  1815  }
  1816  
  1817  // SetWalletPassword set the encrypted password field for the wallet.
  1818  func (db *BoltDB) SetWalletPassword(wid []byte, newEncPW []byte) error {
  1819  	return db.walletsUpdate(func(master *bbolt.Bucket) error {
  1820  		wBkt := master.Bucket(wid)
  1821  		if wBkt == nil {
  1822  			return fmt.Errorf("wallet with ID is %x not known", wid)
  1823  		}
  1824  		b := getCopy(wBkt, walletKey)
  1825  		if b == nil {
  1826  			return fmt.Errorf("no wallet found in bucket")
  1827  		}
  1828  		wallet, err := dexdb.DecodeWallet(b)
  1829  		if err != nil {
  1830  			return err
  1831  		}
  1832  		wallet.EncryptedPW = make([]byte, len(newEncPW))
  1833  		copy(wallet.EncryptedPW, newEncPW)
  1834  		// No need to populate wallet.Balance since it's not part of the
  1835  		// serialization stored in the walletKey sub-bucket.
  1836  
  1837  		return wBkt.Put(walletKey, wallet.Encode())
  1838  	})
  1839  }
  1840  
  1841  // UpdateBalance updates balance in the wallet bucket.
  1842  func (db *BoltDB) UpdateBalance(wid []byte, bal *dexdb.Balance) error {
  1843  	return db.walletsUpdate(func(master *bbolt.Bucket) error {
  1844  		wBkt := master.Bucket(wid)
  1845  		if wBkt == nil {
  1846  			return fmt.Errorf("wallet %x bucket is not a bucket", wid)
  1847  		}
  1848  		return wBkt.Put(balanceKey, bal.Encode())
  1849  	})
  1850  }
  1851  
  1852  // UpdateWalletStatus updates a wallet's status.
  1853  func (db *BoltDB) UpdateWalletStatus(wid []byte, disable bool) error {
  1854  	return db.walletsUpdate(func(master *bbolt.Bucket) error {
  1855  		wBkt := master.Bucket(wid)
  1856  		if wBkt == nil {
  1857  			return fmt.Errorf("wallet %x bucket is not a bucket", wid)
  1858  		}
  1859  		if disable {
  1860  			return wBkt.Put(walletDisabledKey, encode.ByteTrue)
  1861  		}
  1862  		return wBkt.Put(walletDisabledKey, encode.ByteFalse)
  1863  	})
  1864  }
  1865  
  1866  // Wallets loads all wallets from the database.
  1867  func (db *BoltDB) Wallets() ([]*dexdb.Wallet, error) {
  1868  	var wallets []*dexdb.Wallet
  1869  	return wallets, db.walletsView(func(master *bbolt.Bucket) error {
  1870  		c := master.Cursor()
  1871  		// key, _ := c.First()
  1872  		for wid, _ := c.First(); wid != nil; wid, _ = c.Next() {
  1873  			w, err := makeWallet(master.Bucket(wid))
  1874  			if err != nil {
  1875  				return err
  1876  			}
  1877  			wallets = append(wallets, w)
  1878  		}
  1879  		return nil
  1880  	})
  1881  }
  1882  
  1883  // Wallet loads a single wallet from the database.
  1884  func (db *BoltDB) Wallet(wid []byte) (wallet *dexdb.Wallet, err error) {
  1885  	return wallet, db.walletsView(func(master *bbolt.Bucket) error {
  1886  		wallet, err = makeWallet(master.Bucket(wid))
  1887  		return err
  1888  	})
  1889  }
  1890  
  1891  func makeWallet(wBkt *bbolt.Bucket) (*dexdb.Wallet, error) {
  1892  	if wBkt == nil {
  1893  		return nil, fmt.Errorf("wallets bucket value not a nested bucket")
  1894  	}
  1895  	b := getCopy(wBkt, walletKey)
  1896  	if b == nil {
  1897  		return nil, fmt.Errorf("no wallet found in bucket")
  1898  	}
  1899  
  1900  	w, err := dexdb.DecodeWallet(b)
  1901  	if err != nil {
  1902  		return nil, fmt.Errorf("DecodeWallet error: %w", err)
  1903  	}
  1904  
  1905  	balB := getCopy(wBkt, balanceKey)
  1906  	if balB != nil {
  1907  		bal, err := dexdb.DecodeBalance(balB)
  1908  		if err != nil {
  1909  			return nil, fmt.Errorf("DecodeBalance error: %w", err)
  1910  		}
  1911  		w.Balance = bal
  1912  	}
  1913  
  1914  	statusB := wBkt.Get(walletDisabledKey)
  1915  	if statusB != nil {
  1916  		w.Disabled = bytes.Equal(statusB, encode.ByteTrue)
  1917  	}
  1918  	return w, nil
  1919  }
  1920  
  1921  // walletsView is a convenience function for reading from the wallets bucket.
  1922  func (db *BoltDB) walletsView(f bucketFunc) error {
  1923  	return db.withBucket(walletsBucket, db.View, f)
  1924  }
  1925  
  1926  // walletUpdate is a convenience function for updating the wallets bucket.
  1927  func (db *BoltDB) walletsUpdate(f bucketFunc) error {
  1928  	return db.withBucket(walletsBucket, db.Update, f)
  1929  }
  1930  
  1931  // SaveNotification saves the notification.
  1932  func (db *BoltDB) SaveNotification(note *dexdb.Notification) error {
  1933  	if note.Severeness < dexdb.Success {
  1934  		return fmt.Errorf("storage of notification with severity %s is forbidden", note.Severeness)
  1935  	}
  1936  	return db.notesUpdate(func(master *bbolt.Bucket) error {
  1937  		noteB := note.Encode()
  1938  		k := note.ID()
  1939  		noteBkt, err := master.CreateBucketIfNotExists(k)
  1940  		if err != nil {
  1941  			return err
  1942  		}
  1943  		err = noteBkt.Put(stampKey, uint64Bytes(note.TimeStamp))
  1944  		if err != nil {
  1945  			return err
  1946  		}
  1947  		err = noteBkt.Put(severityKey, []byte{byte(note.Severeness)})
  1948  		if err != nil {
  1949  			return err
  1950  		}
  1951  		return noteBkt.Put(noteKey, noteB)
  1952  	})
  1953  }
  1954  
  1955  // AckNotification sets the acknowledgement for a notification.
  1956  func (db *BoltDB) AckNotification(id []byte) error {
  1957  	return db.notesUpdate(func(master *bbolt.Bucket) error {
  1958  		noteBkt := master.Bucket(id)
  1959  		if noteBkt == nil {
  1960  			return fmt.Errorf("notification not found")
  1961  		}
  1962  		return noteBkt.Put(ackKey, byteTrue)
  1963  	})
  1964  }
  1965  
  1966  // NotificationsN reads out the N most recent notifications.
  1967  func (db *BoltDB) NotificationsN(n int) ([]*dexdb.Notification, error) {
  1968  	notes := make([]*dexdb.Notification, 0, n)
  1969  	return notes, db.notesUpdate(func(master *bbolt.Bucket) error {
  1970  		trios := newestBuckets([]*bbolt.Bucket{master}, n, stampKey, nil)
  1971  		for _, trio := range trios {
  1972  			note, err := dexdb.DecodeNotification(getCopy(trio.b, noteKey))
  1973  			if err != nil {
  1974  				return err
  1975  			}
  1976  			note.Ack = bEqual(trio.b.Get(ackKey), byteTrue)
  1977  			note.Id = note.ID()
  1978  			if !bytes.Equal(note.Id, trio.k) {
  1979  				// This notification was initially stored when the serialization
  1980  				// and thus note ID did not include the TopicID. Ignore it, and
  1981  				// flag it acknowledged so we don't have to hear about it again.
  1982  				if !note.Ack {
  1983  					db.log.Tracef("Ignoring stored note with bad key: %x != %x \"%s\"",
  1984  						[]byte(note.Id), trio.k, note.String())
  1985  					_ = trio.b.Put(ackKey, byteTrue)
  1986  				}
  1987  				continue
  1988  			}
  1989  			notes = append(notes, note)
  1990  		}
  1991  		return nil
  1992  	})
  1993  }
  1994  
  1995  // notesView is a convenience function to read from the notifications bucket.
  1996  func (db *BoltDB) notesView(f bucketFunc) error {
  1997  	return db.withBucket(notesBucket, db.View, f)
  1998  }
  1999  
  2000  // notesUpdate is a convenience function for updating the notifications bucket.
  2001  func (db *BoltDB) notesUpdate(f bucketFunc) error {
  2002  	return db.withBucket(notesBucket, db.Update, f)
  2003  }
  2004  
  2005  // SavePokes saves a slice of notifications, overwriting any previously saved
  2006  // slice.
  2007  func (db *BoltDB) SavePokes(pokes []*dexdb.Notification) error {
  2008  	// Just save it as JSON.
  2009  	b, err := json.Marshal(pokes)
  2010  	if err != nil {
  2011  		return fmt.Errorf("JSON marshal error: %w", err)
  2012  	}
  2013  	return db.withBucket(pokesBucket, db.Update, func(bkt *bbolt.Bucket) error {
  2014  		return bkt.Put(pokesKey, b)
  2015  	})
  2016  }
  2017  
  2018  // LoadPokes loads the slice of notifications last saved with SavePokes. The
  2019  // loaded pokes are deleted from the database.
  2020  func (db *BoltDB) LoadPokes() (pokes []*dexdb.Notification, _ error) {
  2021  	return pokes, db.withBucket(pokesBucket, db.Update, func(bkt *bbolt.Bucket) error {
  2022  		b := bkt.Get(pokesKey)
  2023  		if len(b) == 0 { // None saved
  2024  			return nil
  2025  		}
  2026  		if err := json.Unmarshal(b, &pokes); err != nil {
  2027  			return err
  2028  		}
  2029  		return bkt.Delete(pokesKey)
  2030  	})
  2031  }
  2032  
  2033  // newest buckets gets the nested buckets with the hightest timestamp from the
  2034  // specified master buckets. The nested bucket should have an encoded uint64 at
  2035  // the timeKey. An optional filter function can be used to reject buckets.
  2036  func newestBuckets(buckets []*bbolt.Bucket, n int, timeKey []byte, filter func([]byte, *bbolt.Bucket) bool) []*keyTimeTrio {
  2037  	idx := newTimeIndexNewest(n)
  2038  	for _, master := range buckets {
  2039  		master.ForEach(func(k, _ []byte) error {
  2040  			bkt := master.Bucket(k)
  2041  			stamp := intCoder.Uint64(bkt.Get(timeKey))
  2042  			if filter == nil || filter(k, bkt) {
  2043  				idx.add(stamp, k, bkt)
  2044  			}
  2045  			return nil
  2046  		})
  2047  	}
  2048  	return idx.trios
  2049  }
  2050  
  2051  // makeTopLevelBuckets creates a top-level bucket for each of the provided keys,
  2052  // if the bucket doesn't already exist.
  2053  func (db *BoltDB) makeTopLevelBuckets(buckets [][]byte) error {
  2054  	return db.Update(func(tx *bbolt.Tx) error {
  2055  		for _, bucket := range buckets {
  2056  			_, err := tx.CreateBucketIfNotExists(bucket)
  2057  			if err != nil {
  2058  				return err
  2059  			}
  2060  		}
  2061  
  2062  		return nil
  2063  	})
  2064  }
  2065  
  2066  // withBucket is a creates a view into a (probably nested) bucket. The viewer
  2067  // can be read-only (db.View), or read-write (db.Update). The provided
  2068  // bucketFunc will be called with the requested bucket as its only argument.
  2069  func (db *BoltDB) withBucket(bkt []byte, viewer txFunc, f bucketFunc) error {
  2070  	return viewer(func(tx *bbolt.Tx) error {
  2071  		bucket := tx.Bucket(bkt)
  2072  		if bucket == nil {
  2073  			return fmt.Errorf("failed to open %s bucket", string(bkt))
  2074  		}
  2075  		return f(bucket)
  2076  	})
  2077  }
  2078  
  2079  func ovrFlag(overwrite bool) int {
  2080  	if overwrite {
  2081  		return os.O_TRUNC
  2082  	}
  2083  	return os.O_EXCL
  2084  }
  2085  
  2086  // compact writes a compacted copy of the DB into the specified destination
  2087  // file. This function should be called from BackupTo to validate the
  2088  // destination file and create any needed parent folders.
  2089  func (db *BoltDB) compact(dst string, overwrite bool) error {
  2090  	opts := bbolt.Options{
  2091  		OpenFile: func(path string, _ int, mode os.FileMode) (*os.File, error) {
  2092  			return os.OpenFile(path, os.O_RDWR|os.O_CREATE|ovrFlag(overwrite), mode)
  2093  		},
  2094  	}
  2095  	newDB, err := bbolt.Open(dst, 0600, &opts)
  2096  	if err != nil {
  2097  		return fmt.Errorf("unable to compact database: %w", err)
  2098  	}
  2099  
  2100  	const txMaxSize = 1 << 19 // 512 KiB
  2101  	err = bbolt.Compact(newDB, db.DB, txMaxSize)
  2102  	if err != nil {
  2103  		_ = newDB.Close()
  2104  		return fmt.Errorf("unable to compact database: %w", err)
  2105  	}
  2106  	return newDB.Close()
  2107  }
  2108  
  2109  // backup writes a direct copy of the DB into the specified destination file.
  2110  // This function should be called from BackupTo to validate the destination file
  2111  // and create any needed parent folders.
  2112  func (db *BoltDB) backup(dst string, overwrite bool) error {
  2113  	// Just copy. This is like tx.CopyFile but with the overwrite flag set
  2114  	// as specified.
  2115  	f, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE|ovrFlag(overwrite), 0600)
  2116  	if err != nil {
  2117  		return err
  2118  	}
  2119  	defer f.Close()
  2120  
  2121  	err = db.View(func(tx *bbolt.Tx) error {
  2122  		_, err = tx.WriteTo(f)
  2123  		return err
  2124  	})
  2125  	if err != nil {
  2126  		return err
  2127  	}
  2128  	return f.Sync()
  2129  }
  2130  
  2131  // BackupTo makes a copy of the database to the specified file, optionally
  2132  // overwriting and compacting the DB.
  2133  func (db *BoltDB) BackupTo(dst string, overwrite, compact bool) error {
  2134  	// If relative path, use current db path.
  2135  	var dir string
  2136  	if !filepath.IsAbs(dst) {
  2137  		dir = filepath.Dir(db.Path())
  2138  		dst = filepath.Join(dir, dst)
  2139  	}
  2140  	dst = filepath.Clean(dst)
  2141  	dir = filepath.Dir(dst)
  2142  	if dst == filepath.Clean(db.Path()) {
  2143  		return errors.New("destination is the active DB")
  2144  	}
  2145  
  2146  	// Make the parent folder if it does not exists.
  2147  	if _, err := os.Stat(dir); errors.Is(err, fs.ErrNotExist) {
  2148  		err = os.MkdirAll(dir, 0700)
  2149  		if err != nil {
  2150  			return fmt.Errorf("unable to create backup directory: %w", err)
  2151  		}
  2152  	}
  2153  
  2154  	if compact {
  2155  		return db.compact(dst, overwrite)
  2156  	}
  2157  
  2158  	return db.backup(dst, overwrite)
  2159  }
  2160  
  2161  // Backup makes a copy of the database in the "backup" folder, overwriting any
  2162  // existing backup.
  2163  func (db *BoltDB) Backup() error {
  2164  	dir, file := filepath.Split(db.Path())
  2165  	return db.BackupTo(filepath.Join(dir, backupDir, file), true, false)
  2166  }
  2167  
  2168  // bucketPutter enables chained calls to (*bbolt.Bucket).Put with error
  2169  // deferment.
  2170  type bucketPutter struct {
  2171  	bucket *bbolt.Bucket
  2172  	putErr error
  2173  }
  2174  
  2175  // newBucketPutter is a constructor for a bucketPutter.
  2176  func newBucketPutter(bkt *bbolt.Bucket) *bucketPutter {
  2177  	return &bucketPutter{bucket: bkt}
  2178  }
  2179  
  2180  // put calls Put on the underlying bucket. If an error has been encountered in a
  2181  // previous call to push, nothing is done. put enables the *bucketPutter to
  2182  // enable chaining.
  2183  func (bp *bucketPutter) put(k, v []byte) *bucketPutter {
  2184  	if bp.putErr != nil {
  2185  		return bp
  2186  	}
  2187  	bp.putErr = bp.bucket.Put(k, v)
  2188  	return bp
  2189  }
  2190  
  2191  // Return any push error encountered.
  2192  func (bp *bucketPutter) err() error {
  2193  	return bp.putErr
  2194  }
  2195  
  2196  // keyTimeTrio is used to build an on-the-fly time-sorted index.
  2197  type keyTimeTrio struct {
  2198  	k []byte
  2199  	t uint64
  2200  	b *bbolt.Bucket
  2201  }
  2202  
  2203  // timeIndexNewest is a struct used to build an index of sorted keyTimeTrios.
  2204  // The index can have a maximum capacity. If the capacity is set to zero, the
  2205  // index size is unlimited.
  2206  type timeIndexNewest struct {
  2207  	trios []*keyTimeTrio
  2208  	cap   int
  2209  }
  2210  
  2211  // Create a new *timeIndexNewest, with the specified capacity.
  2212  func newTimeIndexNewest(n int) *timeIndexNewest {
  2213  	return &timeIndexNewest{
  2214  		trios: make([]*keyTimeTrio, 0, n),
  2215  		cap:   n,
  2216  	}
  2217  }
  2218  
  2219  // Conditionally add a time-key trio to the index. The trio will only be added
  2220  // if the timeIndexNewest is under capacity and the time t is larger than the
  2221  // oldest trio's time.
  2222  func (idx *timeIndexNewest) add(t uint64, k []byte, b *bbolt.Bucket) {
  2223  	count := len(idx.trios)
  2224  	if idx.cap == 0 || count < idx.cap {
  2225  		idx.trios = append(idx.trios, &keyTimeTrio{
  2226  			// Need to make a copy, and []byte(k) upsets the linter.
  2227  			k: append([]byte(nil), k...),
  2228  			t: t,
  2229  			b: b,
  2230  		})
  2231  	} else {
  2232  		// non-zero length, at capacity.
  2233  		if t <= idx.trios[count-1].t {
  2234  			// Too old. Discard.
  2235  			return
  2236  		}
  2237  		idx.trios[count-1] = &keyTimeTrio{
  2238  			k: append([]byte(nil), k...),
  2239  			t: t,
  2240  			b: b,
  2241  		}
  2242  	}
  2243  	sort.Slice(idx.trios, func(i, j int) bool {
  2244  		// newest (highest t) first, or by lexicographically by key if the times
  2245  		// are equal.
  2246  		t1, t2 := idx.trios[i].t, idx.trios[j].t
  2247  		return t1 > t2 || (t1 == t2 && bytes.Compare(idx.trios[i].k, idx.trios[j].k) == 1)
  2248  	})
  2249  }
  2250  
  2251  // DeleteInactiveOrders deletes orders that are no longer needed for normal
  2252  // operations. Optionally accepts a time to delete orders with a later time
  2253  // stamp. Accepts an optional function to perform on deleted orders.
  2254  func (db *BoltDB) DeleteInactiveOrders(ctx context.Context, olderThan *time.Time,
  2255  	perOrderFn func(ords *dexdb.MetaOrder) error) (int, error) {
  2256  	const batchSize = 1000
  2257  	var olderThanB []byte
  2258  	if olderThan != nil && !olderThan.IsZero() {
  2259  		olderThanB = uint64Bytes(uint64(olderThan.UnixMilli()))
  2260  	} else {
  2261  		olderThanB = uint64Bytes(timeNow())
  2262  	}
  2263  
  2264  	activeMatchOrders := make(map[order.OrderID]struct{})
  2265  	if err := db.View(func(tx *bbolt.Tx) error {
  2266  		// Some archived orders may still be needed for active matches.
  2267  		// Put those order id's in a map to prevent deletion.
  2268  		amb := tx.Bucket(activeMatchesBucket)
  2269  		if err := amb.ForEach(func(k, _ []byte) error {
  2270  			mBkt := amb.Bucket(k)
  2271  			if mBkt == nil {
  2272  				return fmt.Errorf("match %x bucket is not a bucket", k)
  2273  			}
  2274  			oidB := mBkt.Get(orderIDKey)
  2275  			var oid order.OrderID
  2276  			copy(oid[:], oidB)
  2277  			activeMatchOrders[oid] = struct{}{}
  2278  			return nil
  2279  		}); err != nil {
  2280  			return fmt.Errorf("unable to get active matches: %v", err)
  2281  		}
  2282  		return nil
  2283  	}); err != nil {
  2284  		return 0, err
  2285  	}
  2286  
  2287  	// Get the keys of every archived order.
  2288  	var keys [][]byte
  2289  	if err := db.View(func(tx *bbolt.Tx) error {
  2290  		archivedOB := tx.Bucket(archivedOrdersBucket)
  2291  		if archivedOB == nil {
  2292  			return fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket))
  2293  		}
  2294  		archivedOB.ForEach(func(k, _ []byte) error {
  2295  			keys = append(keys, bytes.Clone(k))
  2296  			return nil
  2297  		})
  2298  		return nil
  2299  	}); err != nil {
  2300  		return 0, fmt.Errorf("unable to get archived order keys: %v", err)
  2301  	}
  2302  
  2303  	nDeletedOrders := 0
  2304  	start := time.Now()
  2305  	// Check orders in batches to prevent any single db transaction from
  2306  	// becoming too large.
  2307  	for i := 0; i < len(keys); i += batchSize {
  2308  		if err := ctx.Err(); err != nil {
  2309  			return 0, err
  2310  		}
  2311  
  2312  		// Check if this is the last batch.
  2313  		end := i + batchSize
  2314  		if end > len(keys) {
  2315  			end = len(keys)
  2316  		}
  2317  
  2318  		nDeletedBatch := 0
  2319  		err := db.Update(func(tx *bbolt.Tx) error {
  2320  			archivedOB := tx.Bucket(archivedOrdersBucket)
  2321  			if archivedOB == nil {
  2322  				return fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket))
  2323  			}
  2324  			for j := i; j < end; j++ {
  2325  				key := keys[j]
  2326  				oBkt := archivedOB.Bucket(key)
  2327  
  2328  				var oid order.OrderID
  2329  				copy(oid[:], key)
  2330  
  2331  				// Don't delete this order if it is still active.
  2332  				if order.OrderStatus(intCoder.Uint16(oBkt.Get(statusKey))).IsActive() {
  2333  					db.log.Warnf("active order %v found in inactive bucket", oid)
  2334  					continue
  2335  				}
  2336  
  2337  				// Don't delete this order if it still has active matches.
  2338  				if _, has := activeMatchOrders[oid]; has {
  2339  					continue
  2340  				}
  2341  
  2342  				// Don't delete this order if it is too new.
  2343  				timeB := oBkt.Get(updateTimeKey)
  2344  				if bytes.Compare(timeB, olderThanB) > 0 {
  2345  					continue
  2346  				}
  2347  
  2348  				// Proceed with deletion.
  2349  				o, err := decodeOrderBucket(key, oBkt)
  2350  				if err != nil {
  2351  					return fmt.Errorf("failed to decode order bucket: %v", err)
  2352  				}
  2353  				if err := archivedOB.DeleteBucket(key); err != nil {
  2354  					return fmt.Errorf("failed to delete order bucket: %v", err)
  2355  				}
  2356  				if perOrderFn != nil {
  2357  					if err := perOrderFn(o); err != nil {
  2358  						return fmt.Errorf("problem performing batch function: %v", err)
  2359  					}
  2360  				}
  2361  				nDeletedBatch++
  2362  			}
  2363  			nDeletedOrders += nDeletedBatch
  2364  			return nil
  2365  		})
  2366  		if err != nil {
  2367  			if perOrderFn != nil && nDeletedBatch != 0 {
  2368  				db.log.Warnf("%d orders reported as deleted have been rolled back due to error.", nDeletedBatch)
  2369  			}
  2370  			return 0, fmt.Errorf("unable to delete orders: %v", err)
  2371  		}
  2372  	}
  2373  
  2374  	db.log.Infof("Deleted %d archived orders from the database in %v",
  2375  		nDeletedOrders, time.Since(start))
  2376  
  2377  	return nDeletedOrders, nil
  2378  }
  2379  
  2380  // orderSide Returns wether the order was for buying or selling the asset.
  2381  func orderSide(tx *bbolt.Tx, oid order.OrderID) (sell bool, err error) {
  2382  	oidB := oid[:]
  2383  	ob := tx.Bucket(activeOrdersBucket)
  2384  	if ob == nil {
  2385  		return false, fmt.Errorf("failed to open %s bucket", string(activeOrdersBucket))
  2386  	}
  2387  	oBkt := ob.Bucket(oidB)
  2388  	// If the order is not in the active bucket, check the archived bucket.
  2389  	if oBkt == nil {
  2390  		archivedOB := tx.Bucket(archivedOrdersBucket)
  2391  		if archivedOB == nil {
  2392  			return false, fmt.Errorf("failed to open %s bucket", string(archivedOrdersBucket))
  2393  		}
  2394  		oBkt = archivedOB.Bucket(oidB)
  2395  	}
  2396  	if oBkt == nil {
  2397  		return false, fmt.Errorf("order %s not found", oid)
  2398  	}
  2399  	orderB := getCopy(oBkt, orderKey)
  2400  	if orderB == nil {
  2401  		return false, fmt.Errorf("nil order bytes for order %x", oid)
  2402  	}
  2403  	ord, err := order.DecodeOrder(orderB)
  2404  	if err != nil {
  2405  		return false, fmt.Errorf("error decoding order %x: %w", oid, err)
  2406  	}
  2407  	// Cancel orders have no side.
  2408  	if ord.Type() == order.CancelOrderType {
  2409  		return false, nil
  2410  	}
  2411  	sell = ord.Trade().Sell
  2412  	return
  2413  }
  2414  
  2415  // DeleteInactiveMatches deletes matches that are no longer needed for normal
  2416  // operations. Optionally accepts a time to delete matches with a later time
  2417  // stamp. Accepts an optional function to perform on deleted matches.
  2418  func (db *BoltDB) DeleteInactiveMatches(ctx context.Context, olderThan *time.Time,
  2419  	perMatchFn func(mtch *dexdb.MetaMatch, isSell bool) error) (int, error) {
  2420  	const batchSize = 1000
  2421  	var olderThanB []byte
  2422  	if olderThan != nil && !olderThan.IsZero() {
  2423  		olderThanB = uint64Bytes(uint64(olderThan.UnixMilli()))
  2424  	} else {
  2425  		olderThanB = uint64Bytes(timeNow())
  2426  	}
  2427  
  2428  	activeOrders := make(map[order.OrderID]struct{})
  2429  	if err := db.View(func(tx *bbolt.Tx) error {
  2430  		// Some archived matches still have active orders. Put those
  2431  		// order id's in a map to prevent deletion just in case they
  2432  		// are needed again.
  2433  		aob := tx.Bucket(activeOrdersBucket)
  2434  		if err := aob.ForEach(func(k, _ []byte) error {
  2435  			var oid order.OrderID
  2436  			copy(oid[:], k)
  2437  			activeOrders[oid] = struct{}{}
  2438  			return nil
  2439  		}); err != nil {
  2440  			return fmt.Errorf("unable to get active orders: %v", err)
  2441  		}
  2442  		return nil
  2443  	}); err != nil {
  2444  		return 0, err
  2445  	}
  2446  
  2447  	// Get the keys of every archived match.
  2448  	var keys [][]byte
  2449  	if err := db.View(func(tx *bbolt.Tx) error {
  2450  		archivedMB := tx.Bucket(archivedMatchesBucket)
  2451  		if archivedMB == nil {
  2452  			return fmt.Errorf("failed to open %s bucket", string(archivedMatchesBucket))
  2453  		}
  2454  		archivedMB.ForEach(func(k, _ []byte) error {
  2455  			keys = append(keys, bytes.Clone(k))
  2456  			return nil
  2457  		})
  2458  		return nil
  2459  	}); err != nil {
  2460  		return 0, fmt.Errorf("unable to get archived match keys: %v", err)
  2461  	}
  2462  
  2463  	nDeletedMatches := 0
  2464  	start := time.Now()
  2465  	// Check matches in batches to prevent any single db transaction from
  2466  	// becoming too large.
  2467  	for i := 0; i < len(keys); i += batchSize {
  2468  		if err := ctx.Err(); err != nil {
  2469  			return 0, err
  2470  		}
  2471  
  2472  		// Check if this is the last batch.
  2473  		end := i + batchSize
  2474  		if end > len(keys) {
  2475  			end = len(keys)
  2476  		}
  2477  
  2478  		nDeletedBatch := 0
  2479  		if err := db.Update(func(tx *bbolt.Tx) error {
  2480  			archivedMB := tx.Bucket(archivedMatchesBucket)
  2481  			if archivedMB == nil {
  2482  				return fmt.Errorf("failed to open %s bucket", string(archivedMatchesBucket))
  2483  			}
  2484  			for j := i; j < end; j++ {
  2485  				key := keys[j]
  2486  				mBkt := archivedMB.Bucket(key)
  2487  
  2488  				oidB := mBkt.Get(orderIDKey)
  2489  				var oid order.OrderID
  2490  				copy(oid[:], oidB)
  2491  
  2492  				// Don't delete this match if it still has active orders.
  2493  				if _, has := activeOrders[oid]; has {
  2494  					continue
  2495  				}
  2496  
  2497  				// Don't delete this match if it is too new.
  2498  				timeB := mBkt.Get(stampKey)
  2499  				if bytes.Compare(timeB, olderThanB) > 0 {
  2500  					continue
  2501  				}
  2502  
  2503  				// Proceed with deletion.
  2504  				m, err := loadMatchBucket(mBkt, false)
  2505  				if err != nil {
  2506  					return fmt.Errorf("failed to load match bucket: %v", err)
  2507  				}
  2508  				if err := archivedMB.DeleteBucket(key); err != nil {
  2509  					return fmt.Errorf("failed to delete match bucket: %v", err)
  2510  				}
  2511  				if perMatchFn != nil {
  2512  					isSell, err := orderSide(tx, m.OrderID)
  2513  					if err != nil {
  2514  						return fmt.Errorf("problem getting order side for order %v: %v", m.OrderID, err)
  2515  					}
  2516  					if err := perMatchFn(m, isSell); err != nil {
  2517  						return fmt.Errorf("problem performing batch function: %v", err)
  2518  					}
  2519  				}
  2520  				nDeletedBatch++
  2521  			}
  2522  			nDeletedMatches += nDeletedBatch
  2523  
  2524  			return nil
  2525  		}); err != nil {
  2526  			if perMatchFn != nil && nDeletedBatch != 0 {
  2527  				db.log.Warnf("%d matches reported as deleted have been rolled back due to error.", nDeletedBatch)
  2528  			}
  2529  			return 0, fmt.Errorf("unable to delete matches: %v", err)
  2530  		}
  2531  	}
  2532  
  2533  	db.log.Infof("Deleted %d archived matches from the database in %v",
  2534  		nDeletedMatches, time.Since(start))
  2535  
  2536  	return nDeletedMatches, nil
  2537  }
  2538  
  2539  // SaveDisabledRateSources updates disabled fiat rate sources.
  2540  func (db *BoltDB) SaveDisabledRateSources(disabledSources []string) error {
  2541  	return db.Update(func(tx *bbolt.Tx) error {
  2542  		bkt := tx.Bucket(appBucket)
  2543  		if bkt == nil {
  2544  			return fmt.Errorf("failed to open %s bucket", string(appBucket))
  2545  		}
  2546  		return bkt.Put(disabledRateSourceKey, []byte(strings.Join(disabledSources, ",")))
  2547  	})
  2548  }
  2549  
  2550  // DisabledRateSources retrieves a map of disabled fiat rate sources.
  2551  func (db *BoltDB) DisabledRateSources() (disabledSources []string, err error) {
  2552  	return disabledSources, db.View(func(tx *bbolt.Tx) error {
  2553  		bkt := tx.Bucket(appBucket)
  2554  		if bkt == nil {
  2555  			return fmt.Errorf("no %s bucket", string(appBucket))
  2556  		}
  2557  
  2558  		disabledString := string(bkt.Get(disabledRateSourceKey))
  2559  		if disabledString == "" {
  2560  			return nil
  2561  		}
  2562  
  2563  		disabled := strings.Split(disabledString, ",")
  2564  		disabledSources = make([]string, 0, len(disabled))
  2565  		for _, token := range disabled {
  2566  			if token != "" {
  2567  				disabledSources = append(disabledSources, token)
  2568  			}
  2569  		}
  2570  		return nil
  2571  	})
  2572  }
  2573  
  2574  // SetLanguage stores the language.
  2575  func (db *BoltDB) SetLanguage(lang string) error {
  2576  	return db.Update(func(dbTx *bbolt.Tx) error {
  2577  		bkt := dbTx.Bucket(appBucket)
  2578  		if bkt == nil {
  2579  			return fmt.Errorf("app bucket not found")
  2580  		}
  2581  		return bkt.Put(langKey, []byte(lang))
  2582  	})
  2583  }
  2584  
  2585  // Language retrieves the language stored with SetLanguage. If no language
  2586  // has been stored, an empty string is returned without an error.
  2587  func (db *BoltDB) Language() (lang string, _ error) {
  2588  	return lang, db.View(func(dbTx *bbolt.Tx) error {
  2589  		bkt := dbTx.Bucket(appBucket)
  2590  		if bkt != nil {
  2591  			lang = string(bkt.Get(langKey))
  2592  		}
  2593  		return nil
  2594  	})
  2595  }
  2596  
  2597  // timeNow is the current unix timestamp in milliseconds.
  2598  func timeNow() uint64 {
  2599  	return uint64(time.Now().UnixMilli())
  2600  }
  2601  
  2602  // A couple of common bbolt functions.
  2603  type bucketFunc func(*bbolt.Bucket) error
  2604  type txFunc func(func(*bbolt.Tx) error) error