LCOV - code coverage report
Current view: top level - src/wallet - transaction.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 101 112 90.2 %
Date: 2026-06-25 07:23:51 Functions: 46 54 85.2 %

          Line data    Source code
       1             : // Copyright (c) 2021 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 <serialize.h>
      12             : #include <wallet/ismine.h>
      13             : #include <threadsafety.h>
      14             : #include <tinyformat.h>
      15             : #include <uint256.h>
      16             : #include <util/overloaded.h>
      17             : #include <util/strencodings.h>
      18             : #include <util/string.h>
      19             : 
      20             : #include <variant>
      21             : #include <vector>
      22             : 
      23             : namespace wallet {
      24             : //! State of transaction confirmed in a block.
      25             : struct TxStateConfirmed {
      26             :     uint256 confirmed_block_hash;
      27             :     int confirmed_block_height;
      28             :     int position_in_block;
      29             : 
      30        1742 :     explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
      31         723 :     std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
      32             : };
      33             : 
      34             : //! State of transaction added to mempool.
      35             : struct TxStateInMempool {
      36           6 :     std::string toString() const { return strprintf("InMempool"); }
      37             : };
      38             : 
      39             : //! State of rejected transaction that conflicts with a confirmed block.
      40             : struct TxStateConflicted {
      41             :     uint256 conflicting_block_hash;
      42             :     int conflicting_block_height;
      43             : 
      44           8 :     explicit TxStateConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
      45           0 :     std::string toString() const { return strprintf("Conflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }
      46             : };
      47             : 
      48             : //! State of transaction not confirmed or conflicting with a known block and
      49             : //! not in the mempool. May conflict with the mempool, or with an unknown block,
      50             : //! or be abandoned, never broadcast, or rejected from the mempool for another
      51             : //! reason.
      52             : struct TxStateInactive {
      53             :     bool abandoned;
      54             : 
      55      231150 :     explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
      56          18 :     std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }
      57             : };
      58             : 
      59             : //! State of transaction loaded in an unrecognized state with unexpected hash or
      60             : //! index values. Treated as inactive (with serialized hash and index values
      61             : //! preserved) by default, but may enter another state if transaction is added
      62             : //! to the mempool, or confirmed, or abandoned, or found conflicting.
      63             : struct TxStateUnrecognized {
      64             :     uint256 block_hash;
      65             :     int index;
      66             : 
      67          54 :     TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
      68           0 :     std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }
      69             : };
      70             : 
      71             : //! All possible CWalletTx states
      72             : using TxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateConflicted, TxStateInactive, TxStateUnrecognized>;
      73             : 
      74             : //! Subset of states transaction sync logic is implemented to handle.
      75             : using SyncTxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateInactive>;
      76             : 
      77             : //! Try to interpret deserialized TxStateUnrecognized data as a recognized state.
      78          27 : static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)
      79             : {
      80          27 :     if (data.block_hash == uint256::ZERO) {
      81           6 :         if (data.index == 0) return TxStateInactive{};
      82          25 :     } else if (data.block_hash == uint256::ONE) {
      83           5 :         if (data.index == -1) return TxStateInactive{/*abandoned=*/true};
      84          20 :     } else if (data.index >= 0) {
      85          10 :         return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};
      86           6 :     } else if (data.index == -1) {
      87           3 :         return TxStateConflicted{data.block_hash, /*height=*/-1};
      88             :     }
      89          11 :     return data;
      90          27 : }
      91             : 
      92             : //! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.
      93         777 : static inline uint256 TxStateSerializedBlockHash(const TxState& state)
      94             : {
      95         777 :     return std::visit(util::Overloaded{
      96          20 :         [](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },
      97           7 :         [](const TxStateInMempool& in_mempool) { return uint256::ZERO; },
      98         735 :         [](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },
      99           4 :         [](const TxStateConflicted& conflicted) { return conflicted.conflicting_block_hash; },
     100          11 :         [](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }
     101         777 :     }, state);
     102             : }
     103             : 
     104             : //! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.
     105         777 : static inline int TxStateSerializedIndex(const TxState& state)
     106             : {
     107         777 :     return std::visit(util::Overloaded{
     108          20 :         [](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },
     109           7 :         [](const TxStateInMempool& in_mempool) { return 0; },
     110         735 :         [](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },
     111           4 :         [](const TxStateConflicted& conflicted) { return -1; },
     112          11 :         [](const TxStateUnrecognized& unrecognized) { return unrecognized.index; }
     113         777 :     }, state);
     114             : }
     115             : 
     116             : //! Return TxState or SyncTxState as a string for logging or debugging.
     117             : template<typename T>
     118         747 : std::string TxStateString(const T& state)
     119             : {
     120        1494 :     return std::visit([](const auto& s) { return s.toString(); }, state);
     121             : }
     122             : 
     123             : typedef std::map<std::string, std::string> mapValue_t;
     124             : 
     125             : /** Legacy class used for deserializing vtxPrev for backwards compatibility.
     126             :  * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
     127             :  * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
     128             :  * These need to get deserialized for field alignment when deserializing
     129             :  * a CWalletTx, but the deserialized values are discarded.**/
     130             : class CMerkleTx
     131             : {
     132             : public:
     133             :     template<typename Stream>
     134           0 :     void Unserialize(Stream& s)
     135             :     {
     136           0 :         CTransactionRef tx;
     137           0 :         uint256 hashBlock;
     138           0 :         std::vector<uint256> vMerkleBranch;
     139             :         int nIndex;
     140             : 
     141           0 :         s >> tx >> hashBlock >> vMerkleBranch >> nIndex;
     142           0 :     }
     143             : };
     144             : 
     145             : /**
     146             :  * A transaction with a bunch of additional info that only the owner cares about.
     147             :  * It includes any unrecorded transactions needed to link it back to the block chain.
     148             :  */
     149             : class CWalletTx
     150             : {
     151             : public:
     152             :     /**
     153             :      * Key/value map with information about the transaction.
     154             :      *
     155             :      * The following keys can be read and written through the map and are
     156             :      * serialized in the wallet database:
     157             :      *
     158             :      *     "comment", "to"   - comment strings provided to sendtoaddress,
     159             :      *                         and sendmany wallet RPCs
     160             :      *     "replaces_txid"   - txid (as HexStr) of transaction replaced by
     161             :      *                         bumpfee on transaction created by bumpfee
     162             :      *     "replaced_by_txid" - txid (as HexStr) of transaction created by
     163             :      *                         bumpfee on transaction replaced by bumpfee
     164             :      *     "from", "message" - obsolete fields that could be set in UI prior to
     165             :      *                         2011 (removed in commit 4d9b223)
     166             :      *
     167             :      * The following keys are serialized in the wallet database, but shouldn't
     168             :      * be read or written through the map (they will be temporarily added and
     169             :      * removed from the map during serialization):
     170             :      *
     171             :      *     "fromaccount"     - serialized strFromAccount value
     172             :      *     "n"               - serialized nOrderPos value
     173             :      *     "timesmart"       - serialized nTimeSmart value
     174             :      *     "spent"           - serialized vfSpent value that existed prior to
     175             :      *                         2014 (removed in commit 93a18a3)
     176             :      */
     177             :     mapValue_t mapValue;
     178             :     std::vector<std::pair<std::string, std::string> > vOrderForm;
     179             :     unsigned int fTimeReceivedIsTxTime;
     180             :     unsigned int nTimeReceived; //!< time received by this node
     181             :     /**
     182             :      * Stable timestamp that never changes, and reflects the order a transaction
     183             :      * was added to the wallet. Timestamp is based on the block time for a
     184             :      * transaction added as part of a block, or else the time when the
     185             :      * transaction was received if it wasn't part of a block, with the timestamp
     186             :      * adjusted in both cases so timestamp order matches the order transactions
     187             :      * were added to the wallet. More details can be found in
     188             :      * CWallet::ComputeTimeSmart().
     189             :      */
     190             :     unsigned int nTimeSmart;
     191             :     /**
     192             :      * From me flag is set to 1 for transactions that were created by the wallet
     193             :      * on this bitcoin node, and set to 0 for transactions that were created
     194             :      * externally and came in through the network or sendrawtransaction RPC.
     195             :      */
     196             :     bool fFromMe;
     197             :     int64_t nOrderPos; //!< position in ordered transaction list
     198             :     std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
     199             : 
     200             :     // memory only
     201             :     enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, ANON_CREDIT, DENOM_UCREDIT, DENOM_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
     202             :     mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
     203             :     /**
     204             :      * This flag is true if all m_amounts caches are empty. This is particularly
     205             :      * useful in places where MarkDirty is conditionally called and the
     206             :      * condition can be expensive and thus can be skipped if the flag is true.
     207             :      * See MarkDestinationsDirty.
     208             :      */
     209      116296 :     mutable bool m_is_cache_empty{true};
     210             :     mutable bool fChangeCached;
     211             :     mutable bool fIsChainlocked;
     212             :     mutable bool fIsInstantSendLocked;
     213             :     mutable CAmount nChangeCached;
     214             : 
     215      232592 :     CWalletTx(CTransactionRef tx, const TxState& state) : tx(std::move(tx)), m_state(state)
     216      116296 :     {
     217      116296 :         Init();
     218      232592 :     }
     219             : 
     220      116298 :     void Init()
     221             :     {
     222      116298 :         mapValue.clear();
     223      116298 :         vOrderForm.clear();
     224      116298 :         fTimeReceivedIsTxTime = false;
     225      116298 :         nTimeReceived = 0;
     226      116298 :         nTimeSmart = 0;
     227      116298 :         fFromMe = false;
     228      116298 :         fChangeCached = false;
     229      116298 :         fIsChainlocked = false;
     230      116298 :         fIsInstantSendLocked = false;
     231      116298 :         nChangeCached = 0;
     232      116298 :         nOrderPos = -1;
     233      116298 :     }
     234             : 
     235             :     CTransactionRef tx;
     236             :     TxState m_state;
     237             : 
     238             :     template<typename Stream>
     239         746 :     void Serialize(Stream& s) const
     240             :     {
     241         746 :         mapValue_t mapValueCopy = mapValue;
     242             : 
     243         746 :         mapValueCopy["fromaccount"] = "";
     244         746 :         if (nOrderPos != -1) {
     245         746 :             mapValueCopy["n"] = ToString(nOrderPos);
     246         746 :         }
     247         746 :         if (nTimeSmart) {
     248         746 :             mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
     249         746 :         }
     250             : 
     251         746 :         std::vector<uint8_t> dummy_vector1; //!< Used to be vMerkleBranch
     252         746 :         std::vector<uint8_t> dummy_vector2; //!< Used to be vtxPrev
     253         746 :         bool dummy_bool = false; //!< Used to be fSpent
     254         746 :         uint256 serializedHash = TxStateSerializedBlockHash(m_state);
     255         746 :         int serializedIndex = TxStateSerializedIndex(m_state);
     256         746 :         s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool;
     257         746 :     }
     258             : 
     259             :     template<typename Stream>
     260           2 :     void Unserialize(Stream& s)
     261             :     {
     262           2 :         Init();
     263             : 
     264           2 :         std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
     265           2 :         std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
     266             :         bool dummy_bool; //! Used to be fSpent
     267           2 :         uint256 serialized_block_hash;
     268             :         int serializedIndex;
     269           2 :         s >> tx >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool;
     270             : 
     271           2 :         m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
     272             : 
     273           2 :         const auto it_op = mapValue.find("n");
     274           2 :         nOrderPos = (it_op != mapValue.end()) ? LocaleIndependentAtoi<int64_t>(it_op->second) : -1;
     275           2 :         nTimeSmart = mapValue.count("timesmart") ? (unsigned int)LocaleIndependentAtoi<int64_t>(mapValue["timesmart"]) : 0;
     276             : 
     277           2 :         mapValue.erase("fromaccount");
     278           2 :         mapValue.erase("spent");
     279           2 :         mapValue.erase("n");
     280           2 :         mapValue.erase("timesmart");
     281           2 :     }
     282             : 
     283             :     void SetTx(CTransactionRef arg)
     284             :     {
     285             :         tx = std::move(arg);
     286             :     }
     287             : 
     288             :     //! make sure balances are recalculated
     289         852 :     void MarkDirty()
     290             :     {
     291         852 :         m_amounts[DEBIT].Reset();
     292         852 :         m_amounts[CREDIT].Reset();
     293         852 :         m_amounts[ANON_CREDIT].Reset();
     294         852 :         m_amounts[DENOM_CREDIT].Reset();
     295         852 :         m_amounts[DENOM_UCREDIT].Reset();
     296         852 :         m_amounts[IMMATURE_CREDIT].Reset();
     297         852 :         m_amounts[AVAILABLE_CREDIT].Reset();
     298         852 :         fChangeCached = false;
     299         852 :         m_is_cache_empty = true;
     300         852 :     }
     301             : 
     302             :     /** True if only scriptSigs are different */
     303             :     bool IsEquivalentTo(const CWalletTx& tx) const;
     304             : 
     305             :     bool InMempool() const;
     306             : 
     307             :     int64_t GetTxTime() const;
     308             : 
     309       28444 :     template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
     310           7 :     template<typename T> T* state() { return std::get_if<T>(&m_state); }
     311             : 
     312           1 :     bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
     313           0 :     bool isConflicted() const { return state<TxStateConflicted>(); }
     314             :     bool isUnconfirmed() const { return !isAbandoned() && !isConflicted() && !isConfirmed(); }
     315           0 :     bool isConfirmed() const { return state<TxStateConfirmed>(); }
     316      164368 :     const uint256& GetHash() const LIFETIMEBOUND { return tx->GetHash(); }
     317       25762 :     bool IsCoinBase() const { return tx->IsCoinBase(); }
     318           0 :     bool IsPlatformTransfer() const { return tx->IsPlatformTransfer(); }
     319             : 
     320             :     // Disable copying of CWalletTx objects to prevent bugs where instances get
     321             :     // copied in and out of the mapWallet map, and fields are updated in the
     322             :     // wrong copy.
     323             :     CWalletTx(CWalletTx const &) = delete;
     324             :     void operator=(CWalletTx const &x) = delete;
     325             : };
     326             : } // namespace wallet
     327             : 
     328             : #endif // BITCOIN_WALLET_TRANSACTION_H

Generated by: LCOV version 1.16