/ src / policy / rbf.cpp
rbf.cpp
  1  // Copyright (c) 2016-present The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <policy/rbf.h>
  6  
  7  #include <consensus/amount.h>
  8  #include <kernel/mempool_entry.h>
  9  #include <policy/feerate.h>
 10  #include <primitives/transaction.h>
 11  #include <sync.h>
 12  #include <tinyformat.h>
 13  #include <txmempool.h>
 14  #include <uint256.h>
 15  #include <util/check.h>
 16  #include <util/moneystr.h>
 17  #include <util/rbf.h>
 18  
 19  #include <limits>
 20  #include <vector>
 21  
 22  #include <compare>
 23  
 24  RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
 25  {
 26      AssertLockHeld(pool.cs);
 27  
 28      // First check the transaction itself.
 29      if (SignalsOptInRBF(tx)) {
 30          return RBFTransactionState::REPLACEABLE_BIP125;
 31      }
 32  
 33      // If this transaction is not in our mempool, then we can't be sure
 34      // we will know about all its inputs.
 35      if (!pool.exists(tx.GetHash())) {
 36          return RBFTransactionState::UNKNOWN;
 37      }
 38  
 39      // If all the inputs have nSequence >= maxint-1, it still might be
 40      // signaled for RBF if any unconfirmed parents have signaled.
 41      const auto& entry{*Assert(pool.GetEntry(tx.GetHash()))};
 42      auto ancestors{pool.CalculateMemPoolAncestors(entry)};
 43  
 44      for (CTxMemPool::txiter it : ancestors) {
 45          if (SignalsOptInRBF(it->GetTx())) {
 46              return RBFTransactionState::REPLACEABLE_BIP125;
 47          }
 48      }
 49      return RBFTransactionState::FINAL;
 50  }
 51  
 52  RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
 53  {
 54      // If we don't have a local mempool we can only check the transaction itself.
 55      return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
 56  }
 57  
 58  std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
 59                                                    CTxMemPool& pool,
 60                                                    const CTxMemPool::setEntries& iters_conflicting,
 61                                                    CTxMemPool::setEntries& all_conflicts)
 62  {
 63      AssertLockHeld(pool.cs);
 64      // Rule #5: don't consider replacements that conflict directly with more
 65      // than MAX_REPLACEMENT_CANDIDATES distinct clusters. This implies a bound
 66      // on how many mempool clusters might need to be re-sorted in order to
 67      // process the replacement (though the actual number of clusters we
 68      // relinearize may be greater than this number, due to cluster splitting).
 69      auto num_clusters = pool.GetUniqueClusterCount(iters_conflicting);
 70      if (num_clusters > MAX_REPLACEMENT_CANDIDATES) {
 71          return strprintf("rejecting replacement %s; too many conflicting clusters (%u > %d)",
 72                  tx.GetHash().ToString(),
 73                  num_clusters,
 74                  MAX_REPLACEMENT_CANDIDATES);
 75      }
 76      // Calculate the set of all transactions that would have to be evicted.
 77      for (CTxMemPool::txiter it : iters_conflicting) {
 78          // The cluster count limit ensures that we won't do too much work on a
 79          // single invocation of this function.
 80          pool.CalculateDescendants(it, all_conflicts);
 81      }
 82      return std::nullopt;
 83  }
 84  
 85  std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
 86                                                     const std::set<Txid>& direct_conflicts,
 87                                                     const Txid& txid)
 88  {
 89      for (CTxMemPool::txiter ancestorIt : ancestors) {
 90          const Txid& hashAncestor = ancestorIt->GetTx().GetHash();
 91          if (direct_conflicts.contains(hashAncestor)) {
 92              return strprintf("%s spends conflicting transaction %s",
 93                               txid.ToString(),
 94                               hashAncestor.ToString());
 95          }
 96      }
 97      return std::nullopt;
 98  }
 99  
100  std::optional<std::string> PaysForRBF(CAmount original_fees,
101                                        CAmount replacement_fees,
102                                        size_t replacement_vsize,
103                                        CFeeRate relay_fee,
104                                        const Txid& txid)
105  {
106      // Rule #3: The replacement fees must be greater than or equal to fees of the
107      // transactions it replaces, otherwise the bandwidth used by those conflicting transactions
108      // would not be paid for.
109      if (replacement_fees < original_fees) {
110          return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
111                           txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
112      }
113  
114      // Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
115      // vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
116      // increasing the fee by tiny amounts.
117      CAmount additional_fees = replacement_fees - original_fees;
118      if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
119          return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
120                           txid.ToString(),
121                           FormatMoney(additional_fees),
122                           FormatMoney(relay_fee.GetFee(replacement_vsize)));
123      }
124      return std::nullopt;
125  }
126  
127  std::optional<std::pair<DiagramCheckError, std::string>> ImprovesFeerateDiagram(CTxMemPool::ChangeSet& changeset)
128  {
129      // Require that the replacement strictly improves the mempool's feerate diagram.
130      const auto chunk_results{changeset.CalculateChunksForRBF()};
131  
132      if (!chunk_results.has_value()) {
133          return std::make_pair(DiagramCheckError::UNCALCULABLE, util::ErrorString(chunk_results).original);
134      }
135  
136      if (!std::is_gt(CompareChunks(chunk_results.value().second, chunk_results.value().first))) {
137          return std::make_pair(DiagramCheckError::FAILURE, "insufficient feerate: does not improve feerate diagram");
138      }
139      return std::nullopt;
140  }