// Copyright (c) 2021 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_WALLET_TRANSACTION_H #define BITCOIN_WALLET_TRANSACTION_H #include #include #include #include #include #include #include #include #include #include typedef std::map mapValue_t; /** Legacy class used for deserializing vtxPrev for backwards compatibility. * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3, * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs. * These need to get deserialized for field alignment when deserializing * a CWalletTx, but the deserialized values are discarded.**/ class CMerkleTx { public: template void Unserialize(Stream& s) { CTransactionRef tx; uint256 hashBlock; std::vector vMerkleBranch; int nIndex; s >> tx >> hashBlock >> vMerkleBranch >> nIndex; } }; /** * A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. */ class CWalletTx { private: /** Constant used in hashBlock to indicate tx has been abandoned, only used at * serialization/deserialization to avoid ambiguity with conflicted. */ static constexpr const uint256& ABANDON_HASH = uint256::ONE; public: /** * Key/value map with information about the transaction. * * The following keys can be read and written through the map and are * serialized in the wallet database: * * "comment", "to" - comment strings provided to sendtoaddress, * and sendmany wallet RPCs * "replaces_txid" - txid (as HexStr) of transaction replaced by * bumpfee on transaction created by bumpfee * "replaced_by_txid" - txid (as HexStr) of transaction created by * bumpfee on transaction replaced by bumpfee * "from", "message" - obsolete fields that could be set in UI prior to * 2011 (removed in commit 4d9b223) * * The following keys are serialized in the wallet database, but shouldn't * be read or written through the map (they will be temporarily added and * removed from the map during serialization): * * "fromaccount" - serialized strFromAccount value * "n" - serialized nOrderPos value * "timesmart" - serialized nTimeSmart value * "spent" - serialized vfSpent value that existed prior to * 2014 (removed in commit 93a18a3) */ mapValue_t mapValue; std::vector > vOrderForm; unsigned int fTimeReceivedIsTxTime; unsigned int nTimeReceived; //!< time received by this node /** * Stable timestamp that never changes, and reflects the order a transaction * was added to the wallet. Timestamp is based on the block time for a * transaction added as part of a block, or else the time when the * transaction was received if it wasn't part of a block, with the timestamp * adjusted in both cases so timestamp order matches the order transactions * were added to the wallet. More details can be found in * CWallet::ComputeTimeSmart(). */ unsigned int nTimeSmart; /** * From me flag is set to 1 for transactions that were created by the wallet * on this bitcoin node, and set to 0 for transactions that were created * externally and came in through the network or sendrawtransaction RPC. */ bool fFromMe; int64_t nOrderPos; //!< position in ordered transaction list std::multimap::const_iterator m_it_wtxOrdered; // memory only enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS }; mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS]; /** * This flag is true if all m_amounts caches are empty. This is particularly * useful in places where MarkDirty is conditionally called and the * condition can be expensive and thus can be skipped if the flag is true. * See MarkDestinationsDirty. */ mutable bool m_is_cache_empty{true}; mutable bool fChangeCached; mutable bool fInMempool; mutable CAmount nChangeCached; CWalletTx(CTransactionRef arg) : tx(std::move(arg)) { Init(); } void Init() { mapValue.clear(); vOrderForm.clear(); fTimeReceivedIsTxTime = false; nTimeReceived = 0; nTimeSmart = 0; fFromMe = false; fChangeCached = false; fInMempool = false; nChangeCached = 0; nOrderPos = -1; m_confirm = Confirmation{}; } CTransactionRef tx; /** New transactions start as UNCONFIRMED. At BlockConnected, * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected, * they roll back to UNCONFIRMED. If we detect a conflicting transaction at * block connection, we update conflicted tx and its dependencies as CONFLICTED. * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED * by using the abandontransaction call. This last status may be override by a CONFLICTED * or CONFIRMED transition. */ enum Status { UNCONFIRMED, CONFIRMED, CONFLICTED, ABANDONED }; /** Confirmation includes tx status and a triplet of {block height/block hash/tx index in block} * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned. * Meaning of these fields changes with CONFLICTED state where they instead point to block hash * and block height of the deepest conflicting tx. */ struct Confirmation { Status status; int block_height; uint256 hashBlock; int nIndex; Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {} }; Confirmation m_confirm; template void Serialize(Stream& s) const { mapValue_t mapValueCopy = mapValue; mapValueCopy["fromaccount"] = ""; if (nOrderPos != -1) { mapValueCopy["n"] = ToString(nOrderPos); } if (nTimeSmart) { mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); } std::vector dummy_vector1; //!< Used to be vMerkleBranch std::vector dummy_vector2; //!< Used to be vtxPrev bool dummy_bool = false; //!< Used to be fSpent uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock; int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex; s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool; } template void Unserialize(Stream& s) { Init(); std::vector dummy_vector1; //!< Used to be vMerkleBranch std::vector dummy_vector2; //!< Used to be vtxPrev bool dummy_bool; //! Used to be fSpent int serializedIndex; s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool; /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to * the earliest block in the chain we know this or any in-wallet ancestor conflicts * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned. * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward * compatibility (pre-commit 9ac63d6). */ if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) { setAbandoned(); } else if (serializedIndex == -1) { setConflicted(); } else if (!m_confirm.hashBlock.IsNull()) { m_confirm.nIndex = serializedIndex; setConfirmed(); } const auto it_op = mapValue.find("n"); nOrderPos = (it_op != mapValue.end()) ? LocaleIndependentAtoi(it_op->second) : -1; const auto it_ts = mapValue.find("timesmart"); nTimeSmart = (it_ts != mapValue.end()) ? static_cast(LocaleIndependentAtoi(it_ts->second)) : 0; mapValue.erase("fromaccount"); mapValue.erase("spent"); mapValue.erase("n"); mapValue.erase("timesmart"); } void SetTx(CTransactionRef arg) { tx = std::move(arg); } //! make sure balances are recalculated void MarkDirty() { m_amounts[DEBIT].Reset(); m_amounts[CREDIT].Reset(); m_amounts[IMMATURE_CREDIT].Reset(); m_amounts[AVAILABLE_CREDIT].Reset(); fChangeCached = false; m_is_cache_empty = true; } /** True if only scriptSigs are different */ bool IsEquivalentTo(const CWalletTx& tx) const; bool InMempool() const; int64_t GetTxTime() const; bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; } void setAbandoned() { m_confirm.status = CWalletTx::ABANDONED; m_confirm.hashBlock = uint256(); m_confirm.block_height = 0; m_confirm.nIndex = 0; } bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; } void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; } bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; } void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; } bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; } void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; } const uint256& GetHash() const { return tx->GetHash(); } bool IsCoinBase() const { return tx->IsCoinBase(); } // Disable copying of CWalletTx objects to prevent bugs where instances get // copied in and out of the mapWallet map, and fields are updated in the // wrong copy. CWalletTx(CWalletTx const &) = delete; void operator=(CWalletTx const &x) = delete; }; #endif // BITCOIN_WALLET_TRANSACTION_H