transactionrecord.cpp
1 // Copyright (c) 2011-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 #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 bitcoins 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 Bitcoin 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 Bitcoin 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 } 208 else if (status.depth < RecommendedNumConfirmations) 209 { 210 status.status = TransactionStatus::Confirming; 211 } 212 else 213 { 214 status.status = TransactionStatus::Confirmed; 215 } 216 } 217 status.needsUpdate = false; 218 } 219 220 bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const 221 { 222 assert(!block_hash.IsNull()); 223 return status.m_cur_block_hash != block_hash || status.needsUpdate; 224 } 225 226 QString TransactionRecord::getTxHash() const 227 { 228 return QString::fromStdString(hash.ToString()); 229 } 230 231 int TransactionRecord::getOutputIndex() const 232 { 233 return idx; 234 }