/ src / validationinterface.cpp
validationinterface.cpp
  1  // Copyright (c) 2009-2010 Satoshi Nakamoto
  2  // Copyright (c) 2009-2022 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/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      m_internals->Unregister(callbacks);
131  }
132  
133  void ValidationSignals::UnregisterAllValidationInterfaces()
134  {
135      m_internals->Clear();
136  }
137  
138  void ValidationSignals::CallFunctionInValidationInterfaceQueue(std::function<void()> func)
139  {
140      m_internals->m_task_runner->insert(std::move(func));
141  }
142  
143  void ValidationSignals::SyncWithValidationInterfaceQueue()
144  {
145      AssertLockNotHeld(cs_main);
146      // Block until the validation queue drains
147      std::promise<void> promise;
148      CallFunctionInValidationInterfaceQueue([&promise] {
149          promise.set_value();
150      });
151      promise.get_future().wait();
152  }
153  
154  // Use a macro instead of a function for conditional logging to prevent
155  // evaluating arguments when logging is not enabled.
156  //
157  // NOTE: The lambda captures all local variables by value.
158  #define ENQUEUE_AND_LOG_EVENT(event, fmt, name, ...)           \
159      do {                                                       \
160          auto local_name = (name);                              \
161          LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__);  \
162          m_internals->m_task_runner->insert([=] { \
163              LOG_EVENT(fmt, local_name, __VA_ARGS__);           \
164              event();                                           \
165          });                                                    \
166      } while (0)
167  
168  #define LOG_EVENT(fmt, ...) \
169      LogDebug(BCLog::VALIDATION, fmt "\n", __VA_ARGS__)
170  
171  void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
172      // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
173      // the chain actually updates. One way to ensure this is for the caller to invoke this signal
174      // in the same critical section where the chain is updated
175  
176      auto event = [pindexNew, pindexFork, fInitialDownload, this] {
177          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
178      };
179      ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
180                            pindexNew->GetBlockHash().ToString(),
181                            pindexFork ? pindexFork->GetBlockHash().ToString() : "null",
182                            fInitialDownload);
183  }
184  
185  void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd)
186  {
187      LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip.GetBlockHash().ToString(), new_tip.nHeight);
188      m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
189  }
190  
191  void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
192  {
193      auto event = [tx, mempool_sequence, this] {
194          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
195      };
196      ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
197                            tx.info.m_tx->GetHash().ToString(),
198                            tx.info.m_tx->GetWitnessHash().ToString());
199  }
200  
201  void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
202      auto event = [tx, reason, mempool_sequence, this] {
203          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
204      };
205      ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__,
206                            tx->GetHash().ToString(),
207                            tx->GetWitnessHash().ToString(),
208                            RemovalReasonToString(reason));
209  }
210  
211  void ValidationSignals::BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
212      auto event = [role, pblock, pindex, this] {
213          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); });
214      };
215      ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
216                            pblock->GetHash().ToString(),
217                            pindex->nHeight);
218  }
219  
220  void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
221  {
222      auto event = [txs_removed_for_block, nBlockHeight, this] {
223          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight); });
224      };
225      ENQUEUE_AND_LOG_EVENT(event, "%s: block height=%s txs removed=%s", __func__,
226                            nBlockHeight,
227                            txs_removed_for_block.size());
228  }
229  
230  void ValidationSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
231  {
232      auto event = [pblock, pindex, this] {
233          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); });
234      };
235      ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
236                            pblock->GetHash().ToString(),
237                            pindex->nHeight);
238  }
239  
240  void ValidationSignals::ChainStateFlushed(ChainstateRole role, const CBlockLocator &locator) {
241      auto event = [role, locator, this] {
242          m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); });
243      };
244      ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
245                            locator.IsNull() ? "null" : locator.vHave.front().ToString());
246  }
247  
248  void ValidationSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) {
249      LOG_EVENT("%s: block hash=%s state=%s", __func__,
250                block.GetHash().ToString(), state.ToString());
251      m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
252  }
253  
254  void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
255      LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
256      m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
257  }