wallet_migration.cpp raw

   1  // Copyright (c) 2024-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or https://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <addresstype.h>
   6  #include <bench/bench.h>
   7  #include <consensus/amount.h>
   8  #include <interfaces/wallet.h>
   9  #include <key.h>
  10  #include <primitives/block.h>
  11  #include <primitives/transaction.h>
  12  #include <pubkey.h>
  13  #include <script/script.h>
  14  #include <sync.h>
  15  #include <test/util/setup_common.h>
  16  #include <tinyformat.h>
  17  #include <util/check.h>
  18  #include <util/result.h>
  19  #include <wallet/db.h>
  20  #include <wallet/scriptpubkeyman.h>
  21  #include <wallet/test/util.h>
  22  #include <wallet/transaction.h>
  23  #include <wallet/wallet.h>
  24  #include <wallet/walletdb.h>
  25  
  26  #include <algorithm>
  27  #include <cstddef>
  28  #include <memory>
  29  #include <optional>
  30  #include <string>
  31  #include <utility>
  32  #include <vector>
  33  
  34  namespace wallet{
  35  
  36  static void WalletMigration(benchmark::Bench& bench)
  37  {
  38      const auto test_setup{MakeNoLogFileContext<TestingSetup>()};
  39      const auto loader{MakeWalletLoader(*test_setup->m_node.chain, test_setup->m_args)};
  40  
  41      // Number of imported watch only addresses
  42      int NUM_WATCH_ONLY_ADDR = 20;
  43  
  44      // Add watch-only addresses
  45      std::vector<std::pair<CScript, CTxDestination>> scripts_watch_only;
  46      for (int w = 0; w < NUM_WATCH_ONLY_ADDR; ++w) {
  47          CKey key = GenerateRandomKey();
  48          const PKHash dest{key.GetPubKey()};
  49          scripts_watch_only.emplace_back(GetScriptForDestination(dest), dest);
  50      }
  51  
  52      // Generate transactions and local addresses
  53      std::vector<CKey> keys(500);
  54      std::ranges::generate(keys, []{ return GenerateRandomKey(); });
  55  
  56      std::unique_ptr<CWallet> wallet;
  57      size_t i = 0;
  58      bench.epochs(/*numEpochs=*/1) // run the migration exactly once
  59          .setup([&] {
  60              // Setup legacy wallet
  61              wallet = std::make_unique<CWallet>(test_setup->m_node.chain.get(), std::string(i++, 'A'), CreateMockableWalletDatabase());
  62              LegacyDataSPKM* legacy_spkm = wallet->GetOrCreateLegacyDataSPKM();
  63              WalletBatch batch{wallet->GetDatabase()};
  64  
  65              LOCK(wallet->cs_wallet);
  66  
  67              // Write a best block record as migration expects one to exist
  68              CBlockLocator loc;
  69              batch.WriteBestBlock(loc);
  70  
  71              // Add watch-only addresses
  72              for (size_t w = 0; w < scripts_watch_only.size(); ++w) {
  73                  const auto& [script, dest] = scripts_watch_only.at(w);
  74                  assert(legacy_spkm->LoadWatchOnly(script));
  75                  assert(wallet->SetAddressBook(dest, strprintf("watch_%d", w), /*purpose=*/std::nullopt));
  76                  batch.WriteWatchOnly(script, CKeyMetadata());
  77              }
  78  
  79              // Generate transactions and local addresses
  80              for (size_t j = 0; j < keys.size(); ++j) {
  81                  const CKey& key = keys.at(j);
  82                  // Load key, scripts and create address book record
  83                  CPubKey pubkey = key.GetPubKey();
  84                  Assert(legacy_spkm->LoadKey(key, pubkey));
  85                  CTxDestination dest{PKHash(pubkey)};
  86                  Assert(wallet->SetAddressBook(dest, strprintf("legacy_%d", j), /*purpose=*/std::nullopt));
  87  
  88                  CMutableTransaction mtx;
  89                  mtx.vout.emplace_back(COIN, GetScriptForDestination(dest));
  90                  mtx.vout.emplace_back(COIN, scripts_watch_only.at(j % NUM_WATCH_ONLY_ADDR).first);
  91                  mtx.vin.resize(2);
  92                  wallet->AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}, /*update_wtx=*/nullptr, /*rescanning_old_block=*/true);
  93                  batch.WriteKey(pubkey, key.GetPrivKey(), CKeyMetadata());
  94              }
  95          })
  96          .run([&] {
  97              auto res{MigrateLegacyToDescriptor(std::move(wallet), /*passphrase=*/"", *loader->context())};
  98              assert(res);
  99              assert(res->wallet);
 100              assert(res->watchonly_wallet);
 101          });
 102  }
 103  
 104  BENCHMARK(WalletMigration);
 105  
 106  } // namespace wallet
 107