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/overloaded.h>
14 : : #include <util/strencodings.h>
15 : : #include <util/string.h>
16 : : #include <wallet/types.h>
17 : :
18 : : #include <bitset>
19 : : #include <cstdint>
20 : : #include <map>
21 : : #include <utility>
22 : : #include <variant>
23 : : #include <vector>
24 : :
25 : : namespace interfaces {
26 : : class Chain;
27 : : } // namespace interfaces
28 : :
29 : : namespace wallet {
30 : : //! State of transaction confirmed in a block.
31 : : struct TxStateConfirmed {
32 : : uint256 confirmed_block_hash;
33 : : int confirmed_block_height;
34 : : int position_in_block;
35 : :
36 [ + - + - ]: 453 : explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
[ # # ]
37 [ + - ]: 846 : std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
38 : : };
39 : :
40 : : //! State of transaction added to mempool.
41 : : struct TxStateInMempool {
42 : 9 : std::string toString() const { return strprintf("InMempool"); }
43 : : };
44 : :
45 : : //! State of rejected transaction that conflicts with a confirmed block.
46 : : struct TxStateBlockConflicted {
47 : : uint256 conflicting_block_hash;
48 : : int conflicting_block_height;
49 : :
50 : 3 : explicit TxStateBlockConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
51 [ # # ]: 0 : std::string toString() const { return strprintf("BlockConflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }
52 : : };
53 : :
54 : : //! State of transaction not confirmed or conflicting with a known block and
55 : : //! not in the mempool. May conflict with the mempool, or with an unknown block,
56 : : //! or be abandoned, never broadcast, or rejected from the mempool for another
57 : : //! reason.
58 : : struct TxStateInactive {
59 : : bool abandoned;
60 : :
61 [ + - # # : 116340 : explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
# # # # #
# # # ][ +
- - - - -
- - - - -
+ ]
[ + - + - ]
62 : 6 : std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }
63 : : };
64 : :
65 : : //! State of transaction loaded in an unrecognized state with unexpected hash or
66 : : //! index values. Treated as inactive (with serialized hash and index values
67 : : //! preserved) by default, but may enter another state if transaction is added
68 : : //! to the mempool, or confirmed, or abandoned, or found conflicting.
69 : : struct TxStateUnrecognized {
70 : : uint256 block_hash;
71 : : int index;
72 : :
73 : 27 : TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
74 [ # # ]: 0 : std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }
75 : : };
76 : :
77 : : //! All possible CWalletTx states
78 : : using TxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateBlockConflicted, TxStateInactive, TxStateUnrecognized>;
79 : :
80 : : //! Subset of states transaction sync logic is implemented to handle.
81 : : using SyncTxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateInactive>;
82 : :
83 : : //! Try to interpret deserialized TxStateUnrecognized data as a recognized state.
84 : 27 : static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)
85 : : {
86 [ + + ]: 27 : if (data.block_hash == uint256::ZERO) {
87 [ + + ]: 6 : if (data.index == 0) return TxStateInactive{};
88 [ + + ]: 21 : } else if (data.block_hash == uint256::ONE) {
89 [ + + ]: 5 : if (data.index == -1) return TxStateInactive{/*abandoned=*/true};
90 [ + + ]: 16 : } else if (data.index >= 0) {
91 : 10 : return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};
92 [ + + ]: 6 : } else if (data.index == -1) {
93 : 3 : return TxStateBlockConflicted{data.block_hash, /*height=*/-1};
94 : : }
95 : 11 : return data;
96 : : }
97 : :
98 : : //! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.
99 : 464 : static inline uint256 TxStateSerializedBlockHash(const TxState& state)
100 : : {
101 [ + + ]: 464 : return std::visit(util::Overloaded{
102 [ + + ]: 15 : [](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },
103 : 13 : [](const TxStateInMempool& in_mempool) { return uint256::ZERO; },
104 : 435 : [](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },
105 : 3 : [](const TxStateBlockConflicted& conflicted) { return conflicted.conflicting_block_hash; },
106 : 11 : [](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }
107 : : }, state);
108 : : }
109 : :
110 : : //! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.
111 : 464 : static inline int TxStateSerializedIndex(const TxState& state)
112 : : {
113 [ + + ]: 464 : return std::visit(util::Overloaded{
114 [ + + ]: 8 : [](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },
115 : : [](const TxStateInMempool& in_mempool) { return 0; },
116 : 435 : [](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },
117 : : [](const TxStateBlockConflicted& conflicted) { return -1; },
118 : 11 : [](const TxStateUnrecognized& unrecognized) { return unrecognized.index; }
119 : : }, state);
120 : : }
121 : :
122 : : //! Return TxState or SyncTxState as a string for logging or debugging.
123 : : template<typename T>
124 : 438 : std::string TxStateString(const T& state)
125 : : {
126 : 438 : return std::visit([](const auto& s) { return s.toString(); }, state);
127 : : }
128 : :
129 : : /**
130 : : * Cachable amount subdivided into watchonly and spendable parts.
131 : : */
132 : 467056 : struct CachableAmount
133 : : {
134 : : // NO and ALL are never (supposed to be) cached
135 : : std::bitset<ISMINE_ENUM_ELEMENTS> m_cached;
136 : : CAmount m_value[ISMINE_ENUM_ELEMENTS];
137 : 447 : inline void Reset()
138 : : {
139 : 447 : m_cached.reset();
140 : : }
141 : 33 : void Set(isminefilter filter, CAmount value)
142 : : {
143 : 33 : m_cached.set(filter);
144 : 33 : m_value[filter] = value;
145 : : }
146 : : };
147 : :
148 : :
149 : : typedef std::map<std::string, std::string> mapValue_t;
150 : :
151 : :
152 : : /** Legacy class used for deserializing vtxPrev for backwards compatibility.
153 : : * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
154 : : * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
155 : : * These need to get deserialized for field alignment when deserializing
156 : : * a CWalletTx, but the deserialized values are discarded.**/
157 : : class CMerkleTx
158 : : {
159 : : public:
160 : : template<typename Stream>
161 : 0 : void Unserialize(Stream& s)
162 : : {
163 : 0 : CTransactionRef tx;
164 : 0 : uint256 hashBlock;
165 [ # # ]: 0 : std::vector<uint256> vMerkleBranch;
166 : : int nIndex;
167 : :
168 [ # # # # : 0 : s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;
# # # # ]
169 [ # # ]: 0 : }
170 : : };
171 : :
172 : : /**
173 : : * A transaction with a bunch of additional info that only the owner cares about.
174 : : * It includes any unrecorded transactions needed to link it back to the block chain.
175 : : */
176 : : class CWalletTx
177 : : {
178 : : public:
179 : : /**
180 : : * Key/value map with information about the transaction.
181 : : *
182 : : * The following keys can be read and written through the map and are
183 : : * serialized in the wallet database:
184 : : *
185 : : * "comment", "to" - comment strings provided to sendtoaddress,
186 : : * and sendmany wallet RPCs
187 : : * "replaces_txid" - txid (as HexStr) of transaction replaced by
188 : : * bumpfee on transaction created by bumpfee
189 : : * "replaced_by_txid" - txid (as HexStr) of transaction created by
190 : : * bumpfee on transaction replaced by bumpfee
191 : : * "from", "message" - obsolete fields that could be set in UI prior to
192 : : * 2011 (removed in commit 4d9b223)
193 : : *
194 : : * The following keys are serialized in the wallet database, but shouldn't
195 : : * be read or written through the map (they will be temporarily added and
196 : : * removed from the map during serialization):
197 : : *
198 : : * "fromaccount" - serialized strFromAccount value
199 : : * "n" - serialized nOrderPos value
200 : : * "timesmart" - serialized nTimeSmart value
201 : : * "spent" - serialized vfSpent value that existed prior to
202 : : * 2014 (removed in commit 93a18a3)
203 : : */
204 : : mapValue_t mapValue;
205 : : std::vector<std::pair<std::string, std::string> > vOrderForm;
206 : : unsigned int fTimeReceivedIsTxTime;
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 : : /**
219 : : * From me flag is set to 1 for transactions that were created by the wallet
220 : : * on this bitcoin node, and set to 0 for transactions that were created
221 : : * externally and came in through the network or sendrawtransaction RPC.
222 : : */
223 : : bool fFromMe;
224 : : int64_t nOrderPos; //!< position in ordered transaction list
225 : : std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
226 : :
227 : : // memory only
228 : : enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
229 : : mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
230 : : /**
231 : : * This flag is true if all m_amounts caches are empty. This is particularly
232 : : * useful in places where MarkDirty is conditionally called and the
233 : : * condition can be expensive and thus can be skipped if the flag is true.
234 : : * See MarkDestinationsDirty.
235 : : */
236 : : mutable bool m_is_cache_empty{true};
237 : : mutable bool fChangeCached;
238 : : mutable CAmount nChangeCached;
239 : :
240 [ + + ]: 583820 : CWalletTx(CTransactionRef tx, const TxState& state) : tx(std::move(tx)), m_state(state)
241 : : {
242 : 116764 : Init();
243 : 116764 : }
244 : :
245 : 116766 : void Init()
246 : : {
247 : 116766 : mapValue.clear();
248 : 116766 : vOrderForm.clear();
249 : 116766 : fTimeReceivedIsTxTime = false;
250 : 116766 : nTimeReceived = 0;
251 : 116766 : nTimeSmart = 0;
252 : 116766 : fFromMe = false;
253 : 116766 : fChangeCached = false;
254 : 116766 : nChangeCached = 0;
255 : 116766 : nOrderPos = -1;
256 : 116766 : }
257 : :
258 : : CTransactionRef tx;
259 : : TxState m_state;
260 : :
261 : : // Set of mempool transactions that conflict
262 : : // directly with the transaction, or that conflict
263 : : // with an ancestor transaction. This set will be
264 : : // empty if state is InMempool or Confirmed, but
265 : : // can be nonempty if state is Inactive or
266 : : // BlockConflicted.
267 : : std::set<Txid> mempool_conflicts;
268 : :
269 : : template<typename Stream>
270 : 433 : void Serialize(Stream& s) const
271 : : {
272 [ + - ]: 433 : mapValue_t mapValueCopy = mapValue;
273 : :
274 [ + - + - : 433 : mapValueCopy["fromaccount"] = "";
+ - ]
275 [ + - ]: 433 : if (nOrderPos != -1) {
276 [ + - + - : 433 : mapValueCopy["n"] = util::ToString(nOrderPos);
+ - ]
277 : : }
278 [ + - ]: 433 : if (nTimeSmart) {
279 [ + - + - : 433 : mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
+ - ]
280 : : }
281 : :
282 : 433 : std::vector<uint8_t> dummy_vector1; //!< Used to be vMerkleBranch
283 : 433 : std::vector<uint8_t> dummy_vector2; //!< Used to be vtxPrev
284 : 433 : bool dummy_bool = false; //!< Used to be fSpent
285 [ + - ]: 433 : uint256 serializedHash = TxStateSerializedBlockHash(m_state);
286 : 433 : int serializedIndex = TxStateSerializedIndex(m_state);
287 [ + - + - : 866 : s << TX_WITH_WITNESS(tx) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool;
+ - + - +
- + - + -
+ - + - +
- + - ]
288 : 433 : }
289 : :
290 : : template<typename Stream>
291 : 2 : void Unserialize(Stream& s)
292 : : {
293 : 2 : Init();
294 : :
295 : 2 : std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
296 : 2 : std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
297 : : bool dummy_bool; //! Used to be fSpent
298 : 2 : uint256 serialized_block_hash;
299 : : int serializedIndex;
300 [ + - + - : 2 : s >> TX_WITH_WITNESS(tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool;
+ - + - +
- + - + -
+ - + - +
- + - ]
301 : :
302 : 2 : m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
303 : :
304 [ + - ]: 4 : const auto it_op = mapValue.find("n");
305 [ + - + - ]: 2 : nOrderPos = (it_op != mapValue.end()) ? LocaleIndependentAtoi<int64_t>(it_op->second) : -1;
306 [ + - ]: 4 : const auto it_ts = mapValue.find("timesmart");
307 [ + - + - ]: 2 : nTimeSmart = (it_ts != mapValue.end()) ? static_cast<unsigned int>(LocaleIndependentAtoi<int64_t>(it_ts->second)) : 0;
308 : :
309 [ + - ]: 4 : mapValue.erase("fromaccount");
310 [ + - ]: 4 : mapValue.erase("spent");
311 [ + - ]: 4 : mapValue.erase("n");
312 [ + - ]: 4 : mapValue.erase("timesmart");
313 : 2 : }
314 : :
315 : 0 : void SetTx(CTransactionRef arg)
316 : : {
317 [ # # # # ]: 0 : tx = std::move(arg);
318 : : }
319 : :
320 : : //! make sure balances are recalculated
321 : 447 : void MarkDirty()
322 : : {
323 [ + - - - : 447 : m_amounts[DEBIT].Reset();
- - + + ]
[ + - ]
324 : 447 : m_amounts[CREDIT].Reset();
325 : 447 : m_amounts[IMMATURE_CREDIT].Reset();
326 : 447 : m_amounts[AVAILABLE_CREDIT].Reset();
327 : 447 : fChangeCached = false;
328 [ + - - - : 446 : m_is_cache_empty = true;
- - + + ]
[ + - ]
329 : 1 : }
330 : :
331 : : /** True if only scriptSigs are different */
332 : : bool IsEquivalentTo(const CWalletTx& tx) const;
333 : :
334 : : bool InMempool() const;
335 : :
336 : : int64_t GetTxTime() const;
337 : :
338 [ # # # # ]: 3459 : template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
[ + + - +
- + - - +
+ - + + +
- - + - ]
[ # # # # ]
339 [ + + - + : 8 : template<typename T> T* state() { return std::get_if<T>(&m_state); }
# # # # #
# # # #
# ][ - - -
- - - - -
- - + + -
+ ]
340 : :
341 : : //! Update transaction state when attaching to a chain, filling in heights
342 : : //! of conflicted and confirmed blocks
343 : : void updateState(interfaces::Chain& chain);
344 : :
345 [ + + + - : 29 : bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
+ - - - -
- - - - -
- - - - -
- + + + -
+ - + - ]
[ # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
[ # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
346 [ - + ]: 22 : bool isMempoolConflicted() const { return !mempool_conflicts.empty(); }
347 [ # # # # : 22 : bool isBlockConflicted() const { return state<TxStateBlockConflicted>(); }
# # # # #
# # # # #
# # ][ - -
- - - - -
- - - - -
+ - - + ]
348 [ - + - - : 438 : bool isInactive() const { return state<TxStateInactive>(); }
+ - ]
349 [ + + + - : 7 : bool isUnconfirmed() const { return !isAbandoned() && !isBlockConflicted() && !isMempoolConflicted() && !isConfirmed(); }
+ - ]
350 [ - + - - : 93 : bool isConfirmed() const { return state<TxStateConfirmed>(); }
+ - ]
[ - - + + ]
[ # # # #
# # ]
351 [ # # # # : 116801 : const Txid& GetHash() const LIFETIMEBOUND { return tx->GetHash(); }
# # ][ + +
# # # # #
# # # # #
# # ][ - -
- - - - -
- + - - -
- - ]
[ + - + - ]
352 : 0 : const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return tx->GetWitnessHash(); }
353 [ + + + - : 3661 : bool IsCoinBase() const { return tx->IsCoinBase(); }
+ + + + ]
[ # # # #
# # # # ]
[ # # # # ]
354 : :
355 : : private:
356 : : // Disable copying of CWalletTx objects to prevent bugs where instances get
357 : : // copied in and out of the mapWallet map, and fields are updated in the
358 : : // wrong copy.
359 : : CWalletTx(const CWalletTx&) = default;
360 : 0 : CWalletTx& operator=(const CWalletTx&) = default;
361 : : public:
362 : : // Instead have an explicit copy function
363 : : void CopyFrom(const CWalletTx&);
364 : : };
365 : :
366 : : struct WalletTxOrderComparator {
367 : 2 : bool operator()(const CWalletTx* a, const CWalletTx* b) const
368 : : {
369 [ + - + - : 2 : return a->nOrderPos < b->nOrderPos;
- - ]
370 : : }
371 : : };
372 : : } // namespace wallet
373 : :
374 : : #endif // BITCOIN_WALLET_TRANSACTION_H
|