Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2021-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#ifndef BITCOIN_WALLET_TRANSACTION_H6#define BITCOIN_WALLET_TRANSACTION_H8#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>19#include <bitset>20#include <cstdint>21#include <map>22#include <utility>23#include <variant>24#include <vector>26namespace interfaces {27class Chain;28} // namespace interfaces30namespace wallet {31class WalletBatch;33//! State of transaction confirmed in a block.34struct TxStateConfirmed {35uint256 confirmed_block_hash;36int confirmed_block_height;37int position_in_block;39explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}40std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }41};43//! State of transaction added to mempool.44struct TxStateInMempool {45std::string toString() const { return strprintf("InMempool"); }46};48//! State of rejected transaction that conflicts with a confirmed block.49struct TxStateBlockConflicted {50uint256 conflicting_block_hash;51int conflicting_block_height;53explicit TxStateBlockConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}54std::string toString() const { return strprintf("BlockConflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }55};57//! State of transaction not confirmed or conflicting with a known block and58//! not in the mempool. May conflict with the mempool, or with an unknown block,59//! or be abandoned, never broadcast, or rejected from the mempool for another60//! reason.61struct TxStateInactive {62bool abandoned;64explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}65std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }66};68//! State of transaction loaded in an unrecognized state with unexpected hash or69//! index values. Treated as inactive (with serialized hash and index values70//! preserved) by default, but may enter another state if transaction is added71//! to the mempool, or confirmed, or abandoned, or found conflicting.72struct TxStateUnrecognized {73uint256 block_hash;74int index;76TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}77std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }78};80//! All possible CWalletTx states81using TxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateBlockConflicted, TxStateInactive, TxStateUnrecognized>;83//! Subset of states transaction sync logic is implemented to handle.84using SyncTxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateInactive>;86//! Try to interpret deserialized TxStateUnrecognized data as a recognized state.87static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)88{89if (data.block_hash == uint256::ZERO) {90if (data.index == 0) return TxStateInactive{};91} else if (data.block_hash == uint256::ONE) {92if (data.index == -1) return TxStateInactive{/*abandoned=*/true};93} else if (data.index >= 0) {94return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};95} else if (data.index == -1) {96return TxStateBlockConflicted{data.block_hash, /*height=*/-1};97}98return data;99}101//! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.102static inline uint256 TxStateSerializedBlockHash(const TxState& state)103{104return std::visit(util::Overloaded{105[](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },106[](const TxStateInMempool& in_mempool) { return uint256::ZERO; },107[](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },108[](const TxStateBlockConflicted& conflicted) { return conflicted.conflicting_block_hash; },109[](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }110}, state);111}113//! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.114static inline int TxStateSerializedIndex(const TxState& state)115{116return std::visit(util::Overloaded{117[](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },118[](const TxStateInMempool& in_mempool) { return 0; },119[](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },120[](const TxStateBlockConflicted& conflicted) { return -1; },121[](const TxStateUnrecognized& unrecognized) { return unrecognized.index; }122}, state);123}125//! Return TxState or SyncTxState as a string for logging or debugging.126template<typename T>127std::string TxStateString(const T& state)128{129return std::visit([](const auto& s) { return s.toString(); }, state);130}132/**133* Cachable amount subdivided into avoid reuse and all balances134*/135struct CachableAmount136{137std::optional<CAmount> m_avoid_reuse_value;138std::optional<CAmount> m_all_value;139inline void Reset()140{141m_avoid_reuse_value.reset();142m_all_value.reset();143}144void Set(bool avoid_reuse, CAmount value)145{146if (avoid_reuse) {147m_avoid_reuse_value = value;148} else {149m_all_value = value;150}151}152CAmount Get(bool avoid_reuse)153{154if (avoid_reuse) {155Assert(m_avoid_reuse_value.has_value());156return m_avoid_reuse_value.value();157}158Assert(m_all_value.has_value());159return m_all_value.value();160}161bool IsCached(bool avoid_reuse)162{163if (avoid_reuse) return m_avoid_reuse_value.has_value();164return m_all_value.has_value();165}166};169/** Legacy class used for deserializing vtxPrev for backwards compatibility.170* vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,171* but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.172* These need to get deserialized for field alignment when deserializing173* a CWalletTx, but the deserialized values are discarded.**/174class CMerkleTx175{176public:177template<typename Stream>178void Unserialize(Stream& s)179{180CTransactionRef tx;181uint256 hashBlock;182std::vector<uint256> vMerkleBranch;183int nIndex;185s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;186}187};189/**190* A transaction with a bunch of additional info that only the owner cares about.191* It includes any unrecorded transactions needed to link it back to the block chain.192*/193class CWalletTx194{195public:196// "from" and "message" are obsolete fields that could be set in197// the UI prior to 2011 (removed in commit 4d9b223)198// These fields are kept to avoid losing metadata.199std::optional<std::string> m_from;200std::optional<std::string> m_message;201// Comment strings provided by the user202std::optional<std::string> m_comment;203std::optional<std::string> m_comment_to;204std::optional<Txid> m_replaces_txid;205std::optional<Txid> m_replaced_by_txid;206// BIP 21 URI Messages207std::vector<std::string> m_messages;208// BIP 70 Payment Request (deprecated, field kept to preserve metadata from old wallets)209std::vector<std::string> m_payment_requests;210unsigned int nTimeReceived; //!< time received by this node211/**212* Stable timestamp that never changes, and reflects the order a transaction213* was added to the wallet. Timestamp is based on the block time for a214* transaction added as part of a block, or else the time when the215* transaction was received if it wasn't part of a block, with the timestamp216* adjusted in both cases so timestamp order matches the order transactions217* were added to the wallet. More details can be found in218* CWallet::ComputeTimeSmart().219*/220unsigned int nTimeSmart;221// Cached value for whether the transaction spends any inputs known to the wallet222mutable std::optional<bool> m_cached_from_me{std::nullopt};223int64_t nOrderPos; //!< position in ordered transaction list224std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;226// memory only227enum AmountType { DEBIT, CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };228mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];229/**230* This flag is true if all m_amounts caches are empty. This is particularly231* useful in places where MarkDirty is conditionally called and the232* condition can be expensive and thus can be skipped if the flag is true.233* See MarkDestinationsDirty.234*/235mutable bool m_is_cache_empty{true};236mutable bool fChangeCached;237mutable CAmount nChangeCached;239CWalletTx(CTransactionRef tx, const TxState& state) : m_state(state)240{241Assert(tx);242m_canonical_wtxid = tx->GetWitnessHash();243m_txs.emplace(tx->GetWitnessHash(), std::move(tx));244SetDefaults();245}247template <typename Stream>248CWalletTx(deserialize_type, Stream& s, const std::map<Wtxid, CTransactionRef>& variants) : m_state(TxStateInactive{})249{250Unserialize(s);