Repository: bitcoin/bitcoin
File: src/bench/wallet_migration.cpp (C++)

Code before:
#include <wallet/test/util.h>
#include <wallet/context.h>
#include <wallet/receive.h>
#include <wallet/wallet.h>

#include <optional>

#if defined(USE_BDB) && defined(USE_SQLITE) // only enable benchmark when bdb and sqlite are enabled

namespace wallet{

static void WalletMigration(benchmark::Bench& bench)
{
    const auto test_setup = MakeNoLogFileContext<TestingSetup>();

    WalletContext context;
    context.args = &test_setup->m_args;
    context.chain = test_setup->m_node.chain.get();

    // Number of imported watch only addresses
    int NUM_WATCH_ONLY_ADDR = 20;

    // Setup legacy wallet
    DatabaseOptions options;
    options.use_unsafe_sync = true;
    options.verify = false;
    DatabaseStatus status;
    bilingual_str error;
    auto database = MakeWalletDatabase(fs::PathToString(test_setup->m_path_root / "legacy"), options, status, error);
    uint64_t create_flags = 0;
    auto wallet = TestLoadWallet(std::move(database), context, create_flags);

    // Add watch-only addresses
    std::vector<CScript> scripts_watch_only;
    for (int w = 0; w < NUM_WATCH_ONLY_ADDR; ++w) {
        CKey key = GenerateRandomKey();
        LOCK(wallet->cs_wallet);
        const CScript& script = scripts_watch_only.emplace_back(GetScriptForDestination(GetDestinationForKey(key.GetPubKey(), OutputType::LEGACY)));
        bool res = wallet->ImportScriptPubKeys(strprintf("watch_%d", w), {script},
                                    /*have_solving_data=*/false, /*apply_label=*/true, /*timestamp=*/1);
        assert(res);
    }

    // Generate transactions and local addresses
    for (int j = 0; j < 400; ++j) {
        CMutableTransaction mtx;
        mtx.vout.emplace_back(COIN, GetScriptForDestination(*Assert(wallet->GetNewDestination(OutputType::BECH32, strprintf("bench_%d", j)))));
        mtx.vout.emplace_back(COIN, GetScriptForDestination(*Assert(wallet->GetNewDestination(OutputType::LEGACY, strprintf("legacy_%d", j)))));
        mtx.vout.emplace_back(COIN, scripts_watch_only.at(j % NUM_WATCH_ONLY_ADDR));
        mtx.vin.resize(2);
        wallet->AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}, /*update_wtx=*/nullptr, /*fFlushOnClose=*/false, /*rescanning_old_block=*/true);

Review comment (performance):
just an observation that may be helpful to others this is here to satisfy the need for a record locator in `ApplyMigrationData`:

https://github.com/bitcoin/bitcoin/blob/2d07384243c9552aa8e95c80d7a279e2d224a753/src/wallet/wallet.cpp#L4138-L4142

manually triggering the `chainStateFlushed()` handler is equivalent to:

```suggestion
    WalletBatch(wallet->GetDatabase()).WriteBestBlock(CBlockLocator{});
```

or triggering the `blockConnected` signal

<details>
```suggestion
    CBlock block;
    auto& chainman = test_setup->m_node.chainman;
    {
        LOCK(::cs_main);
        // Add it to the index
        CBlockIndex* pindex{chainman->m_blockman.AddToBlockIndex(block, chainman->m_best_header)};
        // add it to the chain
        chainman->ActiveChain().SetTip(*pindex);
        wallet->blockConnected(ChainstateRole::NORMAL, kernel::MakeBlockInfo(pindex, &block));
    }
```
</details>

This used to be handled by the first "phase" of the now split up `MigrateLegacyToDescriptor()` 

Code after:
#include <test/util/setup_common.h>
#include <wallet/test/util.h>
#include <wallet/context.h>
#include <wallet/receive.h>
#include <wallet/wallet.h>

#include <optional>

#if defined(USE_SQLITE) // only enable benchmark when sqlite is enabled

namespace wallet{

static void WalletMigration(benchmark::Bench& bench)
{
    const auto test_setup = MakeNoLogFileContext<TestingSetup>();

    WalletContext context;
    context.args = &test_setup->m_args;
    context.chain = test_setup->m_node.chain.get();

    // Number of imported watch only addresses
    int NUM_WATCH_ONLY_ADDR = 20;

    // Setup legacy wallet
    std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(test_setup->m_node.chain.get(), "", CreateMockableWalletDatabase());
    wallet->chainStateFlushed(ChainstateRole::NORMAL, CBlockLocator{});
    LegacyDataSPKM* legacy_spkm = wallet->GetOrCreateLegacyDataSPKM();

    // Add watch-only addresses
    std::vector<CScript> scripts_watch_only;
    for (int w = 0; w < NUM_WATCH_ONLY_ADDR; ++w) {
        CKey key = GenerateRandomKey();
        LOCK(wallet->cs_wallet);
        const auto& dest = GetDestinationForKey(key.GetPubKey(), OutputType::LEGACY);
        const CScript& script = scripts_watch_only.emplace_back(GetScriptForDestination(dest));
        assert(legacy_spkm->LoadWatchOnly(script));
        assert(wallet->SetAddressBook(dest, strprintf("watch_%d", w), /*purpose=*/std::nullopt));
    }

    // Generate transactions and local addresses
    for (int j = 0; j < 500; ++j) {
        CKey key = GenerateRandomKey();
        CPubKey pubkey = key.GetPubKey();
        // Load key, scripts and create address book record
        Assert(legacy_spkm->LoadKey(key, pubkey));
        CTxDestination dest{PKHash(pubkey)};
        Assert(wallet->SetAddressBook(dest, strprintf("legacy_%d", j), /*purpose=*/std::nullopt));

        CMutableTransaction mtx;
        mtx.vout.emplace_back(COIN, GetScriptForDestination(dest));
        mtx.vout.emplace_back(COIN, scripts_watch_only.at(j % NUM_WATCH_ONLY_ADDR));