decred.org/dcrwallet/v3@v3.1.0/wallet/udb/upgrades.go (about)

     1  // Copyright (c) 2017-2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package udb
     6  
     7  import (
     8  	"context"
     9  	"crypto/sha256"
    10  
    11  	"decred.org/dcrwallet/v3/errors"
    12  	"decred.org/dcrwallet/v3/internal/compat"
    13  	"decred.org/dcrwallet/v3/wallet/internal/snacl"
    14  	"decred.org/dcrwallet/v3/wallet/walletdb"
    15  	"github.com/decred/dcrd/blockchain/stake/v5"
    16  	"github.com/decred/dcrd/chaincfg/chainhash"
    17  	"github.com/decred/dcrd/chaincfg/v3"
    18  	"github.com/decred/dcrd/dcrutil/v4"
    19  	"github.com/decred/dcrd/gcs/v4/blockcf2"
    20  	"github.com/decred/dcrd/hdkeychain/v3"
    21  	"github.com/decred/dcrd/txscript/v4/stdscript"
    22  	"github.com/decred/dcrd/wire"
    23  )
    24  
    25  // Note: all manager functions always use the latest version of the database.
    26  // Therefore it is extremely important when adding database upgrade code to
    27  // never call any methods of the managers and instead only use the db primitives
    28  // with the correct version passed as parameters.
    29  
    30  const (
    31  	initialVersion = 1
    32  
    33  	// lastUsedAddressIndexVersion is the second version of the database.  It
    34  	// adds indexes for the last used address of BIP0044 accounts, removes the
    35  	// next to use address indexes, removes all references to address pools, and
    36  	// removes all per-address usage tracking.
    37  	//
    38  	// See lastUsedAddressIndexUpgrade for the code that implements the upgrade
    39  	// path.
    40  	lastUsedAddressIndexVersion = 2
    41  
    42  	// votingPreferencesVersion is the third version of the database.  It
    43  	// removes all per-ticket vote bits, replacing them with vote preferences
    44  	// for choices on individual agendas from the current stake version.
    45  	votingPreferencesVersion = 3
    46  
    47  	// noEncryptedSeedVersion is the fourth version of the database.  It removes
    48  	// the encrypted seed that earlier versions may have saved in the database
    49  	// (or more commonly, encrypted zeros on mainnet wallets).
    50  	noEncryptedSeedVersion = 4
    51  
    52  	// lastReturnedAddressVersion is the fifth version of the database.  It adds
    53  	// additional indexes to each BIP0044 account row that keep track of the
    54  	// index of the last returned child address in the internal and external
    55  	// account branches.  This is used to prevent returning identical addresses
    56  	// across application restarts.
    57  	lastReturnedAddressVersion = 5
    58  
    59  	// ticketBucketVersion is the sixth version of the database.  It adds a
    60  	// bucket for recording the hashes of all tickets and provides additional
    61  	// APIs to check the status of tickets and whether they are spent by a vote
    62  	// or revocation.
    63  	ticketBucketVersion = 6
    64  
    65  	// slip0044CoinTypeVersion is the seventh version of the database.  It
    66  	// introduces the possibility of the BIP0044 coin type key being either the
    67  	// legacy coin type used by earlier versions of the wallet, or the coin type
    68  	// assigned to Decred in SLIP0044.  The upgrade does not add or remove any
    69  	// required keys (the upgrade is done in a backwards-compatible way) but the
    70  	// database version is bumped to prevent older software from assuming that
    71  	// coin type 20 exists (the upgrade is not forwards-compatible).
    72  	slip0044CoinTypeVersion = 7
    73  
    74  	// hasExpiryVersion is the eight version of the database. It adds the
    75  	// hasExpiry field to the credit struct, adds fetchRawCreditHasExpiry
    76  	// helper func and extends sstxchange type utxo checks to only make sstchange
    77  	// with expiries set available to spend after coinbase maturity (16 blocks).
    78  	hasExpiryVersion = 8
    79  
    80  	// hasExpiryFixedVersion is the ninth version of the database.  It corrects
    81  	// the previous upgrade by writing the has expiry bit to an unused bit flag
    82  	// rather than in the stake flags and fixes various UTXO selection issues
    83  	// caused by misinterpreting ticket outputs as spendable by regular
    84  	// transactions.
    85  	hasExpiryFixedVersion = 9
    86  
    87  	// cfVersion is the tenth version of the database.  It adds a bucket to
    88  	// store compact filters, which are required for Decred's SPV
    89  	// implementation, and a txmgr namespace root key which tracks whether all
    90  	// main chain compact filters were saved.  This version does not begin to
    91  	// save compact filter headers, since the SPV implementation is expected to
    92  	// use header commitments in a later release for validation.
    93  	cfVersion = 10
    94  
    95  	// lastProcessedTxsBlockVersion is the eleventh version of the database.  It
    96  	// adds a txmgr namespace root key which records the final hash of all
    97  	// blocks since the genesis block which have been processed for relevant
    98  	// transactions.  This is required to distinguish between the main chain tip
    99  	// (which is advanced during headers fetch) and the point at which a startup
   100  	// rescan should occur.  During upgrade, the current tip block is recorded
   101  	// as this block to avoid an additional or extra long rescan from occurring
   102  	// from properly-synced wallets.
   103  	lastProcessedTxsBlockVersion = 11
   104  
   105  	// ticketCommitmentsVersion the twelfth version of the database. It adds
   106  	// the ticketCommitment bucket to the txmgr namespace. This bucket is meant
   107  	// to track outstanding ticket commitment outputs for the purposes of
   108  	// correct balance calculation: it allows non-voting wallets (eg: funding
   109  	// wallets in solo-voting setups or non-voter participants of split tickets)
   110  	// to track their proportional locked funds. In standard (single-voter) VSP
   111  	// setups, it also allows the wallet to discount the pool fee for correct
   112  	// accounting of total locked funds.
   113  	ticketCommitmentsVersion = 12
   114  
   115  	// importedXpubAccountVersion is the thirteenth version of the
   116  	// database.  It introduces the ability to import and track child
   117  	// indexes of arbitrary HD extended public keys.  Imported xpub accounts
   118  	// are associated with a uint32 value in the upper range (above
   119  	// ImportedAddrAccount).  The upgrade does not add or remove any
   120  	// required keys (the upgrade is done in a backwards-compatible way) but
   121  	// the database version is bumped to prevent older software from using
   122  	// an upgraded database with account identifiers that would induce
   123  	// panics.
   124  	importedXpubAccountVersion = 13
   125  
   126  	// unencryptedRedeemScriptsVersion is the 14th version of the database.
   127  	// The goal of this upgrade is to make it possible to read P2SH redeem
   128  	// scripts through the address manager without needing the wallet to be
   129  	// unlocked.  This is made possible by relying on the fact that since
   130  	// mainnet launch, dcrwallet has always recorded imported P2SH redeem
   131  	// scripts both encrypted in the address manager bucket, and unencrypted
   132  	// in the transaction store buckets.  During this upgrade, a check is
   133  	// performed ensuring that no unencrypted scripts are missing.
   134  	// Unencrypted scripts are read from the transaction store buckets and
   135  	// the address manager values are rewritten with these plaintext
   136  	// scripts.  The now-unnecessary transaction store scripts are removed
   137  	// in the upgrade.
   138  	unencryptedRedeemScriptsVersion = 14
   139  
   140  	// blockcf2Version is the 15th version of the databse. This upgrade
   141  	// drops the existing cfilter bucket and recreates it, triggering a
   142  	// re-download of missing cfilters. This is intended to switch the
   143  	// wallet to using the new, consensus-enforced version 2 committed
   144  	// filters.
   145  	blockcf2Version = 15
   146  
   147  	// perTicketVotingPreferencesVersion is the 16th version of the database.
   148  	// It creates a top-level ticketsagendaprefs bucket for storing voting
   149  	// preferences for individual tickets.
   150  	perTicketVotingPreferencesVersion = 16
   151  
   152  	// accountVariablesVersion is the 17th version of the database.  It adds
   153  	// a bucket for each account which records individual key/value pairs
   154  	// for account metadata which can change over time (such as account
   155  	// names, last used child indexes, etc).  This is more efficient than
   156  	// reserializing all account metadata into a single field when only some
   157  	// fields changed.
   158  	accountVariablesVersion = 17
   159  
   160  	// unpublishedTxsVersion is the 18th version of the database.  It
   161  	// adds a bucket for recording unmined transactions which have not yet
   162  	// been published to the network, but still recording them to the
   163  	// database and not considering spent previous outputs to be UTXOs
   164  	// unless the transaction is abandoned.
   165  	unpublishedTxsVersion = 18
   166  
   167  	// tspendPolicyVersion is the 19th version of the database.  It adds a
   168  	// top-level bucket for recording voting policy on treasury-spending
   169  	// transactions.
   170  	tspendPolicyVersion = 19
   171  
   172  	// vspBucketVersion is the 20th version of the database. It adds a
   173  	// a top-level bucket for recording vsp ticket hashes as key and its
   174  	// related fee txs hash.
   175  	vspBucketVersion = 20
   176  
   177  	// vspStatusVersion is the 21st version of the database.  It adds a
   178  	// status field to tracked vspd tickets.
   179  	vspStatusVersion = 21
   180  
   181  	// tspendHashPolicyVersion is the 22nd version of the database.  It adds a
   182  	// top-level bucket for recording voting policy on treasury-spending
   183  	// transactions by transaction hash, rather than by pi key.
   184  	tspendHashPolicyVersion = 22
   185  
   186  	// vspHostVersion is the 23nd version of the database.  It adds a
   187  	// vsp host ane vsp pubkey buckets to the db.
   188  	vspHostVersion = 23
   189  
   190  	// vspTreasuryPoliciesVersion is the 24th version of the database.  It
   191  	// adds top-level buckets for recording the voting policies
   192  	// treasury-spending transactions for specific customer's tickets served
   193  	// by a VSP.
   194  	vspTreasuryPoliciesVersion = 24
   195  
   196  	// importVotingAccount is the 25th version of the database. This version
   197  	// indicates that importing a new account type has been enabled. This
   198  	// account facilitates voting using a special account where the private
   199  	// key from indexes of the internal branch are shared with a vsp. The
   200  	// new type is not recognized by previous wallet versions. This version
   201  	// only updates the db version number so that previous versions will
   202  	// error on startup.
   203  	importVotingAccountVersion = 25
   204  
   205  	// DBVersion is the latest version of the database that is understood by the
   206  	// program.  Databases with recorded versions higher than this will fail to
   207  	// open (meaning any upgrades prevent reverting to older software).
   208  	DBVersion = importVotingAccountVersion
   209  )
   210  
   211  // upgrades maps between old database versions and the upgrade function to
   212  // upgrade the database to the next version.  Note that there was never a
   213  // version zero so upgrades[0] is nil.
   214  var upgrades = [...]func(walletdb.ReadWriteTx, []byte, *chaincfg.Params) error{
   215  	lastUsedAddressIndexVersion - 1:       lastUsedAddressIndexUpgrade,
   216  	votingPreferencesVersion - 1:          votingPreferencesUpgrade,
   217  	noEncryptedSeedVersion - 1:            noEncryptedSeedUpgrade,
   218  	lastReturnedAddressVersion - 1:        lastReturnedAddressUpgrade,
   219  	ticketBucketVersion - 1:               ticketBucketUpgrade,
   220  	slip0044CoinTypeVersion - 1:           slip0044CoinTypeUpgrade,
   221  	hasExpiryVersion - 1:                  hasExpiryUpgrade,
   222  	hasExpiryFixedVersion - 1:             hasExpiryFixedUpgrade,
   223  	cfVersion - 1:                         cfUpgrade,
   224  	lastProcessedTxsBlockVersion - 1:      lastProcessedTxsBlockUpgrade,
   225  	ticketCommitmentsVersion - 1:          ticketCommitmentsUpgrade,
   226  	importedXpubAccountVersion - 1:        importedXpubAccountUpgrade,
   227  	unencryptedRedeemScriptsVersion - 1:   unencryptedRedeemScriptsUpgrade,
   228  	blockcf2Version - 1:                   blockcf2Upgrade,
   229  	perTicketVotingPreferencesVersion - 1: perTicketVotingPreferencesUpgrade,
   230  	accountVariablesVersion - 1:           accountVariablesUpgrade,
   231  	unpublishedTxsVersion - 1:             unpublishedTxsUpgrade,
   232  	tspendPolicyVersion - 1:               tspendPolicyUpgrade,
   233  	vspBucketVersion - 1:                  vspBucketUpgrade,
   234  	vspStatusVersion - 1:                  vspStatusUpgrade,
   235  	tspendHashPolicyVersion - 1:           tspendHashPolicyUpgrade,
   236  	vspHostVersion - 1:                    vspHostVersionUpgrade,
   237  	vspTreasuryPoliciesVersion - 1:        vspTreasuryPoliciesUpgrade,
   238  	importVotingAccountVersion - 1:        importVotingAccountUpgrade,
   239  }
   240  
   241  func lastUsedAddressIndexUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   242  	const oldVersion = 1
   243  	const newVersion = 2
   244  
   245  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   246  	addrmgrBucket := tx.ReadWriteBucket(waddrmgrBucketKey)
   247  	addressBucket := addrmgrBucket.NestedReadBucket(addrBucketName)
   248  	usedAddrBucket := addrmgrBucket.NestedReadBucket(usedAddrBucketName)
   249  
   250  	addressKey := func(hash160 []byte) []byte {
   251  		sha := sha256.Sum256(hash160)
   252  		return sha[:]
   253  	}
   254  
   255  	// Assert that this function is only called on version 1 databases.
   256  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   257  	if err != nil {
   258  		return err
   259  	}
   260  	if dbVersion != oldVersion {
   261  		return errors.E(errors.Invalid, "lastUsedAddressIndexUpgrade inappropriately called")
   262  	}
   263  
   264  	masterKeyPubParams, _, err := fetchMasterKeyParams(addrmgrBucket)
   265  	if err != nil {
   266  		return err
   267  	}
   268  	var masterKeyPub snacl.SecretKey
   269  	err = masterKeyPub.Unmarshal(masterKeyPubParams)
   270  	if err != nil {
   271  		return errors.E(errors.IO, errors.Errorf("unmarshal master pubkey params: %v", err))
   272  	}
   273  	err = masterKeyPub.DeriveKey(&publicPassphrase)
   274  	if err != nil {
   275  		return errors.E(errors.Passphrase, "incorrect public passphrase")
   276  	}
   277  
   278  	cryptoPubKeyEnc, _, err := fetchCryptoKeys(addrmgrBucket)
   279  	if err != nil {
   280  		return err
   281  	}
   282  	cryptoPubKeyCT, err := masterKeyPub.Decrypt(cryptoPubKeyEnc)
   283  	if err != nil {
   284  		return errors.E(errors.Crypto, errors.Errorf("decrypt public crypto key: %v", err))
   285  	}
   286  	cryptoPubKey := &cryptoKey{snacl.CryptoKey{}}
   287  	copy(cryptoPubKey.CryptoKey[:], cryptoPubKeyCT)
   288  
   289  	// Determine how many BIP0044 accounts have been created.  Each of these
   290  	// accounts must be updated.
   291  	lastAccount, err := fetchLastAccount(addrmgrBucket)
   292  	if err != nil {
   293  		return err
   294  	}
   295  
   296  	// Perform account updates on all BIP0044 accounts created thus far.
   297  	for account := uint32(0); account <= lastAccount; account++ {
   298  		// Load the old account info.
   299  		row, err := fetchAccountInfo(addrmgrBucket, account, oldVersion)
   300  		if err != nil {
   301  			return err
   302  		}
   303  
   304  		// Use the crypto public key to decrypt the account public extended key
   305  		// and each branch key.
   306  		serializedKeyPub, err := cryptoPubKey.Decrypt(row.pubKeyEncrypted)
   307  		if err != nil {
   308  			return errors.E(errors.Crypto, errors.Errorf("decrypt extended pubkey: %v", err))
   309  		}
   310  		xpub, err := hdkeychain.NewKeyFromString(string(serializedKeyPub), params)
   311  		if err != nil {
   312  			return errors.E(errors.IO, err)
   313  		}
   314  		xpubExtBranch, err := xpub.Child(ExternalBranch)
   315  		if err != nil {
   316  			return err
   317  		}
   318  		xpubIntBranch, err := xpub.Child(InternalBranch)
   319  		if err != nil {
   320  			return err
   321  		}
   322  
   323  		// Determine the last used internal and external address indexes.  The
   324  		// sentinel value ^uint32(0) means that there has been no usage at all.
   325  		lastUsedExtIndex := ^uint32(0)
   326  		lastUsedIntIndex := ^uint32(0)
   327  		for child := uint32(0); child < hdkeychain.HardenedKeyStart; child++ {
   328  			xpubChild, err := xpubExtBranch.Child(child)
   329  			if errors.Is(err, hdkeychain.ErrInvalidChild) {
   330  				continue
   331  			}
   332  			if err != nil {
   333  				return err
   334  			}
   335  			// This can't error because the function always passes good input to
   336  			// dcrutil.NewAddressPubKeyHash.  Also, while it looks like a
   337  			// mistake to hardcode the mainnet parameters here, it doesn't make
   338  			// any difference since only the pubkey hash is used.  (Why is there
   339  			// no exported method to just return the serialized public key?)
   340  			addr, _ := compat.HD2Address(xpubChild, params)
   341  			if addressBucket.Get(addressKey(addr.Hash160()[:])) == nil {
   342  				// No more recorded addresses for this account.
   343  				break
   344  			}
   345  			if usedAddrBucket.Get(addressKey(addr.Hash160()[:])) != nil {
   346  				lastUsedExtIndex = child
   347  			}
   348  		}
   349  		for child := uint32(0); child < hdkeychain.HardenedKeyStart; child++ {
   350  			// Same as above but search the internal branch.
   351  			xpubChild, err := xpubIntBranch.Child(child)
   352  			if errors.Is(err, hdkeychain.ErrInvalidChild) {
   353  				continue
   354  			}
   355  			if err != nil {
   356  				return err
   357  			}
   358  			addr, _ := compat.HD2Address(xpubChild, params)
   359  			if addressBucket.Get(addressKey(addr.Hash160()[:])) == nil {
   360  				break
   361  			}
   362  			if usedAddrBucket.Get(addressKey(addr.Hash160()[:])) != nil {
   363  				lastUsedIntIndex = child
   364  			}
   365  		}
   366  
   367  		// Convert account row values to the new serialization format that
   368  		// replaces the next to use indexes with the last used indexes.
   369  		row = bip0044AccountInfo(row.pubKeyEncrypted, row.privKeyEncrypted,
   370  			0, 0, lastUsedExtIndex, lastUsedIntIndex, 0, 0, row.name, newVersion)
   371  		err = putBIP0044AccountInfo(addrmgrBucket, account, row)
   372  		if err != nil {
   373  			return err
   374  		}
   375  
   376  		// Remove all data saved for address pool handling.
   377  		addrmgrMetaBucket := addrmgrBucket.NestedReadWriteBucket(metaBucketName)
   378  		err = addrmgrMetaBucket.Delete(accountNumberToAddrPoolKey(false, account))
   379  		if err != nil {
   380  			return err
   381  		}
   382  		err = addrmgrMetaBucket.Delete(accountNumberToAddrPoolKey(true, account))
   383  		if err != nil {
   384  			return err
   385  		}
   386  	}
   387  
   388  	// Remove the used address tracking bucket.
   389  	err = addrmgrBucket.DeleteNestedBucket(usedAddrBucketName)
   390  	if err != nil {
   391  		return errors.E(errors.IO, err)
   392  	}
   393  
   394  	// Write the new database version.
   395  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   396  }
   397  
   398  func votingPreferencesUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   399  	const oldVersion = 2
   400  	const newVersion = 3
   401  
   402  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   403  	stakemgrBucket := tx.ReadWriteBucket(wstakemgrBucketKey)
   404  	ticketPurchasesBucket := stakemgrBucket.NestedReadWriteBucket(sstxRecordsBucketName)
   405  
   406  	// Assert that this function is only called on version 2 databases.
   407  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   408  	if err != nil {
   409  		return err
   410  	}
   411  	if dbVersion != oldVersion {
   412  		return errors.E(errors.Invalid, "votingPreferencesUpgrade inappropriately called")
   413  	}
   414  
   415  	// Update every ticket purchase with the new database version.  This removes
   416  	// all per-ticket vote bits.
   417  	ticketPurchases := make(map[chainhash.Hash]*sstxRecord)
   418  	c := ticketPurchasesBucket.ReadCursor()
   419  	defer c.Close()
   420  	for k, _ := c.First(); k != nil; k, _ = c.Next() {
   421  		var hash chainhash.Hash
   422  		copy(hash[:], k)
   423  		ticketPurchase, err := fetchSStxRecord(stakemgrBucket, &hash, oldVersion)
   424  		if err != nil {
   425  			return err
   426  		}
   427  		ticketPurchases[hash] = ticketPurchase
   428  	}
   429  	for _, ticketPurchase := range ticketPurchases {
   430  		err := putSStxRecord(stakemgrBucket, ticketPurchase, newVersion)
   431  		if err != nil {
   432  			return err
   433  		}
   434  	}
   435  
   436  	// Create the top level bucket for agenda preferences.
   437  	_, err = tx.CreateTopLevelBucket(agendaPreferences.defaultBucketKey())
   438  	if err != nil {
   439  		return err
   440  	}
   441  
   442  	// Write the new database version.
   443  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   444  }
   445  
   446  func noEncryptedSeedUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   447  	const oldVersion = 3
   448  	const newVersion = 4
   449  
   450  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   451  	addrmgrBucket := tx.ReadWriteBucket(waddrmgrBucketKey)
   452  	mainBucket := addrmgrBucket.NestedReadWriteBucket(mainBucketName)
   453  
   454  	// Assert that this function is only called on version 3 databases.
   455  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   456  	if err != nil {
   457  		return err
   458  	}
   459  	if dbVersion != oldVersion {
   460  		return errors.E(errors.Invalid, "noEncryptedSeedUpgrade inappropriately called")
   461  	}
   462  
   463  	// Remove encrypted seed (or encrypted zeros).
   464  	err = mainBucket.Delete(seedName)
   465  	if err != nil {
   466  		return err
   467  	}
   468  
   469  	// Write the new database version.
   470  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   471  }
   472  
   473  func lastReturnedAddressUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   474  	const oldVersion = 4
   475  	const newVersion = 5
   476  
   477  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   478  	addrmgrBucket := tx.ReadWriteBucket(waddrmgrBucketKey)
   479  
   480  	// Assert that this function is only called on version 4 databases.
   481  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   482  	if err != nil {
   483  		return err
   484  	}
   485  	if dbVersion != oldVersion {
   486  		return errors.E(errors.Invalid, "accountAddressCursorsUpgrade inappropriately called")
   487  	}
   488  
   489  	upgradeAcct := func(account uint32) error {
   490  		// Load the old account info.
   491  		row, err := fetchAccountInfo(addrmgrBucket, account, oldVersion)
   492  		if err != nil {
   493  			return err
   494  		}
   495  
   496  		// Convert account row values to the new serialization format that adds
   497  		// the last returned indexes.  Assume that the last used address is also
   498  		// the last returned address.
   499  		row = bip0044AccountInfo(row.pubKeyEncrypted, row.privKeyEncrypted,
   500  			0, 0, row.lastUsedExternalIndex, row.lastUsedInternalIndex,
   501  			row.lastUsedExternalIndex, row.lastUsedInternalIndex,
   502  			row.name, newVersion)
   503  		return putBIP0044AccountInfo(addrmgrBucket, account, row)
   504  	}
   505  
   506  	// Determine how many BIP0044 accounts have been created.  Each of these
   507  	// accounts must be updated.
   508  	lastAccount, err := fetchLastAccount(addrmgrBucket)
   509  	if err != nil {
   510  		return err
   511  	}
   512  
   513  	// Perform account updates on all BIP0044 accounts created thus far.
   514  	for account := uint32(0); account <= lastAccount; account++ {
   515  		err := upgradeAcct(account)
   516  		if err != nil {
   517  			return err
   518  		}
   519  	}
   520  
   521  	// Perform upgrade on the imported account, which is also using the BIP0044
   522  	// row serialization.  The last used and last returned indexes are not used
   523  	// by the imported account but the row must be upgraded regardless to avoid
   524  	// deserialization errors due to the row value length checks.
   525  	err = upgradeAcct(ImportedAddrAccount)
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	// Write the new database version.
   531  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   532  }
   533  
   534  func ticketBucketUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   535  	const oldVersion = 5
   536  	const newVersion = 6
   537  
   538  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   539  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
   540  
   541  	// Assert that this function is only called on version 5 databases.
   542  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   543  	if err != nil {
   544  		return err
   545  	}
   546  	if dbVersion != oldVersion {
   547  		return errors.E(errors.Invalid, "ticketBucketUpgrade inappropriately called")
   548  	}
   549  
   550  	// Create the tickets bucket.
   551  	_, err = txmgrBucket.CreateBucket(bucketTickets)
   552  	if err != nil {
   553  		return err
   554  	}
   555  
   556  	// Add an entry in the tickets bucket for every mined and unmined ticket
   557  	// purchase transaction.  Use -1 as the selected height since this value is
   558  	// unknown at this time and the field is not yet being used.
   559  	ticketHashes := make(map[chainhash.Hash]struct{})
   560  	c := txmgrBucket.NestedReadBucket(bucketTxRecords).ReadCursor()
   561  	for k, v := c.First(); v != nil; k, v = c.Next() {
   562  		var hash chainhash.Hash
   563  		err := readRawTxRecordHash(k, &hash)
   564  		if err != nil {
   565  			c.Close()
   566  			return err
   567  		}
   568  		var rec TxRecord
   569  		err = readRawTxRecord(&hash, v, &rec)
   570  		if err != nil {
   571  			c.Close()
   572  			return err
   573  		}
   574  		if stake.IsSStx(&rec.MsgTx) {
   575  			ticketHashes[hash] = struct{}{}
   576  		}
   577  	}
   578  	c.Close()
   579  
   580  	c = txmgrBucket.NestedReadBucket(bucketUnmined).ReadCursor()
   581  	for k, v := c.First(); v != nil; k, v = c.Next() {
   582  		var hash chainhash.Hash
   583  		err := readRawUnminedHash(k, &hash)
   584  		if err != nil {
   585  			c.Close()
   586  			return err
   587  		}
   588  		var rec TxRecord
   589  		err = readRawTxRecord(&hash, v, &rec)
   590  		if err != nil {
   591  			c.Close()
   592  			return err
   593  		}
   594  		if stake.IsSStx(&rec.MsgTx) {
   595  			ticketHashes[hash] = struct{}{}
   596  		}
   597  	}
   598  	c.Close()
   599  	for ticketHash := range ticketHashes {
   600  		err := putTicketRecord(txmgrBucket, &ticketHash, -1)
   601  		if err != nil {
   602  			return err
   603  		}
   604  	}
   605  
   606  	// Remove previous stakebase input from the unmined inputs bucket, if any
   607  	// was recorded.
   608  	stakebaseOutpoint := canonicalOutPoint(&chainhash.Hash{}, ^uint32(0))
   609  	err = txmgrBucket.NestedReadWriteBucket(bucketUnminedInputs).Delete(stakebaseOutpoint)
   610  	if err != nil {
   611  		return err
   612  	}
   613  
   614  	// Write the new database version.
   615  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   616  }
   617  
   618  func slip0044CoinTypeUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   619  	const oldVersion = 6
   620  	const newVersion = 7
   621  
   622  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   623  
   624  	// Assert that this function is only called on version 6 databases.
   625  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   626  	if err != nil {
   627  		return err
   628  	}
   629  	if dbVersion != oldVersion {
   630  		return errors.E(errors.Invalid, "slip0044CoinTypeUpgrade inappropriately called")
   631  	}
   632  
   633  	// Write the new database version.
   634  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   635  }
   636  
   637  func hasExpiryUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   638  	const oldVersion = 7
   639  	const newVersion = 8
   640  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   641  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
   642  
   643  	// Assert that this function is only called on version 7 databases.
   644  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   645  	if err != nil {
   646  		return err
   647  	}
   648  	if dbVersion != oldVersion {
   649  		return errors.E(errors.Invalid, "hasExpiryUpgrade inappropriately called")
   650  	}
   651  
   652  	// Iterate through all mined credits
   653  	creditsBucket := txmgrBucket.NestedReadWriteBucket(bucketCredits)
   654  	cursor := creditsBucket.ReadWriteCursor()
   655  	creditsKV := map[string][]byte{}
   656  	for k, v := cursor.First(); v != nil; k, v = cursor.Next() {
   657  		hash := extractRawCreditTxHash(k)
   658  		block, err := fetchBlockRecord(txmgrBucket, extractRawCreditHeight(k))
   659  		if err != nil {
   660  			cursor.Close()
   661  			return err
   662  		}
   663  
   664  		_, recV := existsTxRecord(txmgrBucket, &hash, &block.Block)
   665  		record := &TxRecord{}
   666  		err = readRawTxRecord(&hash, recV, record)
   667  		if err != nil {
   668  			cursor.Close()
   669  			return err
   670  		}
   671  
   672  		// Only save credits that need their hasExpiry flag updated
   673  		if record.MsgTx.Expiry != wire.NoExpiryValue {
   674  			vCpy := make([]byte, len(v))
   675  			copy(vCpy, v)
   676  
   677  			vCpy[8] |= 1 << 4
   678  			creditsKV[string(k)] = vCpy
   679  		}
   680  	}
   681  	cursor.Close()
   682  
   683  	for k, v := range creditsKV {
   684  		err = creditsBucket.Put([]byte(k), v)
   685  		if err != nil {
   686  			return err
   687  		}
   688  	}
   689  
   690  	// Iterate through all unmined credits
   691  	unminedCreditsBucket := txmgrBucket.NestedReadWriteBucket(bucketUnminedCredits)
   692  	unminedCursor := unminedCreditsBucket.ReadWriteCursor()
   693  	unminedCreditsKV := map[string][]byte{}
   694  	for k, v := unminedCursor.First(); v != nil; k, v = unminedCursor.Next() {
   695  		hash, err := chainhash.NewHash(extractRawUnminedCreditTxHash(k))
   696  		if err != nil {
   697  			unminedCursor.Close()
   698  			return err
   699  		}
   700  
   701  		recV := existsRawUnmined(txmgrBucket, hash[:])
   702  		record := &TxRecord{}
   703  		err = readRawTxRecord(hash, recV, record)
   704  		if err != nil {
   705  			unminedCursor.Close()
   706  			return err
   707  		}
   708  
   709  		// Only save credits that need their hasExpiry flag updated
   710  		if record.MsgTx.Expiry != wire.NoExpiryValue {
   711  			vCpy := make([]byte, len(v))
   712  			copy(vCpy, v)
   713  
   714  			vCpy[8] |= 1 << 4
   715  			unminedCreditsKV[string(k)] = vCpy
   716  		}
   717  	}
   718  	unminedCursor.Close()
   719  
   720  	for k, v := range unminedCreditsKV {
   721  		err = unminedCreditsBucket.Put([]byte(k), v)
   722  		if err != nil {
   723  			return err
   724  		}
   725  	}
   726  
   727  	// Write the new database version.
   728  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   729  }
   730  
   731  func hasExpiryFixedUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   732  	const oldVersion = 8
   733  	const newVersion = 9
   734  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   735  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
   736  
   737  	// Assert this function is only called on version 8 databases.
   738  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   739  	if err != nil {
   740  		return err
   741  	}
   742  	if dbVersion != oldVersion {
   743  		return errors.E(errors.Invalid, "hasExpiryFixedUpgrade inappropriately called")
   744  	}
   745  
   746  	// Iterate through all mined credits
   747  	creditsBucket := txmgrBucket.NestedReadWriteBucket(bucketCredits)
   748  	cursor := creditsBucket.ReadCursor()
   749  	creditsKV := map[string][]byte{}
   750  	for k, v := cursor.First(); v != nil; k, v = cursor.Next() {
   751  		hash := extractRawCreditTxHash(k)
   752  		block, err := fetchBlockRecord(txmgrBucket, extractRawCreditHeight(k))
   753  		if err != nil {
   754  			cursor.Close()
   755  			return err
   756  		}
   757  
   758  		_, recV := existsTxRecord(txmgrBucket, &hash, &block.Block)
   759  		record := &TxRecord{}
   760  		err = readRawTxRecord(&hash, recV, record)
   761  		if err != nil {
   762  			cursor.Close()
   763  			return err
   764  		}
   765  
   766  		// Only save credits that need their hasExpiry flag updated
   767  		if record.MsgTx.Expiry != wire.NoExpiryValue {
   768  			vCpy := make([]byte, len(v))
   769  			copy(vCpy, v)
   770  
   771  			vCpy[8] &^= 1 << 4 // Clear bad hasExpiry/OP_SSTXCHANGE flag
   772  			vCpy[8] |= 1 << 6  // Set correct hasExpiry flag
   773  			// Reset OP_SSTXCHANGE flag if this is a ticket purchase
   774  			// OP_SSTXCHANGE output.
   775  			out := record.MsgTx.TxOut[extractRawCreditIndex(k)]
   776  			if stake.IsSStx(&record.MsgTx) &&
   777  				(stdscript.IsStakeChangePubKeyHashScript(out.Version, out.PkScript) ||
   778  					stdscript.IsStakeChangeScriptHashScript(out.Version, out.PkScript)) {
   779  				vCpy[8] |= 1 << 4
   780  			}
   781  
   782  			creditsKV[string(k)] = vCpy
   783  		}
   784  	}
   785  	cursor.Close()
   786  
   787  	for k, v := range creditsKV {
   788  		err = creditsBucket.Put([]byte(k), v)
   789  		if err != nil {
   790  			return err
   791  		}
   792  	}
   793  
   794  	// Iterate through all unmined credits
   795  	unminedCreditsBucket := txmgrBucket.NestedReadWriteBucket(bucketUnminedCredits)
   796  	unminedCursor := unminedCreditsBucket.ReadCursor()
   797  	unminedCreditsKV := map[string][]byte{}
   798  	for k, v := unminedCursor.First(); v != nil; k, v = unminedCursor.Next() {
   799  		hash, err := chainhash.NewHash(extractRawUnminedCreditTxHash(k))
   800  		if err != nil {
   801  			unminedCursor.Close()
   802  			return err
   803  		}
   804  
   805  		recV := existsRawUnmined(txmgrBucket, hash[:])
   806  		record := &TxRecord{}
   807  		err = readRawTxRecord(hash, recV, record)
   808  		if err != nil {
   809  			unminedCursor.Close()
   810  			return err
   811  		}
   812  
   813  		// Only save credits that need their hasExpiry flag updated
   814  		if record.MsgTx.Expiry != wire.NoExpiryValue {
   815  			vCpy := make([]byte, len(v))
   816  			copy(vCpy, v)
   817  
   818  			vCpy[8] &^= 1 << 4 // Clear bad hasExpiry/OP_SSTXCHANGE flag
   819  			vCpy[8] |= 1 << 6  // Set correct hasExpiry flag
   820  			// Reset OP_SSTXCHANGE flag if this is a ticket purchase
   821  			// OP_SSTXCHANGE output.
   822  			idx, err := fetchRawUnminedCreditIndex(k)
   823  			if err != nil {
   824  				unminedCursor.Close()
   825  				return err
   826  			}
   827  			out := record.MsgTx.TxOut[idx]
   828  			if stake.IsSStx(&record.MsgTx) &&
   829  				(stdscript.IsStakeChangePubKeyHashScript(out.Version, out.PkScript) ||
   830  					stdscript.IsStakeChangeScriptHashScript(out.Version, out.PkScript)) {
   831  				vCpy[8] |= 1 << 4
   832  			}
   833  
   834  			unminedCreditsKV[string(k)] = vCpy
   835  		}
   836  	}
   837  	unminedCursor.Close()
   838  
   839  	for k, v := range unminedCreditsKV {
   840  		err = unminedCreditsBucket.Put([]byte(k), v)
   841  		if err != nil {
   842  			return err
   843  		}
   844  	}
   845  
   846  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   847  }
   848  
   849  func cfUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   850  	const oldVersion = 9
   851  	const newVersion = 10
   852  
   853  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   854  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
   855  
   856  	// Assert that this function is only called on version 9 databases.
   857  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   858  	if err != nil {
   859  		return err
   860  	}
   861  	if dbVersion != oldVersion {
   862  		return errors.E(errors.Invalid, "cfUpgrade inappropriately called")
   863  	}
   864  
   865  	err = txmgrBucket.Put(rootHaveCFilters, []byte{0})
   866  	if err != nil {
   867  		return errors.E(errors.IO, err)
   868  	}
   869  	_, err = txmgrBucket.CreateBucket(bucketCFilters)
   870  	if err != nil {
   871  		return errors.E(errors.IO, err)
   872  	}
   873  
   874  	// Record cfilter for genesis block.
   875  	//
   876  	// Note: This used to record the actual version 1 cfilter for the
   877  	// genesis block, but this was changed as packages were updated.
   878  	// Version 1 cfilters can no longer be created using the latest major
   879  	// version of the gcs module.  Plus, version 1 cfilters are removed in a
   880  	// later database upgrade, because only version 2 cfilters are used now.
   881  	// So instead, this upgrade path has been modified to record nil bytes
   882  	// for this genesis block v1 cfilter.
   883  	err = putRawCFilter(txmgrBucket, params.GenesisHash[:], nil)
   884  	if err != nil {
   885  		return errors.E(errors.IO, err)
   886  	}
   887  
   888  	// Record all cfilters as saved when only the genesis block is saved.
   889  	var tipHash chainhash.Hash
   890  	copy(tipHash[:], txmgrBucket.Get(rootTipBlock))
   891  	if tipHash == params.GenesisHash {
   892  		err = txmgrBucket.Put(rootHaveCFilters, []byte{1})
   893  		if err != nil {
   894  			return errors.E(errors.IO, err)
   895  		}
   896  	}
   897  
   898  	// Write the new database version.
   899  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   900  }
   901  
   902  func lastProcessedTxsBlockUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   903  	const oldVersion = 10
   904  	const newVersion = 11
   905  
   906  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   907  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
   908  
   909  	// Assert that this function is only called on version 10 databases.
   910  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   911  	if err != nil {
   912  		return err
   913  	}
   914  	if dbVersion != oldVersion {
   915  		return errors.E(errors.Invalid, "lastProcessedTxsBlockUpgrade inappropriately called")
   916  	}
   917  
   918  	// Record the current tip block as the last block since genesis with
   919  	// processed transaction.
   920  	err = txmgrBucket.Put(rootLastTxsBlock, txmgrBucket.Get(rootTipBlock))
   921  	if err != nil {
   922  		return errors.E(errors.IO, err)
   923  	}
   924  
   925  	// Write the new database version.
   926  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
   927  }
   928  
   929  func ticketCommitmentsUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
   930  	const oldVersion = 11
   931  	const newVersion = 12
   932  
   933  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
   934  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
   935  	addrmgrBucket := tx.ReadWriteBucket(waddrmgrBucketKey)
   936  
   937  	// Assert that this function is only called on version 11 databases.
   938  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
   939  	if err != nil {
   940  		return err
   941  	}
   942  	if dbVersion != oldVersion {
   943  		return errors.E(errors.Invalid, "ticketCommitmentsUpgrade inappropriately called")
   944  	}
   945  
   946  	_, err = txmgrBucket.CreateBucket(bucketTicketCommitments)
   947  	if err != nil {
   948  		return errors.E(errors.IO, err)
   949  	}
   950  
   951  	_, err = txmgrBucket.CreateBucket(bucketTicketCommitmentsUsp)
   952  	if err != nil {
   953  		return errors.E(errors.IO, err)
   954  	}
   955  
   956  	// Helper function to handle the details of a single (mined or unmined)
   957  	// transaction.
   958  	handleTxRecCommitments := func(txrec *TxRecord, unmined bool) error {
   959  		for i, txo := range txrec.MsgTx.TxOut {
   960  			if txrec.TxType == stake.TxTypeSStx {
   961  				if i%2 != 1 {
   962  					// Ignore non ticket commitment outputs.
   963  					continue
   964  				}
   965  
   966  				// Decode the address stored in the commitment.
   967  				addr, err := stake.AddrFromSStxPkScrCommitment(txo.PkScript, params)
   968  				if err != nil {
   969  					return errors.E(errors.IO, err)
   970  				}
   971  
   972  				id, err := addressID(normalizeAddress(addr))
   973  				if err != nil {
   974  					return errors.E(errors.Bug, err)
   975  				}
   976  				acct, err := fetchAddrAccount(addrmgrBucket, id)
   977  				if err != nil && errors.Is(err, errors.NotExist) {
   978  					// If this address does not have an account associated
   979  					// with it, it means it's not owned by the wallet.
   980  					continue
   981  				} else if err != nil {
   982  					return errors.E(errors.IO, err)
   983  				}
   984  
   985  				// Decode the amount stored in the commitment.
   986  				amount, err := stake.AmountFromSStxPkScrCommitment(txo.PkScript)
   987  				if err != nil {
   988  					return errors.E(errors.IO, err)
   989  				}
   990  
   991  				log.Debugf("Adding ticket commitment %s:%d (%s) for account %d",
   992  					txrec.Hash, i, amount, acct)
   993  
   994  				// Store both the ticket commitment info and an entry in the
   995  				// unspent index.
   996  				k := keyTicketCommitment(txrec.Hash, uint32(i))
   997  				v := valueTicketCommitment(amount, acct)
   998  				err = putRawTicketCommitment(txmgrBucket, k, v)
   999  				if err != nil {
  1000  					return errors.E(errors.IO, err)
  1001  				}
  1002  
  1003  				v = valueUnspentTicketCommitment(false)
  1004  				err = putRawUnspentTicketCommitment(txmgrBucket, k, v)
  1005  				if err != nil {
  1006  					return errors.E(errors.IO, err)
  1007  				}
  1008  			} else if (txrec.TxType == stake.TxTypeSSGen) || (txrec.TxType == stake.TxTypeSSRtx) {
  1009  				// txoIdx is the original output index of the commitment, given
  1010  				// that "i" is an output index on the spender transaction.
  1011  				txoIdx := uint32(i*2 + 1)
  1012  
  1013  				// ticketHashIdx is the index of the input on the spender
  1014  				// transaction (txrec) where the original ticket hash can be
  1015  				// found.
  1016  				ticketHashIdx := uint32(0)
  1017  				if txrec.TxType == stake.TxTypeSSGen {
  1018  					if i < 2 {
  1019  						// Ignore previous block hash and vote bits outputs.
  1020  						continue
  1021  					}
  1022  
  1023  					// To find the original output index on votes, we skip the
  1024  					// first two outputs (previous block and vote bits) and to
  1025  					// find the original input index we skip the first input
  1026  					// (stakebase).
  1027  					txoIdx = uint32((i-2)*2 + 1)
  1028  					ticketHashIdx = 1
  1029  				}
  1030  
  1031  				ticketHash := txrec.MsgTx.TxIn[ticketHashIdx].PreviousOutPoint.Hash
  1032  
  1033  				k := keyTicketCommitment(ticketHash, txoIdx)
  1034  				v := existsRawTicketCommitment(txmgrBucket, k)
  1035  				if v == nil {
  1036  					// Ignore commitments we were not originally tracking.
  1037  					continue
  1038  				}
  1039  
  1040  				if unmined {
  1041  					// An unmined vote/revocation only marks the ticket
  1042  					// commitment as unminedSpent.
  1043  					log.Debugf("Marking ticket commitment %s:%d unmined spent",
  1044  						ticketHash, txoIdx)
  1045  
  1046  					v = valueUnspentTicketCommitment(true)
  1047  					err = putRawUnspentTicketCommitment(txmgrBucket, k, v)
  1048  				} else {
  1049  					// A mined vote/revocation removes the entry from the
  1050  					// unspent ticket commitment index.
  1051  					log.Debugf("Removing unspent ticket commitment %s:%d",
  1052  						ticketHash, txoIdx)
  1053  
  1054  					err = deleteRawUnspentTicketCommitment(txmgrBucket, k)
  1055  				}
  1056  				if err != nil {
  1057  					return errors.E(errors.IO, err)
  1058  				}
  1059  			}
  1060  		}
  1061  
  1062  		return nil
  1063  	}
  1064  
  1065  	// Rescan the database for stake transactions, creating the commitments when
  1066  	// a ticket is found and deleting it when a vote/revocation is found.
  1067  	it := makeReadBlockIterator(txmgrBucket, 0)
  1068  	var txrec TxRecord
  1069  	for it.next() {
  1070  		for _, txh := range it.elem.transactions {
  1071  			_, v := latestTxRecord(txmgrBucket, txh[:])
  1072  			err = readRawTxRecord(&txh, v, &txrec)
  1073  			if err != nil {
  1074  				return errors.E(errors.IO, err)
  1075  			}
  1076  
  1077  			err = handleTxRecCommitments(&txrec, false)
  1078  			if err != nil {
  1079  				return err
  1080  			}
  1081  		}
  1082  	}
  1083  
  1084  	// Rescan unmined transactions for tickets and votes.
  1085  	err = txmgrBucket.NestedReadBucket(bucketUnmined).ForEach(func(uk, uv []byte) error {
  1086  		var txHash chainhash.Hash
  1087  		var rec TxRecord
  1088  
  1089  		err := readRawUnminedHash(uk, &txHash)
  1090  		if err != nil {
  1091  			return err
  1092  		}
  1093  
  1094  		err = readRawTxRecord(&txHash, uv, &rec)
  1095  		if err != nil {
  1096  			return err
  1097  		}
  1098  
  1099  		return handleTxRecCommitments(&rec, true)
  1100  	})
  1101  	if err != nil {
  1102  		return errors.E(errors.IO, err)
  1103  	}
  1104  
  1105  	log.Debug("Ticket commitments db upgrade done")
  1106  
  1107  	// Write the new database version.
  1108  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1109  }
  1110  
  1111  func importedXpubAccountUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1112  	const oldVersion = 12
  1113  	const newVersion = 13
  1114  
  1115  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1116  
  1117  	// Assert that this function is only called on version 12 databases.
  1118  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1119  	if err != nil {
  1120  		return err
  1121  	}
  1122  	if dbVersion != oldVersion {
  1123  		return errors.E(errors.Invalid, "importedXpubAccountUpgrade inappropriately called")
  1124  	}
  1125  
  1126  	// Write the new database version.
  1127  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1128  }
  1129  
  1130  func unencryptedRedeemScriptsUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1131  	const oldVersion = 13
  1132  	const newVersion = 14
  1133  
  1134  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1135  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
  1136  	addrmgrBucket := tx.ReadWriteBucket(waddrmgrBucketKey)
  1137  
  1138  	// Assert that this function is only called on version 13 databases.
  1139  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1140  	if err != nil {
  1141  		return err
  1142  	}
  1143  	if dbVersion != oldVersion {
  1144  		return errors.E(errors.Invalid, "unencryptedRedeemScriptsUpgrade inappropriately called")
  1145  	}
  1146  
  1147  	// Open the encrypted public data crypto key.
  1148  	masterKeyPubParams, _, err := fetchMasterKeyParams(addrmgrBucket)
  1149  	if err != nil {
  1150  		return err
  1151  	}
  1152  	var masterKeyPub snacl.SecretKey
  1153  	err = masterKeyPub.Unmarshal(masterKeyPubParams)
  1154  	if err != nil {
  1155  		return errors.E(errors.IO, errors.Errorf("unmarshal master pubkey params: %v", err))
  1156  	}
  1157  	err = masterKeyPub.DeriveKey(&publicPassphrase)
  1158  	if err != nil {
  1159  		return errors.E(errors.Passphrase, "incorrect public passphrase")
  1160  	}
  1161  	cryptoPubKeyEnc, _, err := fetchCryptoKeys(addrmgrBucket)
  1162  	if err != nil {
  1163  		return err
  1164  	}
  1165  	cryptoPubKeyCT, err := masterKeyPub.Decrypt(cryptoPubKeyEnc)
  1166  	if err != nil {
  1167  		return errors.E(errors.Crypto, errors.Errorf("decrypt public crypto key: %v", err))
  1168  	}
  1169  	cryptoPubKey := &cryptoKey{snacl.CryptoKey{}}
  1170  	copy(cryptoPubKey.CryptoKey[:], cryptoPubKeyCT)
  1171  
  1172  	// Read all unencrypted redeem scripts saved in the transaction store
  1173  	// into memory.
  1174  	scripts := make(map[[20]byte][]byte)
  1175  	scriptsBucket := txmgrBucket.NestedReadWriteBucket(bucketScripts)
  1176  	cursor := scriptsBucket.ReadCursor()
  1177  	for _, v := cursor.First(); v != nil; _, v = cursor.Next() {
  1178  		script := append(v[:0:0], v...) // copy
  1179  		var hash [20]byte
  1180  		copy(hash[:], dcrutil.Hash160(script))
  1181  		scripts[hash] = script
  1182  	}
  1183  	cursor.Close()
  1184  
  1185  	// Read all script hashes of recorded managed p2sh addresses.
  1186  	// These are only recorded by the "imported" account.
  1187  	p2shRows := make(map[[20]byte]*dbScriptAddressRow)
  1188  	importedAddrsBucket := addrmgrBucket.NestedReadBucket(addrAcctIdxBucketName).
  1189  		NestedReadBucket(uint32ToBytes(ImportedAddrAccount))
  1190  	cursor = nil
  1191  	var ck, cv []byte
  1192  	if importedAddrsBucket != nil {
  1193  		cursor = importedAddrsBucket.ReadCursor()
  1194  		ck, cv = cursor.First()
  1195  	}
  1196  	for ; ck != nil; ck, cv = cursor.Next() {
  1197  		if cv == nil {
  1198  			continue // skip nested buckets
  1199  		}
  1200  		row, err := fetchAddressByHash(addrmgrBucket, ck)
  1201  		if err != nil {
  1202  			return errors.E(errors.IO, err)
  1203  		}
  1204  		r, ok := row.(*dbScriptAddressRow)
  1205  		if !ok {
  1206  			continue
  1207  		}
  1208  		// Decrypt HASH160 using the public data encryption key.
  1209  		decryptedHash160, err := cryptoPubKey.Decrypt(r.encryptedHash)
  1210  		if err != nil {
  1211  			return err
  1212  		}
  1213  		if len(decryptedHash160) != 20 {
  1214  			return errors.E(errors.IO, "decrypted hash160 is not 20 bytes")
  1215  		}
  1216  		var hash160 [20]byte
  1217  		copy(hash160[:], decryptedHash160)
  1218  		p2shRows[hash160] = r
  1219  	}
  1220  	if cursor != nil {
  1221  		cursor.Close()
  1222  	}
  1223  
  1224  	// For every encrypted address manager script, ensure that an
  1225  	// unencrypted version of the script is recorded.  Rewrite each p2sh
  1226  	// address row with the cleartext script.
  1227  	for k, r := range p2shRows {
  1228  		script, ok := scripts[k]
  1229  		if !ok {
  1230  			err := errors.Errorf("missing cleartext script for p2sh hash160 %x", k[:])
  1231  			return errors.E(errors.IO, err)
  1232  		}
  1233  		r.script = script
  1234  		r.rawData = serializeScriptAddress(r.encryptedHash, r.script)
  1235  		err := putAddress(addrmgrBucket, k[:], &r.dbAddressRow)
  1236  		if err != nil {
  1237  			return err
  1238  		}
  1239  		err = scriptsBucket.Delete(k[:])
  1240  		if err != nil {
  1241  			return err
  1242  		}
  1243  		delete(scripts, k)
  1244  	}
  1245  
  1246  	// For each remaining imported script that only appeared in the
  1247  	// transaction store buckets, add an imported p2sh address to the
  1248  	// address manager.
  1249  	for k, script := range scripts {
  1250  		encryptedHash, err := cryptoPubKey.Encrypt(k[:])
  1251  		if err != nil {
  1252  			return err
  1253  		}
  1254  		err = putScriptAddress(addrmgrBucket, k[:], ImportedAddrAccount,
  1255  			encryptedHash, script)
  1256  		if err != nil {
  1257  			return err
  1258  		}
  1259  		err = scriptsBucket.Delete(k[:])
  1260  		if err != nil {
  1261  			return err
  1262  		}
  1263  	}
  1264  
  1265  	// Remove the transaction store's nested scripts bucket.
  1266  	err = txmgrBucket.DeleteNestedBucket(bucketScripts)
  1267  	if err != nil {
  1268  		return err
  1269  	}
  1270  
  1271  	// Remove the address manager's sealed symmetric key for script encryption.
  1272  	addrmgrMainBucket := addrmgrBucket.NestedReadWriteBucket(mainBucketName)
  1273  	err = addrmgrMainBucket.Delete(cryptoScriptKeyName)
  1274  	if err != nil && !errors.Is(err, errors.NotExist) {
  1275  		return err
  1276  	}
  1277  
  1278  	// Write the new database version.
  1279  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1280  }
  1281  
  1282  // genesisPrevScripter fulfills the blockcf2.PrevScripter interface but only
  1283  // for the genesis block which doesn't have any previous scripts.
  1284  type genesisPrevScripter struct{}
  1285  
  1286  func (genesisPrevScripter) PrevScript(*wire.OutPoint) (uint16, []byte, bool) {
  1287  	return 0, nil, false
  1288  }
  1289  
  1290  func blockcf2Upgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1291  	const oldVersion = 14
  1292  	const newVersion = 15
  1293  
  1294  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1295  
  1296  	// Assert that this function is only called on version 14 databases.
  1297  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1298  	if err != nil {
  1299  		return err
  1300  	}
  1301  	if dbVersion != oldVersion {
  1302  		return errors.E(errors.Invalid, "blockcf2Upgrade inappropriately called")
  1303  	}
  1304  
  1305  	txmgrBucket := tx.ReadWriteBucket(wtxmgrBucketKey)
  1306  
  1307  	// Drop all existing blockcf filters by deleting then recreating the
  1308  	// cfilter bucket.
  1309  	err = txmgrBucket.DeleteNestedBucket(bucketCFilters)
  1310  	if err != nil {
  1311  		return errors.E(errors.IO, err)
  1312  	}
  1313  	_, err = txmgrBucket.CreateBucket(bucketCFilters)
  1314  	if err != nil {
  1315  		return errors.E(errors.IO, err)
  1316  	}
  1317  
  1318  	// Reset the rootHaveCFilters flag to false so that cfilters will have
  1319  	// to be downloaded again.
  1320  	err = txmgrBucket.Put(rootHaveCFilters, []byte{0})
  1321  	if err != nil {
  1322  		return errors.E(errors.IO, err)
  1323  	}
  1324  	// Record cfilter for genesis block.
  1325  	f, err := blockcf2.Regular(params.GenesisBlock, genesisPrevScripter{})
  1326  	if err != nil {
  1327  		return err
  1328  	}
  1329  	genesisBcfKey := blockcf2.Key(&params.GenesisBlock.Header.MerkleRoot)
  1330  	err = putRawCFilter(txmgrBucket, params.GenesisHash[:],
  1331  		valueRawCFilter2(genesisBcfKey, f.Bytes()))
  1332  	if err != nil {
  1333  		return errors.E(errors.IO, err)
  1334  	}
  1335  
  1336  	// Record all cfilters as saved when only the genesis block is saved.
  1337  	var tipHash chainhash.Hash
  1338  	copy(tipHash[:], txmgrBucket.Get(rootTipBlock))
  1339  	if tipHash == params.GenesisHash {
  1340  		err = txmgrBucket.Put(rootHaveCFilters, []byte{1})
  1341  		if err != nil {
  1342  			return errors.E(errors.IO, err)
  1343  		}
  1344  	}
  1345  
  1346  	// Write the new database version.
  1347  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1348  }
  1349  
  1350  func perTicketVotingPreferencesUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1351  	const oldVersion = 15
  1352  	const newVersion = 16
  1353  
  1354  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1355  
  1356  	// Assert that this function is only called on version 15 databases.
  1357  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1358  	if err != nil {
  1359  		return err
  1360  	}
  1361  	if dbVersion != oldVersion {
  1362  		return errors.E(errors.Invalid, "perTicketVotingPreferencesUpgrade inappropriately called")
  1363  	}
  1364  
  1365  	// Create the top level bucket for per-ticket voting preferences.
  1366  	_, err = tx.CreateTopLevelBucket(agendaPreferences.ticketsBucketKey())
  1367  	if err != nil {
  1368  		return err
  1369  	}
  1370  
  1371  	// Write the new database version.
  1372  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1373  }
  1374  
  1375  func accountVariablesUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1376  	const oldVersion = 16
  1377  	const newVersion = 17
  1378  
  1379  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1380  
  1381  	// Assert that this function is only called on version 16 databases.
  1382  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1383  	if err != nil {
  1384  		return err
  1385  	}
  1386  	if dbVersion != oldVersion {
  1387  		return errors.E(errors.Invalid, "accountVariablesUpgrade inappropriately called")
  1388  	}
  1389  
  1390  	addrmgrBucket := tx.ReadWriteBucket(waddrmgrBucketKey)
  1391  
  1392  	// For each previous BIP0044 account, create the variables bucket for it
  1393  	// and rewrite both the row and all variables.
  1394  	_, err = addrmgrBucket.CreateBucket(acctVarsBucketName)
  1395  	if err != nil {
  1396  		return errors.E(errors.IO, err)
  1397  	}
  1398  	newAcct := new(dbBIP0044Account)
  1399  	err = forEachAccount(addrmgrBucket, func(account uint32) error {
  1400  		a, err := fetchDBAccount(addrmgrBucket, account, oldVersion)
  1401  		if err != nil {
  1402  			return err
  1403  		}
  1404  		switch a := a.(type) {
  1405  		case *dbBIP0044AccountRow:
  1406  			newAcct.dbAccountRow = a.dbAccountRow
  1407  			newAcct.dbAccountRow.acctType = actBIP0044
  1408  			newAcct.pubKeyEncrypted = a.pubKeyEncrypted
  1409  			newAcct.privKeyEncrypted = a.privKeyEncrypted
  1410  			newAcct.lastUsedExternalIndex = a.lastUsedExternalIndex
  1411  			newAcct.lastUsedInternalIndex = a.lastUsedInternalIndex
  1412  			newAcct.lastReturnedExternalIndex = a.lastReturnedExternalIndex
  1413  			newAcct.lastReturnedInternalIndex = a.lastReturnedInternalIndex
  1414  			newAcct.name = a.name
  1415  			newAcct.dbAccountRow.rawData = newAcct.serializeRow()
  1416  		default:
  1417  			return nil
  1418  		}
  1419  
  1420  		return putNewBIP0044Account(addrmgrBucket, account, newAcct)
  1421  	})
  1422  	if err != nil {
  1423  		return errors.E(errors.IO, err)
  1424  	}
  1425  
  1426  	// Write the new database version.
  1427  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1428  }
  1429  
  1430  func unpublishedTxsUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1431  	const oldVersion = 17
  1432  	const newVersion = 18
  1433  
  1434  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1435  
  1436  	// Assert that this function is only called on version 17 databases.
  1437  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1438  	if err != nil {
  1439  		return err
  1440  	}
  1441  	if dbVersion != oldVersion {
  1442  		return errors.E(errors.Invalid, "unpublishedTxsUpgrade inappropriately called")
  1443  	}
  1444  
  1445  	txmgrNs := tx.ReadWriteBucket(wtxmgrBucketKey)
  1446  	_, err = txmgrNs.CreateBucket(bucketUnpublished)
  1447  	if err != nil {
  1448  		return err
  1449  	}
  1450  
  1451  	// Write the new database version.
  1452  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1453  }
  1454  
  1455  func tspendPolicyUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1456  	const oldVersion = 18
  1457  	const newVersion = 19
  1458  
  1459  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1460  
  1461  	// Assert that this function is only called on version 18 databases.
  1462  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1463  	if err != nil {
  1464  		return err
  1465  	}
  1466  	if dbVersion != oldVersion {
  1467  		return errors.E(errors.Invalid, "tspendPolicyUpgrade inappropriately called")
  1468  	}
  1469  
  1470  	_, err = tx.CreateTopLevelBucket(treasuryPolicyBucketKey)
  1471  	if err != nil {
  1472  		return errors.E(errors.IO, err)
  1473  	}
  1474  
  1475  	// Write the new database version.
  1476  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1477  }
  1478  
  1479  func vspBucketUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1480  	const oldVersion = 19
  1481  	const newVersion = 20
  1482  
  1483  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1484  	// Assert that this function is only called on version 19 databases.
  1485  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1486  	if err != nil {
  1487  		return err
  1488  	}
  1489  	if dbVersion != oldVersion {
  1490  		return errors.E(errors.Invalid, "vspBucketUpgrade inappropriately called")
  1491  	}
  1492  
  1493  	// Create the vsp tickets bucket.
  1494  	_, err = tx.CreateTopLevelBucket(vspBucketKey)
  1495  	if err != nil {
  1496  		return err
  1497  	}
  1498  
  1499  	// Write the new database version.
  1500  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1501  }
  1502  
  1503  func vspStatusUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1504  	const oldVersion = 20
  1505  	const newVersion = 21
  1506  
  1507  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1508  	// Assert that this function is only called on version 20 databases.
  1509  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1510  	if err != nil {
  1511  		return err
  1512  	}
  1513  	if dbVersion != oldVersion {
  1514  		return errors.E(errors.Invalid, "vspStatusUpgrade inappropriately called")
  1515  	}
  1516  
  1517  	bucket := tx.ReadWriteBucket(vspBucketKey)
  1518  	tix := make(map[string][]byte)
  1519  	cursor := bucket.ReadCursor()
  1520  	statusBytes := make([]byte, 4)
  1521  	byteOrder.PutUint32(statusBytes, uint32(VSPFeeProcessErrored))
  1522  	for k, v := cursor.First(); v != nil; k, v = cursor.Next() {
  1523  		tix[string(k)] = append(v[:len(v):len(v)], statusBytes...)
  1524  	}
  1525  	cursor.Close()
  1526  	for k, v := range tix {
  1527  		err := bucket.Put([]byte(k), v)
  1528  		if err != nil {
  1529  			return errors.E(errors.IO, err)
  1530  		}
  1531  	}
  1532  
  1533  	// Write the new database version.
  1534  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1535  }
  1536  
  1537  func tspendHashPolicyUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1538  	const oldVersion = 21
  1539  	const newVersion = 22
  1540  
  1541  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1542  
  1543  	// Assert that this function is only called on version 21 databases.
  1544  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1545  	if err != nil {
  1546  		return err
  1547  	}
  1548  	if dbVersion != oldVersion {
  1549  		return errors.E(errors.Invalid, "tspendHashPolicyUpgrade inappropriately called")
  1550  	}
  1551  
  1552  	_, err = tx.CreateTopLevelBucket(tspendPolicyBucketKey)
  1553  	if err != nil {
  1554  		return errors.E(errors.IO, err)
  1555  	}
  1556  
  1557  	// Write the new database version.
  1558  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1559  }
  1560  
  1561  func vspHostVersionUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1562  	const oldVersion = 22
  1563  	const newVersion = 23
  1564  
  1565  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1566  	// Assert that this function is only called on version 20 databases.
  1567  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1568  	if err != nil {
  1569  		return err
  1570  	}
  1571  	if dbVersion != oldVersion {
  1572  		return errors.E(errors.Invalid, "vspStatusUpgrade inappropriately called")
  1573  	}
  1574  
  1575  	// Create new vsp host bucket
  1576  	_, err = tx.CreateTopLevelBucket(vspHostBucketKey)
  1577  	if err != nil {
  1578  		return errors.E(errors.IO, err)
  1579  	}
  1580  	// Create new vsp pubkey bucket
  1581  	_, err = tx.CreateTopLevelBucket(vspPubKeyBucketKey)
  1582  	if err != nil {
  1583  		return errors.E(errors.IO, err)
  1584  	}
  1585  
  1586  	// Create first entry into vsp host bucket of an 0 length host, keyed
  1587  	// at 0.
  1588  	bucket := tx.ReadWriteBucket(vspHostBucketKey)
  1589  	k := make([]byte, 4)
  1590  	byteOrder.PutUint32(k, 0)
  1591  	buf := make([]byte, 4)
  1592  	byteOrder.PutUint32(buf[0:4], uint32(0))
  1593  	err = bucket.Put(k, make([]byte, 4))
  1594  	if err != nil {
  1595  		return errors.E(errors.IO, err)
  1596  	}
  1597  
  1598  	// Enter current index of vsp host as 0
  1599  	vspIndexBytes := make([]byte, 4)
  1600  	byteOrder.PutUint32(vspIndexBytes, 0)
  1601  	err = bucket.Put(rootVSPHostIndex, vspIndexBytes)
  1602  	if err != nil {
  1603  		return errors.E(errors.IO, err)
  1604  	}
  1605  
  1606  	// Set all existing tickets in the vspBucketKey to have the vsp host entry
  1607  	// of 0.  These will be noticed on the next time the process managed tickets
  1608  	// is completed and all entried will be updated to their proper values
  1609  	// once they have been confirmed at a particular VSP.
  1610  	ticketBucket := tx.ReadWriteBucket(vspBucketKey)
  1611  	tix := make(map[string][]byte)
  1612  	cursor := ticketBucket.ReadCursor()
  1613  	vspHostZero := make([]byte, 4)
  1614  	byteOrder.PutUint32(vspHostZero, uint32(0))
  1615  	for k, v := cursor.First(); v != nil; k, v = cursor.Next() {
  1616  		tix[string(k)] = append(v[:len(v):len(v)], vspHostZero...)
  1617  	}
  1618  	cursor.Close()
  1619  	for k, v := range tix {
  1620  		err := ticketBucket.Put([]byte(k), v)
  1621  		if err != nil {
  1622  			return errors.E(errors.IO, err)
  1623  		}
  1624  	}
  1625  	// Write the new database version.
  1626  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1627  }
  1628  
  1629  func vspTreasuryPoliciesUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1630  	const oldVersion = 23
  1631  	const newVersion = 24
  1632  
  1633  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1634  
  1635  	// Assert that this function is only called on version 22 databases.
  1636  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1637  	if err != nil {
  1638  		return err
  1639  	}
  1640  	if dbVersion != oldVersion {
  1641  		return errors.E(errors.Invalid, "vspTreasuryPoliciesUpgrade inappropriately called")
  1642  	}
  1643  
  1644  	_, err = tx.CreateTopLevelBucket(vspTreasuryPolicyBucketKey)
  1645  	if err != nil {
  1646  		return errors.E(errors.IO, err)
  1647  	}
  1648  	_, err = tx.CreateTopLevelBucket(vspTspendPolicyBucketKey)
  1649  	if err != nil {
  1650  		return errors.E(errors.IO, err)
  1651  	}
  1652  
  1653  	// Write the new database version.
  1654  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1655  }
  1656  
  1657  func importVotingAccountUpgrade(tx walletdb.ReadWriteTx, publicPassphrase []byte, params *chaincfg.Params) error {
  1658  	const oldVersion = 24
  1659  	const newVersion = 25
  1660  
  1661  	metadataBucket := tx.ReadWriteBucket(unifiedDBMetadata{}.rootBucketKey())
  1662  
  1663  	// Assert that this function is only called on version 24 databases.
  1664  	dbVersion, err := unifiedDBMetadata{}.getVersion(metadataBucket)
  1665  	if err != nil {
  1666  		return err
  1667  	}
  1668  	if dbVersion != oldVersion {
  1669  		return errors.E(errors.Invalid, "importVotingAccountUpgrade inappropriately called")
  1670  	}
  1671  
  1672  	// Write the new database version.
  1673  	return unifiedDBMetadata{}.putVersion(metadataBucket, newVersion)
  1674  }
  1675  
  1676  // Upgrade checks whether the any upgrades are necessary before the database is
  1677  // ready for application usage.  If any are, they are performed.
  1678  func Upgrade(ctx context.Context, db walletdb.DB, publicPassphrase []byte, params *chaincfg.Params) error {
  1679  	var version uint32
  1680  	err := walletdb.View(ctx, db, func(tx walletdb.ReadTx) error {
  1681  		var err error
  1682  		metadataBucket := tx.ReadBucket(unifiedDBMetadata{}.rootBucketKey())
  1683  		if metadataBucket == nil {
  1684  			// This could indicate either an unitialized db or one that hasn't
  1685  			// yet been migrated.
  1686  			return errors.E(errors.IO, "missing metadata bucket")
  1687  		}
  1688  		version, err = unifiedDBMetadata{}.getVersion(metadataBucket)
  1689  		return err
  1690  	})
  1691  	if err != nil {
  1692  		return err
  1693  	}
  1694  
  1695  	if version >= DBVersion {
  1696  		// No upgrades necessary.
  1697  		return nil
  1698  	}
  1699  
  1700  	log.Infof("Upgrading database from version %d to %d", version, DBVersion)
  1701  
  1702  	return walletdb.Update(ctx, db, func(tx walletdb.ReadWriteTx) error {
  1703  		// Execute all necessary upgrades in order.
  1704  		for _, upgrade := range upgrades[version:] {
  1705  			err := upgrade(tx, publicPassphrase, params)
  1706  			if err != nil {
  1707  				return err
  1708  			}
  1709  		}
  1710  		return nil
  1711  	})
  1712  }