validationinterface.cpp raw

   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      (util::log::ShouldDebugLog(BCLog::VALIDATION) ? 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  }
 276