From 779e7825dbfba6a1ebd6ad62a8b2f312bd2c6b5f Mon Sep 17 00:00:00 2001 From: brunoerg Date: Sat, 9 Mar 2024 06:22:50 -0300 Subject: [PATCH 1/5] fuzz: wallet: add target for `MigrateToDescriptor` --- src/wallet/test/fuzz/scriptpubkeyman.cpp | 145 ++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp index 243c9c69114d..7e186576bb60 100644 --- a/src/wallet/test/fuzz/scriptpubkeyman.cpp +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -21,7 +21,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -46,10 +48,15 @@ void initialize_spkm() { static const auto testing_setup{MakeNoLogFileContext()}; g_setup = testing_setup.get(); - SelectParams(ChainType::MAIN); MOCKED_DESC_CONVERTER.Init(); } +void initialize_spkm_migration() +{ + static const auto testing_setup{MakeNoLogFileContext()}; + g_setup = testing_setup.get(); +} + static std::optional> CreateWalletDescriptor(FuzzedDataProvider& fuzzed_data_provider) { const std::string mocked_descriptor{fuzzed_data_provider.ConsumeRandomLengthString()}; @@ -193,5 +200,141 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm) (void)spk_manager->GetKeyPoolSize(); } +FUZZ_TARGET(spkm_migration, .init = initialize_spkm_migration) +{ + SeedRandomStateForTest(SeedRand::ZEROS); + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + const auto& node{g_setup->m_node}; + Chainstate& chainstate{node.chainman->ActiveChainstate()}; + + std::unique_ptr wallet_ptr{std::make_unique(node.chain.get(), "", CreateMockableWalletDatabase())}; + CWallet& wallet{*wallet_ptr}; + wallet.m_keypool_size = 1; + { + LOCK(wallet.cs_wallet); + wallet.UnsetWalletFlag(WALLET_FLAG_DESCRIPTORS); + wallet.SetLastBlockProcessed(chainstate.m_chain.Height(), chainstate.m_chain.Tip()->GetBlockHash()); + } + + auto& legacy_data{*wallet.GetOrCreateLegacyDataSPKM()}; + + std::vector keys; + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 30) { + const auto key{ConsumePrivateKey(fuzzed_data_provider)}; + if (!key.IsValid()) return; + auto pub_key{key.GetPubKey()}; + if (!pub_key.IsFullyValid()) return; + if (legacy_data.LoadKey(key, pub_key) && std::find(keys.begin(), keys.end(), key) == keys.end()) keys.push_back(key); + } + + bool add_hd_chain{fuzzed_data_provider.ConsumeBool() && !keys.empty()}; + CHDChain hd_chain; + auto version{fuzzed_data_provider.ConsumeBool() ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE}; + CKey hd_key; + if (add_hd_chain) { + hd_key = PickValue(fuzzed_data_provider, keys); + hd_chain.nVersion = version; + hd_chain.seed_id = hd_key.GetPubKey().GetID(); + legacy_data.LoadHDChain(hd_chain); + } + + bool add_inactive_hd_chain{fuzzed_data_provider.ConsumeBool() && !keys.empty()}; + if (add_inactive_hd_chain) { + hd_key = PickValue(fuzzed_data_provider, keys); + hd_chain.nVersion = fuzzed_data_provider.ConsumeBool() ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; + hd_chain.seed_id = hd_key.GetPubKey().GetID(); + legacy_data.AddInactiveHDChain(hd_chain); + } + + bool watch_only = false; + const auto pub_key = ConsumeDeserializable(fuzzed_data_provider); + if (!pub_key || !pub_key->IsFullyValid()) return; + auto script_dest{GetScriptForDestination(WitnessV0KeyHash{*pub_key})}; + if (fuzzed_data_provider.ConsumeBool()) { + script_dest = GetScriptForDestination(CTxDestination{PKHash(*pub_key)}); + } + if (legacy_data.LoadWatchOnly(script_dest)) watch_only = true; + + size_t added_script{0}; + bool good_data{true}; + LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 30) { + CallOneOf( + fuzzed_data_provider, + [&] { + CKey key; + if (!keys.empty()) { + key = PickValue(fuzzed_data_provider, keys); + } else { + key = ConsumePrivateKey(fuzzed_data_provider, /*compressed=*/fuzzed_data_provider.ConsumeBool()); + } + if (!key.IsValid()) return; + auto pub_key{key.GetPubKey()}; + CScript script; + CallOneOf( + fuzzed_data_provider, + [&] { + script = GetScriptForDestination(CTxDestination{PKHash(pub_key)}); + }, + [&] { + script = GetScriptForDestination(WitnessV0KeyHash(pub_key)); + }, + [&] { + std::optional script_opt{ConsumeDeserializable(fuzzed_data_provider)}; + if (!script_opt) { + good_data = false; + return; + } + script = script_opt.value(); + } + ); + if (fuzzed_data_provider.ConsumeBool()) script = GetScriptForDestination(ScriptHash(script)); + if (!legacy_data.HaveCScript(CScriptID(script)) && legacy_data.AddCScript(script)) added_script++; + }, + [&] { + CKey key; + if (!keys.empty()) { + key = PickValue(fuzzed_data_provider, keys); + } else { + key = ConsumePrivateKey(fuzzed_data_provider, /*compressed=*/fuzzed_data_provider.ConsumeBool()); + } + if (!key.IsValid()) return; + const auto num_keys{fuzzed_data_provider.ConsumeIntegralInRange(1, MAX_PUBKEYS_PER_MULTISIG)}; + std::vector pubkeys; + pubkeys.emplace_back(key.GetPubKey()); + for (size_t i = 1; i < num_keys; i++) { + if (fuzzed_data_provider.ConsumeBool()) { + pubkeys.emplace_back(key.GetPubKey()); + } else { + CKey private_key{ConsumePrivateKey(fuzzed_data_provider, /*compressed=*/fuzzed_data_provider.ConsumeBool())}; + if (!private_key.IsValid()) return; + pubkeys.emplace_back(private_key.GetPubKey()); + } + } + if (pubkeys.size() < num_keys) return; + CScript multisig_script{GetScriptForMultisig(num_keys, pubkeys)}; + if (!legacy_data.HaveCScript(CScriptID(multisig_script)) && legacy_data.AddCScript(multisig_script)) { + added_script++; + } + } + ); + } + + auto result{legacy_data.MigrateToDescriptor()}; + assert(result); + size_t added_chains{static_cast(add_hd_chain) + static_cast(add_inactive_hd_chain)}; + if ((add_hd_chain && version >= CHDChain::VERSION_HD_CHAIN_SPLIT) || (!add_hd_chain && add_inactive_hd_chain)) { + added_chains *= 2; + } + size_t added_size{keys.size() + added_chains}; + if (added_script > 0) { + assert(result->desc_spkms.size() >= added_size); + } else { + assert(result->desc_spkms.size() == added_size); + } + if (watch_only) assert(!result->watch_descs.empty()); + if (!result->solvable_descs.empty()) assert(added_script > 0); +} + } // namespace } // namespace wallet From 79467e3ec7c2d756c4d824f642ed3856a4e72883 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 10 Mar 2026 20:13:49 +0000 Subject: [PATCH 2/5] threading: never require logging from sync.h sync.h is low-level and should not require any other subsystems. Move the lone remaining logging call to the .cpp. Any cost incurred by an additional function call should be trivial compared to the logging itself. --- src/sync.cpp | 14 ++++++++++++++ src/sync.h | 22 +++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/sync.cpp b/src/sync.cpp index e59f86b94fca..0e5c623d2126 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -19,6 +20,19 @@ #include #include +#ifdef DEBUG_LOCKCONTENTION + +template +void ContendedLock(std::string_view name, std::string_view file, int nLine, LockType& lock) +{ + LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", name, file, nLine), BCLog::LOCK); + lock.lock(); +} +template void ContendedLock(std::string_view name, std::string_view file, int nLine, std::unique_lock& lock); +template void ContendedLock(std::string_view name, std::string_view file, int nLine, std::unique_lock& lock); + +#endif + #ifdef DEBUG_LOCKORDER // // Early deadlock detection. diff --git a/src/sync.h b/src/sync.h index 548b51d9d168..00fdaae942b8 100644 --- a/src/sync.h +++ b/src/sync.h @@ -6,10 +6,6 @@ #ifndef BITCOIN_SYNC_H #define BITCOIN_SYNC_H -#ifdef DEBUG_LOCKCONTENTION -#include -#endif - #include // IWYU pragma: export #include @@ -77,6 +73,16 @@ inline void DeleteLock(void* cs) {} inline bool LockStackEmpty() { return true; } #endif +/* + * Called when a mutex fails to lock immediately because it is held by another + * thread, or spuriously. Responsible for locking the lock before returning. + */ +#ifdef DEBUG_LOCKCONTENTION + +template +void ContendedLock(std::string_view name, std::string_view file, int nLine, LockType& lock); +#endif + /** * Template mixin that adds -Wthread-safety locking annotations and lock order * checking to a subset of the mutex API. @@ -151,10 +157,12 @@ class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock { EnterCritical(pszName, pszFile, nLine, Base::mutex()); #ifdef DEBUG_LOCKCONTENTION - if (Base::try_lock()) return; - LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK); -#endif + if (!Base::try_lock()) { + ContendedLock(pszName, pszFile, nLine, static_cast(*this)); + } +#else Base::lock(); +#endif } bool TryEnter(const char* pszName, const char* pszFile, int nLine) From 8b0fb64c0243fa06d97a394dfb4500f97ef05f7b Mon Sep 17 00:00:00 2001 From: sedited Date: Mon, 2 Mar 2026 09:42:18 +0100 Subject: [PATCH 3/5] validation: Move validation signal events to task runner Currently arguments passed through the validation interface are copied three times. Once on capture in the event, again when the event itself is copied into the task runner lambda, and when the various arguments used by the logging statement are copied into the lambda. This change avoids the variables captured by the event being copied again. Next to avoiding needless copies, this is done in preparation of the following two commits, which seek to clarify the ownership semantics of the blocks passed through the validation interface. Co-authored-by: stickies-v --- src/validationinterface.cpp | 78 +++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index c7be6abc3a1e..3c142f8db282 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -156,33 +156,39 @@ void ValidationSignals::SyncWithValidationInterfaceQueue() // Use a macro instead of a function for conditional logging to prevent // evaluating arguments when logging is not enabled. -// -// NOTE: The lambda captures all local variables by value. -#define ENQUEUE_AND_LOG_EVENT(event, fmt, name, ...) \ - do { \ - auto local_name = (name); \ - LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__); \ - m_internals->m_task_runner->insert([=] { \ - LOG_EVENT(fmt, local_name, __VA_ARGS__); \ - event(); \ - }); \ +#define ENQUEUE_AND_LOG_EVENT(event, log_msg) \ + do { \ + static_assert(std::is_rvalue_reference_v, \ + "event must be passed as an rvalue"); \ + static_assert(std::is_rvalue_reference_v, \ + "log_msg must be passed as an rvalue"); \ + auto enqueue_log_msg = (log_msg); \ + LOG_EVENT("Enqueuing %s", enqueue_log_msg); \ + m_internals->m_task_runner->insert([local_log_msg = std::move(enqueue_log_msg), local_event = (event)] { \ + LOG_EVENT("%s", local_log_msg); \ + local_event(); \ + }); \ } while (0) +#define LOG_MSG(fmt, ...) \ + (ShouldLog(BCLog::VALIDATION, BCLog::Level::Debug) ? tfm::format((fmt), __VA_ARGS__) : std::string{}) + #define LOG_EVENT(fmt, ...) \ - LogDebug(BCLog::VALIDATION, fmt "\n", __VA_ARGS__) + LogDebug(BCLog::VALIDATION, fmt, __VA_ARGS__) void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which // the chain actually updates. One way to ensure this is for the caller to invoke this signal // in the same critical section where the chain is updated - auto event = [pindexNew, pindexFork, fInitialDownload, this] { - m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); }); - }; - ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__, + auto log_msg = LOG_MSG("%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__, pindexNew->GetBlockHash().ToString(), pindexFork ? pindexFork->GetBlockHash().ToString() : "null", fInitialDownload); + auto event = [pindexNew, pindexFork, fInitialDownload, this] { + m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); }); + }; + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd) @@ -193,61 +199,67 @@ void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd) void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence) { + auto log_msg = LOG_MSG("%s: txid=%s wtxid=%s", __func__, + tx.info.m_tx->GetHash().ToString(), + tx.info.m_tx->GetWitnessHash().ToString()); auto event = [tx, mempool_sequence, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); }); }; - ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__, - tx.info.m_tx->GetHash().ToString(), - tx.info.m_tx->GetWitnessHash().ToString()); + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) { - auto event = [tx, reason, mempool_sequence, this] { - m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); }); - }; - ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__, + auto log_msg = LOG_MSG("%s: txid=%s wtxid=%s reason=%s", __func__, tx->GetHash().ToString(), tx->GetWitnessHash().ToString(), RemovalReasonToString(reason)); + auto event = [tx, reason, mempool_sequence, this] { + m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); }); + }; + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::BlockConnected(const ChainstateRole& role, const std::shared_ptr& pblock, const CBlockIndex* pindex) { + auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__, + pblock->GetHash().ToString(), + pindex->nHeight); auto event = [role, pblock, pindex, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); }); }; - ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__, - pblock->GetHash().ToString(), - pindex->nHeight); + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector& txs_removed_for_block, unsigned int nBlockHeight) { + auto log_msg = LOG_MSG("%s: block height=%s txs removed=%s", __func__, + nBlockHeight, + txs_removed_for_block.size()); auto event = [txs_removed_for_block, nBlockHeight, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight); }); }; - ENQUEUE_AND_LOG_EVENT(event, "%s: block height=%s txs removed=%s", __func__, - nBlockHeight, - txs_removed_for_block.size()); + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::BlockDisconnected(const std::shared_ptr& pblock, const CBlockIndex* pindex) { + auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__, + pblock->GetHash().ToString(), + pindex->nHeight); auto event = [pblock, pindex, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); }); }; - ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__, - pblock->GetHash().ToString(), - pindex->nHeight); + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::ChainStateFlushed(const ChainstateRole& role, const CBlockLocator& locator) { + auto log_msg = LOG_MSG("%s: block hash=%s", __func__, + locator.IsNull() ? "null" : locator.vHave.front().ToString()); auto event = [role, locator, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); }); }; - ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__, - locator.IsNull() ? "null" : locator.vHave.front().ToString()); + ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } void ValidationSignals::BlockChecked(const std::shared_ptr& block, const BlockValidationState& state) From 4d02d2b316a41119e77448056339cb8fb9ae7b6a Mon Sep 17 00:00:00 2001 From: sedited Date: Fri, 30 Jan 2026 12:48:04 +0100 Subject: [PATCH 4/5] validation: Move block into BlockConnected signal This makes existing behaviour of the block's destructor triggering on the scheduler thread more explicit by moving it to the thread. The scheduler thread doing so is useful, since it does not block the thread doing validation while releasing a block's memory. Previously, both the caller and the queued event lambda held copies of the shared_ptr. The block would typically be freed on the scheduler thread - but only because it went out of scope before the queued event on the scheduler thread ran. If the scheduler ran first, the block would instead be freed on the validation thread. Now, ownership is transferred at each step when invoking the BlockConnected signal: connected_blocks yields via std::move, BlockConnected takes by value, and the event lambda move-captures the shared_ptr. Though it is possible that this only decrements the block's reference count, blocks are also read from disk in `ConnectTip`, which now explicitly results in their memory being released on the scheduler thread. --- src/validation.cpp | 4 ++-- src/validationinterface.cpp | 4 ++-- src/validationinterface.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 1efe109b6f60..f9cc7d78e1b9 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3391,9 +3391,9 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr< } pindexNewTip = m_chain.Tip(); - for (const auto& [index, block] : connected_blocks) { + for (auto& [index, block] : std::move(connected_blocks)) { if (m_chainman.m_options.signals) { - m_chainman.m_options.signals->BlockConnected(chainstate_role, Assert(block), Assert(index)); + m_chainman.m_options.signals->BlockConnected(chainstate_role, std::move(Assert(block)), Assert(index)); } } diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 3c142f8db282..3e939de964ee 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -219,12 +219,12 @@ void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); } -void ValidationSignals::BlockConnected(const ChainstateRole& role, const std::shared_ptr& pblock, const CBlockIndex* pindex) +void ValidationSignals::BlockConnected(const ChainstateRole& role, std::shared_ptr pblock, const CBlockIndex* pindex) { auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__, pblock->GetHash().ToString(), pindex->nHeight); - auto event = [role, pblock, pindex, this] { + auto event = [role, pblock = std::move(pblock), pindex, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); }); }; ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); diff --git a/src/validationinterface.h b/src/validationinterface.h index 4777e8dca8db..641afd555be2 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -223,7 +223,7 @@ class ValidationSignals { void TransactionAddedToMempool(const NewMempoolTransactionInfo&, uint64_t mempool_sequence); void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence); void MempoolTransactionsRemovedForBlock(const std::vector&, unsigned int nBlockHeight); - void BlockConnected(const kernel::ChainstateRole&, const std::shared_ptr&, const CBlockIndex* pindex); + void BlockConnected(const kernel::ChainstateRole&, std::shared_ptr, const CBlockIndex* pindex); void BlockDisconnected(const std::shared_ptr &, const CBlockIndex* pindex); void ChainStateFlushed(const kernel::ChainstateRole&, const CBlockLocator&); void BlockChecked(const std::shared_ptr&, const BlockValidationState&); From d6f680b4275296e4a7f9e6ea693149e59800e495 Mon Sep 17 00:00:00 2001 From: sedited Date: Sat, 28 Feb 2026 12:13:53 +0100 Subject: [PATCH 5/5] validation: Move block into BlockDisconnected signal This makes existing behaviour of the block's destructor triggering on the scheduler thread more explicit by moving it to the thread. The scheduler thread doing so is useful, since it does not block the thread doing validation while releasing a block's memory. DisconnectTip already creates and destroys the block itself, so moving it into the validation signals is well scoped. --- src/validation.cpp | 2 +- src/validationinterface.cpp | 4 ++-- src/validationinterface.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index f9cc7d78e1b9..00c1bab501f3 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2982,7 +2982,7 @@ bool Chainstate::DisconnectTip(BlockValidationState& state, DisconnectedBlockTra // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: if (m_chainman.m_options.signals) { - m_chainman.m_options.signals->BlockDisconnected(pblock, pindexDelete); + m_chainman.m_options.signals->BlockDisconnected(std::move(pblock), pindexDelete); } return true; } diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 3e939de964ee..45f38b3740b3 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -241,12 +241,12 @@ void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector& pblock, const CBlockIndex* pindex) +void ValidationSignals::BlockDisconnected(std::shared_ptr pblock, const CBlockIndex* pindex) { auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__, pblock->GetHash().ToString(), pindex->nHeight); - auto event = [pblock, pindex, this] { + auto event = [pblock = std::move(pblock), pindex, this] { m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); }); }; ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg)); diff --git a/src/validationinterface.h b/src/validationinterface.h index 641afd555be2..8cfe41380ecd 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -224,7 +224,7 @@ class ValidationSignals { void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence); void MempoolTransactionsRemovedForBlock(const std::vector&, unsigned int nBlockHeight); void BlockConnected(const kernel::ChainstateRole&, std::shared_ptr, const CBlockIndex* pindex); - void BlockDisconnected(const std::shared_ptr &, const CBlockIndex* pindex); + void BlockDisconnected(std::shared_ptr, const CBlockIndex* pindex); void ChainStateFlushed(const kernel::ChainstateRole&, const CBlockLocator&); void BlockChecked(const std::shared_ptr&, const BlockValidationState&); void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr&);