Branch data Line data Source code
1 : : // Copyright (c) 2021-2022 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 [ + - + - ]: 139645 : explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
[ + - # # ]
38 [ + - ]: 39838 : 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 : 3434 : 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 : 267 : explicit TxStateBlockConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
52 [ # # ]: 0 : 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 [ + - + - : 131063 : explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
- + + - +
- - + ]
[ + + # # ]
[ + - - -
- - - - -
- - - ]
63 : 2046 : 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 : 7688 : TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
75 [ # # ]: 0 : 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 : 7688 : static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)
86 : : {
87 [ + + ]: 7688 : if (data.block_hash == uint256::ZERO) {
88 [ + + ]: 80 : if (data.index == 0) return TxStateInactive{};
89 [ + + ]: 7608 : } else if (data.block_hash == uint256::ONE) {
90 [ + + ]: 125 : if (data.index == -1) return TxStateInactive{/*abandoned=*/true};
91 [ + + ]: 7483 : } else if (data.index >= 0) {
92 : 7383 : return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};
93 [ + + ]: 100 : } else if (data.index == -1) {
94 : 97 : return TxStateBlockConflicted{data.block_hash, /*height=*/-1};
95 : : }
96 : 11 : return data;
97 : : }
98 : :
99 : : //! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.
100 : 26768 : static inline uint256 TxStateSerializedBlockHash(const TxState& state)
101 : : {
102 [ + + ]: 26768 : return std::visit(util::Overloaded{
103 [ + + ]: 4855 : [](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },
104 : 5020 : [](const TxStateInMempool& in_mempool) { return uint256::ZERO; },
105 : 21353 : [](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },
106 : 173 : [](const TxStateBlockConflicted& conflicted) { return conflicted.conflicting_block_hash; },
107 : 11 : [](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }
108 : : }, state);
109 : : }
110 : :
111 : : //! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.
112 : 26768 : static inline int TxStateSerializedIndex(const TxState& state)
113 : : {
114 [ + + ]: 26768 : return std::visit(util::Overloaded{
115 [ + + ]: 3221 : [](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },
116 : : [](const TxStateInMempool& in_mempool) { return 0; },
117 : 21353 : [](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },
118 : : [](const TxStateBlockConflicted& conflicted) { return -1; },
119 : 11 : [](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 : 25399 : std::string TxStateString(const T& state)
126 : : {
127 : 25399 : return std::visit([](const auto& s) { return s.toString(); }, state);
128 : : }
129 : :
130 : : /**
131 : : * Cachable amount subdivided into watchonly and spendable parts.
132 : : */
133 : 298786 : struct CachableAmount
134 : : {
135 : : // NO and ALL are never (supposed to be) cached
136 : : std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
137 : : CAmount m_value[ISMINE_ENUM_ELEMENTS];
138 : 61483 : inline void Reset()
139 : : {
140 : 61483 : m_cached.reset();
141 : : }
142 : 10884 : void Set(isminefilter filter, CAmount value)
143 : : {
144 : 10884 : m_cached.set(filter);
145 : 10884 : m_value[filter] = value;
146 : : }
147 : : };
148 : :
149 : :
150 : : typedef std::map<std::string, std::string> mapValue_t;
151 : :
152 : :
153 : : /** Legacy class used for deserializing vtxPrev for backwards compatibility.
154 : : * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
155 : : * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
156 : : * These need to get deserialized for field alignment when deserializing
157 : : * a CWalletTx, but the deserialized values are discarded.**/
158 : : class CMerkleTx
159 : : {
160 : : public:
161 : : template<typename Stream>
162 : 0 : void Unserialize(Stream& s)
163 : : {
164 : 0 : CTransactionRef tx;
165 : 0 : uint256 hashBlock;
166 [ # # ]: 0 : std::vector<uint256> vMerkleBranch;
167 : : int nIndex;
168 : :
169 [ # # # # : 0 : s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;
# # # # ]
170 [ # # ]: 0 : }
171 : : };
172 : :
173 : : /**
174 : : * A transaction with a bunch of additional info that only the owner cares about.
175 : : * It includes any unrecorded transactions needed to link it back to the block chain.
176 : : */
177 : : class CWalletTx
178 : : {
179 : : public:
180 : : /**
181 : : * Key/value map with information about the transaction.
182 : : *
183 : : * The following keys can be read and written through the map and are
184 : : * serialized in the wallet database:
185 : : *
186 : : * "comment", "to" - comment strings provided to sendtoaddress,
187 : : * and sendmany wallet RPCs
188 : : * "replaces_txid" - txid (as HexStr) of transaction replaced by
189 : : * bumpfee on transaction created by bumpfee
190 : : * "replaced_by_txid" - txid (as HexStr) of transaction created by
191 : : * bumpfee on transaction replaced by bumpfee
192 : : * "from", "message" - obsolete fields that could be set in UI prior to
193 : : * 2011 (removed in commit 4d9b223)
194 : : *
195 : : * The following keys are serialized in the wallet database, but shouldn't
196 : : * be read or written through the map (they will be temporarily added and
197 : : * removed from the map during serialization):
198 : : *
199 : : * "fromaccount" - serialized strFromAccount value
200 : : * "n" - serialized nOrderPos value
201 : : * "timesmart" - serialized nTimeSmart value
202 : : * "spent" - serialized vfSpent value that existed prior to
203 : : * 2014 (removed in commit 93a18a3)
204 : : */
205 : : mapValue_t mapValue;
206 : : std::vector<std::pair<std::string, std::string> > vOrderForm;
207 : : unsigned int nTimeReceived; //!< time received by this node
208 : : /**
209 : : * Stable timestamp that never changes, and reflects the order a transaction
210 : : * was added to the wallet. Timestamp is based on the block time for a
211 : : * transaction added as part of a block, or else the time when the
212 : : * transaction was received if it wasn't part of a block, with the timestamp
213 : : * adjusted in both cases so timestamp order matches the order transactions
214 : : * were added to the wallet. More details can be found in
215 : : * CWallet::ComputeTimeSmart().
216 : : */
217 : : unsigned int nTimeSmart;
218 : : int64_t nOrderPos; //!< position in ordered transaction list
219 : : std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
220 : :
221 : : // memory only
222 : : enum AmountType { DEBIT, CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
223 : : mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
224 : : /**
225 : : * This flag is true if all m_amounts caches are empty. This is particularly
226 : : * useful in places where MarkDirty is conditionally called and the
227 : : * condition can be expensive and thus can be skipped if the flag is true.
228 : : * See MarkDestinationsDirty.
229 : : */
230 : : mutable bool m_is_cache_empty{true};
231 : : mutable bool fChangeCached;
232 : : mutable CAmount nChangeCached;
233 : :
234 [ + + ]: 448179 : CWalletTx(CTransactionRef tx, const TxState& state) : tx(std::move(tx)), m_state(state)
235 : : {
236 : 149393 : Init();
237 : 149393 : }
238 : :
239 : 157056 : void Init()
240 : : {
241 : 157056 : mapValue.clear();
242 : 157056 : vOrderForm.clear();
243 : 157056 : nTimeReceived = 0;
244 : 157056 : nTimeSmart = 0;
245 : 157056 : fChangeCached = false;
246 : 157056 : nChangeCached = 0;
247 : 157056 : nOrderPos = -1;
248 : 157056 : }
249 : :
250 : : CTransactionRef tx;
251 : : TxState m_state;
252 : :
253 : : // Set of mempool transactions that conflict
254 : : // directly with the transaction, or that conflict
255 : : // with an ancestor transaction. This set will be
256 : : // empty if state is InMempool or Confirmed, but
257 : : // can be nonempty if state is Inactive or
258 : : // BlockConflicted.
259 : : std::set<Txid> mempool_conflicts;
260 : :
261 : : template<typename Stream>
262 : 23733 : void Serialize(Stream& s) const
263 : : {
264 [ + - ]: 23733 : mapValue_t mapValueCopy = mapValue;
265 : :
266 [ + - + - : 23733 : mapValueCopy["fromaccount"] = "";
+ - ]
267 [ + - ]: 23733 : if (nOrderPos != -1) {
268 [ + - + - : 23733 : mapValueCopy["n"] = util::ToString(nOrderPos);
+ - ]
269 : : }
270 [ + - ]: 23733 : if (nTimeSmart) {
271 [ + - + - : 23733 : mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
+ - ]
272 : : }
273 : :
274 : 23733 : std::vector<uint8_t> dummy_vector1; //!< Used to be vMerkleBranch
275 : 23733 : std::vector<uint8_t> dummy_vector2; //!< Used to be vtxPrev
276 : 23733 : bool dummy_bool = false; //!< Used to be fFromMe, and fSpent
277 : 23733 : uint32_t dummy_int = 0; // Used to be fTimeReceivedIsTxTime
278 [ + - ]: 23733 : uint256 serializedHash = TxStateSerializedBlockHash(m_state);
279 : 23733 : int serializedIndex = TxStateSerializedIndex(m_state);
280 [ + - + - : 47466 : s << TX_WITH_WITNESS(tx) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
+ - + - +
- + - + -
+ - + - +
- + - ]
281 : 23733 : }
282 : :
283 : : template<typename Stream>
284 : 7663 : void Unserialize(Stream& s)
285 : : {
286 : 7663 : Init();
287 : :
288 : 7663 : std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
289 : 7663 : std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
290 : : bool dummy_bool; //! Used to be fFromMe, and fSpent
291 : : uint32_t dummy_int; // Used to be fTimeReceivedIsTxTime
292 : 7663 : uint256 serialized_block_hash;
293 : : int serializedIndex;
294 [ + - + - : 7663 : s >> TX_WITH_WITNESS(tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
+ - + - +
- + - + -
+ - + - +
- + - ]
295 : :
296 : 7663 : m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
297 : :
298 [ + - ]: 15326 : const auto it_op = mapValue.find("n");
299 [ + - + - ]: 7663 : nOrderPos = (it_op != mapValue.end()) ? LocaleIndependentAtoi<int64_t>(it_op->second) : -1;
300 [ + - ]: 15326 : const auto it_ts = mapValue.find("timesmart");
301 [ + - + - ]: 7663 : nTimeSmart = (it_ts != mapValue.end()) ? static_cast<unsigned int>(LocaleIndependentAtoi<int64_t>(it_ts->second)) : 0;
302 : :
303 [ + - ]: 15326 : mapValue.erase("fromaccount");
304 [ + - ]: 15326 : mapValue.erase("spent");
305 [ + - ]: 15326 : mapValue.erase("n");
306 [ + - ]: 15326 : mapValue.erase("timesmart");
307 : 7663 : }
308 : :
309 : 14 : void SetTx(CTransactionRef arg)
310 : : {
311 [ - + - - ]: 14 : tx = std::move(arg);
312 : : }
313 : :
314 : : //! make sure balances are recalculated
315 : 61483 : void MarkDirty()
316 : : {
317 [ + - + + : 61483 : m_amounts[DEBIT].Reset();
+ - + - ]
318 : 61483 : m_amounts[CREDIT].Reset();
319 : 61483 : fChangeCached = false;
320 [ + - + + : 39519 : m_is_cache_empty = true;
+ - + - ]
321 : 21964 : }
322 : :
323 : : /** True if only scriptSigs are different */
324 : : bool IsEquivalentTo(const CWalletTx& tx) const;
325 : :
326 : : bool InMempool() const;
327 : :
328 : : int64_t GetTxTime() const;
329 : :
330 [ # # # # ]: 2707697 : template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
[ + + + +
- + - + +
+ - + + +
+ + ][ + +
# # # # #
# # # # #
# # # # ]
331 [ + + + + : 30207 : template<typename T> T* state() { return std::get_if<T>(&m_state); }
+ + + - +
- + + -
+ ][ + + +
+ # # # #
# # # # #
# ]
332 : :
333 : : //! Update transaction state when attaching to a chain, filling in heights
334 : : //! of conflicted and confirmed blocks
335 : : void updateState(interfaces::Chain& chain);
336 : :
337 [ # # # # : 338804 : bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
# # ][ + +
+ + + - +
- + + + +
+ - + + +
+ + - + +
+ + + + +
+ ]
338 [ + + ]: 330625 : bool isMempoolConflicted() const { return !mempool_conflicts.empty(); }
339 [ + + + + ]: 409290 : bool isBlockConflicted() const { return state<TxStateBlockConflicted>(); }
[ + + + -
+ - + + +
- + - + +
+ + ]
340 [ + + + - : 33599 : bool isInactive() const { return state<TxStateInactive>(); }
+ - ][ + + ]
341 [ + + + + : 13545 : bool isUnconfirmed() const { return !isAbandoned() && !isBlockConflicted() && !isMempoolConflicted() && !isConfirmed(); }
+ - ]
342 [ + + + + : 574505 : bool isConfirmed() const { return state<TxStateConfirmed>(); }
+ + + + ]
[ # # ]
[ + - + + ]
343 [ + + ][ + - : 978243 : const Txid& GetHash() const LIFETIMEBOUND { return tx->GetHash(); }
- - - - -
- + - + -
+ - - - ]
[ + - + -
+ - + - ]
344 : 1967 : const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return tx->GetWitnessHash(); }
345 [ # # # # ]: 770926 : bool IsCoinBase() const { return tx->IsCoinBase(); }
[ + + + +
+ + + + ]
[ + + ]
346 : :
347 : : private:
348 : : // Disable copying of CWalletTx objects to prevent bugs where instances get
349 : : // copied in and out of the mapWallet map, and fields are updated in the
350 : : // wrong copy.
351 : : CWalletTx(const CWalletTx&) = default;
352 : 14 : CWalletTx& operator=(const CWalletTx&) = default;
353 : : public:
354 : : // Instead have an explicit copy function
355 : : void CopyFrom(const CWalletTx&);
356 : : };
357 : :
358 : : struct WalletTxOrderComparator {
359 : 270 : bool operator()(const CWalletTx* a, const CWalletTx* b) const
360 : : {
361 [ + + + + : 270 : return a->nOrderPos < b->nOrderPos;
+ - ]
362 : : }
363 : : };
364 : :
365 : : class WalletTXO
366 : : {
367 : : private:
368 : : const CWalletTx& m_wtx;
369 : : const CTxOut& m_output;
370 : : isminetype m_ismine;
371 : :
372 : : public:
373 : 44493 : WalletTXO(const CWalletTx& wtx, const CTxOut& output, const isminetype ismine)
374 : 44493 : : m_wtx(wtx),
375 : 44493 : m_output(output),
376 : 44493 : m_ismine(ismine)
377 : : {
378 : 44493 : Assume(std::ranges::find(wtx.tx->vout, output) != wtx.tx->vout.end());
379 : 44493 : }
380 : :
381 [ + - + - ]: 777160 : const CWalletTx& GetWalletTx() const { return m_wtx; }
[ + - ]
382 : :
383 [ + - + - : 740222 : const CTxOut& GetTxOut() const { return m_output; }
+ - ][ + - ]
384 : :
385 [ + - ]: 39467 : isminetype GetIsMine() const { return m_ismine; }
386 : 12794 : void SetIsMine(isminetype ismine) { m_ismine = ismine; }
387 : : };
388 : : } // namespace wallet
389 : :
390 : : #endif // BITCOIN_WALLET_TRANSACTION_H
|