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