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

Code before:
{
    AssertLockHeld(cs_wallet);
    bilingual_str str_err;  // future: make RunWithinTxn return a util::Result
    bool was_txn_committed = RunWithinTxn(GetDatabase(), /*process_desc=*/"remove transactions", [&](WalletBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
        util::Result<void> result{RemoveTxs(batch, txs_to_remove)};
        if (!result) str_err = util::ErrorString(result);
        return result.has_value();
    });
    if (!str_err.empty()) return util::Error{str_err};
    if (!was_txn_committed) return util::Error{_("Error starting/committing db txn for wallet transactions removal process")};
    return {}; // all good
}

util::Result<void> CWallet::RemoveTxs(WalletBatch& batch, std::vector<Txid>& txs_to_remove)
{
    AssertLockHeld(cs_wallet);
    if (!batch.HasActiveTxn()) return util::Error{strprintf(_("The transactions removal process can only be executed within a db txn"))};

    // Check for transaction existence and remove entries from disk
    std::vector<decltype(mapWallet)::const_iterator> erased_txs;
    bilingual_str str_err;
    for (const Txid& hash : txs_to_remove) {
        auto it_wtx = mapWallet.find(hash);
        if (it_wtx == mapWallet.end()) {
            return util::Error{strprintf(_("Transaction %s does not belong to this wallet"), hash.GetHex())};
        }
        if (!batch.EraseTx(hash)) {
            return util::Error{strprintf(_("Failure removing transaction: %s"), hash.GetHex())};
        }
        erased_txs.emplace_back(it_wtx);
    }

    // Register callback to update the memory state only when the db txn is actually dumped to disk
    batch.RegisterTxnListener({.on_commit=[&, erased_txs]() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
        // Update the in-memory state and notify upper layers about the removals
        for (const auto& it : erased_txs) {
            const Txid hash{it->first};
            wtxOrdered.erase(it->second.m_it_wtxOrdered);
            for (const auto& txin : it->second.tx->vin)
                mapTxSpends.erase(txin.prevout);
            for (unsigned int i = 0; i < it->second.tx->vout.size(); ++i) {
                m_txos.erase(COutPoint(hash, i));
            }
            mapWallet.erase(it);
            NotifyTransactionChanged(hash, CT_DELETED);
        }

        MarkDirty();
    }, .on_abort={}});

    return {};

Review comment (suggestion):
nano nit:

```suggestion
                auto [first, last] = mapTxSpends.equal_range(txin.prevout);
                for (auto iter = first; iter != last; ++iter) {
```

Code after:
    AssertLockHeld(cs_wallet);
    if (!batch.HasActiveTxn()) return util::Error{strprintf(_("The transactions removal process can only be executed within a db txn"))};

    // Check for transaction existence and remove entries from disk
    std::vector<decltype(mapWallet)::const_iterator> erased_txs;
    bilingual_str str_err;
    for (const Txid& hash : txs_to_remove) {
        auto it_wtx = mapWallet.find(hash);
        if (it_wtx == mapWallet.end()) {
            return util::Error{strprintf(_("Transaction %s does not belong to this wallet"), hash.GetHex())};
        }
        if (!batch.EraseTx(hash)) {
            return util::Error{strprintf(_("Failure removing transaction: %s"), hash.GetHex())};
        }
        erased_txs.emplace_back(it_wtx);
    }

    // Register callback to update the memory state only when the db txn is actually dumped to disk
    batch.RegisterTxnListener({.on_commit=[&, erased_txs]() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) {
        // Update the in-memory state and notify upper layers about the removals
        for (const auto& it : erased_txs) {
            const Txid hash{it->first};
            wtxOrdered.erase(it->second.m_it_wtxOrdered);
            for (const auto& txin : it->second.tx->vin) {
                auto range = mapTxSpends.equal_range(txin.prevout);
                for (auto iter = range.first; iter != range.second; ++iter) {
                    if (iter->second == hash) {
                        mapTxSpends.erase(iter);
                        break;
                    }
                }
            }
            for (unsigned int i = 0; i < it->second.tx->vout.size(); ++i) {
                m_txos.erase(COutPoint(hash, i));
            }
            mapWallet.erase(it);
            NotifyTransactionChanged(hash, CT_DELETED);
        }

        MarkDirty();
    }, .on_abort={}});

    return {};
}

bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::optional<AddressPurpose>& new_purpose)
{
    bool fUpdated = false;
    bool is_mine;
    std::optional<AddressPurpose> purpose;
    {