Dash Core  0.12.2.1
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2014-2017 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 "sendcoinsdialog.h"
7 #include "ui_sendcoinsdialog.h"
8 
9 #include "addresstablemodel.h"
10 #include "bitcoinunits.h"
11 #include "clientmodel.h"
12 #include "coincontroldialog.h"
13 #include "guiutil.h"
14 #include "optionsmodel.h"
15 #include "platformstyle.h"
16 #include "sendcoinsentry.h"
17 #include "walletmodel.h"
18 
19 #include "base58.h"
20 #include "coincontrol.h"
21 #include "validation.h" // mempool and minRelayTxFee
22 #include "ui_interface.h"
23 #include "txmempool.h"
24 #include "wallet/wallet.h"
25 
26 #include "privatesend.h"
27 
28 #include <QMessageBox>
29 #include <QScrollBar>
30 #include <QSettings>
31 #include <QTextDocument>
32 
33 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) :
34  QDialog(parent),
35  ui(new Ui::SendCoinsDialog),
36  clientModel(0),
37  model(0),
38  fNewRecipientAllowed(true),
39  fFeeMinimized(true),
40  platformStyle(platformStyle)
41 {
42  ui->setupUi(this);
43  QString theme = GUIUtil::getThemeName();
44 
46  ui->addButton->setIcon(QIcon());
47  ui->clearButton->setIcon(QIcon());
48  ui->sendButton->setIcon(QIcon());
49  } else {
50  ui->addButton->setIcon(QIcon(":/icons/" + theme + "/add"));
51  ui->clearButton->setIcon(QIcon(":/icons/" + theme + "/remove"));
52  ui->sendButton->setIcon(QIcon(":/icons/" + theme + "/send"));
53  }
54 
56 
57  addEntry();
58 
59  connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
60  connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
61 
62  // Coin Control
63  connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
64  connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
65  connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
66 
67  // Dash specific
68  QSettings settings;
69  if (!settings.contains("bUseDarkSend"))
70  settings.setValue("bUseDarkSend", false);
71  if (!settings.contains("bUseInstantX"))
72  settings.setValue("bUseInstantX", false);
73 
74  bool fUsePrivateSend = settings.value("bUseDarkSend").toBool();
75  bool fUseInstantSend = settings.value("bUseInstantX").toBool();
76  if(fLiteMode) {
77  ui->checkUsePrivateSend->setChecked(false);
78  ui->checkUsePrivateSend->setVisible(false);
79  ui->checkUseInstantSend->setVisible(false);
82  }
83  else{
84  ui->checkUsePrivateSend->setChecked(fUsePrivateSend);
85  ui->checkUseInstantSend->setChecked(fUseInstantSend);
88  }
89 
90  connect(ui->checkUsePrivateSend, SIGNAL(stateChanged ( int )), this, SLOT(updateDisplayUnit()));
91  connect(ui->checkUseInstantSend, SIGNAL(stateChanged ( int )), this, SLOT(updateInstantSend()));
92 
93  // Coin Control: 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  connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
102  connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
103  connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
104  connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
105  connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
106  connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
107  connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
108  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
109  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
110  ui->labelCoinControlFee->addAction(clipboardFeeAction);
111  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
112  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
113  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
114  ui->labelCoinControlChange->addAction(clipboardChangeAction);
115 
116  // init transaction fee section
117  if (!settings.contains("fFeeSectionMinimized"))
118  settings.setValue("fFeeSectionMinimized", true);
119  if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
120  settings.setValue("nFeeRadio", 1); // custom
121  if (!settings.contains("nFeeRadio"))
122  settings.setValue("nFeeRadio", 0); // recommended
123  if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
124  settings.setValue("nCustomFeeRadio", 1); // total at least
125  if (!settings.contains("nCustomFeeRadio"))
126  settings.setValue("nCustomFeeRadio", 0); // per kilobyte
127  if (!settings.contains("nSmartFeeSliderPosition"))
128  settings.setValue("nSmartFeeSliderPosition", 0);
129  if (!settings.contains("nTransactionFee"))
130  settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
131  if (!settings.contains("fPayOnlyMinFee"))
132  settings.setValue("fPayOnlyMinFee", false);
133  if (!settings.contains("fSendFreeTransactions"))
134  settings.setValue("fSendFreeTransactions", false);
135 
136  ui->groupFee->setId(ui->radioSmartFee, 0);
137  ui->groupFee->setId(ui->radioCustomFee, 1);
138  ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
140  ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
141  ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
142  ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
143  ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
144  ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
145  ui->checkBoxFreeTx->setChecked(settings.value("fSendFreeTransactions").toBool());
146  minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
147 }
148 
150 {
151  this->clientModel = clientModel;
152 
153  if (clientModel) {
154  connect(clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
155  }
156 }
157 
159 {
160  this->model = model;
161 
162  if(model && model->getOptionsModel())
163  {
164  for(int i = 0; i < ui->entries->count(); ++i)
165  {
166  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
167  if(entry)
168  {
169  entry->setModel(model);
170  }
171  }
172 
176  connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
178 
179  // Coin Control
180  connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
181  connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
184 
185  // fee section
186  connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
187  connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
188  connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
189  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
190  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
191  connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
192  connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
193  connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
194  connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
195  connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
196  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
197  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
198  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
199  connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
200  connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
201  connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
207  }
208 }
209 
211 {
212  QSettings settings;
213  settings.setValue("fFeeSectionMinimized", fFeeMinimized);
214  settings.setValue("nFeeRadio", ui->groupFee->checkedId());
215  settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
216  settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
217  settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
218  settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
219  settings.setValue("fSendFreeTransactions", ui->checkBoxFreeTx->isChecked());
220 
221  delete ui;
222 }
223 
225 {
226  if(!model || !model->getOptionsModel())
227  return;
228 
229  QList<SendCoinsRecipient> recipients;
230  bool valid = true;
231 
232  for(int i = 0; i < ui->entries->count(); ++i)
233  {
234  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
235  if(entry)
236  {
237  if(entry->validate())
238  {
239  recipients.append(entry->getValue());
240  }
241  else
242  {
243  valid = false;
244  }
245  }
246  }
247 
248  if(!valid || recipients.isEmpty())
249  {
250  return;
251  }
252 
253  QString strFunds = tr("using") + " <b>" + tr("anonymous funds") + "</b>";
254  QString strFee = "";
255  recipients[0].inputType = ONLY_DENOMINATED;
256 
257  if(ui->checkUsePrivateSend->isChecked()) {
258  recipients[0].inputType = ONLY_DENOMINATED;
259  strFunds = tr("using") + " <b>" + tr("anonymous funds") + "</b>";
260  QString strNearestAmount(
263  strFee = QString(tr(
264  "(privatesend requires this amount to be rounded up to the nearest %1)."
265  ).arg(strNearestAmount));
266  } else {
267  recipients[0].inputType = ALL_COINS;
268  strFunds = tr("using") + " <b>" + tr("any available funds (not anonymous)") + "</b>";
269  }
270 
271  if(ui->checkUseInstantSend->isChecked()) {
272  recipients[0].fUseInstantSend = true;
273  strFunds += " ";
274  strFunds += tr("and InstantSend");
275  } else {
276  recipients[0].fUseInstantSend = false;
277  }
278 
279 
280  fNewRecipientAllowed = false;
281  // request unlock only if was locked or unlocked for mixing:
282  // this way we let users unlock by walletpassphrase or by menu
283  // and make many transactions while unlocking through this dialog
284  // will call relock
286  if(encStatus == model->Locked || encStatus == model->UnlockedForMixingOnly)
287  {
289  if(!ctx.isValid())
290  {
291  // Unlock wallet was cancelled
292  fNewRecipientAllowed = true;
293  return;
294  }
295  send(recipients, strFee, strFunds);
296  return;
297  }
298  // already unlocked or not encrypted at all
299  send(recipients, strFee, strFunds);
300 }
301 
302 void SendCoinsDialog::send(QList<SendCoinsRecipient> recipients, QString strFee, QString strFunds)
303 {
304  // prepare transaction for getting txFee earlier
305  WalletModelTransaction currentTransaction(recipients);
306  WalletModel::SendCoinsReturn prepareStatus;
307  if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
308  prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
309  else
310  prepareStatus = model->prepareTransaction(currentTransaction);
311 
312  // process prepareStatus and on error generate message shown to user
313  processSendCoinsReturn(prepareStatus,
315 
316  if(prepareStatus.status != WalletModel::OK) {
317  fNewRecipientAllowed = true;
318  return;
319  }
320 
321  CAmount txFee = currentTransaction.getTransactionFee();
322 
323  // Format confirmation message
324  QStringList formatted;
325  Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients())
326  {
327  // generate bold amount string
328  QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
329  amount.append("</b> ").append(strFunds);
330 
331  // generate monospace address string
332  QString address = "<span style='font-family: monospace;'>" + rcp.address;
333  address.append("</span>");
334 
335  QString recipientElement;
336 
337  if (!rcp.paymentRequest.IsInitialized()) // normal payment
338  {
339  if(rcp.label.length() > 0) // label with address
340  {
341  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
342  recipientElement.append(QString(" (%1)").arg(address));
343  }
344  else // just address
345  {
346  recipientElement = tr("%1 to %2").arg(amount, address);
347  }
348  }
349  else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
350  {
351  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
352  }
353  else // unauthenticated payment request
354  {
355  recipientElement = tr("%1 to %2").arg(amount, address);
356  }
357 
358  formatted.append(recipientElement);
359  }
360 
361  QString questionString = tr("Are you sure you want to send?");
362  questionString.append("<br /><br />%1");
363 
364  if(txFee > 0)
365  {
366  // append fee string if a fee is required
367  questionString.append("<hr /><span style='color:#aa0000;'>");
368  questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
369  questionString.append("</span> ");
370  questionString.append(tr("are added as transaction fee"));
371  questionString.append(" ");
372  questionString.append(strFee);
373 
374  // append transaction size
375  questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
376  }
377 
378  // add total amount in all subdivision units
379  questionString.append("<hr />");
380  CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
381  QStringList alternativeUnits;
383  {
384  if(u != model->getOptionsModel()->getDisplayUnit())
385  alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
386  }
387 
388  // Show total amount + all alternative units
389  questionString.append(tr("Total Amount = <b>%1</b><br />= %2")
391  .arg(alternativeUnits.join("<br />= ")));
392 
393  // Limit number of displayed entries
394  int messageEntries = formatted.size();
395  int displayedEntries = 0;
396  for(int i = 0; i < formatted.size(); i++){
397  if(i >= MAX_SEND_POPUP_ENTRIES){
398  formatted.removeLast();
399  i--;
400  }
401  else{
402  displayedEntries = i+1;
403  }
404  }
405  questionString.append("<hr />");
406  questionString.append(tr("<b>(%1 of %2 entries displayed)</b>").arg(displayedEntries).arg(messageEntries));
407 
408  // Display message box
409  QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
410  questionString.arg(formatted.join("<br />")),
411  QMessageBox::Yes | QMessageBox::Cancel,
412  QMessageBox::Cancel);
413 
414  if(retval != QMessageBox::Yes)
415  {
416  fNewRecipientAllowed = true;
417  return;
418  }
419 
420  // now send the prepared transaction
421  WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
422  // process sendStatus and on error generate message shown to user
423  processSendCoinsReturn(sendStatus);
424 
425  if (sendStatus.status == WalletModel::OK)
426  {
427  accept();
430  }
431  fNewRecipientAllowed = true;
432 }
433 
435 {
436  // Remove entries until only one left
437  while(ui->entries->count())
438  {
439  ui->entries->takeAt(0)->widget()->deleteLater();
440  }
441  addEntry();
442 
444 }
445 
447 {
448  clear();
449 }
450 
452 {
453  clear();
454 }
455 
457 {
458  SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
459  entry->setModel(model);
460  ui->entries->addWidget(entry);
461  connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
462  connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
463  connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
464 
465  // Focus the field, so that entry can start immediately
466  entry->clear();
467  entry->setFocus();
469  qApp->processEvents();
470  QScrollBar* bar = ui->scrollArea->verticalScrollBar();
471  if(bar)
472  bar->setSliderPosition(bar->maximum());
473 
475  return entry;
476 }
477 
479 {
480  setupTabChain(0);
482 }
483 
485 {
486  entry->hide();
487 
488  // If the last entry is about to be removed add an empty one
489  if (ui->entries->count() == 1)
490  addEntry();
491 
492  entry->deleteLater();
493 
495 }
496 
497 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
498 {
499  for(int i = 0; i < ui->entries->count(); ++i)
500  {
501  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
502  if(entry)
503  {
504  prev = entry->setupTabChain(prev);
505  }
506  }
507  QWidget::setTabOrder(prev, ui->sendButton);
508  QWidget::setTabOrder(ui->sendButton, ui->clearButton);
509  QWidget::setTabOrder(ui->clearButton, ui->addButton);
510  return ui->addButton;
511 }
512 
513 void SendCoinsDialog::setAddress(const QString &address)
514 {
515  SendCoinsEntry *entry = 0;
516  // Replace the first entry if it is still unused
517  if(ui->entries->count() == 1)
518  {
519  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
520  if(first->isClear())
521  {
522  entry = first;
523  }
524  }
525  if(!entry)
526  {
527  entry = addEntry();
528  }
529 
530  entry->setAddress(address);
531 }
532 
534 {
536  return;
537 
538  SendCoinsEntry *entry = 0;
539  // Replace the first entry if it is still unused
540  if(ui->entries->count() == 1)
541  {
542  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
543  if(first->isClear())
544  {
545  entry = first;
546  }
547  }
548  if(!entry)
549  {
550  entry = addEntry();
551  }
552 
553  entry->setValue(rv);
555 }
556 
558 {
559  // Just paste the entry, all pre-checks
560  // are done in paymentserver.cpp.
561  pasteEntry(rv);
562  return true;
563 }
564 
565 void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& anonymizedBalance,
566  const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
567 {
568  Q_UNUSED(unconfirmedBalance);
569  Q_UNUSED(immatureBalance);
570  Q_UNUSED(anonymizedBalance);
571  Q_UNUSED(watchBalance);
572  Q_UNUSED(watchUnconfirmedBalance);
573  Q_UNUSED(watchImmatureBalance);
574 
575  if(model && model->getOptionsModel())
576  {
577  uint64_t bal = 0;
578  QSettings settings;
579  settings.setValue("bUseDarkSend", ui->checkUsePrivateSend->isChecked());
580  if(ui->checkUsePrivateSend->isChecked()) {
581  bal = anonymizedBalance;
582  } else {
583  bal = balance;
584  }
585 
587  }
588 }
589 
591 {
599 }
600 
602 {
603  QSettings settings;
604  settings.setValue("bUseInstantX", ui->checkUseInstantSend->isChecked());
607 }
608 
609 void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
610 {
611  QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
612  // Default to a warning message, override if error message is needed
613  msgParams.second = CClientUIInterface::MSG_WARNING;
614 
615  // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
616  // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
617  // all others are used only in WalletModel::prepareTransaction()
618  switch(sendCoinsReturn.status)
619  {
621  msgParams.first = tr("The recipient address is not valid. Please recheck.");
622  break;
624  msgParams.first = tr("The amount to pay must be larger than 0.");
625  break;
627  msgParams.first = tr("The amount exceeds your balance.");
628  break;
630  msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
631  break;
633  msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
634  break;
636  msgParams.first = tr("Transaction creation failed!");
637  msgParams.second = CClientUIInterface::MSG_ERROR;
638  break;
640  msgParams.first = tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
641  msgParams.second = CClientUIInterface::MSG_ERROR;
642  break;
644  msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee));
645  break;
647  msgParams.first = tr("Payment request expired.");
648  msgParams.second = CClientUIInterface::MSG_ERROR;
649  break;
650  // included to prevent a compiler warning.
651  case WalletModel::OK:
652  default:
653  return;
654  }
655 
656  Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
657 }
658 
660 {
661  ui->labelFeeMinimized->setVisible(fMinimize);
662  ui->buttonChooseFee ->setVisible(fMinimize);
663  ui->buttonMinimizeFee->setVisible(!fMinimize);
664  ui->frameFeeSelection->setVisible(!fMinimize);
665  ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
666  fFeeMinimized = fMinimize;
667 }
668 
670 {
671  minimizeFeeSection(false);
672 }
673 
675 {
677  minimizeFeeSection(true);
678 }
679 
681 {
682  ui->radioCustomPerKilobyte->setChecked(true);
684 }
685 
687 {
688  ui->sliderSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
689  ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
690  ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
691  ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
692  ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
693  ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
694  ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
695  ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
696  ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
697  ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
698  ui->radioCustomAtLeast ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() && CoinControlDialog::coinControl->HasSelected());
699  ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
700 }
701 
703 {
704  if (ui->radioSmartFee->isChecked())
705  {
707  payTxFee = CFeeRate(0);
708 
709  // set nMinimumTotalFee to 0 to not accidentally pay a custom fee
711  }
712  else
713  {
716 
717  // if user has selected to set a minimum absolute fee, pass the value to coincontrol
718  // set nMinimumTotalFee to 0 in case of user has selected that the fee is per KB
720  }
721 
722  fSendFreeTransactions = ui->checkBoxFreeTx->isChecked();
723 }
724 
726 {
727  if(!model || !model->getOptionsModel())
728  return;
729 
730  if (ui->radioSmartFee->isChecked())
731  ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
732  else {
734  ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
735  }
736 }
737 
739 {
740  if (model && model->getOptionsModel())
741  ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
743  );
744 }
745 
747 {
748  if(!model || !model->getOptionsModel())
749  return;
750 
751  int nBlocksToConfirm = defaultConfirmTarget - ui->sliderSmartFee->value();
752  int estimateFoundAtBlocks = nBlocksToConfirm;
753  CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
754  if (feeRate <= CFeeRate(0)) // not enough data => minfee
755  {
757  std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
758  ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
759  ui->labelFeeEstimation->setText("");
760  }
761  else
762  {
764  std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
765  ui->labelSmartFee2->hide();
766  ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
767  }
768 
770 }
771 
772 // Coin Control: copy label "Quantity" to clipboard
774 {
776 }
777 
778 // Coin Control: copy label "Amount" to clipboard
780 {
781  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
782 }
783 
784 // Coin Control: copy label "Fee" to clipboard
786 {
787  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
788 }
789 
790 // Coin Control: copy label "After fee" to clipboard
792 {
793  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
794 }
795 
796 // Coin Control: copy label "Bytes" to clipboard
798 {
800 }
801 
802 // Coin Control: copy label "Dust" to clipboard
804 {
806 }
807 
808 // Coin Control: copy label "Change" to clipboard
810 {
811  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
812 }
813 
814 // Coin Control: settings menu - coin control enabled/disabled by user
816 {
817  ui->frameCoinControl->setVisible(checked);
818 
819  if (!checked && model) // coin control features disabled
821 
823 }
824 
825 // Coin Control: button inputs -> show actual coin control dialog
827 {
829  dlg.setModel(model);
830  dlg.exec();
832 }
833 
834 // Coin Control: checkbox custom change address
836 {
837  if (state == Qt::Unchecked)
838  {
841  }
842  else
843  // use this to re-validate an already entered address
845 
846  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
847 }
848 
849 // Coin Control: custom change address changed
851 {
852  if (model && model->getAddressTableModel())
853  {
854  // Default to no change address until verified
856  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
857 
858  CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
859 
860  if (text.isEmpty()) // Nothing entered
861  {
862  ui->labelCoinControlChangeLabel->setText("");
863  }
864  else if (!addr.IsValid()) // Invalid address
865  {
866  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Dash address"));
867  }
868  else // Valid address
869  {
870  CKeyID keyid;
871  addr.GetKeyID(keyid);
872  if (!model->havePrivKey(keyid)) // Unknown change address
873  {
874  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
875  }
876  else // Known change address
877  {
878  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
879 
880  // Query label
881  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
882  if (!associatedLabel.isEmpty())
883  ui->labelCoinControlChangeLabel->setText(associatedLabel);
884  else
885  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
886 
888  }
889  }
890  }
891 }
892 
893 // Coin Control: update labels
895 {
896  if (!model || !model->getOptionsModel())
897  return;
898 
900  {
901  // enable minimum absolute fee UI controls
902  ui->radioCustomAtLeast->setVisible(true);
903 
904  // only enable the feature if inputs are selected
905  ui->radioCustomAtLeast->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() &&CoinControlDialog::coinControl->HasSelected());
906  }
907  else
908  {
909  // in case coin control is disabled (=default), hide minimum absolute fee UI controls
910  ui->radioCustomAtLeast->setVisible(false);
911  return;
912  }
913 
914  // set pay amounts
917  for(int i = 0; i < ui->entries->count(); ++i)
918  {
919  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
920  if(entry && !entry->isHidden())
921  {
922  SendCoinsRecipient rcp = entry->getValue();
924  if (rcp.fSubtractFeeFromAmount)
926  }
927  }
928 
929  ui->checkUsePrivateSend->setChecked(CoinControlDialog::coinControl->fUsePrivateSend);
930 
931  if (CoinControlDialog::coinControl->HasSelected())
932  {
933  // actual coin control calculation
935 
936  // show coin control stats
938  ui->widgetCoinControl->show();
939  }
940  else
941  {
942  // hide coin control stats
944  ui->widgetCoinControl->hide();
946  }
947 }
QLabel * labelCoinControlLowOutput
CTxDestination destChange
Definition: coincontrol.h:14
static QList< CAmount > payAmounts
QWidget * scrollAreaWidgetContents
QHBoxLayout * horizontalLayoutSmartFee
QLabel * labelCoinControlAutomaticallySelected
QButtonGroup * groupCustomFee
QPushButton * buttonMinimizeFee
bool getCoinControlFeatures()
Definition: optionsmodel.h:76
QButtonGroup * groupFee
void on_buttonChooseFee_clicked()
static void updateLabels(WalletModel *, QDialog *)
bool IsValid() const
Definition: base58.cpp:247
void setEnabled(bool fEnabled)
void coinControlClipboardFee()
CFeeRate estimateSmartFee(int nBlocks, int *answerFoundAtBlocks=NULL) const
Definition: txmempool.cpp:883
void coinControlClipboardAfterFee()
CAmount maxTxFee
Definition: wallet.cpp:46
QPushButton * buttonChooseFee
bool HasSelected() const
Definition: coincontrol.h:40
QList< SendCoinsRecipient > getRecipients()
ClientModel * clientModel
QT_END_NAMESPACE const int defaultConfirmTarget
Definition: pubkey.h:27
bool fUseInstantSend
Definition: coincontrol.h:16
static CAmount GetSmallestDenomination()
Definition: privatesend.h:329
EncryptionStatus getEncryptionStatus() const
void updateGlobalFeeVariables()
void on_buttonMinimizeFee_clicked()
QLabel * labelCoinControlInsuffFunds
void coinControlFeatureChanged(bool)
void coinControlButtonClicked()
void SetNull()
Definition: coincontrol.h:29
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
#define ASYMP_UTF8
QRadioButton * radioCustomAtLeast
void updateFeeSectionControls()
bool fSubtractFeeFromAmount
Definition: walletmodel.h:63
dictionary settings
CAmount getWatchImmatureBalance() const
void coinControlUpdateLabels()
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:112
const PlatformStyle * platformStyle
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
void setValue(const SendCoinsRecipient &value)
SendCoinsRecipient getValue()
CAmount getAnonymizedBalance() const
Definition: walletmodel.cpp:91
int64_t CAmount
Definition: amount.h:14
static QString formatHtmlWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as HTML string (with unit)
bool fLiteMode
Definition: util.cpp:109
void coinControlClipboardChange()
unsigned int nTxConfirmTarget
Definition: wallet.cpp:47
bool fSendFreeTransactions
Definition: wallet.cpp:49
void setDisplayUnit(int unit)
QWidget * setupTabChain(QWidget *prev)
void setClipboard(const QString &str)
Definition: guiutil.cpp:937
void setModel(WalletModel *model)
void coinControlClipboardBytes()
void message(const QString &title, const QString &message, unsigned int style)
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl=NULL)
SendCoinsReturn sendCoins(WalletModelTransaction &transaction)
bool havePrivKey(const CKeyID &address) const
QCheckBox * checkBoxCoinControlChange
void setupUi(QDialog *SendCoinsDialog)
QString labelForAddress(const QString &address) const
void coinControlChangeChecked(int)
static secp256k1_context * ctx
Definition: tests.c:42
CAmount nMinimumTotalFee
Minimum absolute fee (not per kilobyte)
Definition: coincontrol.h:22
void removeEntry(SendCoinsEntry *entry)
QRadioButton * radioCustomPerKilobyte
QCheckBox * checkUsePrivateSend
CAmount GetFeePerK() const
Definition: amount.h:47
static CFeeRate fallbackFee
Definition: wallet.h:910
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:261
void setAddress(const QString &address)
QPushButton * pushButtonCoinControl
QLabel * labelCoinControlChangeLabel
SendCoinsEntry * addEntry()
OptionsModel * getOptionsModel()
Ui::SendCoinsDialog * ui
void setModel(WalletModel *model)
void send(QList< SendCoinsRecipient > recipients, QString strFee, QString strFunds)
CAmount getWatchUnconfirmedBalance() const
QLabel * labelCoinControlQuantity
void setClientModel(ClientModel *clientModel)
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
QWidget * setupTabChain(QWidget *prev)
WalletModel * model
PaymentRequestPlus paymentRequest
Definition: walletmodel.h:59
void UnSelectAll()
Definition: coincontrol.h:60
QCheckBox * checkUseInstantSend
bool getImagesOnButtons() const
Definition: platformstyle.h:21
static bool fSubtractFeeFromAmount
CTxDestination Get() const
Definition: base58.cpp:260
void coinControlClipboardLowOutput()
CTxMemPool mempool
void setModel(WalletModel *model)
QCheckBox * checkBoxMinimumFee
QScrollArea * scrollArea
void updateFeeMinimizedLabel()
QRadioButton * radioSmartFee
void setAddress(const QString &address)
QString getThemeName()
Definition: guiutil.cpp:902
QString authenticatedMerchant
Definition: walletmodel.h:61
void coinControlClipboardAmount()
static const int MAX_SEND_POPUP_ENTRIES
QPushButton * sendButton
static CCoinControl * coinControl
QRadioButton * radioCustomFee
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE)
bool GetKeyID(CKeyID &keyID) const
Definition: base58.cpp:291
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=0)
CAmount getUnconfirmedBalance() const
Definition: walletmodel.cpp:96
static CAmount GetRequiredFee(unsigned int nTxBytes)
Definition: wallet.cpp:3591
int getDisplayUnit()
Definition: optionsmodel.h:73
void minimizeFeeSection(bool fMinimize)
static const CAmount DEFAULT_TRANSACTION_FEE
-paytxfee default
Definition: wallet.h:47
QValidatedLineEdit * lineEditCoinControlChange
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
void coinControlClipboardQuantity()
CAmount getBalance(const CCoinControl *coinControl=NULL) const
Definition: walletmodel.cpp:73
CAmount getWatchBalance() const
BitcoinAmountField * customFee
QPushButton * clearButton
void setValue(const CAmount &value)
CAmount getImmatureBalance() const
AddressTableModel * getAddressTableModel()
bool fUsePrivateSend
Definition: coincontrol.h:15
void coinControlChangeEdited(const QString &)
QLabel * labelCoinControlAfterFee
void setEnabled(bool enabled)
void setBalance(const CAmount &balance, const CAmount &unconfirmedBalance, const CAmount &immatureBalance, const CAmount &anonymizedBalance, const CAmount &watchOnlyBalance, const CAmount &watchUnconfBalance, const CAmount &watchImmatureBalance)
UnlockContext requestUnlock(bool fForMixingOnly=false)
void setSingleStep(const CAmount &step)
void pasteEntry(const SendCoinsRecipient &rv)