Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

coincontroldialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2014-2019 The Dash Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <qt/coincontroldialog.h>
7 #include <qt/forms/ui_coincontroldialog.h>
8 
9 #include <qt/addresstablemodel.h>
10 #include <qt/bitcoinunits.h>
11 #include <qt/guiutil.h>
12 #include <qt/optionsmodel.h>
13 #include <txmempool.h>
14 #include <qt/walletmodel.h>
15 
16 #include <wallet/coincontrol.h>
17 #include <init.h>
18 #include <policy/fees.h>
19 #include <policy/policy.h>
20 #include <validation.h> // For mempool
21 #include <wallet/fees.h>
22 #include <wallet/wallet.h>
23 
25 
26 #include <QApplication>
27 #include <QCheckBox>
28 #include <QCursor>
29 #include <QDialogButtonBox>
30 #include <QFlags>
31 #include <QIcon>
32 #include <QSettings>
33 #include <QTreeWidget>
34 
35 QList<CAmount> CoinControlDialog::payAmounts;
38 
39 bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const {
40  int column = treeWidget()->sortColumn();
42  return data(column, Qt::UserRole).toLongLong() < other.data(column, Qt::UserRole).toLongLong();
43  return QTreeWidgetItem::operator<(other);
44 }
45 
47  QDialog(parent),
48  ui(new Ui::CoinControlDialog),
49  model(0)
50 {
51  ui->setupUi(this);
52 
53  GUIUtil::setFont({ui->labelCoinControlQuantityText,
54  ui->labelCoinControlBytesText,
55  ui->labelCoinControlAmountText,
56  ui->labelCoinControlLowOutputText,
57  ui->labelCoinControlFeeText,
58  ui->labelCoinControlAfterFeeText,
59  ui->labelCoinControlChangeText
61 
63 
65 
66  // context menu actions
67  QAction *copyAddressAction = new QAction(tr("Copy address"), this);
68  QAction *copyLabelAction = new QAction(tr("Copy label"), this);
69  QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
70  copyTransactionHashAction = new QAction(tr("Copy transaction ID"), this); // we need to enable/disable this
71  lockAction = new QAction(tr("Lock unspent"), this); // we need to enable/disable this
72  unlockAction = new QAction(tr("Unlock unspent"), this); // we need to enable/disable this
73 
74  // context menu
75  contextMenu = new QMenu(this);
76  contextMenu->addAction(copyAddressAction);
77  contextMenu->addAction(copyLabelAction);
78  contextMenu->addAction(copyAmountAction);
80  contextMenu->addSeparator();
81  contextMenu->addAction(lockAction);
82  contextMenu->addAction(unlockAction);
83 
84  // context menu signals
85  connect(ui->treeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
86  connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
87  connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
88  connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
89  connect(copyTransactionHashAction, SIGNAL(triggered()), this, SLOT(copyTransactionHash()));
90  connect(lockAction, SIGNAL(triggered()), this, SLOT(lockCoin()));
91  connect(unlockAction, SIGNAL(triggered()), this, SLOT(unlockCoin()));
92 
93  // clipboard actions
94  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
95  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
96  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
97  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
98  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
99  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
100  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
101 
102  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(clipboardQuantity()));
103  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(clipboardAmount()));
104  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(clipboardFee()));
105  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(clipboardAfterFee()));
106  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(clipboardBytes()));
107  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(clipboardLowOutput()));
108  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(clipboardChange()));
109 
110  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
111  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
112  ui->labelCoinControlFee->addAction(clipboardFeeAction);
113  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
114  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
115  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
116  ui->labelCoinControlChange->addAction(clipboardChangeAction);
117 
118  // toggle tree/list mode
119  connect(ui->radioTreeMode, SIGNAL(toggled(bool)), this, SLOT(radioTreeMode(bool)));
120  connect(ui->radioListMode, SIGNAL(toggled(bool)), this, SLOT(radioListMode(bool)));
121 
122  // click on checkbox
123  connect(ui->treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(viewItemChanged(QTreeWidgetItem*, int)));
124 
125  // click on header
126 #if QT_VERSION < 0x050000
127  ui->treeWidget->header()->setClickable(true);
128 #else
129  ui->treeWidget->header()->setSectionsClickable(true);
130 #endif
131  connect(ui->treeWidget->header(), SIGNAL(sectionClicked(int)), this, SLOT(headerSectionClicked(int)));
132 
133  // ok button
134  connect(ui->buttonBox, SIGNAL(clicked( QAbstractButton*)), this, SLOT(buttonBoxClicked(QAbstractButton*)));
135 
136  // (un)select all
137  connect(ui->pushButtonSelectAll, SIGNAL(clicked()), this, SLOT(buttonSelectAllClicked()));
138 
139  // Toggle lock state
140  connect(ui->pushButtonToggleLock, SIGNAL(clicked()), this, SLOT(buttonToggleLockClicked()));
141 
142  // change coin control first column label due Qt4 bug.
143  // see https://github.com/bitcoin/bitcoin/issues/5716
144  ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString());
145 
146  ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 94);
147  ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 100);
148  ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170);
149  ui->treeWidget->setColumnWidth(COLUMN_PRIVATESEND_ROUNDS, 110);
150  ui->treeWidget->setColumnWidth(COLUMN_DATE, 120);
151  ui->treeWidget->setColumnWidth(COLUMN_CONFIRMATIONS, 110);
152 
153  ui->treeWidget->header()->setStretchLastSection(false);
154  ui->treeWidget->header()->setSectionResizeMode(COLUMN_ADDRESS, QHeaderView::Stretch);
155 
156  // default view is sorted by amount desc
157  sortView(COLUMN_AMOUNT, Qt::DescendingOrder);
158 
159  // restore list mode and sortorder as a convenience feature
160  QSettings settings;
161  if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool())
162  ui->radioTreeMode->click();
163  if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
164  sortView(settings.value("nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt()));
165 }
166 
168 {
169  QSettings settings;
170  settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
171  settings.setValue("nCoinControlSortColumn", sortColumn);
172  settings.setValue("nCoinControlSortOrder", (int)sortOrder);
173 
174  delete ui;
175 }
176 
178 {
179  this->model = _model;
180 
181  if(_model && _model->getOptionsModel() && _model->getAddressTableModel())
182  {
183  updateView();
185  CoinControlDialog::updateLabels(_model, this);
186  }
187 }
188 
189 // ok button
190 void CoinControlDialog::buttonBoxClicked(QAbstractButton* button)
191 {
192  if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole)
193  done(QDialog::Accepted); // closes the dialog
194 }
195 
196 // (un)select all
198 {
199  Qt::CheckState state = Qt::Checked;
200  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
201  {
202  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != Qt::Unchecked)
203  {
204  state = Qt::Unchecked;
205  break;
206  }
207  }
208  ui->treeWidget->setEnabled(false);
209  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++)
210  if (ui->treeWidget->topLevelItem(i)->checkState(COLUMN_CHECKBOX) != state)
211  ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state);
212  ui->treeWidget->setEnabled(true);
213  if (state == Qt::Unchecked)
214  coinControl()->UnSelectAll(); // just to be sure
216 }
217 
218 // Toggle lock state
220 {
221  QTreeWidgetItem *item;
222  // Works in list-mode only
223  if(ui->radioListMode->isChecked()){
224  ui->treeWidget->setEnabled(false);
225  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++){
226  item = ui->treeWidget->topLevelItem(i);
227  COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
228  // Don't toggle the lock state of partially mixed coins if they are not hidden in PrivateSend mode
230  continue;
231  }
232  if (model->isLockedCoin(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())){
233  model->unlockCoin(outpt);
234  item->setDisabled(false);
235  item->setIcon(COLUMN_CHECKBOX, QIcon());
236  }
237  else{
238  model->lockCoin(outpt);
239  item->setDisabled(true);
240  item->setIcon(COLUMN_CHECKBOX, GUIUtil::getIcon("lock_closed", GUIUtil::ThemedColor::RED));
241  }
243  }
244  ui->treeWidget->setEnabled(true);
246  }
247  else{
248  QMessageBox msgBox(this);
249  msgBox.setObjectName("lockMessageBox");
250  msgBox.setText(tr("Please switch to \"List mode\" to use this function."));
251  msgBox.exec();
252  }
253 }
254 
255 // context menu
256 void CoinControlDialog::showMenu(const QPoint &point)
257 {
258  QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
259  if(item)
260  {
261  contextMenuItem = item;
262 
263  // disable some items (like Copy Transaction ID, lock, unlock) for tree roots in context menu
264  if (item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
265  {
266  copyTransactionHashAction->setEnabled(true);
267  if (model->isLockedCoin(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt()))
268  {
269  lockAction->setEnabled(false);
270  unlockAction->setEnabled(true);
271  }
272  else
273  {
274  lockAction->setEnabled(true);
275  unlockAction->setEnabled(false);
276  }
277  }
278  else // this means click on parent node in tree mode -> disable all
279  {
280  copyTransactionHashAction->setEnabled(false);
281  lockAction->setEnabled(false);
282  unlockAction->setEnabled(false);
283  }
284 
285  // show context menu
286  contextMenu->exec(QCursor::pos());
287  }
288 }
289 
290 // context menu action: copy amount
292 {
294 }
295 
296 // context menu action: copy label
298 {
299  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_LABEL).length() == 0 && contextMenuItem->parent())
301  else
303 }
304 
305 // context menu action: copy address
307 {
308  if (ui->radioTreeMode->isChecked() && contextMenuItem->text(COLUMN_ADDRESS).length() == 0 && contextMenuItem->parent())
310  else
312 }
313 
314 // context menu action: copy transaction id
316 {
318 }
319 
320 // context menu action: lock coin
322 {
323  if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked)
324  contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
325 
326  COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
327  model->lockCoin(outpt);
328  contextMenuItem->setDisabled(true);
331 }
332 
333 // context menu action: unlock coin
335 {
336  COutPoint outpt(uint256S(contextMenuItem->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), contextMenuItem->data(COLUMN_ADDRESS, VOutRole).toUInt());
337  model->unlockCoin(outpt);
338  contextMenuItem->setDisabled(false);
339  contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon());
341 }
342 
343 // copy label "Quantity" to clipboard
345 {
346  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
347 }
348 
349 // copy label "Amount" to clipboard
351 {
352  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
353 }
354 
355 // copy label "Fee" to clipboard
357 {
358  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
359 }
360 
361 // copy label "After fee" to clipboard
363 {
364  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
365 }
366 
367 // copy label "Bytes" to clipboard
369 {
370  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
371 }
372 
373 // copy label "Dust" to clipboard
375 {
376  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
377 }
378 
379 // copy label "Change" to clipboard
381 {
382  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
383 }
384 
385 // treeview: sort
386 void CoinControlDialog::sortView(int column, Qt::SortOrder order)
387 {
388  sortColumn = column;
389  sortOrder = order;
390  ui->treeWidget->sortItems(column, order);
391  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
392 }
393 
394 // treeview: clicked on header
396 {
397  if (logicalIndex == COLUMN_CHECKBOX) // click on most left column -> do nothing
398  {
399  ui->treeWidget->header()->setSortIndicator(sortColumn, sortOrder);
400  }
401  else
402  {
403  if (sortColumn == logicalIndex)
404  sortOrder = ((sortOrder == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder);
405  else
406  {
407  sortColumn = logicalIndex;
408  sortOrder = ((sortColumn == COLUMN_LABEL || sortColumn == COLUMN_ADDRESS) ? Qt::AscendingOrder : Qt::DescendingOrder); // if label or address then default => asc, else default => desc
409  }
410 
412  }
413 }
414 
415 // toggle tree mode
417 {
418  if (checked && model)
419  updateView();
420 }
421 
422 // toggle list mode
424 {
425  if (checked && model)
426  updateView();
427 }
428 
429 // checkbox clicked by user
430 void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
431 {
432  if (column == COLUMN_CHECKBOX && item->data(COLUMN_ADDRESS, TxHashRole).toString().length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode)
433  {
434  COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt());
435 
436  if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked)
437  coinControl()->UnSelect(outpt);
438  else if (item->isDisabled()) // locked (this happens if "check all" through parent node)
439  item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
440  else {
441  coinControl()->Select(outpt);
442  }
443 
444  // selection changed -> update labels
445  if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all
447  }
448 
449  // TODO: Remove this temporary qt5 fix after Qt5.3 and Qt5.4 are no longer used.
450  // Fixed in Qt5.5 and above: https://bugreports.qt.io/browse/QTBUG-43473
451 #if QT_VERSION >= 0x050000
452  else if (column == COLUMN_CHECKBOX && item->childCount() > 0)
453  {
454  if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
455  item->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
456  }
457 #endif
458 }
459 
460 // shows count of locked unspent outputs
462 {
463  std::vector<COutPoint> vOutpts;
464  model->listLockedCoins(vOutpts);
465  if (vOutpts.size() > 0)
466  {
467  ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size()));
468  ui->labelLocked->setVisible(true);
469  }
470  else ui->labelLocked->setVisible(false);
471 }
472 
474 {
476  updateView();
478 }
479 
480 void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
481 {
482  if (!model)
483  return;
484 
485  // nPayAmount
486  CAmount nPayAmount = 0;
487  bool fDust = false;
488  CMutableTransaction txDummy;
489  for (const CAmount &amount : CoinControlDialog::payAmounts)
490  {
491  nPayAmount += amount;
492 
493  if (amount > 0)
494  {
495  CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0));
496  txDummy.vout.push_back(txout);
497  fDust |= IsDust(txout, ::dustRelayFee);
498  }
499  }
500 
501  CAmount nAmount = 0;
502  CAmount nPayFee = 0;
503  CAmount nAfterFee = 0;
504  CAmount nChange = 0;
505  unsigned int nBytes = 0;
506  unsigned int nBytesInputs = 0;
507  unsigned int nQuantity = 0;
508  int nQuantityUncompressed = 0;
509  bool fUnselectedSpent{false};
510  bool fUnselectedNonMixed{false};
511 
512  std::vector<COutPoint> vCoinControl;
513  std::vector<COutput> vOutputs;
514  coinControl()->ListSelected(vCoinControl);
515  model->getOutputs(vCoinControl, vOutputs);
516 
517  for (const COutput& out : vOutputs) {
518  // unselect already spent, very unlikely scenario, this could happen
519  // when selected are spent elsewhere, like rpc or another computer
520  uint256 txhash = out.tx->GetHash();
521  COutPoint outpt(txhash, out.i);
522  if (model->isSpent(outpt))
523  {
524  coinControl()->UnSelect(outpt);
525  fUnselectedSpent = true;
526  continue;
527  }
528 
529  // Quantity
530  nQuantity++;
531 
532  // Amount
533  nAmount += out.tx->tx->vout[out.i].nValue;
534 
535  // Bytes
536  CTxDestination address;
537  if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address))
538  {
539  CPubKey pubkey;
540  CKeyID *keyid = boost::get<CKeyID>(&address);
541  if (keyid && model->getPubKey(*keyid, pubkey))
542  {
543  nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
544  }
545  else
546  nBytesInputs += 148; // in all error cases, simply assume 148 here
547  }
548  else nBytesInputs += 148;
549  }
550 
551  // calculation
552  if (nQuantity > 0)
553  {
554  // Bytes
555  nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
556 
557  // in the subtract fee from amount case, we can tell if zero change already and subtract the bytes, so that fee calculation afterwards is accurate
559  if (nAmount - nPayAmount == 0)
560  nBytes -= 34;
561 
562  // Fee
563  nPayFee = GetMinimumFee(nBytes, *coinControl(), ::mempool, ::feeEstimator, nullptr /* FeeCalculation */);
564 
565  if (nPayAmount > 0)
566  {
567  nChange = nAmount - nPayAmount;
568 
569  // PrivateSend Fee = overpay
570  if(coinControl()->IsUsingPrivateSend() && nChange > 0)
571  {
572  nPayFee = std::max(nChange, nPayFee);
573  nChange = 0;
575  nBytes -= 34; // we didn't detect lack of change above
577  nChange -= nPayFee;
578  }
579 
580  // Never create dust outputs; if we would, just add the dust to the fee.
581  if (nChange > 0 && nChange < MIN_CHANGE)
582  {
583  CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
584  if (IsDust(txout, ::dustRelayFee))
585  {
586  nPayFee += nChange;
587  nChange = 0;
589  nBytes -= 34; // we didn't detect lack of change above
590  }
591  }
592 
593  if (nChange == 0 && !CoinControlDialog::fSubtractFeeFromAmount)
594  nBytes -= 34;
595  }
596 
597  // after fee
598  nAfterFee = std::max<CAmount>(nAmount - nPayFee, 0);
599  }
600 
601  // actually update labels
602  int nDisplayUnit = BitcoinUnits::DASH;
603  if (model && model->getOptionsModel())
604  nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
605 
606  QLabel *l1 = dialog->findChild<QLabel *>("labelCoinControlQuantity");
607  QLabel *l2 = dialog->findChild<QLabel *>("labelCoinControlAmount");
608  QLabel *l3 = dialog->findChild<QLabel *>("labelCoinControlFee");
609  QLabel *l4 = dialog->findChild<QLabel *>("labelCoinControlAfterFee");
610  QLabel *l5 = dialog->findChild<QLabel *>("labelCoinControlBytes");
611  QLabel *l7 = dialog->findChild<QLabel *>("labelCoinControlLowOutput");
612  QLabel *l8 = dialog->findChild<QLabel *>("labelCoinControlChange");
613 
614  // enable/disable "dust" and "change"
615  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setEnabled(nPayAmount > 0);
616  dialog->findChild<QLabel *>("labelCoinControlLowOutput") ->setEnabled(nPayAmount > 0);
617  dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setEnabled(nPayAmount > 0);
618  dialog->findChild<QLabel *>("labelCoinControlChange") ->setEnabled(nPayAmount > 0);
619 
620  // stats
621  l1->setText(QString::number(nQuantity)); // Quantity
622  l2->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAmount)); // Amount
623  l3->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nPayFee)); // Fee
624  l4->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nAfterFee)); // After Fee
625  l5->setText(((nBytes > 0) ? ASYMP_UTF8 : "") + QString::number(nBytes)); // Bytes
626  l7->setText(fDust ? tr("yes") : tr("no")); // Dust
627  l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
628  if (nPayFee > 0)
629  {
630  l3->setText(ASYMP_UTF8 + l3->text());
631  l4->setText(ASYMP_UTF8 + l4->text());
632  if (nChange > 0 && !CoinControlDialog::fSubtractFeeFromAmount)
633  l8->setText(ASYMP_UTF8 + l8->text());
634  }
635 
636  // turn label red when dust
637  l7->setStyleSheet((fDust) ? GUIUtil::getThemedStyleQString(GUIUtil::ThemedStyle::TS_ERROR) : "");
638 
639  // tool tips
640  QString toolTipDust = tr("This label turns red if any recipient receives an amount smaller than the current dust threshold.");
641 
642  // how many satoshis the estimated fee can vary per byte we guess wrong
643  double dFeeVary = (nBytes != 0) ? (double)nPayFee / nBytes : 0;
644 
645  QString toolTip4 = tr("Can vary +/- %1 duff(s) per input.").arg(dFeeVary);
646 
647  l3->setToolTip(toolTip4);
648  l4->setToolTip(toolTip4);
649  l7->setToolTip(toolTipDust);
650  l8->setToolTip(toolTip4);
651  dialog->findChild<QLabel *>("labelCoinControlFeeText") ->setToolTip(l3->toolTip());
652  dialog->findChild<QLabel *>("labelCoinControlAfterFeeText") ->setToolTip(l4->toolTip());
653  dialog->findChild<QLabel *>("labelCoinControlBytesText") ->setToolTip(l5->toolTip());
654  dialog->findChild<QLabel *>("labelCoinControlLowOutputText")->setToolTip(l7->toolTip());
655  dialog->findChild<QLabel *>("labelCoinControlChangeText") ->setToolTip(l8->toolTip());
656 
657  // Insufficient funds
658  QLabel *label = dialog->findChild<QLabel *>("labelCoinControlInsuffFunds");
659  if (label)
660  label->setVisible(nChange < 0);
661 
662  // Warn about unselected coins
663  if (fUnselectedSpent) {
664  QMessageBox::warning(dialog, "CoinControl",
665  tr("Some coins were unselected because they were spent."),
666  QMessageBox::Ok, QMessageBox::Ok);
667  } else if (fUnselectedNonMixed) {
668  QMessageBox::warning(dialog, "CoinControl",
669  tr("Some coins were unselected because they do not have enough mixing rounds."),
670  QMessageBox::Ok, QMessageBox::Ok);
671  }
672 }
673 
674 void CoinControlDialog::usePrivateSend(bool fUsePrivateSend)
675 {
677 }
678 
680 {
682  static CCoinControl coinControlNormal;
683  coinControlNormal.UsePrivateSend(false);
684  return &coinControlNormal;
685  } else {
686  static CCoinControl coinControlPrivateSend;
687  coinControlPrivateSend.UsePrivateSend(true);
688  return &coinControlPrivateSend;
689  }
690 }
691 
693 {
695  return;
696 
697  bool fNormalMode = mode == Mode::NORMAL;
698  ui->treeWidget->setColumnHidden(COLUMN_PRIVATESEND_ROUNDS, fNormalMode);
699  ui->treeWidget->setColumnHidden(COLUMN_LABEL, !fNormalMode);
700  ui->radioTreeMode->setVisible(fNormalMode);
701  ui->radioListMode->setVisible(fNormalMode);
702 
704  fHideAdditional = false;
705  ui->hideButton->setVisible(false);
706  }
707 
708  QString strHideButton;
709  switch (mode) {
710  case Mode::NORMAL:
711  if (fHideAdditional) {
712  strHideButton = tr("Show all coins");
713  } else {
714  strHideButton = tr("Hide PrivateSend coins");
715  }
716  break;
717  case Mode::PRIVATESEND:
718  if (fHideAdditional) {
719  strHideButton = tr("Show all PrivateSend coins");
720  } else {
721  strHideButton = tr("Show spendable coins only");
722  }
723  ui->radioListMode->setChecked(true);
724  break;
725  }
726  ui->hideButton->setText(strHideButton);
727 
728  bool treeMode = ui->radioTreeMode->isChecked();
729  ui->treeWidget->clear();
730  ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox
731  ui->treeWidget->setAlternatingRowColors(!treeMode);
732  QFlags<Qt::ItemFlag> flgCheckbox = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
733  QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
734 
735  int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
736 
737  std::map<QString, std::vector<COutput> > mapCoins;
738  model->listCoins(mapCoins);
739 
740  for (const std::pair<QString, std::vector<COutput>>& coins : mapCoins) {
741  CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem();
742  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
743  QString sWalletAddress = coins.first;
744  QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress);
745  if (sWalletLabel.isEmpty())
746  sWalletLabel = tr("(no label)");
747 
748  if (treeMode)
749  {
750  // wallet address
751  ui->treeWidget->addTopLevelItem(itemWalletAddress);
752 
753  itemWalletAddress->setFlags(flgTristate);
754  itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
755 
756  // label
757  itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel);
758  itemWalletAddress->setToolTip(COLUMN_LABEL, sWalletLabel);
759 
760  // address
761  itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress);
762  itemWalletAddress->setToolTip(COLUMN_ADDRESS, sWalletAddress);
763  }
764 
765  CAmount nSum = 0;
766  int nChildren = 0;
767  for (const COutput& out : coins.second) {
768  COutPoint outpoint = COutPoint(out.tx->tx->GetHash(), out.i);
769  bool fFullyMixed{false};
770  CAmount nAmount = out.tx->tx->vout[out.i].nValue;
771 
772  bool fPrivateSendAmount = CPrivateSend::IsDenominatedAmount(nAmount) || CPrivateSend::IsCollateralAmount(nAmount);
773 
774  if (coinControl()->IsUsingPrivateSend()) {
775  fFullyMixed = model->isFullyMixed(outpoint);
776  if ((fHideAdditional && !fFullyMixed) || (!fHideAdditional && !fPrivateSendAmount)) {
777  coinControl()->UnSelect(outpoint);
778  continue;
779  }
780  } else {
781  if (fHideAdditional && fPrivateSendAmount) {
782  coinControl()->UnSelect(outpoint);
783  continue;
784  }
785  }
786 
787  nSum += out.tx->tx->vout[out.i].nValue;
788  nChildren++;
789 
790  CCoinControlWidgetItem* itemOutput;
791  if (treeMode) {
792  itemOutput = new CCoinControlWidgetItem(itemWalletAddress);
793  } else {
794  itemOutput = new CCoinControlWidgetItem(ui->treeWidget);
795  }
796  itemOutput->setFlags(flgCheckbox);
797  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked);
798 
799  // address
800  CTxDestination outputAddress;
801  QString sAddress = "";
802  if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) {
803  sAddress = QString::fromStdString(EncodeDestination(outputAddress));
804 
805  // if listMode or change => show dash address. In tree mode, address is not shown again for direct wallet address outputs
806  if (!treeMode || (!(sAddress == sWalletAddress))) {
807  itemOutput->setText(COLUMN_ADDRESS, sAddress);
808  }
809 
810  itemOutput->setToolTip(COLUMN_ADDRESS, sAddress);
811  }
812 
813  // label
814  if (!(sAddress == sWalletAddress)) { //change
815  // tooltip from where the change comes from
816  itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress));
817  itemOutput->setText(COLUMN_LABEL, tr("(change)"));
818  } else if (!treeMode) {
819  QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress);
820  if (sLabel.isEmpty()) {
821  sLabel = tr("(no label)");
822  }
823  itemOutput->setText(COLUMN_LABEL, sLabel);
824  }
825 
826  // amount
827  itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
828  itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue));
829  itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly
830 
831  // date
832  itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
833  itemOutput->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime()));
834  itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.tx->GetTxTime()));
835 
836  // PrivateSend rounds
837  int nRounds = model->getRealOutpointPrivateSendRounds(outpoint);
838  if (nRounds >= 0 || LogAcceptCategory(BCLog::PRIVATESEND)) {
839  itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, QString::number(nRounds));
840  } else {
841  itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, tr("n/a"));
842  }
843  itemOutput->setData(COLUMN_PRIVATESEND_ROUNDS, Qt::UserRole, QVariant((qlonglong)nRounds));
844 
845  // confirmations
846  itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.nDepth));
847  itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.nDepth));
848 
849  // transaction hash
850  uint256 txhash = out.tx->GetHash();
851  itemOutput->setData(COLUMN_ADDRESS, TxHashRole, QString::fromStdString(txhash.GetHex()));
852 
853  // vout index
854  itemOutput->setData(COLUMN_ADDRESS, VOutRole, out.i);
855 
856  // disable locked coins
857  if (model->isLockedCoin(txhash, out.i)) {
858  COutPoint outpt(txhash, out.i);
859  coinControl()->UnSelect(outpt); // just to be sure
860  itemOutput->setDisabled(true);
861  itemOutput->setIcon(COLUMN_CHECKBOX, GUIUtil::getIcon("lock_closed", GUIUtil::ThemedColor::RED));
862  }
863 
864  // set checkbox
865  if (coinControl()->IsSelected(COutPoint(txhash, out.i))) {
866  itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked);
867  }
868 
869  if (coinControl()->IsUsingPrivateSend() && !fHideAdditional && !fFullyMixed) {
870  itemOutput->setDisabled(true);
871  }
872  }
873 
874  // amount
875  if (treeMode)
876  {
877  itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
878  itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
879  itemWalletAddress->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
880  itemWalletAddress->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)nSum));
881  }
882  }
883 
884  // expand all partially selected and hide the empty
885  if (treeMode)
886  {
887  for (int i = 0; i < ui->treeWidget->topLevelItemCount(); i++) {
888  QTreeWidgetItem* topLevelItem = ui->treeWidget->topLevelItem(i);
889  topLevelItem->setHidden(topLevelItem->childCount() == 0);
890  if (topLevelItem->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked)
891  topLevelItem->setExpanded(true);
892  }
893  }
894 
895  // sort view
897  ui->treeWidget->setEnabled(true);
898 }
static CoinControlDialog::Mode mode
uint8_t data[WIDTH]
Definition: uint256.h:24
bool isLockedCoin(uint256 hash, unsigned int n) const
void listCoins(std::map< QString, std::vector< COutput > > &mapCoins) const
void viewItemChanged(QTreeWidgetItem *, int)
CTxMemPool mempool
void UsePrivateSend(bool fUsePrivateSend)
Definition: coincontrol.h:103
void getOutputs(const std::vector< COutPoint > &vOutpoints, std::vector< COutput > &vOutputs)
int i
Definition: wallet.h:570
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:80
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:158
void lockCoin(COutPoint &output)
static bool IsDenominatedAmount(CAmount nInputAmount)
void setFont(const std::vector< QWidget *> &vecWidgets, FontWeight weight, int nPointSize, bool fItalic)
Workaround to set correct font styles in all themes since there is a bug in macOS which leads to issu...
Definition: guiutil.cpp:1552
const uint256 & GetHash() const
Definition: wallet.h:272
void ListSelected(std::vector< COutPoint > &vOutpoints) const
Definition: coincontrol.h:96
QString dateTimeStr(const QDateTime &date)
Definition: guiutil.cpp:258
static void usePrivateSend(bool fUsePrivateSend)
AddressTableModel * getAddressTableModel()
void UnSelect(const COutPoint &output)
Definition: coincontrol.h:86
#define ASYMP_UTF8
Coin Control Features.
Definition: coincontrol.h:28
static bool LogAcceptCategory(uint64_t category)
Definition: util.h:152
int getDisplayUnit() const
Definition: optionsmodel.h:82
static const CAmount MIN_CHANGE
target minimum change amount
Definition: wallet.h:66
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
CBlockPolicyEstimator feeEstimator
Definition: validation.cpp:249
bool done
static QList< CAmount > payAmounts
void updateFonts()
Update the font of all widgets where a custom font has been set with GUIUtil::setFont.
Definition: guiutil.cpp:1563
bool operator<(const QTreeWidgetItem &other) const
bool getPubKey(const CKeyID &address, CPubKey &vchPubKeyOut) const
QAction * copyTransactionHashAction
int nDepth
Definition: wallet.h:571
Ui::CoinControlDialog * ui
CTransactionRef tx
Definition: wallet.h:210
void setClipboard(const QString &str)
Definition: guiutil.cpp:1817
void Select(const COutPoint &output)
Definition: coincontrol.h:81
uint256 uint256S(const char *str)
Definition: uint256.h:143
An encapsulated public key.
Definition: pubkey.h:30
QString labelForAddress(const QString &address) const
CoinControlDialog(QWidget *parent=0)
QIcon getIcon(const QString &strIcon, const ThemedColor color, const ThemedColor colorAlternative, const QString &strIconPath)
Helper to get an icon colorized with the given color (replaces black) and colorAlternative (replaces ...
Definition: guiutil.cpp:216
bool IsUsingPrivateSend() const
Definition: coincontrol.h:108
bool isSpent(const COutPoint &outpoint) const
static void updateLabels(WalletModel *, QDialog *)
friend class CCoinControlWidgetItem
An output of a transaction.
Definition: transaction.h:144
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:26
std::vector< CTxOut > vout
Definition: transaction.h:294
void UnSelectAll()
Definition: coincontrol.h:91
void disableMacFocusRect(const QWidget *w)
Disable the OS default focus rect for macOS because we have custom focus rects set in the css files...
Definition: guiutil.cpp:1789
256-bit opaque blob.
Definition: uint256.h:123
void setModel(WalletModel *model)
static bool fSubtractFeeFromAmount
QTreeWidgetItem * contextMenuItem
std::string EncodeDestination(const CTxDestination &dest)
Definition: base58.cpp:329
CPrivateSendClientManager privateSendClient
void listLockedCoins(std::vector< COutPoint > &vOutpts)
int64_t GetTxTime() const
Definition: wallet.cpp:1923
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:389
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:100
int getRealOutpointPrivateSendRounds(const COutPoint &outpoint) const
static QString removeSpaces(QString text)
Definition: bitcoinunits.h:117
void unlockCoin(COutPoint &output)
QString getThemedStyleQString(ThemedStyle style)
Helper to get css style strings which are injected into rich text through qt.
Definition: guiutil.cpp:210
A reference to a CKey: the Hash160 of its serialized public key.
Definition: pubkey.h:20
const CWalletTx * tx
Definition: wallet.h:569
std::string GetHex() const
Definition: uint256.cpp:21
void sortView(int, Qt::SortOrder)
Qt::SortOrder sortOrder
static bool IsCollateralAmount(CAmount nInputAmount)
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:35
A mutable version of CTransaction.
Definition: transaction.h:291
void buttonBoxClicked(QAbstractButton *)
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
WalletModel * model
void showMenu(const QPoint &)
CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl &coin_control, const CTxMemPool &pool, const CBlockPolicyEstimator &estimator, FeeCalculation *feeCalc)
Estimate the minimum fee considering user set parameters and the required fee.
Definition: fees.cpp:21
bool operator<(const vote_time_pair_t &p1, const vote_time_pair_t &p2)
bool isFullyMixed(const COutPoint &outpoint) const
static QString format(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
OptionsModel * getOptionsModel()
static CCoinControl * coinControl()
bool IsCompressed() const
Check whether this is a compressed public key.
Definition: pubkey.h:174
CFeeRate dustRelayFee
Definition: policy.cpp:177
Released under the MIT license