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