ephemeral_policy.cpp raw

   1  // Copyright (c) 2024-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 <consensus/validation.h>
   6  #include <policy/ephemeral_policy.h>
   7  #include <policy/feerate.h>
   8  #include <policy/packages.h>
   9  #include <policy/policy.h>
  10  #include <primitives/transaction.h>
  11  #include <txmempool.h>
  12  #include <util/check.h>
  13  #include <util/hasher.h>
  14  
  15  #include <algorithm>
  16  #include <cstdint>
  17  #include <map>
  18  #include <memory>
  19  #include <unordered_set>
  20  #include <utility>
  21  #include <vector>
  22  
  23  bool PreCheckEphemeralTx(const CTransaction& tx, CFeeRate dust_relay_rate, CAmount base_fee, CAmount mod_fee, TxValidationState& state)
  24  {
  25      // We never want to give incentives to mine this transaction alone
  26      if ((base_fee != 0 || mod_fee != 0) && !GetDust(tx, dust_relay_rate).empty()) {
  27          return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "dust", "tx with dust output must be 0-fee");
  28      }
  29  
  30      return true;
  31  }
  32  
  33  bool CheckEphemeralSpends(const Package& package, CFeeRate dust_relay_rate, const CTxMemPool& tx_pool, TxValidationState& out_child_state, Wtxid& out_child_wtxid)
  34  {
  35      if (!Assume(std::ranges::all_of(package, [](const auto& tx){return tx != nullptr;}))) {
  36          // Bail out of spend checks if caller gave us an invalid package
  37          return true;
  38      }
  39  
  40      std::map<Txid, CTransactionRef> map_txid_ref;
  41      for (const auto& tx : package) {
  42          map_txid_ref[tx->GetHash()] = tx;
  43      }
  44  
  45      for (const auto& tx : package) {
  46          std::unordered_set<Txid, SaltedTxidHasher> processed_parent_set;
  47          std::unordered_set<COutPoint, SaltedOutpointHasher> unspent_parent_dust;
  48  
  49          for (const auto& tx_input : tx->vin) {
  50              const Txid& parent_txid{tx_input.prevout.hash};
  51              // Skip parents we've already checked dust for
  52              if (processed_parent_set.contains(parent_txid)) continue;
  53  
  54              // We look for an in-package or in-mempool dependency
  55              CTransactionRef parent_ref = nullptr;
  56              if (auto it = map_txid_ref.find(parent_txid); it != map_txid_ref.end()) {
  57                  parent_ref = it->second;
  58              } else {
  59                  parent_ref = tx_pool.get(parent_txid);
  60              }
  61  
  62              // Check for dust on parents
  63              if (parent_ref) {
  64                  for (uint32_t out_index = 0; out_index < parent_ref->vout.size(); out_index++) {
  65                      const auto& tx_output = parent_ref->vout[out_index];
  66                      if (IsDust(tx_output, dust_relay_rate)) {
  67                          unspent_parent_dust.insert(COutPoint(parent_txid, out_index));
  68                      }
  69                  }
  70              }
  71  
  72              processed_parent_set.insert(parent_txid);
  73          }
  74  
  75          if (unspent_parent_dust.empty()) {
  76              continue;
  77          }
  78  
  79          // Now that we have gathered parents' dust, make sure it's spent
  80          // by the child
  81          for (const auto& tx_input : tx->vin) {
  82              unspent_parent_dust.erase(tx_input.prevout);
  83          }
  84  
  85          if (!unspent_parent_dust.empty()) {
  86              const Txid& out_child_txid = tx->GetHash();
  87              out_child_wtxid = tx->GetWitnessHash();
  88              out_child_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "missing-ephemeral-spends",
  89                                  strprintf("tx %s (wtxid=%s) did not spend parent's ephemeral dust", out_child_txid.ToString(), out_child_wtxid.ToString()));
  90              return false;
  91          }
  92      }
  93  
  94      return true;
  95  }
  96