validationinterface.cpp raw

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