/ src / qt / transactionrecord.cpp
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  }