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