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(¶ms.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 }