validationinterface.cpp raw
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-present 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
6 #include <validationinterface.h>
7
8 #include <chain.h>
9 #include <consensus/validation.h>
10 #include <kernel/mempool_entry.h>
11 #include <kernel/mempool_removal_reason.h>
12 #include <kernel/types.h>
13 #include <primitives/block.h>
14 #include <primitives/transaction.h>
15 #include <util/check.h>
16 #include <util/log.h>
17 #include <util/task_runner.h>
18
19 #include <future>
20 #include <memory>
21 #include <unordered_map>
22 #include <utility>
23
24 using kernel::ChainstateRole;
25
26 /**
27 * ValidationSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.
28 *
29 * A std::unordered_map is used to track what callbacks are currently
30 * registered, and a std::list is used to store the callbacks that are
31 * currently registered as well as any callbacks that are just unregistered
32 * and about to be deleted when they are done executing.
33 */
34 class ValidationSignalsImpl
35 {
36 private:
37 Mutex m_mutex;
38 //! List entries consist of a callback pointer and reference count. The
39 //! count is equal to the number of current executions of that entry, plus 1
40 //! if it's registered. It cannot be 0 because that would imply it is
41 //! unregistered and also not being executed (so shouldn't exist).
42 struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1; };
43 std::list<ListEntry> m_list GUARDED_BY(m_mutex);
44 std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
45
46 public:
47 std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
48
49 explicit ValidationSignalsImpl(std::unique_ptr<util::TaskRunnerInterface> task_runner)
50 : m_task_runner{std::move(Assert(task_runner))} {}
51
52 void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
53 {
54 LOCK(m_mutex);
55 auto inserted = m_map.emplace(callbacks.get(), m_list.end());
56 if (inserted.second) inserted.first->second = m_list.emplace(m_list.end());
57 inserted.first->second->callbacks = std::move(callbacks);
58 }
59
60 void Unregister(CValidationInterface* callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
61 {
62 LOCK(m_mutex);
63 auto it = m_map.find(callbacks);
64 if (it != m_map.end()) {
65 if (!--it->second->count) m_list.erase(it->second);
66 m_map.erase(it);
67 }
68 }
69
70 //! Clear unregisters every previously registered callback, erasing every
71 //! map entry. After this call, the list may still contain callbacks that
72 //! are currently executing, but it will be cleared when they are done
73 //! executing.
74 void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
75 {
76 LOCK(m_mutex);
77 for (const auto& entry : m_map) {
78 if (!--entry.second->count) m_list.erase(entry.second);
79 }
80 m_map.clear();
81 }
82
83 template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
84 {
85 WAIT_LOCK(m_mutex, lock);
86 for (auto it = m_list.begin(); it != m_list.end();) {
87 ++it->count;
88 {
89 REVERSE_LOCK(lock, m_mutex);
90 f(*it->callbacks);
91 }
92 it = --it->count ? std::next(it) : m_list.erase(it);
93 }
94 }
95 };
96
97 ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)
98 : m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}
99
100 ValidationSignals::~ValidationSignals() = default;
101
102 void ValidationSignals::FlushBackgroundCallbacks()
103 {
104 m_internals->m_task_runner->flush();
105 }
106
107 size_t ValidationSignals::CallbacksPending()
108 {
109 return m_internals->m_task_runner->size();
110 }
111
112 void ValidationSignals::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
113 {
114 // Each connection captures the shared_ptr to ensure that each callback is
115 // executed before the subscriber is destroyed. For more details see #18338.
116 m_internals->Register(std::move(callbacks));
117 }
118
119 void ValidationSignals::RegisterValidationInterface(CValidationInterface* callbacks)
120 {
121 // Create a shared_ptr with a no-op deleter - CValidationInterface lifecycle
122 // is managed by the caller.
123 RegisterSharedValidationInterface({callbacks, [](CValidationInterface*){}});
124 }
125
126 void ValidationSignals::UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
127 {
128 UnregisterValidationInterface(callbacks.get());
129 }
130
131 void ValidationSignals::UnregisterValidationInterface(CValidationInterface* callbacks)
132 {
133 m_internals->Unregister(callbacks);
134 }
135
136 void ValidationSignals::UnregisterAllValidationInterfaces()
137 {
138 m_internals->Clear();
139 }
140
141 void ValidationSignals::CallFunctionInValidationInterfaceQueue(std::function<void()> func)
142 {
143 m_internals->m_task_runner->insert(std::move(func));
144 }
145
146 void ValidationSignals::SyncWithValidationInterfaceQueue()
147 {
148 AssertLockNotHeld(cs_main);
149 // Block until the validation queue drains
150 std::promise<void> promise;
151 CallFunctionInValidationInterfaceQueue([&promise] {
152 promise.set_value();
153 });
154 promise.get_future().wait();
155 }
156
157 // Use a macro instead of a function for conditional logging to prevent
158 // evaluating arguments when logging is not enabled.
159 #define ENQUEUE_AND_LOG_EVENT(event, log_msg) \
160 do { \
161 static_assert(std::is_rvalue_reference_v<decltype((event))>, \
162 "event must be passed as an rvalue"); \
163 static_assert(std::is_rvalue_reference_v<decltype((log_msg))>, \
164 "log_msg must be passed as an rvalue"); \
165 auto enqueue_log_msg = (log_msg); \
166 LOG_EVENT("Enqueuing %s", enqueue_log_msg); \
167 m_internals->m_task_runner->insert([local_log_msg = std::move(enqueue_log_msg), local_event = (event)] { \
168 LOG_EVENT("%s", local_log_msg); \
169 local_event(); \
170 }); \
171 } while (0)
172
173 #define LOG_MSG(fmt, ...) \
174 (util::log::ShouldDebugLog(BCLog::VALIDATION) ? tfm::format((fmt), __VA_ARGS__) : std::string{})
175
176 #define LOG_EVENT(fmt, ...) \
177 LogDebug(BCLog::VALIDATION, fmt, __VA_ARGS__)
178
179 void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
180 // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
181 // the chain actually updates. One way to ensure this is for the caller to invoke this signal
182 // in the same critical section where the chain is updated
183
184 auto log_msg = LOG_MSG("%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
185 pindexNew->GetBlockHash().ToString(),
186 pindexFork ? pindexFork->GetBlockHash().ToString() : "null",
187 fInitialDownload);
188 auto event = [pindexNew, pindexFork, fInitialDownload, this] {
189 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
190 };
191 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
192 }
193
194 void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd)
195 {
196 LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip.GetBlockHash().ToString(), new_tip.nHeight);
197 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
198 }
199
200 void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
201 {
202 auto log_msg = LOG_MSG("%s: txid=%s wtxid=%s", __func__,
203 tx.info.m_tx->GetHash().ToString(),
204 tx.info.m_tx->GetWitnessHash().ToString());
205 auto event = [tx, mempool_sequence, this] {
206 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
207 };
208 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
209 }
210
211 void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
212 auto log_msg = LOG_MSG("%s: txid=%s wtxid=%s reason=%s", __func__,
213 tx->GetHash().ToString(),
214 tx->GetWitnessHash().ToString(),
215 RemovalReasonToString(reason));
216 auto event = [tx, reason, mempool_sequence, this] {
217 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
218 };
219 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
220 }
221
222 void ValidationSignals::BlockConnected(const ChainstateRole& role, std::shared_ptr<const CBlock> pblock, const CBlockIndex* pindex)
223 {
224 auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__,
225 pblock->GetHash().ToString(),
226 pindex->nHeight);
227 auto event = [role, pblock = std::move(pblock), pindex, this] {
228 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); });
229 };
230 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
231 }
232
233 void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
234 {
235 auto log_msg = LOG_MSG("%s: block height=%s txs removed=%s", __func__,
236 nBlockHeight,
237 txs_removed_for_block.size());
238 auto event = [txs_removed_for_block, nBlockHeight, this] {
239 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight); });
240 };
241 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
242 }
243
244 void ValidationSignals::BlockDisconnected(std::shared_ptr<const CBlock> pblock, const CBlockIndex* pindex)
245 {
246 auto log_msg = LOG_MSG("%s: block hash=%s block height=%d", __func__,
247 pblock->GetHash().ToString(),
248 pindex->nHeight);
249 auto event = [pblock = std::move(pblock), pindex, this] {
250 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); });
251 };
252 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
253 }
254
255 void ValidationSignals::ChainStateFlushed(const ChainstateRole& role, const CBlockLocator& locator)
256 {
257 auto log_msg = LOG_MSG("%s: block hash=%s", __func__,
258 locator.IsNull() ? "null" : locator.vHave.front().ToString());
259 auto event = [role, locator, this] {
260 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); });
261 };
262 ENQUEUE_AND_LOG_EVENT(std::move(event), std::move(log_msg));
263 }
264
265 void ValidationSignals::BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state)
266 {
267 LOG_EVENT("%s: block hash=%s state=%s", __func__,
268 block->GetHash().ToString(), state.ToString());
269 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
270 }
271
272 void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
273 LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
274 m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
275 }
276