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