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