2015-02-05 01:11:44 +01:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2022-12-24 23:49:50 +00:00
|
|
|
// Copyright (c) 2009-2022 The Bitcoin Core developers
|
2015-02-05 01:11:44 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <validationinterface.h>
|
2017-10-02 18:24:59 -04:00
|
|
|
|
2019-08-21 17:48:52 -07:00
|
|
|
#include <chain.h>
|
|
|
|
#include <consensus/validation.h>
|
2019-09-23 13:54:21 -04:00
|
|
|
#include <kernel/chain.h>
|
2023-11-03 12:34:29 +01:00
|
|
|
#include <kernel/mempool_entry.h>
|
2019-08-21 17:48:52 -07:00
|
|
|
#include <logging.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <primitives/block.h>
|
2019-08-21 17:48:52 -07:00
|
|
|
#include <primitives/transaction.h>
|
2023-11-27 17:15:11 +01:00
|
|
|
#include <util/check.h>
|
|
|
|
#include <util/task_runner.h>
|
2017-04-10 14:55:49 -04:00
|
|
|
|
2017-12-24 12:13:13 -05:00
|
|
|
#include <future>
|
2019-07-23 23:47:17 +02:00
|
|
|
#include <unordered_map>
|
2018-12-30 14:19:14 -08:00
|
|
|
#include <utility>
|
2015-02-05 01:11:44 +01:00
|
|
|
|
2023-01-20 16:25:14 +01:00
|
|
|
std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept;
|
2022-10-28 18:59:41 -04:00
|
|
|
|
2022-05-03 18:34:31 +02:00
|
|
|
/**
|
2024-01-24 14:12:33 +01:00
|
|
|
* ValidationSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.
|
2022-05-03 18:34:31 +02:00
|
|
|
*
|
|
|
|
* A std::unordered_map is used to track what callbacks are currently
|
|
|
|
* registered, and a std::list is used to store the callbacks that are
|
|
|
|
* currently registered as well as any callbacks that are just unregistered
|
|
|
|
* and about to be deleted when they are done executing.
|
|
|
|
*/
|
2024-01-24 14:12:33 +01:00
|
|
|
class ValidationSignalsImpl
|
2022-05-03 18:34:31 +02:00
|
|
|
{
|
2020-04-04 11:44:39 -04:00
|
|
|
private:
|
|
|
|
Mutex m_mutex;
|
|
|
|
//! List entries consist of a callback pointer and reference count. The
|
|
|
|
//! count is equal to the number of current executions of that entry, plus 1
|
|
|
|
//! if it's registered. It cannot be 0 because that would imply it is
|
|
|
|
//! unregistered and also not being executed (so shouldn't exist).
|
|
|
|
struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1; };
|
|
|
|
std::list<ListEntry> m_list GUARDED_BY(m_mutex);
|
|
|
|
std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
|
|
|
|
|
|
|
|
public:
|
2023-11-27 17:15:11 +01:00
|
|
|
std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
|
2017-04-10 14:55:49 -04:00
|
|
|
|
2023-11-27 17:15:11 +01:00
|
|
|
explicit ValidationSignalsImpl(std::unique_ptr<util::TaskRunnerInterface> task_runner)
|
|
|
|
: m_task_runner{std::move(Assert(task_runner))} {}
|
2020-04-04 11:44:39 -04:00
|
|
|
|
2022-04-20 16:47:29 +10:00
|
|
|
void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
2020-04-04 11:44:39 -04:00
|
|
|
{
|
|
|
|
LOCK(m_mutex);
|
|
|
|
auto inserted = m_map.emplace(callbacks.get(), m_list.end());
|
|
|
|
if (inserted.second) inserted.first->second = m_list.emplace(m_list.end());
|
|
|
|
inserted.first->second->callbacks = std::move(callbacks);
|
|
|
|
}
|
|
|
|
|
2022-04-20 16:47:29 +10:00
|
|
|
void Unregister(CValidationInterface* callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
2020-04-04 11:44:39 -04:00
|
|
|
{
|
|
|
|
LOCK(m_mutex);
|
|
|
|
auto it = m_map.find(callbacks);
|
|
|
|
if (it != m_map.end()) {
|
|
|
|
if (!--it->second->count) m_list.erase(it->second);
|
|
|
|
m_map.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Clear unregisters every previously registered callback, erasing every
|
|
|
|
//! map entry. After this call, the list may still contain callbacks that
|
|
|
|
//! are currently executing, but it will be cleared when they are done
|
|
|
|
//! executing.
|
2022-04-20 16:47:29 +10:00
|
|
|
void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
2020-04-04 11:44:39 -04:00
|
|
|
{
|
|
|
|
LOCK(m_mutex);
|
2020-04-06 19:06:29 -07:00
|
|
|
for (const auto& entry : m_map) {
|
|
|
|
if (!--entry.second->count) m_list.erase(entry.second);
|
2020-04-04 11:44:39 -04:00
|
|
|
}
|
|
|
|
m_map.clear();
|
|
|
|
}
|
|
|
|
|
2022-04-20 16:47:29 +10:00
|
|
|
template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
2020-04-04 11:44:39 -04:00
|
|
|
{
|
|
|
|
WAIT_LOCK(m_mutex, lock);
|
|
|
|
for (auto it = m_list.begin(); it != m_list.end();) {
|
|
|
|
++it->count;
|
|
|
|
{
|
|
|
|
REVERSE_LOCK(lock);
|
|
|
|
f(*it->callbacks);
|
|
|
|
}
|
|
|
|
it = --it->count ? std::next(it) : m_list.erase(it);
|
|
|
|
}
|
|
|
|
}
|
2017-01-19 16:17:14 -05:00
|
|
|
};
|
|
|
|
|
2023-11-27 17:15:11 +01:00
|
|
|
ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)
|
|
|
|
: m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}
|
2024-01-18 20:23:26 +01:00
|
|
|
|
2024-07-08 11:11:58 +02:00
|
|
|
ValidationSignals::~ValidationSignals() = default;
|
2024-01-18 20:23:26 +01:00
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::FlushBackgroundCallbacks()
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
2023-11-27 17:15:11 +01:00
|
|
|
m_internals->m_task_runner->flush();
|
2017-06-27 19:07:52 -04:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
size_t ValidationSignals::CallbacksPending()
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
2023-11-27 17:15:11 +01:00
|
|
|
return m_internals->m_task_runner->size();
|
2017-12-04 18:31:36 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
|
|
|
// Each connection captures the shared_ptr to ensure that each callback is
|
|
|
|
// executed before the subscriber is destroyed. For more details see #18338.
|
2024-01-18 20:23:26 +01:00
|
|
|
m_internals->Register(std::move(callbacks));
|
2015-02-05 01:11:44 +01:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::RegisterValidationInterface(CValidationInterface* callbacks)
|
2020-03-10 15:46:20 -04:00
|
|
|
{
|
|
|
|
// Create a shared_ptr with a no-op deleter - CValidationInterface lifecycle
|
|
|
|
// is managed by the caller.
|
|
|
|
RegisterSharedValidationInterface({callbacks, [](CValidationInterface*){}});
|
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
|
2020-03-10 15:46:20 -04:00
|
|
|
{
|
|
|
|
UnregisterValidationInterface(callbacks.get());
|
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::UnregisterValidationInterface(CValidationInterface* callbacks)
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
2023-11-27 11:46:36 +01:00
|
|
|
m_internals->Unregister(callbacks);
|
2015-02-05 01:11:44 +01:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::UnregisterAllValidationInterfaces()
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
2024-01-18 20:23:26 +01:00
|
|
|
m_internals->Clear();
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::CallFunctionInValidationInterfaceQueue(std::function<void()> func)
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
2023-11-27 17:15:11 +01:00
|
|
|
m_internals->m_task_runner->insert(std::move(func));
|
2017-06-08 11:13:46 -04:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::SyncWithValidationInterfaceQueue()
|
2020-04-27 10:21:19 -04:00
|
|
|
{
|
2017-12-24 12:13:13 -05:00
|
|
|
AssertLockNotHeld(cs_main);
|
|
|
|
// Block until the validation queue drains
|
|
|
|
std::promise<void> promise;
|
|
|
|
CallFunctionInValidationInterfaceQueue([&promise] {
|
|
|
|
promise.set_value();
|
|
|
|
});
|
|
|
|
promise.get_future().wait();
|
|
|
|
}
|
|
|
|
|
2019-08-21 17:48:52 -07:00
|
|
|
// 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__); \
|
2023-11-27 17:15:11 +01:00
|
|
|
m_internals->m_task_runner->insert([=] { \
|
2019-08-21 17:48:52 -07:00
|
|
|
LOG_EVENT(fmt, local_name, __VA_ARGS__); \
|
|
|
|
event(); \
|
|
|
|
}); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define LOG_EVENT(fmt, ...) \
|
|
|
|
LogPrint(BCLog::VALIDATION, fmt "\n", __VA_ARGS__)
|
2017-01-20 15:08:14 -05:00
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
|
2018-04-15 11:22:28 -04:00
|
|
|
// 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
|
|
|
|
|
2019-08-21 17:48:52 -07:00
|
|
|
auto event = [pindexNew, pindexFork, fInitialDownload, this] {
|
2020-04-04 11:44:39 -04:00
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
|
2019-08-21 17:48:52 -07:00
|
|
|
};
|
|
|
|
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);
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2023-09-29 13:58:16 +01:00
|
|
|
void ValidationSignals::ActiveTipChange(const CBlockIndex *new_tip, bool is_ibd)
|
|
|
|
{
|
|
|
|
LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip->GetBlockHash().ToString(), new_tip->nHeight);
|
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
|
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
|
2023-11-03 17:04:30 +01:00
|
|
|
{
|
Add 'sequence' zmq publisher to track all block (dis)connects, mempool deltas
Using the zmq notifications to avoid excessive mempool polling can be difficult
given the current notifications available. It announces all transactions
being added to mempool or included in blocks, but announces no evictions
and gives no indication if the transaction is in the mempool or a block.
Block notifications for zmq are also substandard, in that it only announces
block tips, while all block transactions are still announced.
This commit adds a unified stream which can be used to closely track mempool:
1) getrawmempool to fill out mempool knowledge
2) if txhash is announced, add or remove from set
based on add/remove flag
3) if blockhash is announced, get block txn list,
remove from those transactions local view of mempool
4) if we drop a sequence number, go to (1)
The mempool sequence number starts at the value 1, and
increments each time a transaction enters the mempool,
or is evicted from the mempool for any reason, including
block inclusion. The mempool sequence number is published
via ZMQ for any transaction-related notification.
These features allow for ZMQ/RPC consumer to track mempool
state in a more exacting way, without unnecesarily polling
getrawmempool. See interface_zmq.py::test_mempool_sync for
example usage.
2020-09-04 11:55:58 -04:00
|
|
|
auto event = [tx, mempool_sequence, this] {
|
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
|
2019-08-21 17:48:52 -07:00
|
|
|
};
|
|
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
|
2023-11-03 17:04:30 +01:00
|
|
|
tx.info.m_tx->GetHash().ToString(),
|
|
|
|
tx.info.m_tx->GetWitnessHash().ToString());
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
|
Add 'sequence' zmq publisher to track all block (dis)connects, mempool deltas
Using the zmq notifications to avoid excessive mempool polling can be difficult
given the current notifications available. It announces all transactions
being added to mempool or included in blocks, but announces no evictions
and gives no indication if the transaction is in the mempool or a block.
Block notifications for zmq are also substandard, in that it only announces
block tips, while all block transactions are still announced.
This commit adds a unified stream which can be used to closely track mempool:
1) getrawmempool to fill out mempool knowledge
2) if txhash is announced, add or remove from set
based on add/remove flag
3) if blockhash is announced, get block txn list,
remove from those transactions local view of mempool
4) if we drop a sequence number, go to (1)
The mempool sequence number starts at the value 1, and
increments each time a transaction enters the mempool,
or is evicted from the mempool for any reason, including
block inclusion. The mempool sequence number is published
via ZMQ for any transaction-related notification.
These features allow for ZMQ/RPC consumer to track mempool
state in a more exacting way, without unnecesarily polling
getrawmempool. See interface_zmq.py::test_mempool_sync for
example usage.
2020-09-04 11:55:58 -04:00
|
|
|
auto event = [tx, reason, mempool_sequence, this] {
|
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
|
2019-08-21 17:48:52 -07:00
|
|
|
};
|
2022-10-28 18:59:41 -04:00
|
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__,
|
2020-05-22 16:30:07 -04:00
|
|
|
tx->GetHash().ToString(),
|
2022-10-28 18:59:41 -04:00
|
|
|
tx->GetWitnessHash().ToString(),
|
|
|
|
RemovalReasonToString(reason));
|
2019-07-23 23:47:17 +02:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
|
2019-09-23 13:54:21 -04:00
|
|
|
auto event = [role, pblock, pindex, this] {
|
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); });
|
2019-08-21 17:48:52 -07:00
|
|
|
};
|
|
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
|
|
|
|
pblock->GetHash().ToString(),
|
|
|
|
pindex->nHeight);
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
|
2023-11-03 12:34:29 +01:00
|
|
|
{
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
|
2019-07-24 15:41:41 -04:00
|
|
|
{
|
2019-08-21 17:48:52 -07:00
|
|
|
auto event = [pblock, pindex, this] {
|
2020-04-04 11:44:39 -04:00
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); });
|
2019-08-21 17:48:52 -07:00
|
|
|
};
|
|
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
|
|
|
|
pblock->GetHash().ToString(),
|
|
|
|
pindex->nHeight);
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::ChainStateFlushed(ChainstateRole role, const CBlockLocator &locator) {
|
2019-09-23 13:54:21 -04:00
|
|
|
auto event = [role, locator, this] {
|
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); });
|
2019-08-21 17:48:52 -07:00
|
|
|
};
|
|
|
|
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
|
|
|
|
locator.IsNull() ? "null" : locator.vHave.front().ToString());
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) {
|
2019-08-21 17:48:52 -07:00
|
|
|
LOG_EVENT("%s: block hash=%s state=%s", __func__,
|
2019-11-08 13:22:36 -08:00
|
|
|
block.GetHash().ToString(), state.ToString());
|
2020-04-04 11:44:39 -04:00
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
|
2017-01-19 16:17:14 -05:00
|
|
|
}
|
|
|
|
|
2024-01-24 14:12:33 +01:00
|
|
|
void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
|
2019-08-21 17:48:52 -07:00
|
|
|
LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
|
2020-04-04 11:44:39 -04:00
|
|
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
|
2015-02-05 01:11:44 +01:00
|
|
|
}
|