transaction.h raw

   1  // Copyright (c) 2021-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  #ifndef BITCOIN_WALLET_TRANSACTION_H
   6  #define BITCOIN_WALLET_TRANSACTION_H
   7  
   8  #include <attributes.h>
   9  #include <consensus/amount.h>
  10  #include <primitives/transaction.h>
  11  #include <tinyformat.h>
  12  #include <uint256.h>
  13  #include <util/check.h>
  14  #include <util/overloaded.h>
  15  #include <util/strencodings.h>
  16  #include <util/string.h>
  17  #include <wallet/types.h>
  18  
  19  #include <bitset>
  20  #include <cstdint>
  21  #include <map>
  22  #include <utility>
  23  #include <variant>
  24  #include <vector>
  25  
  26  namespace interfaces {
  27  class Chain;
  28  } // namespace interfaces
  29  
  30  namespace wallet {
  31  //! State of transaction confirmed in a block.
  32  struct TxStateConfirmed {
  33      uint256 confirmed_block_hash;
  34      int confirmed_block_height;
  35      int position_in_block;
  36  
  37      explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
  38      std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
  39  };
  40  
  41  //! State of transaction added to mempool.
  42  struct TxStateInMempool {
  43      std::string toString() const { return strprintf("InMempool"); }
  44  };
  45  
  46  //! State of rejected transaction that conflicts with a confirmed block.
  47  struct TxStateBlockConflicted {
  48      uint256 conflicting_block_hash;
  49      int conflicting_block_height;
  50  
  51      explicit TxStateBlockConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
  52      std::string toString() const { return strprintf("BlockConflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }
  53  };
  54  
  55  //! State of transaction not confirmed or conflicting with a known block and
  56  //! not in the mempool. May conflict with the mempool, or with an unknown block,
  57  //! or be abandoned, never broadcast, or rejected from the mempool for another
  58  //! reason.
  59  struct TxStateInactive {
  60      bool abandoned;
  61  
  62      explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
  63      std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }
  64  };
  65  
  66  //! State of transaction loaded in an unrecognized state with unexpected hash or
  67  //! index values. Treated as inactive (with serialized hash and index values
  68  //! preserved) by default, but may enter another state if transaction is added
  69  //! to the mempool, or confirmed, or abandoned, or found conflicting.
  70  struct TxStateUnrecognized {
  71      uint256 block_hash;
  72      int index;
  73  
  74      TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
  75      std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }
  76  };
  77  
  78  //! All possible CWalletTx states
  79  using TxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateBlockConflicted, TxStateInactive, TxStateUnrecognized>;
  80  
  81  //! Subset of states transaction sync logic is implemented to handle.
  82  using SyncTxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateInactive>;
  83  
  84  //! Try to interpret deserialized TxStateUnrecognized data as a recognized state.
  85  static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)
  86  {
  87      if (data.block_hash == uint256::ZERO) {
  88          if (data.index == 0) return TxStateInactive{};
  89      } else if (data.block_hash == uint256::ONE) {
  90          if (data.index == -1) return TxStateInactive{/*abandoned=*/true};
  91      } else if (data.index >= 0) {
  92          return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};
  93      } else if (data.index == -1) {
  94          return TxStateBlockConflicted{data.block_hash, /*height=*/-1};
  95      }
  96      return data;
  97  }
  98  
  99  //! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.
 100  static inline uint256 TxStateSerializedBlockHash(const TxState& state)
 101  {
 102      return std::visit(util::Overloaded{
 103          [](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },
 104          [](const TxStateInMempool& in_mempool) { return uint256::ZERO; },
 105          [](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },
 106          [](const TxStateBlockConflicted& conflicted) { return conflicted.conflicting_block_hash; },
 107          [](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }
 108      }, state);
 109  }
 110  
 111  //! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.
 112  static inline int TxStateSerializedIndex(const TxState& state)
 113  {
 114      return std::visit(util::Overloaded{
 115          [](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },
 116          [](const TxStateInMempool& in_mempool) { return 0; },
 117          [](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },
 118          [](const TxStateBlockConflicted& conflicted) { return -1; },
 119          [](const TxStateUnrecognized& unrecognized) { return unrecognized.index; }
 120      }, state);
 121  }
 122  
 123  //! Return TxState or SyncTxState as a string for logging or debugging.
 124  template<typename T>
 125  std::string TxStateString(const T& state)
 126  {
 127      return std::visit([](const auto& s) { return s.toString(); }, state);
 128  }
 129  
 130  /**
 131   * Cachable amount subdivided into avoid reuse and all balances
 132   */
 133  struct CachableAmount
 134  {
 135      std::optional<CAmount> m_avoid_reuse_value;
 136      std::optional<CAmount> m_all_value;
 137      inline void Reset()
 138      {
 139          m_avoid_reuse_value.reset();
 140          m_all_value.reset();
 141      }
 142      void Set(bool avoid_reuse, CAmount value)
 143      {
 144          if (avoid_reuse) {
 145              m_avoid_reuse_value = value;
 146          } else {
 147              m_all_value = value;
 148          }
 149      }
 150      CAmount Get(bool avoid_reuse)
 151      {
 152          if (avoid_reuse) {
 153              Assert(m_avoid_reuse_value.has_value());
 154              return m_avoid_reuse_value.value();
 155          }
 156          Assert(m_all_value.has_value());
 157          return m_all_value.value();
 158      }
 159      bool IsCached(bool avoid_reuse)
 160      {
 161          if (avoid_reuse) return m_avoid_reuse_value.has_value();
 162          return m_all_value.has_value();
 163      }
 164  };
 165  
 166  
 167  /** Legacy class used for deserializing vtxPrev for backwards compatibility.
 168   * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
 169   * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
 170   * These need to get deserialized for field alignment when deserializing
 171   * a CWalletTx, but the deserialized values are discarded.**/
 172  class CMerkleTx
 173  {
 174  public:
 175      template<typename Stream>
 176      void Unserialize(Stream& s)
 177      {
 178          CTransactionRef tx;
 179          uint256 hashBlock;
 180          std::vector<uint256> vMerkleBranch;
 181          int nIndex;
 182  
 183          s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;
 184      }
 185  };
 186  
 187  /**
 188   * A transaction with a bunch of additional info that only the owner cares about.
 189   * It includes any unrecorded transactions needed to link it back to the block chain.
 190   */
 191  class CWalletTx
 192  {
 193  public:
 194      // "from" and "message" are obsolete fields that could be set in
 195      // the UI prior to 2011 (removed in commit 4d9b223)
 196      // These fields are kept to avoid losing metadata.
 197      std::optional<std::string> m_from;
 198      std::optional<std::string> m_message;
 199      // Comment strings provided by the user
 200      std::optional<std::string> m_comment;
 201      std::optional<std::string> m_comment_to;
 202      std::optional<Txid> m_replaces_txid;
 203      std::optional<Txid> m_replaced_by_txid;
 204      // BIP 21 URI Messages
 205      std::vector<std::string> m_messages;
 206      // BIP 70 Payment Request (deprecated, field kept to preserve metadata from old wallets)
 207      std::vector<std::string> m_payment_requests;
 208      unsigned int nTimeReceived; //!< time received by this node
 209      /**
 210       * Stable timestamp that never changes, and reflects the order a transaction
 211       * was added to the wallet. Timestamp is based on the block time for a
 212       * transaction added as part of a block, or else the time when the
 213       * transaction was received if it wasn't part of a block, with the timestamp
 214       * adjusted in both cases so timestamp order matches the order transactions
 215       * were added to the wallet. More details can be found in
 216       * CWallet::ComputeTimeSmart().
 217       */
 218      unsigned int nTimeSmart;
 219      // Cached value for whether the transaction spends any inputs known to the wallet
 220      mutable std::optional<bool> m_cached_from_me{std::nullopt};
 221      int64_t nOrderPos; //!< position in ordered transaction list
 222      std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
 223  
 224      // memory only
 225      enum AmountType { DEBIT, CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
 226      mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
 227      /**
 228       * This flag is true if all m_amounts caches are empty. This is particularly
 229       * useful in places where MarkDirty is conditionally called and the
 230       * condition can be expensive and thus can be skipped if the flag is true.
 231       * See MarkDestinationsDirty.
 232       */
 233      mutable bool m_is_cache_empty{true};
 234      mutable bool fChangeCached;
 235      mutable CAmount nChangeCached;
 236  
 237      CWalletTx(CTransactionRef tx, const TxState& state) : tx(std::move(tx)), m_state(state)
 238      {
 239          Init();
 240      }
 241  
 242      void Init()
 243      {
 244          nTimeReceived = 0;
 245          nTimeSmart = 0;
 246          fChangeCached = false;
 247          nChangeCached = 0;
 248          nOrderPos = -1;
 249      }
 250  
 251      CTransactionRef tx;
 252      TxState m_state;
 253  
 254      // Set of mempool transactions that conflict
 255      // directly with the transaction, or that conflict
 256      // with an ancestor transaction. This set will be
 257      // empty if state is InMempool or Confirmed, but
 258      // can be nonempty if state is Inactive or
 259      // BlockConflicted.
 260      std::set<Txid> mempool_conflicts;
 261  
 262      // Track v3 mempool tx that spends from this tx
 263      // so that we don't try to create another unconfirmed child
 264      std::optional<Txid> truc_child_in_mempool;
 265  
 266      template<typename Stream>
 267      void Serialize(Stream& s) const
 268      {
 269          std::map<std::string, std::string> string_values;
 270          if (m_from) string_values["from"] = *m_from;
 271          if (m_message) string_values["message"] = *m_message;
 272          if (m_comment) string_values["comment"] = *m_comment;
 273          if (m_comment_to) string_values["to"] = *m_comment_to;
 274          if (m_replaces_txid) string_values["replaces_txid"] = m_replaces_txid->ToString();
 275          if (m_replaced_by_txid) string_values["replaced_by_txid"] = m_replaced_by_txid->ToString();
 276          string_values["fromaccount"] = "";
 277          if (nOrderPos != -1) string_values["n"] = util::ToString(nOrderPos);
 278          if (nTimeSmart) string_values["timesmart"] = strprintf("%u", nTimeSmart);
 279  
 280          std::vector<std::pair<std::string, std::string>> msgs_reqs;
 281          msgs_reqs.reserve(m_messages.size() + m_payment_requests.size());
 282          for (const std::string& msg : m_messages) {
 283              msgs_reqs.emplace_back("Message", msg);
 284          }
 285          for (const std::string& req : m_payment_requests) {
 286              msgs_reqs.emplace_back("PaymentRequest", req);
 287          }
 288  
 289          std::vector<uint8_t> dummy_vector1; // Used to be vMerkleBranch
 290          std::vector<uint8_t> dummy_vector2; // Used to be vtxPrev
 291          bool dummy_bool = false; // Used to be fFromMe, and fSpent
 292          uint32_t dummy_int = 0; // Used to be fTimeReceivedIsTxTime
 293          uint256 serializedHash = TxStateSerializedBlockHash(m_state);
 294          int serializedIndex = TxStateSerializedIndex(m_state);
 295          s << TX_WITH_WITNESS(tx) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << string_values << msgs_reqs << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
 296      }
 297  
 298      template<typename Stream>
 299      void Unserialize(Stream& s)
 300      {
 301          Init();
 302  
 303          std::vector<uint256> dummy_vector1; // Used to be vMerkleBranch
 304          std::vector<CMerkleTx> dummy_vector2; // Used to be vtxPrev
 305          bool dummy_bool; // Used to be fFromMe, and fSpent
 306          uint32_t dummy_int; // Used to be fTimeReceivedIsTxTime
 307          uint256 serialized_block_hash;
 308          int serializedIndex;
 309          std::map<std::string, std::string> string_values;
 310          std::vector<std::pair<std::string, std::string>> msgs_reqs;
 311          s >> TX_WITH_WITNESS(tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> string_values >> msgs_reqs >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
 312  
 313          m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
 314  
 315          string_values.erase("fromaccount");
 316          string_values.erase("spent");
 317          for (const auto& [key, value] : string_values) {
 318              if (key == "n") nOrderPos = LocaleIndependentAtoi<int64_t>(value);
 319              else if (key == "timesmart") nTimeSmart = LocaleIndependentAtoi<int64_t>(value);
 320              else if (key == "from") m_from = value;
 321              else if (key == "message") m_message = value;
 322              else if (key == "comment") m_comment = value;
 323              else if (key == "to") m_comment_to = value;
 324              else if (key == "replaces_txid") m_replaces_txid = Txid::FromHex(value);
 325              else if (key == "replaced_by_txid") m_replaced_by_txid = Txid::FromHex(value);
 326              else {
 327                  throw std::runtime_error("Unexpected value in CWalletTx strings value map");
 328              }
 329          }
 330  
 331          for (const auto& [type, data] : msgs_reqs) {
 332              if (type == "Message") m_messages.emplace_back(data);
 333              else if (type == "PaymentRequest") m_payment_requests.emplace_back(data);
 334              else {
 335                  throw std::runtime_error("Unknown type in CWalletTx messages and requests vector");
 336              }
 337          }
 338      }
 339  
 340      void SetTx(CTransactionRef arg)
 341      {
 342          tx = std::move(arg);
 343      }
 344  
 345      //! make sure balances are recalculated
 346      void MarkDirty()
 347      {
 348          m_amounts[DEBIT].Reset();
 349          m_amounts[CREDIT].Reset();
 350          fChangeCached = false;
 351          m_is_cache_empty = true;
 352          m_cached_from_me = std::nullopt;
 353      }
 354  
 355      /** True if only scriptSigs are different */
 356      bool IsEquivalentTo(const CWalletTx& tx) const;
 357  
 358      bool InMempool() const;
 359  
 360      int64_t GetTxTime() const;
 361  
 362      template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
 363      template<typename T> T* state() { return std::get_if<T>(&m_state); }
 364  
 365      //! Update transaction state when attaching to a chain, filling in heights
 366      //! of conflicted and confirmed blocks
 367      void updateState(interfaces::Chain& chain);
 368  
 369      bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
 370      bool isMempoolConflicted() const { return !mempool_conflicts.empty(); }
 371      bool isBlockConflicted() const { return state<TxStateBlockConflicted>(); }
 372      bool isInactive() const { return state<TxStateInactive>(); }
 373      bool isUnconfirmed() const { return !isAbandoned() && !isBlockConflicted() && !isMempoolConflicted() && !isConfirmed(); }
 374      bool isConfirmed() const { return state<TxStateConfirmed>(); }
 375      const Txid& GetHash() const LIFETIMEBOUND { return tx->GetHash(); }
 376      const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return tx->GetWitnessHash(); }
 377      bool IsCoinBase() const { return tx->IsCoinBase(); }
 378  
 379  private:
 380      // Disable copying of CWalletTx objects to prevent bugs where instances get
 381      // copied in and out of the mapWallet map, and fields are updated in the
 382      // wrong copy.
 383      CWalletTx(const CWalletTx&) = default;
 384      CWalletTx& operator=(const CWalletTx&) = default;
 385  public:
 386      // Instead have an explicit copy function
 387      void CopyFrom(const CWalletTx&);
 388  };
 389  
 390  struct WalletTxOrderComparator {
 391      bool operator()(const CWalletTx* a, const CWalletTx* b) const
 392      {
 393          return a->nOrderPos < b->nOrderPos;
 394      }
 395  };
 396  
 397  class WalletTXO
 398  {
 399  private:
 400      const CWalletTx& m_wtx;
 401      const CTxOut& m_output;
 402  
 403  public:
 404      WalletTXO(const CWalletTx& wtx, const CTxOut& output)
 405      : m_wtx(wtx),
 406      m_output(output)
 407      {
 408          Assume(std::ranges::find(wtx.tx->vout, output) != wtx.tx->vout.end());
 409      }
 410  
 411      const CWalletTx& GetWalletTx() const { return m_wtx; }
 412  
 413      const CTxOut& GetTxOut() const { return m_output; }
 414  };
 415  } // namespace wallet
 416  
 417  #endif // BITCOIN_WALLET_TRANSACTION_H
 418