PIVX Core  5.6.99
P2P Digital Currency
transactiontablemodel.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2014 The Bitcoin developers
2 // Copyright (c) 2014-2016 The Dash developers
3 // Copyright (c) 2016-2021 The PIVX Core developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
8 
9 #include "addresstablemodel.h"
10 #include "guiconstants.h"
11 #include "guiutil.h"
12 #include "optionsmodel.h"
13 #include "transactionrecord.h"
14 #include "walletmodel.h"
15 
16 #include "interfaces/handler.h"
17 #include "sync.h"
18 #include "uint256.h"
19 #include "wallet/wallet.h"
20 
21 #include <algorithm>
22 
23 #include <QColor>
24 #include <QDateTime>
25 #include <QIcon>
26 #include <QtConcurrent/QtConcurrent>
27 #include <QFuture>
28 
29 #define SINGLE_THREAD_MAX_TXES_SIZE 4000
30 
31 // Maximum amount of loaded records in ram in the first load.
32 // If the user has more and want to load them:
33 // TODO, add load on demand in pages (not every tx loaded all the time into the records list).
34 #define MAX_AMOUNT_LOADED_RECORDS 20000
35 
36 // Amount column is right-aligned it contains numbers
37 static int column_alignments[] = {
38  Qt::AlignLeft | Qt::AlignVCenter, /* status */
39  Qt::AlignLeft | Qt::AlignVCenter, /* watchonly */
40  Qt::AlignLeft | Qt::AlignVCenter, /* date */
41  Qt::AlignLeft | Qt::AlignVCenter, /* type */
42  Qt::AlignLeft | Qt::AlignVCenter, /* address */
43  Qt::AlignRight | Qt::AlignVCenter /* amount */
44 };
45 
46 // Comparison operator for sort/binary search of model tx list
47 struct TxLessThan {
48  bool operator()(const TransactionRecord& a, const TransactionRecord& b) const
49  {
50  return a.hash < b.hash;
51  }
52  bool operator()(const TransactionRecord& a, const uint256& b) const
53  {
54  return a.hash < b;
55  }
56  bool operator()(const uint256& a, const TransactionRecord& b) const
57  {
58  return a < b.hash;
59  }
60 };
61 
63 {
64  QList<TransactionRecord> records;
65  qint64 nFirstLoadedTxTime{0};
66 };
67 
68 // Private implementation
70 {
71 public:
73  parent(parent)
74  {
75  }
76 
77  CWallet* wallet{nullptr};
79 
80  /* Local cache of wallet.
81  * As it is in the same order as the CWallet, by definition
82  * this is sorted by sha256.
83  */
84  QList<TransactionRecord> cachedWallet;
85 
90  qint64 nFirstLoadedTxTime{0};
91 
92  /* Query entire wallet anew from core.
93  */
95  {
96  qDebug() << "TransactionTablePriv::refreshWallet";
97  cachedWallet.clear();
98 
99  std::vector<CWalletTx> walletTxes = wallet->getWalletTxs();
100 
101  // Divide the work between multiple threads to speedup the process if the vector is larger than 4k txes
102  std::size_t txesSize = walletTxes.size();
103  if (txesSize > SINGLE_THREAD_MAX_TXES_SIZE) {
104 
105  // First check if the amount of txs exceeds the UI limit
106  if (txesSize > MAX_AMOUNT_LOADED_RECORDS) {
107  // Sort the txs by date
108  sort(walletTxes.begin(), walletTxes.end(),
109  [](const CWalletTx & a, const CWalletTx & b) -> bool {
110  return a.GetTxTime() > b.GetTxTime();
111  });
112 
113  // Only latest ones.
114  walletTxes = std::vector<CWalletTx>(walletTxes.begin(), walletTxes.begin() + MAX_AMOUNT_LOADED_RECORDS);
115  txesSize = walletTxes.size();
116  }
117 
118  // Simple way to get the processors count
119  std::size_t threadsCount = (QThreadPool::globalInstance()->maxThreadCount() / 2 ) + 1;
120 
121  // Size of the tx subsets
122  std::size_t const subsetSize = txesSize / (threadsCount + 1);
123  std::size_t totalSumSize = 0;
124  QList<QFuture<ConvertTxToVectorResult>> tasks;
125 
126  // Subsets + run task
127  for (std::size_t i = 0; i < threadsCount; ++i) {
128  tasks.append(
129  QtConcurrent::run(
131  this,
132  wallet,
133  std::vector<CWalletTx>(walletTxes.begin() + totalSumSize, walletTxes.begin() + totalSumSize + subsetSize)
134  )
135  );
136  totalSumSize += subsetSize;
137  }
138 
139  // Now take the remaining ones and do the work here
140  std::size_t const remainingSize = txesSize - totalSumSize;
141  auto res = convertTxToRecords(this, wallet,
142  std::vector<CWalletTx>(walletTxes.end() - remainingSize, walletTxes.end())
143  );
144  cachedWallet.append(res.records);
145  nFirstLoadedTxTime = res.nFirstLoadedTxTime;
146 
147  for (auto &future : tasks) {
148  future.waitForFinished();
149  ConvertTxToVectorResult convertRes = future.result();
150  cachedWallet.append(convertRes.records);
151  if (nFirstLoadedTxTime > convertRes.nFirstLoadedTxTime) {
153  }
154  }
155 
156  // Now that all records have been cached, sort them by tx hash
157  std::sort(cachedWallet.begin(), cachedWallet.end(), TxLessThan());
158 
159  } else {
160  // Single thread flow
161  ConvertTxToVectorResult convertRes = convertTxToRecords(this, wallet, walletTxes);
162  cachedWallet.append(convertRes.records);
164  }
165  }
166 
168  {
169  Q_EMIT parent->txLoaded(QString::fromStdString(rec.hash.GetHex()),
170  rec.type, rec.status.status);
171  }
172 
174  const CWallet* wallet,
175  const std::vector<CWalletTx>& walletTxes)
176  {
178  for (const auto& tx : walletTxes) {
179  QList<TransactionRecord> records = TransactionRecord::decomposeTransaction(wallet, tx);
180  if (records.isEmpty()) continue;
181  qint64 time = records.first().time;
182  if (res.nFirstLoadedTxTime == 0 || res.nFirstLoadedTxTime > time) {
183  res.nFirstLoadedTxTime = time;
184  }
185  for (const auto& rec : records) {
186  tablePriv->emitTxLoaded(rec);
187  }
188  res.records.append(records);
189  }
190  return res;
191  }
192 
193  /* Update our model of the wallet incrementally, to synchronize our model of the wallet
194  with that of the core.
195 
196  Call with transaction that was added, removed or changed.
197  */
198  void updateWallet(const uint256& hash, int status, bool showTransaction, TransactionRecord& ret)
199  {
200  qDebug() << "TransactionTablePriv::updateWallet : " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
201 
202  // Find bounds of this transaction in model
203  QList<TransactionRecord>::iterator lower = std::lower_bound(
204  cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
205  QList<TransactionRecord>::iterator upper = std::upper_bound(
206  cachedWallet.begin(), cachedWallet.end(), hash, TxLessThan());
207  int lowerIndex = (lower - cachedWallet.begin());
208  int upperIndex = (upper - cachedWallet.begin());
209  bool inModel = (lower != upper);
210 
211  if (status == CT_UPDATED) {
212  if (showTransaction && !inModel)
213  status = CT_NEW; /* Not in model, but want to show, treat as new */
214  if (!showTransaction && inModel)
215  status = CT_DELETED; /* In model, but want to hide, treat as deleted */
216  }
217 
218  qDebug() << " inModel=" + QString::number(inModel) +
219  " Index=" + QString::number(lowerIndex) + "-" + QString::number(upperIndex) +
220  " showTransaction=" + QString::number(showTransaction) + " derivedStatus=" + QString::number(status);
221 
222  switch (status) {
223  case CT_NEW:
224  if (inModel) {
225  qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is already in model";
226  break;
227  }
228  if (showTransaction) {
229  // Find transaction in wallet
230  const CWalletTx* wtx = wallet->GetWalletTx(hash);
231  if (!wtx) {
232  qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_NEW, but transaction is not in wallet";
233  break;
234  }
235 
236  // As old transactions are still getting updated (+20k range),
237  // do not add them if we deliberately didn't load them at startup.
239  return;
240  }
241 
242  // Added -- insert at the right position
243  QList<TransactionRecord> toInsert =
245  if (!toInsert.isEmpty()) { /* only if something to insert */
246  parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex + toInsert.size() - 1);
247  int insert_idx = lowerIndex;
248  for (const TransactionRecord& rec : toInsert) {
249  cachedWallet.insert(insert_idx, rec);
250  insert_idx += 1;
251  ret = rec; // Return record
252  }
253  parent->endInsertRows();
254  }
255  }
256  break;
257  case CT_DELETED:
258  if (!inModel) {
259  qWarning() << "TransactionTablePriv::updateWallet : Warning: Got CT_DELETED, but transaction is not in model";
260  break;
261  }
262  // Removed -- remove entire transaction from table
263  parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex - 1);
264  cachedWallet.erase(lower, upper);
265  parent->endRemoveRows();
266  break;
267  case CT_UPDATED:
268  // Miscellaneous updates -- nothing to do, status update will take care of this, and is only computed for
269  // visible transactions.
270  for (int i = lowerIndex; i < upperIndex; i++) {
271  TransactionRecord *rec = &cachedWallet[i];
272  rec->status.needsUpdate = true;
273  }
274  break;
275  }
276  }
277 
278  int size()
279  {
280  return cachedWallet.size();
281  }
282 
283  TransactionRecord* index(int cur_block_num, const uint256& cur_block_hash, int idx)
284  {
285  if (idx >= 0 && idx < cachedWallet.size()) {
286  TransactionRecord* rec = &cachedWallet[idx];
287  if (!cur_block_hash.IsNull() && rec->statusUpdateNeeded(cur_block_num)) {
288  // If a status update is needed (blocks came in since last check),
289  // update the status of this transaction from the wallet. Otherwise,
290  // simply re-use the cached status.
291  TRY_LOCK(wallet->cs_wallet, lockWallet);
292  if (lockWallet) {
293  auto mi = wallet->mapWallet.find(rec->hash);
294  if (mi != wallet->mapWallet.end()) {
295  rec->updateStatus(mi->second, cur_block_num);
296  }
297  }
298  }
299  return rec;
300  }
301  return nullptr;
302  }
303 };
304 
305 TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel* parent) : QAbstractTableModel(parent),
306  wallet(wallet),
307  walletModel(parent),
308  priv(new TransactionTablePriv(wallet, this)),
309  fProcessingQueuedTransactions(false)
310 {
311  columns << QString() << QString() << tr("Date") << tr("Type") << tr("Address") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit());
312 
314 }
315 
317 {
318  priv->refreshWallet();
320 }
321 
323 {
325  delete priv;
326 }
327 
330 {
332  Q_EMIT headerDataChanged(Qt::Horizontal, Amount, Amount);
333 }
334 
335 void TransactionTableModel::updateTransaction(const QString& hash, int status, bool showTransaction)
336 {
337  uint256 updated;
338  updated.SetHex(hash.toStdString());
339 
340  TransactionRecord rec(0);
341  priv->updateWallet(updated, status, showTransaction, rec);
342 
343  if (!rec.isNull())
344  Q_EMIT txArrived(hash, rec.isCoinStake(), rec.isMNReward(), rec.isAnyColdStakingType());
345 }
346 
348 {
349  // Blocks came in since last poll.
350  // Invalidate status (number of confirmations) and (possibly) description
351  // for all rows. Qt is smart enough to only actually request the data for the
352  // visible rows.
353  Q_EMIT dataChanged(index(0, Status), index(priv->size() - 1, Status));
354  Q_EMIT dataChanged(index(0, ToAddress), index(priv->size() - 1, ToAddress));
355 }
356 
357 int TransactionTableModel::rowCount(const QModelIndex& parent) const
358 {
359  Q_UNUSED(parent);
360  return priv->size();
361 }
362 
363 int TransactionTableModel::columnCount(const QModelIndex& parent) const
364 {
365  Q_UNUSED(parent);
366  return columns.length();
367 }
368 
370  return priv->size();
371 }
372 
374 {
375  QString status;
376 
377  switch (wtx->status.status) {
379  status = tr("Open for %n more block(s)", "", wtx->status.open_for);
380  break;
382  status = tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx->status.open_for));
383  break;
385  status = tr("Unconfirmed");
386  break;
388  status = tr("Confirming (%1 of %2 recommended confirmations)").arg(wtx->status.depth).arg(TransactionRecord::RecommendedNumConfirmations);
389  break;
391  status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth);
392  break;
394  status = tr("Conflicted");
395  break;
397  status = tr("Immature (%1 confirmations, will be available after %2)").arg(wtx->status.depth).arg(wtx->status.depth + wtx->status.matures_in);
398  break;
400  status = tr("Orphan Block - Generated but not accepted. This does not impact your holdings.");
401  break;
402  }
403 
404  return status;
405 }
406 
408 {
409  if (wtx->time) {
410  return GUIUtil::dateTimeStr(wtx->time);
411  }
412  return QString();
413 }
414 
415 /* Look up address in address book, if found return label (address)
416  otherwise just return (address)
417  */
418 QString TransactionTableModel::lookupAddress(const std::string& address, bool tooltip) const
419 {
420  QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(address));
421  QString description;
422  if (!label.isEmpty()) {
423  description += label + QString(" ");
424  }
425  if (label.isEmpty() || tooltip) {
426  description += QString::fromStdString(address);
427  }
428  return description;
429 }
430 
432 {
433  switch (wtx->type) {
435  return tr("Received with");
437  return tr("Masternode Reward");
439  return tr("Budget Payment");
441  return tr("Received from");
444  return tr("Sent to");
446  return tr("Payment to yourself");
448  return tr("Shielding coins to yourself");
450  return tr("Unshielding coins to yourself");
452  return tr("Shielded change, transfer between own shielded addresses");
454  return tr("%1 Stake").arg(CURRENCY_UNIT.c_str());
456  return tr("z%1 Stake").arg(CURRENCY_UNIT.c_str());
458  return tr("%1 Cold Stake").arg(CURRENCY_UNIT.c_str());
460  return tr("%1 Stake on behalf of").arg(CURRENCY_UNIT.c_str());
464  return tr("Stake delegation");
467  return tr("Stake delegation spent by");
469  return tr("Mined");
471  return tr("Converted %1 to z%1").arg(CURRENCY_UNIT.c_str());
473  return tr("Spent z%1").arg(CURRENCY_UNIT.c_str());
475  return tr("Received %1 from z%1").arg(CURRENCY_UNIT.c_str());
477  return tr("Minted Change as z%1 from z%1 Spend").arg(CURRENCY_UNIT.c_str());
479  return tr("Converted z%1 to %1").arg(CURRENCY_UNIT.c_str());
481  return tr("Received with shielded");
483  return tr("Received shielded memo");
485  return tr("Shielded send to");
487  return tr("Burned PIVs");
488  default:
489  return QString();
490  }
491 }
492 
494 {
495  switch (wtx->type) {
501  return QIcon(":/icons/tx_mined");
505  return QIcon(":/icons/tx_input");
509  return QIcon("://ic-transaction-sent");
510  default:
511  return QIcon(":/icons/tx_inout");
512  }
513 }
514 
515 QString TransactionTableModel::formatTxToAddress(const TransactionRecord* wtx, bool tooltip) const
516 {
517  QString watchAddress;
518  if (tooltip) {
519  // Mark transactions involving watch-only addresses by adding " (watch-only)"
520  watchAddress = wtx->involvesWatchAddress ? QString(" (") + tr("watch-only") + QString(")") : "";
521  }
522 
523  switch (wtx->type) {
525  return QString::fromStdString(wtx->address) + watchAddress;
535  return lookupAddress(wtx->address, tooltip);
539  // todo: add addressbook support for shielded addresses.
540  return QString::fromStdString(wtx->address);
542  return QString::fromStdString(wtx->address) + watchAddress;
544  return QString::fromStdString(wtx->address); // the address here is storing the op_return data.
548  return tr("Anonymous");
557  QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address));
558  return label.isEmpty() ? "" : label;
559  }
563  // Do not show the send to self address. todo: add addressbook for shielded addr
564  return "";
565  default: {
566  if (watchAddress.isEmpty()) {
567  return tr("No information");
568  } else {
569  return tr("(n/a)") + watchAddress;
570  }
571  }
572  }
573 }
574 
576 {
577  switch (wtx->type) {
578  // Show addresses without label in a less visible color
583  QString label = walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(wtx->address));
584  if (label.isEmpty())
585  return COLOR_BAREADDRESS;
586  }
588  default:
589  // To avoid overriding above conditional formats a default text color for this QTableView is not defined in stylesheet,
590  // so we must always return a color here
591  return COLOR_BLACK;
592  }
593 }
594 
595 QString TransactionTableModel::formatTxAmount(const TransactionRecord* wtx, bool showUnconfirmed, BitcoinUnits::SeparatorStyle separators) const
596 {
597  QString str = BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->credit + wtx->debit, false, separators);
598  if (showUnconfirmed) {
599  if (!wtx->status.countsForBalance) {
600  str = QString("[") + str + QString("]");
601  }
602  }
603  return QString(str);
604 }
605 
607 {
608  switch (wtx->status.status) {
613  return QIcon(":/icons/transaction_0");
615  switch (wtx->status.depth) {
616  case 1:
617  return QIcon(":/icons/transaction_1");
618  case 2:
619  return QIcon(":/icons/transaction_2");
620  case 3:
621  return QIcon(":/icons/transaction_3");
622  case 4:
623  return QIcon(":/icons/transaction_4");
624  default:
625  return QIcon(":/icons/transaction_5");
626  };
628  return QIcon(":/icons/transaction_confirmed");
630  return QIcon(":/icons/transaction_conflicted");
632  int total = wtx->status.depth + wtx->status.matures_in;
633  int part = (wtx->status.depth * 5 / total) + 1;
634  return QIcon(QString(":/icons/transaction_%1").arg(part));
635  }
637  return QIcon(":/icons/transaction_0");
638  default:
639  return COLOR_BLACK;
640  }
641 }
642 
644 {
645  if (wtx->involvesWatchAddress)
646  return QIcon(":/icons/eye");
647  else
648  return QVariant();
649 }
650 
652 {
653  return formatTxStatus(rec);
654 }
655 
656 QVariant TransactionTableModel::data(const QModelIndex& index, int role) const
657 {
658  if (!index.isValid())
659  return QVariant();
660  TransactionRecord* rec = static_cast<TransactionRecord*>(index.internalPointer());
661 
662  switch (role) {
663  case Qt::DecorationRole:
664  switch (index.column()) {
665  case Status:
666  return txStatusDecoration(rec);
667  case Watchonly:
668  return txWatchonlyDecoration(rec);
669  case ToAddress:
670  return txAddressDecoration(rec);
671  }
672  break;
673  case Qt::DisplayRole:
674  switch (index.column()) {
675  case Date:
676  return formatTxDate(rec);
677  case Type:
678  return formatTxType(rec);
679  case ToAddress:
680  return formatTxToAddress(rec, false);
681  case Amount:
683  }
684  break;
685  case Qt::EditRole:
686  // Edit role is used for sorting, so return the unformatted values
687  switch (index.column()) {
688  case Status:
689  return QString::fromStdString(rec->status.sortKey);
690  case Date:
691  return rec->time;
692  case Type:
693  return formatTxType(rec);
694  case Watchonly:
695  return (rec->involvesWatchAddress ? 1 : 0);
696  case ToAddress:
697  return formatTxToAddress(rec, true);
698  case Amount:
699  return qint64(rec->credit + rec->debit);
700  }
701  break;
702  case Qt::ToolTipRole:
703  return formatTooltip(rec);
704  case Qt::TextAlignmentRole:
705  return column_alignments[index.column()];
706  case Qt::ForegroundRole:
707  // Minted
711  return COLOR_ORPHAN;
712  else
713  return COLOR_STAKE;
714 
715  }
716  // Conflicted tx
718  return COLOR_CONFLICTED;
719  }
720  // Unconfimed or immature
722  return COLOR_UNCONFIRMED;
723  }
724  if (index.column() == Amount && (rec->credit + rec->debit) < 0) {
725  return COLOR_NEGATIVE;
726  }
727  if (index.column() == ToAddress) {
728  return addressColor(rec);
729  }
730 
731  // To avoid overriding above conditional formats a default text color for this QTableView is not defined in stylesheet,
732  // so we must always return a color here
733  return COLOR_BLACK;
734  case TypeRole:
735  return rec->type;
736  case SizeRole:
737  return rec->size;
738  case DateRole:
739  return QDateTime::fromTime_t(static_cast<uint>(rec->time));
740  case WatchonlyRole:
741  return rec->involvesWatchAddress;
743  return txWatchonlyDecoration(rec);
744  case AddressRole:
745  return QString::fromStdString(rec->address);
746  case LabelRole:
747  return walletModel->getAddressTableModel()->labelForAddress(QString::fromStdString(rec->address));
748  case AmountRole:
749  return qint64(rec->credit + rec->debit);
751  return rec->shieldedCredit ? qint64(*rec->shieldedCredit) : 0;
752  case TxHashRole:
753  return QString::fromStdString(rec->hash.ToString());
754  case ConfirmedRole:
755  return rec->status.countsForBalance;
756  case FormattedAmountRole:
757  // Used for copy/export, so don't include separators
758  return formatTxAmount(rec, false, BitcoinUnits::separatorNever);
759  case StatusRole:
760  return rec->status.status;
761  }
762  return QVariant();
763 }
764 
765 QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientation, int role) const
766 {
767  if (orientation == Qt::Horizontal) {
768  if (role == Qt::DisplayRole) {
769  return columns[section];
770  } else if (role == Qt::TextAlignmentRole) {
771  return column_alignments[section];
772  } else if (role == Qt::ToolTipRole) {
773  switch (section) {
774  case Status:
775  return tr("Transaction status. Hover over this field to show number of confirmations.");
776  case Date:
777  return tr("Date and time that the transaction was received.");
778  case Type:
779  return tr("Type of transaction.");
780  case Watchonly:
781  return tr("Whether or not a watch-only address is involved in this transaction.");
782  case ToAddress:
783  return tr("Destination address of transaction.");
784  case Amount:
785  return tr("Amount removed from or added to balance.");
786  }
787  }
788  }
789  return QVariant();
790 }
791 
792 QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex& parent) const
793 {
794  Q_UNUSED(parent);
797  row);
798  if (data) {
799  return createIndex(row, column, data);
800  }
801  return QModelIndex();
802 }
803 
805 {
806  // emit dataChanged to update Amount column with the current unit
808  Q_EMIT dataChanged(index(0, Amount), index(priv->size() - 1, Amount));
809 }
810 
811 // queue notifications to show a non freezing progress dialog e.g. for rescan
813 public:
814  TransactionNotification() = delete; // need to specify hash and status
816 
817  void invoke(QObject* ttm)
818  {
819  QString strHash = QString::fromStdString(hash.GetHex());
820  qDebug() << "NotifyTransactionChanged : " + strHash + " status= " + QString::number(status);
821  QMetaObject::invokeMethod(ttm, "updateTransaction", Qt::QueuedConnection,
822  Q_ARG(QString, strHash),
823  Q_ARG(int, status),
824  Q_ARG(bool, true));
825  }
826 
827 private:
830 };
831 
832 static bool fQueueNotifications = false;
833 static std::vector<TransactionNotification> vQueueNotifications;
834 
835 static void NotifyTransactionChanged(TransactionTableModel* ttm, CWallet* wallet, const uint256& hash, ChangeType status)
836 {
837 
838  TransactionNotification notification(hash, status);
839 
840  if (fQueueNotifications)
841  {
842  vQueueNotifications.push_back(notification);
843  return;
844  }
845  notification.invoke(ttm);
846 }
847 
848 static void ShowProgress(TransactionTableModel* ttm, const std::string& title, int nProgress)
849 {
850  if (nProgress == 0)
851  fQueueNotifications = true;
852 
853  if (nProgress == 100) {
854  fQueueNotifications = false;
855  if (vQueueNotifications.size() > 10) // prevent balloon spam, show maximum 10 balloons
856  QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, true));
857  for (unsigned int i = 0; i < vQueueNotifications.size(); ++i) {
858  if (vQueueNotifications.size() - i <= 10)
859  QMetaObject::invokeMethod(ttm, "setProcessingQueuedTransactions", Qt::QueuedConnection, Q_ARG(bool, false));
860 
861  vQueueNotifications[i].invoke(ttm);
862  }
863  std::vector<TransactionNotification>().swap(vQueueNotifications); // clear
864  }
865 }
866 
868 {
869  // Connect signals to wallet
870  m_handler_transaction_changed = interfaces::MakeHandler(wallet->NotifyTransactionChanged.connect(std::bind(NotifyTransactionChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)));
871  m_handler_show_progress = interfaces::MakeHandler(wallet->ShowProgress.connect(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2)));
872 }
873 
875 {
876  // Disconnect signals from wallet
877  m_handler_transaction_changed->disconnect();
878  m_handler_show_progress->disconnect();
879 }
false
Definition: bls_dkg.cpp:151
QString labelForAddress(const QString &address) const
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard, bool cleanRemainderZeros=true)
Format as string.
static QString getAmountColumnTitle(int unit)
Gets title for amount column including current display unit if optionsModel reference available *‍/.
A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,...
Definition: wallet.h:577
std::map< uint256, CWalletTx > mapWallet
Definition: wallet.h:766
RecursiveMutex cs_wallet
Definition: wallet.h:720
boost::signals2::signal< void(CWallet *wallet, const uint256 &hashTx, ChangeType status)> NotifyTransactionChanged
Wallet transaction added, removed or updated.
Definition: wallet.h:1239
boost::signals2::signal< void(const std::string &title, int nProgress)> ShowProgress
Show progress e.g.
Definition: wallet.h:1242
A transaction with a bunch of additional info that only the owner cares about.
Definition: wallet.h:325
int getDisplayUnit()
Definition: optionsmodel.h:74
void displayUnitChanged(int unit)
UI model for a transaction.
bool isMNReward() const
Return true if the tx is a MN reward.
static const int RecommendedNumConfirmations
Number of confirmation recommended for accepting a transaction.
Optional< CAmount > shieldedCredit
bool isCoinStake() const
Return true if the tx is a coinstake.
TransactionStatus status
Status: can change with block chain update.
void updateStatus(const CWalletTx &wtx, int chainHeight)
Update status from core wallet tx.
bool isAnyColdStakingType() const
Return true if the tx is a any cold staking type tx.
bool isNull() const
Return true if the tx hash is null and/or if the size is 0.
static QList< TransactionRecord > decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx)
Decompose CWallet transaction to model transaction records.
bool statusUpdateNeeded(int blockHeight) const
Return whether a status update is needed.
bool involvesWatchAddress
Whether the transaction was sent/received with a watch-only address.
bool countsForBalance
Transaction counts towards available balance.
@ Confirmed
Have 6 or more confirmations (normal tx) or fully mature (mined tx)
@ OpenUntilDate
Normal (sent/received) transactions.
@ Unconfirmed
Not yet mined into a block.
@ Immature
Generated (mined) transactions.
@ Confirming
Confirmed, but waiting for the recommended number of confirmations.
@ NotAccepted
Mined but not accepted.
@ OpenUntilBlock
Transaction not yet final, waiting for block.
@ Conflicted
Conflicts with other transaction or mempool.
qint64 open_for
Timestamp if status==OpenUntilDate, otherwise number of additional blocks that need to be mined befor...
std::string sortKey
Sorting key based on status.
UI model for the transaction table of a wallet.
QVariant txStatusDecoration(const TransactionRecord *wtx) const
TransactionTablePriv * priv
QVariant addressColor(const TransactionRecord *wtx) const
@ SizeRole
Transaction size in bytes.
@ LabelRole
Label of address related to transaction.
@ TypeRole
Type of transaction.
@ StatusRole
Transaction status (TransactionRecord::Status)
@ DateRole
Date and time this transaction was created.
@ TxHashRole
Transaction hash.
@ ShieldedCreditAmountRole
Credit amount of transaction.
@ AddressRole
Address of transaction.
@ WatchonlyDecorationRole
Watch-only icon.
@ WatchonlyRole
Watch-only boolean.
@ AmountRole
Net amount of transaction.
@ ConfirmedRole
Is transaction confirmed?
@ FormattedAmountRole
Formatted amount, without brackets when unconfirmed.
QString formatTxAmount(const TransactionRecord *wtx, bool showUnconfirmed=true, BitcoinUnits::SeparatorStyle separators=BitcoinUnits::separatorStandard) const
QString formatTooltip(const TransactionRecord *rec) const
QVariant data(const QModelIndex &index, int role) const override
void txArrived(const QString &hash, const bool isCoinStake, const bool isMNReward, const bool isCSAnyType)
QVariant txWatchonlyDecoration(const TransactionRecord *wtx) const
void updateTransaction(const QString &hash, int status, bool showTransaction)
QString formatTxStatus(const TransactionRecord *wtx) const
std::unique_ptr< interfaces::Handler > m_handler_transaction_changed
int columnCount(const QModelIndex &parent) const override
void txLoaded(const QString &hash, const int txType, const int txStatus)
TransactionTableModel(CWallet *wallet, WalletModel *parent=nullptr)
std::unique_ptr< interfaces::Handler > m_handler_show_progress
int rowCount(const QModelIndex &parent) const override
void updateAmountColumnTitle()
Updates the column title to "Amount (DisplayUnit)" and emits headerDataChanged() signal for table hea...
QString formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
QString formatTxType(const TransactionRecord *wtx) const
QString lookupAddress(const std::string &address, bool tooltip) const
QVariant txAddressDecoration(const TransactionRecord *wtx) const
QString formatTxDate(const TransactionRecord *wtx) const
qint64 nFirstLoadedTxTime
Time of the oldest transaction loaded into the model.
TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent)
TransactionTableModel * parent
TransactionRecord * index(int cur_block_num, const uint256 &cur_block_hash, int idx)
static ConvertTxToVectorResult convertTxToRecords(TransactionTablePriv *tablePriv, const CWallet *wallet, const std::vector< CWalletTx > &walletTxes)
QList< TransactionRecord > cachedWallet
void updateWallet(const uint256 &hash, int status, bool showTransaction, TransactionRecord &ret)
void emitTxLoaded(const TransactionRecord &rec)
Interface to PIVX wallet from Qt view code.
Definition: walletmodel.h:109
int getLastBlockProcessedNum() const
AddressTableModel * getAddressTableModel()
OptionsModel * getOptionsModel()
uint256 getLastBlockProcessed() const
void SetHex(const char *psz)
Definition: uint256.cpp:31
std::string ToString() const
Definition: uint256.cpp:65
bool IsNull() const
Definition: uint256.h:36
std::string GetHex() const
Definition: uint256.cpp:21
256-bit opaque blob.
Definition: uint256.h:138
const std::string CURRENCY_UNIT
Definition: feerate.cpp:11
std::vector< CWalletTx > getWalletTxs()
Definition: wallet.cpp:175
int64_t GetTxTime() const
Definition: wallet.cpp:1541
const CWalletTx * GetWalletTx(const uint256 &hash) const
Definition: wallet.cpp:166
#define COLOR_CONFLICTED
Definition: guiconstants.h:35
#define COLOR_UNCONFIRMED
Definition: guiconstants.h:25
#define COLOR_TX_STATUS_OPENUNTILDATE
Definition: guiconstants.h:31
#define COLOR_STAKE
Definition: guiconstants.h:39
#define COLOR_ORPHAN
Definition: guiconstants.h:37
#define COLOR_NEGATIVE
Definition: guiconstants.h:27
#define COLOR_BLACK
Definition: guiconstants.h:33
#define COLOR_BAREADDRESS
Definition: guiconstants.h:29
ChangeType
General change type (added, updated, removed).
Definition: guiinterface.h:24
@ CT_UPDATED
Definition: guiinterface.h:26
@ CT_DELETED
Definition: guiinterface.h:27
@ CT_NEW
Definition: guiinterface.h:25
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:69
std::unique_ptr< Handler > MakeHandler(boost::signals2::connection connection)
Return handler wrapping a boost signal connection.
Definition: handler.cpp:26
QList< TransactionRecord > records
TransactionNotification(uint256 hash, ChangeType status)
TransactionNotification()=delete
bool operator()(const TransactionRecord &a, const TransactionRecord &b) const
bool operator()(const TransactionRecord &a, const uint256 &b) const
bool operator()(const uint256 &a, const TransactionRecord &b) const
#define TRY_LOCK(cs, name)
Definition: sync.h:224
#define SINGLE_THREAD_MAX_TXES_SIZE
#define MAX_AMOUNT_LOADED_RECORDS