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