Bitcoin Core 29.99.0
P2P Digital Currency
validationinterface.cpp
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2022 The Bitcoin Core developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
7
8#include <chain.h>
10#include <kernel/chain.h>
13#include <logging.h>
14#include <primitives/block.h>
16#include <util/check.h>
17#include <util/task_runner.h>
18
19#include <future>
20#include <memory>
21#include <unordered_map>
22#include <utility>
23
33{
34private:
40 struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1; };
41 std::list<ListEntry> m_list GUARDED_BY(m_mutex);
42 std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
43
44public:
45 std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
46
47 explicit ValidationSignalsImpl(std::unique_ptr<util::TaskRunnerInterface> task_runner)
48 : m_task_runner{std::move(Assert(task_runner))} {}
49
50 void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
51 {
53 auto inserted = m_map.emplace(callbacks.get(), m_list.end());
54 if (inserted.second) inserted.first->second = m_list.emplace(m_list.end());
55 inserted.first->second->callbacks = std::move(callbacks);
56 }
57
59 {
61 auto it = m_map.find(callbacks);
62 if (it != m_map.end()) {
63 if (!--it->second->count) m_list.erase(it->second);
64 m_map.erase(it);
65 }
66 }
67
73 {
75 for (const auto& entry : m_map) {
76 if (!--entry.second->count) m_list.erase(entry.second);
77 }
78 m_map.clear();
79 }
80
81 template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
82 {
83 WAIT_LOCK(m_mutex, lock);
84 for (auto it = m_list.begin(); it != m_list.end();) {
85 ++it->count;
86 {
87 REVERSE_LOCK(lock, m_mutex);
88 f(*it->callbacks);
89 }
90 it = --it->count ? std::next(it) : m_list.erase(it);
91 }
92 }
93};
94
95ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)
96 : m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}
97
99
101{
102 m_internals->m_task_runner->flush();
103}
104
106{
107 return m_internals->m_task_runner->size();
108}
109
110void ValidationSignals::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
111{
112 // Each connection captures the shared_ptr to ensure that each callback is
113 // executed before the subscriber is destroyed. For more details see #18338.
114 m_internals->Register(std::move(callbacks));
115}
116
118{
119 // Create a shared_ptr with a no-op deleter - CValidationInterface lifecycle
120 // is managed by the caller.
122}
123
124void ValidationSignals::UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
125{
126 UnregisterValidationInterface(callbacks.get());
127}
128
130{
131 m_internals->Unregister(callbacks);
132}
133
135{
136 m_internals->Clear();
137}
138
140{
141 m_internals->m_task_runner->insert(std::move(func));
142}
143
145{
147 // Block until the validation queue drains
148 std::promise<void> promise;
150 promise.set_value();
151 });
152 promise.get_future().wait();
153}
154
155// Use a macro instead of a function for conditional logging to prevent
156// evaluating arguments when logging is not enabled.
157//
158// NOTE: The lambda captures all local variables by value.
159#define ENQUEUE_AND_LOG_EVENT(event, fmt, name, ...) \
160 do { \
161 auto local_name = (name); \
162 LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__); \
163 m_internals->m_task_runner->insert([=] { \
164 LOG_EVENT(fmt, local_name, __VA_ARGS__); \
165 event(); \
166 }); \
167 } while (0)
168
169#define LOG_EVENT(fmt, ...) \
170 LogDebug(BCLog::VALIDATION, fmt "\n", __VA_ARGS__)
171
172void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
173 // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
174 // the chain actually updates. One way to ensure this is for the caller to invoke this signal
175 // in the same critical section where the chain is updated
176
177 auto event = [pindexNew, pindexFork, fInitialDownload, this] {
178 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
179 };
180 ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
181 pindexNew->GetBlockHash().ToString(),
182 pindexFork ? pindexFork->GetBlockHash().ToString() : "null",
183 fInitialDownload);
184}
185
186void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd)
187{
188 LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip.GetBlockHash().ToString(), new_tip.nHeight);
189 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
190}
191
193{
194 auto event = [tx, mempool_sequence, this] {
195 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
196 };
197 ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
198 tx.info.m_tx->GetHash().ToString(),
199 tx.info.m_tx->GetWitnessHash().ToString());
200}
201
203 auto event = [tx, reason, mempool_sequence, this] {
204 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
205 };
206 ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__,
207 tx->GetHash().ToString(),
208 tx->GetWitnessHash().ToString(),
209 RemovalReasonToString(reason));
210}
211
212void ValidationSignals::BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
213 auto event = [role, pblock, pindex, this] {
214 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); });
215 };
216 ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
217 pblock->GetHash().ToString(),
218 pindex->nHeight);
219}
220
221void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
222{
223 auto event = [txs_removed_for_block, nBlockHeight, this] {
224 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight); });
225 };
226 ENQUEUE_AND_LOG_EVENT(event, "%s: block height=%s txs removed=%s", __func__,
227 nBlockHeight,
228 txs_removed_for_block.size());
229}
230
231void ValidationSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
232{
233 auto event = [pblock, pindex, this] {
234 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); });
235 };
236 ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
237 pblock->GetHash().ToString(),
238 pindex->nHeight);
239}
240
242 auto event = [role, locator, this] {
243 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); });
244 };
245 ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
246 locator.IsNull() ? "null" : locator.vHave.front().ToString());
247}
248
249void ValidationSignals::BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state)
250{
251 LOG_EVENT("%s: block hash=%s state=%s", __func__,
252 block->GetHash().ToString(), state.ToString());
253 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
254}
255
256void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
257 LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
258 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
259}
#define Assert(val)
Identity function.
Definition: check.h:106
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:141
uint256 GetBlockHash() const
Definition: chain.h:243
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:153
Implement this to subscribe to events generated in validation and mempool.
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr< const CBlock > &block)
Notifies listeners that a block which builds directly on our current tip has been received and connec...
virtual void ChainStateFlushed(ChainstateRole role, const CBlockLocator &locator)
Notifies listeners of the new active block chain on-disk.
virtual void BlockConnected(ChainstateRole role, const std::shared_ptr< const CBlock > &block, const CBlockIndex *pindex)
Notifies listeners of a block being connected.
virtual void TransactionRemovedFromMempool(const CTransactionRef &tx, MemPoolRemovalReason reason, uint64_t mempool_sequence)
Notifies listeners of a transaction leaving mempool.
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload)
Notifies listeners when the block chain tip advances.
virtual void MempoolTransactionsRemovedForBlock(const std::vector< RemovedMempoolTransactionInfo > &txs_removed_for_block, unsigned int nBlockHeight)
virtual void TransactionAddedToMempool(const NewMempoolTransactionInfo &tx, uint64_t mempool_sequence)
Notifies listeners of a transaction having been added to mempool.
virtual void BlockChecked(const std::shared_ptr< const CBlock > &, const BlockValidationState &)
Notifies listeners of a block validation result.
virtual void ActiveTipChange(const CBlockIndex &new_tip, bool is_ibd)
Notifies listeners any time the block chain tip changes, synchronously.
virtual void BlockDisconnected(const std::shared_ptr< const CBlock > &block, const CBlockIndex *pindex)
Notifies listeners of a block being disconnected Provides the block that was disconnected.
void UnregisterValidationInterface(CValidationInterface *callbacks)
Unregister subscriber.
void CallFunctionInValidationInterfaceQueue(std::function< void()> func)
Pushes a function to callback onto the notification queue, guaranteeing any callbacks generated prior...
void RegisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Register subscriber.
ValidationSignals(std::unique_ptr< util::TaskRunnerInterface > task_runner)
void BlockDisconnected(const std::shared_ptr< const CBlock > &, const CBlockIndex *pindex)
void BlockChecked(const std::shared_ptr< const CBlock > &, const BlockValidationState &)
void UnregisterAllValidationInterfaces()
Unregister all subscribers.
void RegisterValidationInterface(CValidationInterface *callbacks)
Register subscriber.
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr< const CBlock > &)
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)
void TransactionAddedToMempool(const NewMempoolTransactionInfo &, uint64_t mempool_sequence)
void ChainStateFlushed(ChainstateRole, const CBlockLocator &)
void ActiveTipChange(const CBlockIndex &, bool)
void SyncWithValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main)
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...
void MempoolTransactionsRemovedForBlock(const std::vector< RemovedMempoolTransactionInfo > &, unsigned int nBlockHeight)
void TransactionRemovedFromMempool(const CTransactionRef &, MemPoolRemovalReason, uint64_t mempool_sequence)
void FlushBackgroundCallbacks()
Call any remaining callbacks on the calling thread.
std::unique_ptr< ValidationSignalsImpl > m_internals
void BlockConnected(ChainstateRole, const std::shared_ptr< const CBlock > &, const CBlockIndex *pindex)
void UnregisterSharedValidationInterface(std::shared_ptr< CValidationInterface > callbacks)
Unregister subscriber.
ValidationSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.
void Register(std::shared_ptr< CValidationInterface > callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
std::unique_ptr< util::TaskRunnerInterface > m_task_runner
void Unregister(CValidationInterface *callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
std::unordered_map< CValidationInterface *, std::list< ListEntry >::iterator > m_map GUARDED_BY(m_mutex)
std::list< ListEntry > m_list GUARDED_BY(m_mutex)
void Iterate(F &&f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
ValidationSignalsImpl(std::unique_ptr< util::TaskRunnerInterface > task_runner)
void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Clear unregisters every previously registered callback, erasing every map entry.
std::string ToString() const
Definition: validation.h:111
std::string ToString() const
Definition: uint256.cpp:21
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
ChainstateRole
This enum describes the various roles a specific Chainstate instance can take.
Definition: chain.h:25
std::string RemovalReasonToString(const MemPoolRemovalReason &r) noexcept
MemPoolRemovalReason
Reason why a transaction was removed from the mempool, this is passed to the notification signal.
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
Describes a place in the block chain to another node such that if the other node doesn't have the sam...
Definition: block.h:124
std::vector< uint256 > vHave
Definition: block.h:134
bool IsNull() const
Definition: block.h:152
const CTransactionRef m_tx
List entries consist of a callback pointer and reference count.
int count
std::shared_ptr< CValidationInterface > callbacks
#define WAIT_LOCK(cs, name)
Definition: sync.h:265
#define AssertLockNotHeld(cs)
Definition: sync.h:142
#define REVERSE_LOCK(g, cs)
Definition: sync.h:245
#define LOCK(cs)
Definition: sync.h:259
This header provides an interface and simple implementation for a task runner.
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:51
#define LOG_EVENT(fmt,...)
#define ENQUEUE_AND_LOG_EVENT(event, fmt, name,...)