forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtransaction.h
More file actions
454 lines (394 loc) · 17.7 KB
/
Copy pathtransaction.h
File metadata and controls
454 lines (394 loc) · 17.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
// Copyright (c) 2021-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_TRANSACTION_H
#define BITCOIN_WALLET_TRANSACTION_H
#include <attributes.h>
#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <tinyformat.h>
#include <uint256.h>
#include <util/check.h>
#include <util/overloaded.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <wallet/types.h>
#include <bitset>
#include <cstdint>
#include <map>
#include <utility>
#include <variant>
#include <vector>
namespace interfaces {
class Chain;
} // namespace interfaces
namespace wallet {
//! State of transaction confirmed in a block.
struct TxStateConfirmed {
uint256 confirmed_block_hash;
int confirmed_block_height;
int position_in_block;
explicit TxStateConfirmed(const uint256& block_hash, int height, int index) : confirmed_block_hash(block_hash), confirmed_block_height(height), position_in_block(index) {}
std::string toString() const { return strprintf("Confirmed (block=%s, height=%i, index=%i)", confirmed_block_hash.ToString(), confirmed_block_height, position_in_block); }
};
//! State of transaction added to mempool.
struct TxStateInMempool {
std::string toString() const { return strprintf("InMempool"); }
};
//! State of rejected transaction that conflicts with a confirmed block.
struct TxStateBlockConflicted {
uint256 conflicting_block_hash;
int conflicting_block_height;
explicit TxStateBlockConflicted(const uint256& block_hash, int height) : conflicting_block_hash(block_hash), conflicting_block_height(height) {}
std::string toString() const { return strprintf("BlockConflicted (block=%s, height=%i)", conflicting_block_hash.ToString(), conflicting_block_height); }
};
//! State of transaction not confirmed or conflicting with a known block and
//! not in the mempool. May conflict with the mempool, or with an unknown block,
//! or be abandoned, never broadcast, or rejected from the mempool for another
//! reason.
struct TxStateInactive {
bool abandoned;
explicit TxStateInactive(bool abandoned = false) : abandoned(abandoned) {}
std::string toString() const { return strprintf("Inactive (abandoned=%i)", abandoned); }
};
//! State of transaction loaded in an unrecognized state with unexpected hash or
//! index values. Treated as inactive (with serialized hash and index values
//! preserved) by default, but may enter another state if transaction is added
//! to the mempool, or confirmed, or abandoned, or found conflicting.
struct TxStateUnrecognized {
uint256 block_hash;
int index;
TxStateUnrecognized(const uint256& block_hash, int index) : block_hash(block_hash), index(index) {}
std::string toString() const { return strprintf("Unrecognized (block=%s, index=%i)", block_hash.ToString(), index); }
};
//! All possible CWalletTx states
using TxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateBlockConflicted, TxStateInactive, TxStateUnrecognized>;
//! Subset of states transaction sync logic is implemented to handle.
using SyncTxState = std::variant<TxStateConfirmed, TxStateInMempool, TxStateInactive>;
//! Try to interpret deserialized TxStateUnrecognized data as a recognized state.
static inline TxState TxStateInterpretSerialized(TxStateUnrecognized data)
{
if (data.block_hash == uint256::ZERO) {
if (data.index == 0) return TxStateInactive{};
} else if (data.block_hash == uint256::ONE) {
if (data.index == -1) return TxStateInactive{/*abandoned=*/true};
} else if (data.index >= 0) {
return TxStateConfirmed{data.block_hash, /*height=*/-1, data.index};
} else if (data.index == -1) {
return TxStateBlockConflicted{data.block_hash, /*height=*/-1};
}
return data;
}
//! Get TxState serialized block hash. Inverse of TxStateInterpretSerialized.
static inline uint256 TxStateSerializedBlockHash(const TxState& state)
{
return std::visit(util::Overloaded{
[](const TxStateInactive& inactive) { return inactive.abandoned ? uint256::ONE : uint256::ZERO; },
[](const TxStateInMempool& in_mempool) { return uint256::ZERO; },
[](const TxStateConfirmed& confirmed) { return confirmed.confirmed_block_hash; },
[](const TxStateBlockConflicted& conflicted) { return conflicted.conflicting_block_hash; },
[](const TxStateUnrecognized& unrecognized) { return unrecognized.block_hash; }
}, state);
}
//! Get TxState serialized block index. Inverse of TxStateInterpretSerialized.
static inline int TxStateSerializedIndex(const TxState& state)
{
return std::visit(util::Overloaded{
[](const TxStateInactive& inactive) { return inactive.abandoned ? -1 : 0; },
[](const TxStateInMempool& in_mempool) { return 0; },
[](const TxStateConfirmed& confirmed) { return confirmed.position_in_block; },
[](const TxStateBlockConflicted& conflicted) { return -1; },
[](const TxStateUnrecognized& unrecognized) { return unrecognized.index; }
}, state);
}
//! Return TxState or SyncTxState as a string for logging or debugging.
template<typename T>
std::string TxStateString(const T& state)
{
return std::visit([](const auto& s) { return s.toString(); }, state);
}
/**
* Cachable amount subdivided into avoid reuse and all balances
*/
struct CachableAmount
{
std::optional<CAmount> m_avoid_reuse_value;
std::optional<CAmount> m_all_value;
inline void Reset()
{
m_avoid_reuse_value.reset();
m_all_value.reset();
}
void Set(bool avoid_reuse, CAmount value)
{
if (avoid_reuse) {
m_avoid_reuse_value = value;
} else {
m_all_value = value;
}
}
CAmount Get(bool avoid_reuse)
{
if (avoid_reuse) {
Assert(m_avoid_reuse_value.has_value());
return m_avoid_reuse_value.value();
}
Assert(m_all_value.has_value());
return m_all_value.value();
}
bool IsCached(bool avoid_reuse)
{
if (avoid_reuse) return m_avoid_reuse_value.has_value();
return m_all_value.has_value();
}
};
/** Legacy class used for deserializing vtxPrev for backwards compatibility.
* vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
* but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
* These need to get deserialized for field alignment when deserializing
* a CWalletTx, but the deserialized values are discarded.**/
class CMerkleTx
{
public:
template<typename Stream>
void Unserialize(Stream& s)
{
CTransactionRef tx;
uint256 hashBlock;
std::vector<uint256> vMerkleBranch;
int nIndex;
s >> TX_WITH_WITNESS(tx) >> hashBlock >> vMerkleBranch >> nIndex;
}
};
/**
* A transaction with a bunch of additional info that only the owner cares about.
* It includes any unrecorded transactions needed to link it back to the block chain.
*/
class CWalletTx
{
public:
// "from" and "message" are obsolete fields that could be set in
// the UI prior to 2011 (removed in commit 4d9b223)
// These fields are kept to avoid losing metadata.
std::optional<std::string> m_from;
std::optional<std::string> m_message;
// Comment strings provided by the user
std::optional<std::string> m_comment;
std::optional<std::string> m_comment_to;
std::optional<Txid> m_replaces_txid;
std::optional<Txid> m_replaced_by_txid;
// BIP 21 URI Messages
std::vector<std::string> m_messages;
// BIP 70 Payment Request (deprecated, field kept to preserve metadata from old wallets)
std::vector<std::string> m_payment_requests;
unsigned int nTimeReceived; //!< time received by this node
/**
* Stable timestamp that never changes, and reflects the order a transaction
* was added to the wallet. Timestamp is based on the block time for a
* transaction added as part of a block, or else the time when the
* transaction was received if it wasn't part of a block, with the timestamp
* adjusted in both cases so timestamp order matches the order transactions
* were added to the wallet. More details can be found in
* CWallet::ComputeTimeSmart().
*/
unsigned int nTimeSmart;
// Cached value for whether the transaction spends any inputs known to the wallet
mutable std::optional<bool> m_cached_from_me{std::nullopt};
int64_t nOrderPos; //!< position in ordered transaction list
std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
// memory only
enum AmountType { DEBIT, CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
/**
* This flag is true if all m_amounts caches are empty. This is particularly
* useful in places where MarkDirty is conditionally called and the
* condition can be expensive and thus can be skipped if the flag is true.
* See MarkDestinationsDirty.
*/
mutable bool m_is_cache_empty{true};
mutable bool fChangeCached;
mutable CAmount nChangeCached;
CWalletTx(CTransactionRef tx, const TxState& state) : m_state(state)
{
Assert(tx);
m_canonical_wtxid = tx->GetWitnessHash();
m_txs.emplace(tx->GetWitnessHash(), std::move(tx));
SetDefaults();
}
template <typename Stream>
CWalletTx(deserialize_type, Stream& s, const std::map<Wtxid, CTransactionRef>& variants) : m_state(TxStateInactive{})
{
Unserialize(s);
const Txid& canonical_txid = GetHash();
for (const auto& [wtxid, tx] : variants) {
if (tx->GetHash() != canonical_txid) throw std::runtime_error("variant txid does not match wallet txid");
}
// Merge witness variants
m_txs.insert(variants.begin(), variants.end());
Assert(m_txs.contains(GetWitnessHash()));
}
TxState m_state;
// Set of mempool transactions that conflict
// directly with the transaction, or that conflict
// with an ancestor transaction. This set will be
// empty if state is InMempool or Confirmed, but
// can be nonempty if state is Inactive or
// BlockConflicted.
std::set<Txid> mempool_conflicts;
// Track v3 mempool tx that spends from this tx
// so that we don't try to create another unconfirmed child
std::optional<Txid> truc_child_in_mempool;
template<typename Stream>
void Serialize(Stream& s) const
{
std::map<std::string, std::string> string_values;
if (m_from) string_values["from"] = *m_from;
if (m_message) string_values["message"] = *m_message;
if (m_comment) string_values["comment"] = *m_comment;
if (m_comment_to) string_values["to"] = *m_comment_to;
if (m_replaces_txid) string_values["replaces_txid"] = m_replaces_txid->ToString();
if (m_replaced_by_txid) string_values["replaced_by_txid"] = m_replaced_by_txid->ToString();
string_values["fromaccount"] = "";
if (nOrderPos != -1) string_values["n"] = util::ToString(nOrderPos);
if (nTimeSmart) string_values["timesmart"] = strprintf("%u", nTimeSmart);
std::vector<std::pair<std::string, std::string>> msgs_reqs;
msgs_reqs.reserve(m_messages.size() + m_payment_requests.size());
for (const std::string& msg : m_messages) {
msgs_reqs.emplace_back("Message", msg);
}
for (const std::string& req : m_payment_requests) {
msgs_reqs.emplace_back("PaymentRequest", req);
}
std::vector<uint8_t> dummy_vector1; // Used to be vMerkleBranch
std::vector<uint8_t> dummy_vector2; // Used to be vtxPrev
bool dummy_bool = false; // Used to be fFromMe, and fSpent
uint32_t dummy_int = 0; // Used to be fTimeReceivedIsTxTime
uint256 serializedHash = TxStateSerializedBlockHash(m_state);
int serializedIndex = TxStateSerializedIndex(m_state);
s << TX_WITH_WITNESS(GetTx()) << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << string_values << msgs_reqs << dummy_int << nTimeReceived << dummy_bool << dummy_bool;
}
template<typename Stream>
void Unserialize(Stream& s)
{
Init();
std::vector<uint256> dummy_vector1; // Used to be vMerkleBranch
std::vector<CMerkleTx> dummy_vector2; // Used to be vtxPrev
bool dummy_bool; // Used to be fFromMe, and fSpent
uint32_t dummy_int; // Used to be fTimeReceivedIsTxTime
uint256 serialized_block_hash;
int serializedIndex;
std::map<std::string, std::string> string_values;
std::vector<std::pair<std::string, std::string>> msgs_reqs;
CTransactionRef canonical_tx;
s >> TX_WITH_WITNESS(canonical_tx) >> serialized_block_hash >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> string_values >> msgs_reqs >> dummy_int >> nTimeReceived >> dummy_bool >> dummy_bool;
m_canonical_wtxid = canonical_tx->GetWitnessHash();
m_txs.emplace(m_canonical_wtxid, std::move(canonical_tx));
m_state = TxStateInterpretSerialized({serialized_block_hash, serializedIndex});
string_values.erase("fromaccount");
string_values.erase("spent");
for (const auto& [key, value] : string_values) {
if (key == "n") nOrderPos = LocaleIndependentAtoi<int64_t>(value);
else if (key == "timesmart") nTimeSmart = LocaleIndependentAtoi<int64_t>(value);
else if (key == "from") m_from = value;
else if (key == "message") m_message = value;
else if (key == "comment") m_comment = value;
else if (key == "to") m_comment_to = value;
else if (key == "replaces_txid") m_replaces_txid = Txid::FromHex(value);
else if (key == "replaced_by_txid") m_replaced_by_txid = Txid::FromHex(value);
else {
throw std::runtime_error("Unexpected value in CWalletTx strings value map");
}
}
for (const auto& [type, data] : msgs_reqs) {
if (type == "Message") m_messages.emplace_back(data);
else if (type == "PaymentRequest") m_payment_requests.emplace_back(data);
else {
throw std::runtime_error("Unknown type in CWalletTx messages and requests vector");
}
}
}
CTransactionRef GetTx() const { return m_txs.at(m_canonical_wtxid); }
// Update the state of this wallet transaction along with a transaction that may have a different wtxid.
// If the given transaction has a different wtxid, the transaction is stored if it has not been seen before.
// The canonical wtxid is also updated. The tx that is confirmed becomes canonical. For unconfirmed txs,
// those with witnesses are preferred, followed by least weight.
bool Update(CTransactionRef tx, const TxState& new_state);
//! make sure balances are recalculated
void MarkDirty()
{
m_amounts[DEBIT].Reset();
m_amounts[CREDIT].Reset();
fChangeCached = false;
m_is_cache_empty = true;
m_cached_from_me = std::nullopt;
}
/** True if only scriptSigs are different */
bool IsEquivalentTo(const CWalletTx& tx) const;
bool InMempool() const;
int64_t GetTxTime() const;
template<typename T> const T* state() const { return std::get_if<T>(&m_state); }
template<typename T> T* state() { return std::get_if<T>(&m_state); }
//! Update transaction state when attaching to a chain, filling in heights
//! of conflicted and confirmed blocks
void updateState(interfaces::Chain& chain);
bool isAbandoned() const { return state<TxStateInactive>() && state<TxStateInactive>()->abandoned; }
bool isMempoolConflicted() const { return !mempool_conflicts.empty(); }
bool isBlockConflicted() const { return state<TxStateBlockConflicted>(); }
bool isInactive() const { return state<TxStateInactive>(); }
bool isUnconfirmed() const { return !isAbandoned() && !isBlockConflicted() && !isMempoolConflicted() && !isConfirmed(); }
bool isConfirmed() const { return state<TxStateConfirmed>(); }
const Txid& GetHash() const LIFETIMEBOUND { return GetTx()->GetHash(); }
const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return GetTx()->GetWitnessHash(); }
bool IsCoinBase() const { return GetTx()->IsCoinBase(); }
const std::map<Wtxid, CTransactionRef>& GetTxs() const { return m_txs; }
// Disable copying of CWalletTx objects to prevent bugs where instances get
// copied in and out of the mapWallet map, and fields are updated in the
// wrong copy.
CWalletTx(const CWalletTx&) = delete;
CWalletTx& operator=(const CWalletTx&) = delete;
// Enable the default move constructor
CWalletTx(CWalletTx&&) = default;
private:
void SetDefaults()
{
nTimeReceived = 0;
nTimeSmart = 0;
fChangeCached = false;
nChangeCached = 0;
nOrderPos = -1;
}
void Init()
{
m_txs.clear();
m_canonical_wtxid = Wtxid{};
SetDefaults();
}
Wtxid m_canonical_wtxid;
std::map<Wtxid, CTransactionRef> m_txs;
//! Set m_canonical_wtxid to the best variant under the unconfirmed rule
//! (witnessed preferred, then least weight). Ignores state.
void RecomputeCanonical();
};
struct WalletTxOrderComparator {
bool operator()(const CWalletTx* a, const CWalletTx* b) const
{
return a->nOrderPos < b->nOrderPos;
}
};
class WalletTXO
{
private:
const CWalletTx& m_wtx;
const CTxOut& m_output;
public:
WalletTXO(const CWalletTx& wtx, const CTxOut& output)
: m_wtx(wtx),
m_output(output)
{
Assume(std::ranges::find(wtx.GetTx()->vout, output) != wtx.GetTx()->vout.end());
}
const CWalletTx& GetWalletTx() const { return m_wtx; }
const CTxOut& GetTxOut() const { return m_output; }
};
} // namespace wallet
#endif // BITCOIN_WALLET_TRANSACTION_H