transactionrecord.cpp raw
1 // Copyright (c) 2011-2022 The Limenka 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 #include <qt/transactionrecord.h>
6
7 #include <chain.h>
8 #include <interfaces/wallet.h>
9 #include <key_io.h>
10 #include <wallet/types.h>
11
12 #include <stdint.h>
13
14 #include <QDateTime>
15
16 using wallet::ISMINE_NO;
17 using wallet::ISMINE_SPENDABLE;
18 using wallet::ISMINE_WATCH_ONLY;
19 using wallet::isminetype;
20
21 /* Return positive answer if transaction should be shown in list.
22 */
23 bool TransactionRecord::showTransaction()
24 {
25 // There are currently no cases where we hide transactions, but
26 // we may want to use this in the future for things like RBF.
27 return true;
28 }
29
30 /*
31 * Decompose CWallet transaction to model transaction records.
32 */
33 QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interfaces::WalletTx& wtx)
34 {
35 QList<TransactionRecord> parts;
36 int64_t nTime = wtx.time;
37 CAmount nCredit = wtx.credit;
38 CAmount nDebit = wtx.debit;
39 CAmount nNet = nCredit - nDebit;
40 uint256 hash = wtx.tx->GetHash();
41 std::map<std::string, std::string> mapValue = wtx.value_map;
42
43 bool involvesWatchAddress = false;
44 isminetype fAllFromMe = ISMINE_SPENDABLE;
45 bool any_from_me = false;
46 if (wtx.is_coinbase) {
47 fAllFromMe = ISMINE_NO;
48 } else {
49 for (const isminetype mine : wtx.txin_is_mine)
50 {
51 if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
52 if(fAllFromMe > mine) fAllFromMe = mine;
53 if (mine) any_from_me = true;
54 }
55 }
56
57 if (fAllFromMe || !any_from_me) {
58 for (const isminetype mine : wtx.txout_is_mine)
59 {
60 if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true;
61 }
62
63 CAmount nTxFee = nDebit - wtx.tx->GetValueOut();
64
65 for(unsigned int i = 0; i < wtx.tx->vout.size(); i++)
66 {
67 const CTxOut& txout = wtx.tx->vout[i];
68
69 if (fAllFromMe) {
70 // Change is only really possible if we're the sender
71 // Otherwise, someone just sent limenkas to a change address, which should be shown
72 if (wtx.txout_is_change[i]) {
73 continue;
74 }
75
76 //
77 // Debit
78 //
79
80 TransactionRecord sub(hash, nTime);
81 sub.idx = i;
82 sub.involvesWatchAddress = involvesWatchAddress;
83
84 if (!std::get_if<CNoDestination>(&wtx.txout_address[i]))
85 {
86 // Sent to Limenka Address
87 sub.type = TransactionRecord::SendToAddress;
88 sub.address = EncodeDestination(wtx.txout_address[i]);
89 }
90 else
91 {
92 // Sent to IP, or other non-address transaction like OP_EVAL
93 sub.type = TransactionRecord::SendToOther;
94 sub.address = mapValue["to"];
95 }
96
97 CAmount nValue = txout.nValue;
98 /* Add fee to first output */
99 if (nTxFee > 0)
100 {
101 nValue += nTxFee;
102 nTxFee = 0;
103 }
104 sub.debit = -nValue;
105
106 parts.append(sub);
107 }
108
109 isminetype mine = wtx.txout_is_mine[i];
110 if(mine)
111 {
112 //
113 // Credit
114 //
115
116 TransactionRecord sub(hash, nTime);
117 sub.idx = i; // vout index
118 sub.credit = txout.nValue;
119 sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY;
120 if (wtx.txout_address_is_mine[i])
121 {
122 // Received by Limenka Address
123 sub.type = TransactionRecord::RecvWithAddress;
124 sub.address = EncodeDestination(wtx.txout_address[i]);
125 }
126 else
127 {
128 // Received by IP connection (deprecated features), or a multisignature or other non-simple transaction
129 sub.type = TransactionRecord::RecvFromOther;
130 sub.address = mapValue["from"];
131 }
132 if (wtx.is_coinbase)
133 {
134 // Generated
135 sub.type = TransactionRecord::Generated;
136 }
137
138 parts.append(sub);
139 }
140 }
141 } else {
142 //
143 // Mixed debit transaction, can't break down payees
144 //
145 parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
146 parts.last().involvesWatchAddress = involvesWatchAddress;
147 }
148
149 return parts;
150 }
151
152 void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, const uint256& block_hash, int numBlocks, int64_t block_time)
153 {
154 // Determine transaction status
155
156 // Sort order, unrecorded transactions sort to the top
157 int typesort;
158 switch (type) {
159 case SendToAddress: case SendToOther:
160 typesort = 2; break;
161 case RecvWithAddress: case RecvFromOther:
162 typesort = 3; break;
163 default:
164 typesort = 9;
165 }
166 status.sortKey = strprintf("%010d-%01d-%010u-%03d-%d",
167 wtx.block_height,
168 wtx.is_coinbase ? 1 : 0,
169 wtx.time_received,
170 idx,
171 typesort);
172 status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0);
173 status.depth = wtx.depth_in_main_chain;
174 status.m_cur_block_hash = block_hash;
175
176 // For generated transactions, determine maturity
177 if (type == TransactionRecord::Generated) {
178 if (wtx.blocks_to_maturity > 0)
179 {
180 status.status = TransactionStatus::Immature;
181
182 if (wtx.is_in_main_chain)
183 {
184 status.matures_in = wtx.blocks_to_maturity;
185 }
186 else
187 {
188 status.status = TransactionStatus::NotAccepted;
189 }
190 }
191 else
192 {
193 status.status = TransactionStatus::Confirmed;
194 }
195 }
196 else
197 {
198 if (status.depth < 0)
199 {
200 status.status = TransactionStatus::Conflicted;
201 }
202 else if (status.depth == 0)
203 {
204 status.status = TransactionStatus::Unconfirmed;
205 if (wtx.is_abandoned)
206 status.status = TransactionStatus::Abandoned;
207 } else if (wtx.is_assumed) {
208 status.status = TransactionStatus::AssumedConfirmed;
209 }
210 else if (status.depth < RecommendedNumConfirmations)
211 {
212 status.status = TransactionStatus::Confirming;
213 }
214 else
215 {
216 status.status = TransactionStatus::Confirmed;
217 }
218 }
219 status.needsUpdate = false;
220 }
221
222 bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const
223 {
224 assert(!block_hash.IsNull());
225 return status.m_cur_block_hash != block_hash || status.needsUpdate;
226 }
227
228 QString TransactionRecord::getTxHash() const
229 {
230 return QString::fromStdString(hash.ToString());
231 }
232
233 int TransactionRecord::getOutputIndex() const
234 {
235 return idx;
236 }
237