Repository: bitcoin/bitcoin
File: src/wallet/scriptpubkeyman.cpp (C++)

Code before:
            desc_spk_man->TopUpWithDB(batch);
            auto desc_spks_set = desc_spk_man->GetScriptPubKeys();
            desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end());

            out.desc_spkms.push_back(std::move(desc_spk_man));
        }

        // Remove the scriptPubKeys from our current set
        for (const CScript& desc_spk : desc_spks) {
            auto del_it = spks.find(desc_spk);
            assert(del_it != spks.end());
            assert(IsMine(desc_spk) != ISMINE_NO);
            it = spks.erase(del_it);
        }
    }

    // Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH
    // So we have to check if any of our scripts are a multisig and if so, add the P2SH
    for (const auto& script_pair : mapScripts) {
        const CScript script = script_pair.second;

        // Get birthdate from script meta
        uint64_t creation_time = 0;
        const auto& it = m_script_metadata.find(CScriptID(script));
        if (it != m_script_metadata.end()) {
            creation_time = it->second.nCreateTime;
        }

        std::vector<std::vector<unsigned char>> sols;
        TxoutType type = Solver(script, sols);
        if (type == TxoutType::MULTISIG) {
            CScript sh_spk = GetScriptForDestination(ScriptHash(script));
            CTxDestination witdest = WitnessV0ScriptHash(script);
            CScript witprog = GetScriptForDestination(witdest);
            CScript sh_wsh_spk = GetScriptForDestination(ScriptHash(witprog));

            // We only want the multisigs that we have not already seen, i.e. they are not watchonly and not spendable
            // For P2SH, a multisig is not ISMINE_NO when:
            // * All keys are in the wallet
            // * The multisig itself is watch only
            // * The P2SH is watch only
            // For P2SH-P2WSH, if the script is in the wallet, then it will have the same conditions as P2SH.
            // For P2WSH, a multisig is not ISMINE_NO when, other than the P2SH conditions:
            // * The P2WSH script is in the wallet and it is being watched
            std::vector<std::vector<unsigned char>> keys(sols.begin() + 1, sols.begin() + sols.size() - 1);
            if (HaveWatchOnly(sh_spk) || HaveWatchOnly(script) || HaveKeys(keys, *this) || (HaveCScript(CScriptID(witprog)) && HaveWatchOnly(witprog))) {
                // The above emulates IsMine for these 3 scriptPubKeys, so double check that by running IsMine
                assert(IsMine(sh_spk) != ISMINE_NO || IsMine(witprog) != ISMINE_NO || IsMine(sh_wsh_spk) != ISMINE_NO);
                continue;
            }
            assert(IsMine(sh_spk) == ISMINE_NO && IsMine(witprog) == ISMINE_NO && IsMine(sh_wsh_spk) == ISMINE_NO);

Review comment (performance):
in "wallet: batch MigrateToDescriptor() db transactions" (https://github.com/bitcoin/bitcoin/pull/28574/commits/5cc89e5eba7330ea3ab64ca66e364170dcd0b4f7):

Could get rid of the `key.GetPubKey()` call here and completely get rid of the `privkeyid` set above:

https://github.com/bitcoin/bitcoin/pull/28574/commits/5cc89e5eba7330ea3ab64ca66e364170dcd0b4f7#diff-55ce403d9c3440b4a38a261b7d452f84dd9a1d09b2a5ef1b4d3ac0c3567b9106R1907-R1910

since `keys.origins` is a pair of the `keyid` and `CPubKey`. Running the bench with with this suggestion seemed to help:

```
# after
|               ns/op |                op/s |    err% |          ins/op |          cyc/op |    IPC |         bra/op |   miss% |     total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|    6,395,421,573.00 |                0.16 |    0.0% |32,790,285,573.00 |11,895,327,005.00 |  2.757 |3,044,102,935.00 |    0.2% |      6.40 | `WalletMigration`
```

6.94 -> 6.84 -> 6.4


Code after:
        std::set<CKeyID> privkeyids;
        for (const auto& key_orig_pair : keys.origins) {
            privkeyids.insert(key_orig_pair.first);
        }

        std::vector<CScript> desc_spks;

        // Make the descriptor string with private keys
        std::string desc_str;
        bool watchonly = !desc->ToPrivateString(*this, desc_str);
        if (watchonly && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
            out.watch_descs.emplace_back(desc->ToString(), creation_time);

            // Get the scriptPubKeys without writing this to the wallet
            FlatSigningProvider provider;
            desc->Expand(0, provider, desc_spks, provider);
        } else {
            // Make the DescriptorScriptPubKeyMan and get the scriptPubKeys
            WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0);
            auto desc_spk_man = std::make_unique<DescriptorScriptPubKeyMan>(m_storage, w_desc, /*keypool_size=*/0);
            for (const auto& keyid : privkeyids) {
                CKey key;
                if (!GetKey(keyid, key)) {
                    continue;
                }
                WITH_LOCK(desc_spk_man->cs_desc_man, desc_spk_man->AddDescriptorKeyWithDB(batch, key, key.GetPubKey()));
            }
            desc_spk_man->TopUpWithDB(batch);
            auto desc_spks_set = desc_spk_man->GetScriptPubKeys();
            desc_spks.insert(desc_spks.end(), desc_spks_set.begin(), desc_spks_set.end());

            out.desc_spkms.push_back(std::move(desc_spk_man));
        }

        // Remove the scriptPubKeys from our current set
        for (const CScript& desc_spk : desc_spks) {
            auto del_it = spks.find(desc_spk);
            assert(del_it != spks.end());
            assert(IsMine(desc_spk) != ISMINE_NO);
            it = spks.erase(del_it);
        }
    }

    // Multisigs are special. They don't show up as ISMINE_SPENDABLE unless they are in a P2SH
    // So we have to check if any of our scripts are a multisig and if so, add the P2SH
    for (const auto& script_pair : mapScripts) {
        const CScript script = script_pair.second;

        // Get birthdate from script meta
        uint64_t creation_time = 0;
        const auto& it = m_script_metadata.find(CScriptID(script));