Branch data Line data Source code
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 : : class WalletBatch;
32 : :
33 : : //! State of transaction confirmed in a block.
34 : : struct TxStateConfirmed {
35 : : uint256 confirmed_block_hash;
36 : : int confirmed_block_height;
37 : : int position_in_block;
38 : :
39 [ + - ]: 856 : explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
40 [ + - ]: 1648 : std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
41 : : };
42 : :
43 : : //! State of transaction added to mempool.
44 : : struct TxStateInMempool {
45 : 6 : std::string toString() const { return strprintf("InMempool"); }
46 : : };
47 : :
48 : : //! State of rejected transaction that conflicts with a confirmed block.
49 : : struct TxStateBlockConflicted {
50 : : uint256 conflicting_block_hash;
51 : : int conflicting_block_height;
52 : :
53 : 3 : explicit TxStateBlockConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
54 [ # # ]: 0 : std::string toString() const { return strprintf("BlockConflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }
55 : : };
56 : :
57 : : //! State of transaction not confirmed or conflicting with a known block and
58 : : //! 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 another
60 : : //! reason.
61 : : struct TxStateInactive {
62 : : bool abandoned;
63 : :
64 [ + - ]: 116259 : explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
[ + - + - ]
[ - - + -
- - - - -
- - - -
- ]
65 : 6 : std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }
66 : : };
67 : :
68 : : //! State of transaction loaded in an unrecognized state with unexpected hash or
69 : : //! index values. Treated as inactive (with serialized hash and index values
70 : : //! preserved) by default, but may enter another state if transaction is added
71 : : //! to the mempool, or confirmed, or abandoned, or found conflicting.
72 : : struct TxStateUnrecognized {
73 : : uint256 block_hash;
74 : : int index;
75 : :
76 : 25 : TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
77 [ # # ]: 0 : std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }
78 : : };
79 : :
80 : : //! All possible CWalletTx states
81 : : using TxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateBlockConflicted, TxStateInactive, TxStateUnrecognized>;
82 : :
83 : : //! Subset of states transaction sync logic is implemented to handle.
84 : : using SyncTxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateInactive>;
85 : :
86 : : //! Try to interpret deserialized TxStateUnrecognized data as a recognized state.
87 : 28 : static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)
88 : : {
89 [ + + ]: 28 : if (data.block_hash == uint256::ZERO) {
90 [ + + ]: 7 : if (data.index == 0) return TxStateInactive{};
91 [ + + ]: 21 : } else if (data.block_hash == uint256::ONE) {
92 [ + + ]: 5 : if (data.index == -1) return TxStateInactive{/*abandoned=*/true};
93 [ + + ]: 16 : } else if (data.index >= 0) {
94 : 10 : return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};
95 [ + + ]: 6 : } else if (data.index == -1) {
96 : 3 : return TxStateBlockConflicted{data.block_hash, /*height=*/-1};
97 : : }
98 : 11 : return data;
99 : : }
100 : :
101 : : //! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.
102 : 863 : static inline uint256 TxStateSerializedBlockHash(const TxState& state)
103 : : {
104 [ + + ]: 31 : return std::visit(util::Overloaded{
105 [ + + ]: 17 : [](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },
106 : 10 : [](const TxStateInMempool& in_mempool) { return uint256::ZERO; },
107 : 836 : [](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },
108 : 3 : [](const TxStateBlockConflicted& conflicted) { return conflicted.conflicting_block_hash; },
109 : 11 : [](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }
110 : : }, state);
111 : : }
112 : :
113 : : //! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.
114 : 863 : static inline int TxStateSerializedIndex(const TxState& state)
115 : : {
116 [ + + ]: 31 : return std::visit(util::Overloaded{
117 [ + + ]: 9 : [](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },
118 : : [](const TxStateInMempool& in_mempool) { return 0; },
119 : 836 : [](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },
120 : : [](const TxStateBlockConflicted& conflicted) { return -1; },
121 : 11 : [](const TxStateUnrecognized& unrecognized) { return unrecognized.index; }
122 : : }, state);
123 : : }
124 : :
125 : : //! Return TxState or SyncTxState as a string for logging or debugging.
126 : : template<typename T>
127 : 836 : std::string TxStateString(const T& state)
128 : : {
129 : 836 : return std::visit([](const auto& s) { return s.toString(); }, state);
130 : : }
131 : :
132 : : /**
133 : : * Cachable amount subdivided into avoid reuse and all balances
134 : : */
135 : : struct CachableAmount
136 : : {
137 : : std::optional<CAmount> m_avoid_reuse_value;
138 : : std::optional<CAmount> m_all_value;
139 : 1694 : inline void Reset()
140 : : {
141 : 3388 : m_avoid_reuse_value.reset();
142 [ - + - + ]: 1694 : m_all_value.reset();
143 : : }
144 : 0 : void Set(bool avoid_reuse, CAmount value)
145 : : {
146 [ # # ]: 0 : if (avoid_reuse) {
147 : 0 : m_avoid_reuse_value = value;
148 : : } else {
149 : 0 : m_all_value = value;
150 : : }
151 : : }
152 : 0 : CAmount Get(bool avoid_reuse)
153 : : {
154 [ # # ]: 0 : if (avoid_reuse) {
155 [ # # ]: 0 : Assert(m_avoid_reuse_value.has_value());
156 : 0 : return m_avoid_reuse_value.value();
157 : : }
158 [ # # ]: 0 : Assert(m_all_value.has_value());
159 : 0 : return m_all_value.value();
160 : : }
161 : 0 : bool IsCached(bool avoid_reuse)
162 : : {
163 [ # # ]: 0 : if (avoid_reuse) return m_avoid_reuse_value.has_value();
164 : 0 : return m_all_value.has_value();
165 : : }
166 : : };
167 : :
168 : :
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 deserializing
173 : : * a CWalletTx, but the deserialized values are discarded.**/
174 : : class CMerkleTx
175 : : {
176 : : public:
177 : : template<typename Stream>
178 : 0 : void Unserialize(Stream& s)
179 : : {
180 : 0 : CTransactionRef tx;
181 : 0 : uint256 hashBlock;
182 [ # # ]: 0 : std::vector<uint256> vMerkleBranch;
183 : : int nIndex;
184 : :
185 [ # # # # : 0 : s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;
# # # # ]
186 [ # # ]: 0 : }
187 : : };
188 : :
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 : : */
193 : : class CWalletTx
194 : : {
195 : : public:
196 : : // "from" and "message" are obsolete fields that could be set in
197 : : // the UI prior to 2011 (removed in commit 4d9b223)
198 : : // These fields are kept to avoid losing metadata.
199 : : std::optional<std::string> m_from;
200 : : std::optional<std::string> m_message;
201 : : // Comment strings provided by the user
202 : : std::optional<std::string> m_comment;
203 : : std::optional<std::string> m_comment_to;
204 : : std::optional<Txid> m_replaces_txid;
205 : : std::optional<Txid> m_replaced_by_txid;
206 : : // BIP 21 URI Messages
207 : : std::vector<std::string> m_messages;
208 : : // BIP 70 Payment Request (deprecated, field kept to preserve metadata from old wallets)
209 : : std::vector<std::string> m_payment_requests;
210 : : unsigned int nTimeReceived; //!< time received by this node
211 : : /**
212 : : * Stable timestamp that never changes, and reflects the order a transaction
213 : : * was added to the wallet. Timestamp is based on the block time for a
214 : : * transaction added as part of a block, or else the time when the
215 : : * transaction was received if it wasn't part of a block, with the timestamp
216 : : * adjusted in both cases so timestamp order matches the order transactions
217 : : * were added to the wallet. More details can be found in
218 : : * CWallet::ComputeTimeSmart().
219 : : */
220 : : unsigned int nTimeSmart;
221 : : // Cached value for whether the transaction spends any inputs known to the wallet
222 : : mutable std::optional<bool> m_cached_from_me{std::nullopt};
223 : : int64_t nOrderPos; //!< position in ordered transaction list
224 : : std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
225 : :
226 : : // memory only
227 : : enum AmountType { DEBIT, CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
228 : : mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
229 : : /**
230 : : * This flag is true if all m_amounts caches are empty. This is particularly
231 : : * useful in places where MarkDirty is conditionally called and the
232 : : * condition can be expensive and thus can be skipped if the flag is true.
233 : : * See MarkDestinationsDirty.
234 : : */
235 : : mutable bool m_is_cache_empty{true};
236 : : mutable bool fChangeCached;
237 : : mutable CAmount nChangeCached;
238 : :
239 [ - + ]: 117081 : CWalletTx(CTransactionRef tx, const TxState& state) : m_state(state)
240 : : {
241 [ - + ]: 117081 : Assert(tx);
242 [ + - ]: 117081 : m_canonical_wtxid = tx->GetWitnessHash();
243 [ + - ]: 117081 : m_txs.emplace(tx->GetWitnessHash(), std::move(tx));
244 : 117081 : SetDefaults();
245 : 0 : }
246 : :
247 : : template <typename Stream>
248 [ + - ]: 3 : CWalletTx(deserialize_type, Stream& s, const std::map<Wtxid, CTransactionRef>& variants) : m_state(TxStateInactive{})
249 : : {
250 [ + - ]: 3 : Unserialize(s);
251 [ + - ]: 3 : const Txid& canonical_txid = GetHash();
252 [ + + + + ]: 5 : for (const auto& [wtxid, tx] : variants) {
253 [ + + + - ]: 3 : if (tx->GetHash() != canonical_txid) throw std::runtime_error("variant txid does not match wallet txid");
254 : : }
255 : : // Merge witness variants
256 [ + - ]: 2 : m_txs.insert(variants.begin(), variants.end());
257 [ + - - + ]: 2 : Assert(m_txs.contains(GetWitnessHash()));
258 : 5 : }
259 : :
260 : : TxState m_state;
261 : :
262 : : // Set of mempool transactions that conflict
263 : : // directly with the transaction, or that conflict
264 : : // with an ancestor transaction. This set will be
265 : : // empty if state is InMempool or Confirmed, but
266 : : // can be nonempty if state is Inactive or
267 : : // BlockConflicted.
268 : : std::set<Txid> mempool_conflicts;
269 : :
270 : : // Track v3 mempool tx that spends from this tx
271 : : // so that we don't try to create another unconfirmed child
272 : : std::optional<Txid> truc_child_in_mempool;
273 : :
274 : : template<typename Stream>
275 [ - + ]: 832 : void Serialize(Stream& s) const
276 : : {
277 : 832 : std::map<std::string, std::string> string_values;
278 [ - + - - : 832 : if (m_from) string_values["from"] = *m_from;
- - - - ]
279 [ - + - - : 832 : if (m_message) string_values["message"] = *m_message;
- - - - ]
280 [ - + - - : 832 : if (m_comment) string_values["comment"] = *m_comment;
- - - - ]
281 [ - + - - : 832 : if (m_comment_to) string_values["to"] = *m_comment_to;
- - - - ]
282 [ - + - - : 832 : if (m_replaces_txid) string_values["replaces_txid"] = m_replaces_txid->ToString();
- - - - ]
283 [ - + - - : 832 : if (m_replaced_by_txid) string_values["replaced_by_txid"] = m_replaced_by_txid->ToString();
- - - - ]
284 [ + - + - : 832 : string_values["fromaccount"] = "";
+ - ]
285 [ + + + - : 832 : if (nOrderPos != -1) string_values["n"] = util::ToString(nOrderPos);
+ - + - ]
286 [ + + + - : 832 : if (nTimeSmart) string_values["timesmart"] = strprintf("%u", nTimeSmart);
+ - + - ]
287 : :
288 : 832 : std::vector<std::pair<std::string, std::string>> msgs_reqs;
289 [ - + - + : 832 : msgs_reqs.reserve(m_messages.size() + m_payment_requests.size());
+ - ]
290 [ - + ]: 832 : for (const std::string& msg : m_messages) {
291 [ # # ]: 0 : msgs_reqs.emplace_back("Message", msg);
292 : : }
293 [ - + ]: 832 : for (const std::string& req : m_payment_requests) {
294 [ # # ]: 0 : msgs_reqs.emplace_back("PaymentRequest", req);
295 : : }
296 : :
297 : 832 : std::vector<uint8_t> dummy_vector1; // Used to be vMerkleBranch
298 : 832 : std::vector<uint8_t> dummy_vector2; // Used to be vtxPrev
299 : 832 : bool dummy_bool = false; // Used to be fFromMe, and fSpent
300 : 832 : uint32_t dummy_int = 0; // Used to be fTimeReceivedIsTxTime
301 [ + - ]: 832 : uint256 serializedHash = TxStateSerializedBlockHash(m_state);
302 : 832 : int serializedIndex = TxStateSerializedIndex(m_state);
303 [ + - + - : 2496 : s << TX_WITH_WITNESS(GetTx()) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << string_values << msgs_reqs << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - ]
304 : 832 : }
305 : :
306 : : template<typename Stream>
307 : 3 : void Unserialize(Stream& s)
308 : : {
309 : 3 : Init();
310 : :
311 : 3 : std::vector<uint256> dummy_vector1; // Used to be vMerkleBranch
312 : 3 : 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 [ + - ]: 3 : uint256 serialized_block_hash;
316 : : int serializedIndex;
317 : 3 : std::map<std::string, std::string> string_values;
318 : 3 : std::vector<std::pair<std::string, std::string>> msgs_reqs;
319 [ + - ]: 3 : CTransactionRef canonical_tx;
320 [ + - + - : 6 : s >> TX_WITH_WITNESS(canonical_tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> string_values >> msgs_reqs >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
321 : 3 : m_canonical_wtxid = canonical_tx->GetWitnessHash();
322 [ + - ]: 3 : m_txs.emplace(m_canonical_wtxid, std::move(canonical_tx));
323 : :
324 : 3 : m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
325 : :
326 [ + - ]: 6 : string_values.erase("fromaccount");
327 [ + - ]: 6 : string_values.erase("spent");
328 [ + + + + ]: 7 : for (const auto& [key, value] : string_values) {
329 [ + + + - ]: 6 : if (key == "n") nOrderPos = LocaleIndependentAtoi<int64_t>(value);
330 [ + - + - ]: 4 : else if (key == "timesmart") nTimeSmart = LocaleIndependentAtoi<int64_t>(value);
331 [ # # # # ]: 0 : else if (key == "from") m_from = value;
332 [ # # # # ]: 0 : else if (key == "message") m_message = value;
333 [ # # # # ]: 0 : else if (key == "comment") m_comment = value;
334 [ # # # # ]: 0 : else if (key == "to") m_comment_to = value;
335 [ # # # # ]: 0 : else if (key == "replaces_txid") m_replaces_txid = Txid::FromHex(value);
336 [ # # # # ]: 0 : else if (key == "replaced_by_txid") m_replaced_by_txid = Txid::FromHex(value);
337 : : else {
338 [ # # ]: 0 : throw std::runtime_error("Unexpected value in CWalletTx strings value map");
339 : : }
340 : : }
341 : :
342 [ - - - + ]: 3 : for (const auto& [type, data] : msgs_reqs) {
343 [ # # # # ]: 0 : if (type == "Message") m_messages.emplace_back(data);
344 [ # # # # ]: 0 : else if (type == "PaymentRequest") m_payment_requests.emplace_back(data);
345 : : else {
346 [ # # ]: 0 : throw std::runtime_error("Unknown type in CWalletTx messages and requests vector");
347 : : }
348 : : }
349 : 3 : }
350 : :
351 [ + - ]: 246394 : CTransactionRef GetTx() const { return m_txs.at(m_canonical_wtxid); }
352 : :
353 : : // Update the state of this wallet transaction along with a transaction that may have a different wtxid.
354 : : // If the given transaction has a different wtxid, the transaction is stored if it has not been seen before.
355 : : // The canonical wtxid is also updated. The tx that is confirmed becomes canonical. For unconfirmed txs,
356 : : // those with witnesses are preferred, followed by least weight.
357 : : bool Update(CTransactionRef tx, const TxState& new_state, WalletBatch& batch, bool metadata_changed);
358 : :
359 : : //! make sure balances are recalculated
360 : 847 : void MarkDirty()
361 : : {
362 [ - + ]: 847 : m_amounts[DEBIT].Reset();
363 [ - + ]: 847 : m_amounts[CREDIT].Reset();
364 : 847 : fChangeCached = false;
365 : 847 : m_is_cache_empty = true;
366 [ + + ]: 847 : m_cached_from_me = std::nullopt;
367 : 847 : }
368 : :
369 : : /** True if tx is a malleation of this, i.e. it has the exact same version, locktime,
370 : : * input order, input outpoints, input sequences, and outputs. Input scriptSigs and input scriptWitnesses may differ. */
371 : : bool IsMalleation(const CWalletTx& tx) const;
372 : :
373 : : bool InMempool() const;
374 : :
375 : : int64_t GetTxTime() const;
376 : :
377 [ + + - + : 6532 : template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
- + - - +
+ - + + +
+ - + - ]
[ # # # # ]
[ # # # #
# # # # #
# # # # #
# # # # ]
378 [ - - - - : 13 : template<typename T> T* state() { return std::get_if<T>(&m_state); }
- - - - -
- - + -
- ][ - - +
+ + + - +
+ + # # #
# ]
379 : :
380 : : //! Update transaction state when attaching to a chain, filling in heights
381 : : //! of conflicted and confirmed blocks
382 : : void updateState(interfaces::Chain& chain);
383 : :
384 [ + + + - : 28 : bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
- - - - -
- - - - -
- - - - -
- - - - -
- + - - +
- + - ][ #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
385 [ - - - + ]: 21 : bool isMempoolConflicted() const { return !mempool_conflicts.empty(); }
386 [ - - - - : 21 : bool isBlockConflicted() const { return state<TxStateBlockConflicted>(); }
- - - - -
- - - - -
- - + - -
+ ][ # # #
# # # # #
# # # # #
# # # # #
# # ]
387 [ - + - - : 838 : bool isInactive() const { return state<TxStateInactive>(); }
+ - ][ - -
# # # # ]
388 [ + + + - : 13 : bool isUnconfirmed() const { return !isAbandoned() && !isBlockConflicted() && !isMempoolConflicted() && !isConfirmed(); }
+ - ]
389 [ + + + + : 106 : bool isConfirmed() const { return state<TxStateConfirmed>(); }
+ + ][ - +
- - - + -
+ - - ][ #
# # # # #
# # # # ]
390 [ + - ]: 118038 : const Txid& GetHash() const LIFETIMEBOUND { return GetTx()->GetHash(); }
391 [ + - ]: 2 : const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return GetTx()->GetWitnessHash(); }
392 [ + - ]: 5275 : bool IsCoinBase() const { return GetTx()->IsCoinBase(); }
393 : :
394 [ # # ]: 830 : const std::map<Wtxid, CTransactionRef>& GetTxs() const { return m_txs; }
395 : :
396 : : // Disable copying of CWalletTx objects to prevent bugs where instances get
397 : : // copied in and out of the mapWallet map, and fields are updated in the
398 : : // wrong copy.
399 : : CWalletTx(const CWalletTx&) = delete;
400 : : CWalletTx& operator=(const CWalletTx&) = delete;
401 : :
402 : : // Enable the default move constructor
403 [ + + ]: 6 : CWalletTx(CWalletTx&&) = default;
404 : :
405 : : private:
406 : 117084 : void SetDefaults()
407 : : {
408 : 117084 : nTimeReceived = 0;
409 : 117084 : nTimeSmart = 0;
410 : 117084 : fChangeCached = false;
411 : 117084 : nChangeCached = 0;
412 : 117081 : nOrderPos = -1;
413 : : }
414 : :
415 : 3 : void Init()
416 : : {
417 : 3 : m_txs.clear();
418 : 3 : m_canonical_wtxid = Wtxid{};
419 : 3 : SetDefaults();
420 : 3 : }
421 : :
422 : : Wtxid m_canonical_wtxid;
423 : : std::map<Wtxid, CTransactionRef> m_txs;
424 : :
425 : : //! Set m_canonical_wtxid to the best variant under the unconfirmed rule
426 : : //! (witnessed preferred, then least weight). Ignores state.
427 : : void RecomputeCanonical();
428 : : };
429 : :
430 : : struct WalletTxOrderComparator {
431 : 2 : bool operator()(const CWalletTx* a, const CWalletTx* b) const
432 : : {
433 [ + - + - : 2 : return a->nOrderPos < b->nOrderPos;
- - ]
434 : : }
435 : : };
436 : :
437 : : class WalletTXO
438 : : {
439 : : private:
440 : : const CWalletTx& m_wtx;
441 : : const CTxOut& m_output;
442 : :
443 : : public:
444 : 831 : WalletTXO(const CWalletTx& wtx, const CTxOut& output)
445 : 831 : : m_wtx(wtx),
446 : 831 : m_output(output)
447 : : {
448 [ + - + - : 1662 : Assume(std::ranges::find(wtx.GetTx()->vout, output) != wtx.GetTx()->vout.end());
+ - ]
449 : 831 : }
450 : :
451 [ - - + - ]: 2781 : const CWalletTx& GetWalletTx() const { return m_wtx; }
[ + - ]
452 : :
453 [ - - - - : 2781 : const CTxOut& GetTxOut() const { return m_output; }
- + ]
454 : : };
455 : : } // namespace wallet
456 : :
457 : : #endif // BITCOIN_WALLET_TRANSACTION_H
|