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