/ src / validationinterface.cpp
validationinterface.cpp
  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      (ShouldLog(BCLog::VALIDATION, BCLog::Level::Debug) ? 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  }