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 : 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 : 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
|