/ 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  //
160  // NOTE: The lambda captures all local variables by value.
161  #define ENQUEUE_AND_LOG_EVENT(event, fmt, name, ...)           \
162      do {                                                       \
163          auto local_name = (name);                              \
164          LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__);  \
165          m_internals->m_task_runner->insert([=] { \
166              LOG_EVENT(fmt, local_name, __VA_ARGS__);           \
167              event();                                           \
168          });                                                    \
169      } while (0)
170  
171  #define LOG_EVENT(fmt, ...) \
172      LogDebug(BCLog::VALIDATION, fmt "\n", __VA_ARGS__)
173  
174  void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
175      // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
176      // the chain actually updates. One way to ensure this is for the caller to invoke this signal
177      // in the same critical section where the chain is updated
178  
179      auto event = [pindexNew, pindexFork, fInitialDownload, this] {
180          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
181      };
182      ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
183                            pindexNew->GetBlockHash().ToString(),
184                            pindexFork ? pindexFork->GetBlockHash().ToString() : "null",
185                            fInitialDownload);
186  }
187  
188  void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd)
189  {
190      LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip.GetBlockHash().ToString(), new_tip.nHeight);
191      m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
192  }
193  
194  void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
195  {
196      auto event = [tx, mempool_sequence, this] {
197          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
198      };
199      ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
200                            tx.info.m_tx->GetHash().ToString(),
201                            tx.info.m_tx->GetWitnessHash().ToString());
202  }
203  
204  void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
205      auto event = [tx, reason, mempool_sequence, this] {
206          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
207      };
208      ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__,
209                            tx->GetHash().ToString(),
210                            tx->GetWitnessHash().ToString(),
211                            RemovalReasonToString(reason));
212  }
213  
214  void ValidationSignals::BlockConnected(const ChainstateRole& role, const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
215  {
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(const ChainstateRole& role, const CBlockLocator& locator)
245  {
246      auto event = [role, locator, this] {
247          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); });
248      };
249      ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
250                            locator.IsNull() ? "null" : locator.vHave.front().ToString());
251  }
252  
253  void ValidationSignals::BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state)
254  {
255      LOG_EVENT("%s: block hash=%s state=%s", __func__,
256                block->GetHash().ToString(), state.ToString());
257      m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
258  }
259  
260  void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
261      LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
262      m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
263  }