mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-05-05 01:10:54 +02:00
e57980b4738c10344baf136de3e050a3cb958ca5 [mempool] Remove NotifyEntryAdded and NotifyEntryRemoved callbacks (John Newbery) 2dd561f36124972d2364f941de9c3417c65f05b6 [validation] Remove pool member from ConnectTrace (John Newbery) 969b65f3f527631ede1a31c7855151e5c5d91f8f [validation] Remove NotifyEntryRemoved callback from ConnectTrace (John Newbery) 5613f9842b4000fed088b8cf7b99674c328d15e1 [validation] Remove conflictedTxs from PerBlockConnectTrace (John Newbery) cdb893443cc16edf974f099b8485e04b3db1b1d7 [validation interface] Remove vtxConflicted from BlockConnected (John Newbery) 1168394d759b13af68acec6d5bfa04aaa24561f8 [wallet] Notify conflicted transactions in TransactionRemovedFromMempool (John Newbery) Pull request description: These boost signals were added in #9371, before we had a `TransactionRemovedFromMempool` method in the validation interface. The `NotifyEntryAdded` callback was used by validation to build a vector of conflicted transactions when connecting a block, which the wallet was notified of in the `BlockConnected` CValidationInterface callback. Now that we have a `TransactionRemovedFromMempool` callback, we can fire that signal directly from the mempool for conflicted transactions. Note that #9371 was implemented to ensure `-walletnotify` events were fired for these conflicted transaction. We inadvertently stopped sending these notifications in #16624 (Sep 2019 commit 7e89994). We should probably fix that, but in a different PR. ACKs for top commit: jonatack: Re-ACK e57980b ryanofsky: Code review ACK e57980b4738c10344baf136de3e050a3cb958ca5, no code changes since previous review, but helpful new code comments have been added and the PR description is now more clear about where the old code came from Tree-SHA512: 3bdbaf1ef2731e788462d4756e69c42a1efdcf168691ce1bbfdaa4b7b55ac3c5b1fd4ab7b90bcdec653703600501b4224d252cfc086aef28f9ce0da3b0563a69
203 lines
9.1 KiB
C++
203 lines
9.1 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <validationinterface.h>
|
|
|
|
#include <chain.h>
|
|
#include <consensus/validation.h>
|
|
#include <logging.h>
|
|
#include <primitives/block.h>
|
|
#include <primitives/transaction.h>
|
|
#include <scheduler.h>
|
|
|
|
#include <future>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
|
|
#include <boost/signals2/signal.hpp>
|
|
|
|
struct ValidationInterfaceConnections {
|
|
boost::signals2::scoped_connection UpdatedBlockTip;
|
|
boost::signals2::scoped_connection TransactionAddedToMempool;
|
|
boost::signals2::scoped_connection BlockConnected;
|
|
boost::signals2::scoped_connection BlockDisconnected;
|
|
boost::signals2::scoped_connection TransactionRemovedFromMempool;
|
|
boost::signals2::scoped_connection ChainStateFlushed;
|
|
boost::signals2::scoped_connection BlockChecked;
|
|
boost::signals2::scoped_connection NewPoWValidBlock;
|
|
};
|
|
|
|
struct MainSignalsInstance {
|
|
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
|
|
boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
|
|
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex)> BlockConnected;
|
|
boost::signals2::signal<void (const std::shared_ptr<const CBlock>&, const CBlockIndex* pindex)> BlockDisconnected;
|
|
boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
|
|
boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
|
|
boost::signals2::signal<void (const CBlock&, const BlockValidationState&)> BlockChecked;
|
|
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
|
|
|
|
// We are not allowed to assume the scheduler only runs in one thread,
|
|
// but must ensure all callbacks happen in-order, so we end up creating
|
|
// our own queue here :(
|
|
SingleThreadedSchedulerClient m_schedulerClient;
|
|
std::unordered_map<CValidationInterface*, ValidationInterfaceConnections> m_connMainSignals;
|
|
|
|
explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
|
|
};
|
|
|
|
static CMainSignals g_signals;
|
|
|
|
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
|
|
assert(!m_internals);
|
|
m_internals.reset(new MainSignalsInstance(&scheduler));
|
|
}
|
|
|
|
void CMainSignals::UnregisterBackgroundSignalScheduler() {
|
|
m_internals.reset(nullptr);
|
|
}
|
|
|
|
void CMainSignals::FlushBackgroundCallbacks() {
|
|
if (m_internals) {
|
|
m_internals->m_schedulerClient.EmptyQueue();
|
|
}
|
|
}
|
|
|
|
size_t CMainSignals::CallbacksPending() {
|
|
if (!m_internals) return 0;
|
|
return m_internals->m_schedulerClient.CallbacksPending();
|
|
}
|
|
|
|
CMainSignals& GetMainSignals()
|
|
{
|
|
return g_signals;
|
|
}
|
|
|
|
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
|
ValidationInterfaceConnections& conns = g_signals.m_internals->m_connMainSignals[pwalletIn];
|
|
conns.UpdatedBlockTip = g_signals.m_internals->UpdatedBlockTip.connect(std::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
|
conns.TransactionAddedToMempool = g_signals.m_internals->TransactionAddedToMempool.connect(std::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, std::placeholders::_1));
|
|
conns.BlockConnected = g_signals.m_internals->BlockConnected.connect(std::bind(&CValidationInterface::BlockConnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
|
|
conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
|
|
conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
|
|
conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
|
|
conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
|
|
conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
|
|
}
|
|
|
|
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
|
if (g_signals.m_internals) {
|
|
g_signals.m_internals->m_connMainSignals.erase(pwalletIn);
|
|
}
|
|
}
|
|
|
|
void UnregisterAllValidationInterfaces() {
|
|
if (!g_signals.m_internals) {
|
|
return;
|
|
}
|
|
g_signals.m_internals->m_connMainSignals.clear();
|
|
}
|
|
|
|
void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) {
|
|
g_signals.m_internals->m_schedulerClient.AddToProcessQueue(std::move(func));
|
|
}
|
|
|
|
void SyncWithValidationInterfaceQueue() {
|
|
AssertLockNotHeld(cs_main);
|
|
// Block until the validation queue drains
|
|
std::promise<void> promise;
|
|
CallFunctionInValidationInterfaceQueue([&promise] {
|
|
promise.set_value();
|
|
});
|
|
promise.get_future().wait();
|
|
}
|
|
|
|
// 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_schedulerClient.AddToProcessQueue([=] { \
|
|
LOG_EVENT(fmt, local_name, __VA_ARGS__); \
|
|
event(); \
|
|
}); \
|
|
} while (0)
|
|
|
|
#define LOG_EVENT(fmt, ...) \
|
|
LogPrint(BCLog::VALIDATION, fmt "\n", __VA_ARGS__)
|
|
|
|
void CMainSignals::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->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
|
|
};
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
|
|
pindexNew->GetBlockHash().ToString(),
|
|
pindexFork ? pindexFork->GetBlockHash().ToString() : "null",
|
|
fInitialDownload);
|
|
}
|
|
|
|
void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
|
|
auto event = [ptx, this] {
|
|
m_internals->TransactionAddedToMempool(ptx);
|
|
};
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
|
|
ptx->GetHash().ToString(),
|
|
ptx->GetWitnessHash().ToString());
|
|
}
|
|
|
|
void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
|
|
auto event = [ptx, this] {
|
|
m_internals->TransactionRemovedFromMempool(ptx);
|
|
};
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
|
|
ptx->GetHash().ToString(),
|
|
ptx->GetWitnessHash().ToString());
|
|
}
|
|
|
|
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
|
|
auto event = [pblock, pindex, this] {
|
|
m_internals->BlockConnected(pblock, pindex);
|
|
};
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
|
|
pblock->GetHash().ToString(),
|
|
pindex->nHeight);
|
|
}
|
|
|
|
void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
|
|
{
|
|
auto event = [pblock, pindex, this] {
|
|
m_internals->BlockDisconnected(pblock, pindex);
|
|
};
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
|
|
pblock->GetHash().ToString(),
|
|
pindex->nHeight);
|
|
}
|
|
|
|
void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
|
|
auto event = [locator, this] {
|
|
m_internals->ChainStateFlushed(locator);
|
|
};
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
|
|
locator.IsNull() ? "null" : locator.vHave.front().ToString());
|
|
}
|
|
|
|
void CMainSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) {
|
|
LOG_EVENT("%s: block hash=%s state=%s", __func__,
|
|
block.GetHash().ToString(), state.ToString());
|
|
m_internals->BlockChecked(block, state);
|
|
}
|
|
|
|
void CMainSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
|
|
LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
|
|
m_internals->NewPoWValidBlock(pindex, block);
|
|
}
|