/ src / wallet / transaction.h
transaction.h
  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  typedef std::map<std::string, std::string> mapValue_t;
168  
169  
170  /** Legacy class used for deserializing vtxPrev for backwards compatibility.
171   * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
172   * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
173   * These need to get deserialized for field alignment when deserializing
174   * a CWalletTx, but the deserialized values are discarded.**/
175  class CMerkleTx
176  {
177  public:
178      template<typename Stream>
179      void Unserialize(Stream& s)
180      {
181          CTransactionRef tx;
182          uint256 hashBlock;
183          std::vector<uint256> vMerkleBranch;
184          int nIndex;
185  
186          s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;
187      }
188  };
189  
190  /**
191   * A transaction with a bunch of additional info that only the owner cares about.
192   * It includes any unrecorded transactions needed to link it back to the block chain.
193   */
194  class CWalletTx
195  {
196  public:
197      /**
198       * Key/value map with information about the transaction.
199       *
200       * The following keys can be read and written through the map and are
201       * serialized in the wallet database:
202       *
203       *     "comment", "to"   - comment strings provided to sendtoaddress,
204       *                         and sendmany wallet RPCs
205       *     "replaces_txid"   - txid (as HexStr) of transaction replaced by
206       *                         bumpfee on transaction created by bumpfee
207       *     "replaced_by_txid" - txid (as HexStr) of transaction created by
208       *                         bumpfee on transaction replaced by bumpfee
209       *     "from", "message" - obsolete fields that could be set in UI prior to
210       *                         2011 (removed in commit 4d9b223)
211       *
212       * The following keys are serialized in the wallet database, but shouldn't
213       * be read or written through the map (they will be temporarily added and
214       * removed from the map during serialization):
215       *
216       *     "fromaccount"     - serialized strFromAccount value
217       *     "n"               - serialized nOrderPos value
218       *     "timesmart"       - serialized nTimeSmart value
219       *     "spent"           - serialized vfSpent value that existed prior to
220       *                         2014 (removed in commit 93a18a3)
221       */
222      mapValue_t mapValue;
223      std::vector<std::pair<std::string, std::string> > vOrderForm;
224      unsigned int nTimeReceived; //!< time received by this node
225      /**
226       * Stable timestamp that never changes, and reflects the order a transaction
227       * was added to the wallet. Timestamp is based on the block time for a
228       * transaction added as part of a block, or else the time when the
229       * transaction was received if it wasn't part of a block, with the timestamp
230       * adjusted in both cases so timestamp order matches the order transactions
231       * were added to the wallet. More details can be found in
232       * CWallet::ComputeTimeSmart().
233       */
234      unsigned int nTimeSmart;
235      // Cached value for whether the transaction spends any inputs known to the wallet
236      mutable std::optional<bool> m_cached_from_me{std::nullopt};
237      int64_t nOrderPos; //!< position in ordered transaction list
238      std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
239  
240      // memory only
241      enum AmountType { DEBIT, CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
242      mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
243      /**
244       * This flag is true if all m_amounts caches are empty. This is particularly
245       * useful in places where MarkDirty is conditionally called and the
246       * condition can be expensive and thus can be skipped if the flag is true.
247       * See MarkDestinationsDirty.
248       */
249      mutable bool m_is_cache_empty{true};
250      mutable bool fChangeCached;
251      mutable CAmount nChangeCached;
252  
253      CWalletTx(CTransactionRef tx, const TxState& state) : tx(std::move(tx)), m_state(state)
254      {
255          Init();
256      }
257  
258      void Init()
259      {
260          mapValue.clear();
261          vOrderForm.clear();
262          nTimeReceived = 0;
263          nTimeSmart = 0;
264          fChangeCached = false;
265          nChangeCached = 0;
266          nOrderPos = -1;
267      }
268  
269      CTransactionRef tx;
270      TxState m_state;
271  
272      // Set of mempool transactions that conflict
273      // directly with the transaction, or that conflict
274      // with an ancestor transaction. This set will be
275      // empty if state is InMempool or Confirmed, but
276      // can be nonempty if state is Inactive or
277      // BlockConflicted.
278      std::set<Txid> mempool_conflicts;
279  
280      // Track v3 mempool tx that spends from this tx
281      // so that we don't try to create another unconfirmed child
282      std::optional<Txid> truc_child_in_mempool;
283  
284      template<typename Stream>
285      void Serialize(Stream& s) const
286      {
287          mapValue_t mapValueCopy = mapValue;
288  
289          mapValueCopy["fromaccount"] = "";
290          if (nOrderPos != -1) {
291              mapValueCopy["n"] = util::ToString(nOrderPos);
292          }
293          if (nTimeSmart) {
294              mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
295          }
296  
297          std::vector<uint8_t> dummy_vector1; // Used to be vMerkleBranch
298          std::vector<uint8_t> dummy_vector2; // Used to be vtxPrev
299          bool dummy_bool = false; // Used to be fFromMe, and fSpent
300          uint32_t dummy_int = 0; // Used to be fTimeReceivedIsTxTime
301          uint256 serializedHash = TxStateSerializedBlockHash(m_state);
302          int serializedIndex = TxStateSerializedIndex(m_state);
303          s << TX_WITH_WITNESS(tx) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
304      }
305  
306      template<typename Stream>
307      void Unserialize(Stream& s)
308      {
309          Init();
310  
311          std::vector<uint256> dummy_vector1; // Used to be vMerkleBranch
312          std::vector<CMerkleTx> dummy_vector2; // Used to be vtxPrev
313          bool dummy_bool; // Used to be fFromMe, and fSpent
314          uint32_t dummy_int; // Used to be fTimeReceivedIsTxTime
315          uint256 serialized_block_hash;
316          int serializedIndex;
317          s >> TX_WITH_WITNESS(tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
318  
319          m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
320  
321          const auto it_op = mapValue.find("n");
322          nOrderPos = (it_op != mapValue.end()) ? LocaleIndependentAtoi<int64_t>(it_op->second) : -1;
323          const auto it_ts = mapValue.find("timesmart");
324          nTimeSmart = (it_ts != mapValue.end()) ? static_cast<unsigned int>(LocaleIndependentAtoi<int64_t>(it_ts->second)) : 0;
325  
326          mapValue.erase("fromaccount");
327          mapValue.erase("spent");
328          mapValue.erase("n");
329          mapValue.erase("timesmart");
330      }
331  
332      void SetTx(CTransactionRef arg)
333      {
334          tx = std::move(arg);
335      }
336  
337      //! make sure balances are recalculated
338      void MarkDirty()
339      {
340          m_amounts[DEBIT].Reset();
341          m_amounts[CREDIT].Reset();
342          fChangeCached = false;
343          m_is_cache_empty = true;
344          m_cached_from_me = std::nullopt;
345      }
346  
347      /** True if only scriptSigs are different */
348      bool IsEquivalentTo(const CWalletTx& tx) const;
349  
350      bool InMempool() const;
351  
352      int64_t GetTxTime() const;
353  
354      template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
355      template<typename T> T* state() { return std::get_if<T>(&m_state); }
356  
357      //! Update transaction state when attaching to a chain, filling in heights
358      //! of conflicted and confirmed blocks
359      void updateState(interfaces::Chain& chain);
360  
361      bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
362      bool isMempoolConflicted() const { return !mempool_conflicts.empty(); }
363      bool isBlockConflicted() const { return state<TxStateBlockConflicted>(); }
364      bool isInactive() const { return state<TxStateInactive>(); }
365      bool isUnconfirmed() const { return !isAbandoned() && !isBlockConflicted() && !isMempoolConflicted() && !isConfirmed(); }
366      bool isConfirmed() const { return state<TxStateConfirmed>(); }
367      const Txid& GetHash() const LIFETIMEBOUND { return tx->GetHash(); }
368      const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return tx->GetWitnessHash(); }
369      bool IsCoinBase() const { return tx->IsCoinBase(); }
370  
371  private:
372      // Disable copying of CWalletTx objects to prevent bugs where instances get
373      // copied in and out of the mapWallet map, and fields are updated in the
374      // wrong copy.
375      CWalletTx(const CWalletTx&) = default;
376      CWalletTx& operator=(const CWalletTx&) = default;
377  public:
378      // Instead have an explicit copy function
379      void CopyFrom(const CWalletTx&);
380  };
381  
382  struct WalletTxOrderComparator {
383      bool operator()(const CWalletTx* a, const CWalletTx* b) const
384      {
385          return a->nOrderPos < b->nOrderPos;
386      }
387  };
388  
389  class WalletTXO
390  {
391  private:
392      const CWalletTx& m_wtx;
393      const CTxOut& m_output;
394  
395  public:
396      WalletTXO(const CWalletTx& wtx, const CTxOut& output)
397      : m_wtx(wtx),
398      m_output(output)
399      {
400          Assume(std::ranges::find(wtx.tx->vout, output) != wtx.tx->vout.end());
401      }
402  
403      const CWalletTx& GetWalletTx() const { return m_wtx; }
404  
405      const CTxOut& GetTxOut() const { return m_output; }
406  };
407  } // namespace wallet
408  
409  #endif // BITCOIN_WALLET_TRANSACTION_H