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