Bitcoin Core  24.99.0
P2P Digital Currency
sendcoinsdialog.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2022 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #if defined(HAVE_CONFIG_H)
7 #endif
8 
9 #include <qt/sendcoinsdialog.h>
10 #include <qt/forms/ui_sendcoinsdialog.h>
11 
12 #include <qt/addresstablemodel.h>
13 #include <qt/bitcoinunits.h>
14 #include <qt/clientmodel.h>
15 #include <qt/coincontroldialog.h>
16 #include <qt/guiutil.h>
17 #include <qt/optionsmodel.h>
18 #include <qt/platformstyle.h>
19 #include <qt/sendcoinsentry.h>
20 
21 #include <chainparams.h>
22 #include <interfaces/node.h>
23 #include <key_io.h>
24 #include <node/interface_ui.h>
25 #include <policy/fees.h>
26 #include <txmempool.h>
27 #include <validation.h>
28 #include <wallet/coincontrol.h>
29 #include <wallet/fees.h>
30 #include <wallet/wallet.h>
31 
32 #include <array>
33 #include <chrono>
34 #include <fstream>
35 #include <memory>
36 
37 #include <QFontMetrics>
38 #include <QScrollBar>
39 #include <QSettings>
40 #include <QTextDocument>
41 
44 
45 static constexpr std::array confTargets{2, 4, 6, 12, 24, 48, 144, 504, 1008};
46 int getConfTargetForIndex(int index) {
47  if (index+1 > static_cast<int>(confTargets.size())) {
48  return confTargets.back();
49  }
50  if (index < 0) {
51  return confTargets[0];
52  }
53  return confTargets[index];
54 }
55 int getIndexForConfTarget(int target) {
56  for (unsigned int i = 0; i < confTargets.size(); i++) {
57  if (confTargets[i] >= target) {
58  return i;
59  }
60  }
61  return confTargets.size() - 1;
62 }
63 
64 SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
65  QDialog(parent, GUIUtil::dialog_flags),
66  ui(new Ui::SendCoinsDialog),
67  m_coin_control(new CCoinControl),
68  platformStyle(_platformStyle)
69 {
70  ui->setupUi(this);
71 
72  if (!_platformStyle->getImagesOnButtons()) {
73  ui->addButton->setIcon(QIcon());
74  ui->clearButton->setIcon(QIcon());
75  ui->sendButton->setIcon(QIcon());
76  } else {
77  ui->addButton->setIcon(_platformStyle->SingleColorIcon(":/icons/add"));
78  ui->clearButton->setIcon(_platformStyle->SingleColorIcon(":/icons/remove"));
79  ui->sendButton->setIcon(_platformStyle->SingleColorIcon(":/icons/send"));
80  }
81 
82  GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
83 
84  addEntry();
85 
86  connect(ui->addButton, &QPushButton::clicked, this, &SendCoinsDialog::addEntry);
87  connect(ui->clearButton, &QPushButton::clicked, this, &SendCoinsDialog::clear);
88 
89  // Coin Control
90  connect(ui->pushButtonCoinControl, &QPushButton::clicked, this, &SendCoinsDialog::coinControlButtonClicked);
91  connect(ui->checkBoxCoinControlChange, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlChangeChecked);
92  connect(ui->lineEditCoinControlChange, &QValidatedLineEdit::textEdited, this, &SendCoinsDialog::coinControlChangeEdited);
93 
94  // Coin Control: clipboard actions
95  QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
96  QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
97  QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
98  QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
99  QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
100  QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
101  QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
102  connect(clipboardQuantityAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardQuantity);
103  connect(clipboardAmountAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAmount);
104  connect(clipboardFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardFee);
105  connect(clipboardAfterFeeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardAfterFee);
106  connect(clipboardBytesAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardBytes);
107  connect(clipboardLowOutputAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardLowOutput);
108  connect(clipboardChangeAction, &QAction::triggered, this, &SendCoinsDialog::coinControlClipboardChange);
109  ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
110  ui->labelCoinControlAmount->addAction(clipboardAmountAction);
111  ui->labelCoinControlFee->addAction(clipboardFeeAction);
112  ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
113  ui->labelCoinControlBytes->addAction(clipboardBytesAction);
114  ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
115  ui->labelCoinControlChange->addAction(clipboardChangeAction);
116 
117  // init transaction fee section
118  QSettings settings;
119  if (!settings.contains("fFeeSectionMinimized"))
120  settings.setValue("fFeeSectionMinimized", true);
121  if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
122  settings.setValue("nFeeRadio", 1); // custom
123  if (!settings.contains("nFeeRadio"))
124  settings.setValue("nFeeRadio", 0); // recommended
125  if (!settings.contains("nSmartFeeSliderPosition"))
126  settings.setValue("nSmartFeeSliderPosition", 0);
127  if (!settings.contains("nTransactionFee"))
128  settings.setValue("nTransactionFee", (qint64)DEFAULT_PAY_TX_FEE);
129  ui->groupFee->setId(ui->radioSmartFee, 0);
130  ui->groupFee->setId(ui->radioCustomFee, 1);
131  ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
132  ui->customFee->SetAllowEmpty(false);
133  ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
134  minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
135 
136  GUIUtil::ExceptionSafeConnect(ui->sendButton, &QPushButton::clicked, this, &SendCoinsDialog::sendButtonClicked);
137 }
138 
140 {
141  this->clientModel = _clientModel;
142 
143  if (_clientModel) {
145  }
146 }
147 
149 {
150  this->model = _model;
151 
152  if(_model && _model->getOptionsModel())
153  {
154  for(int i = 0; i < ui->entries->count(); ++i)
155  {
156  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
157  if(entry)
158  {
159  entry->setModel(_model);
160  }
161  }
162 
165  refreshBalance();
166 
167  // Coin Control
170  ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
172 
173  // fee section
174  for (const int n : confTargets) {
175  ui->confTargetSelector->addItem(tr("%1 (%2 blocks)").arg(GUIUtil::formatNiceTimeOffset(n*Params().GetConsensus().nPowTargetSpacing)).arg(n));
176  }
177  connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::updateSmartFeeLabel);
178  connect(ui->confTargetSelector, qOverload<int>(&QComboBox::currentIndexChanged), this, &SendCoinsDialog::coinControlUpdateLabels);
179 
180 #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
181  connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::updateFeeSectionControls);
182  connect(ui->groupFee, &QButtonGroup::idClicked, this, &SendCoinsDialog::coinControlUpdateLabels);
183 #else
184  connect(ui->groupFee, qOverload<int>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::updateFeeSectionControls);
185  connect(ui->groupFee, qOverload<int>(&QButtonGroup::buttonClicked), this, &SendCoinsDialog::coinControlUpdateLabels);
186 #endif
187 
189  connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::updateSmartFeeLabel);
190  connect(ui->optInRBF, &QCheckBox::stateChanged, this, &SendCoinsDialog::coinControlUpdateLabels);
191  CAmount requiredFee = model->wallet().getRequiredFee(1000);
192  ui->customFee->SetMinValue(requiredFee);
193  if (ui->customFee->value() < requiredFee) {
194  ui->customFee->setValue(requiredFee);
195  }
196  ui->customFee->setSingleStep(requiredFee);
199 
200  // set default rbf checkbox state
201  ui->optInRBF->setCheckState(Qt::Checked);
202 
203  if (model->wallet().hasExternalSigner()) {
204  //: "device" usually means a hardware wallet.
205  ui->sendButton->setText(tr("Sign on device"));
206  if (gArgs.GetArg("-signer", "") != "") {
207  ui->sendButton->setEnabled(true);
208  ui->sendButton->setToolTip(tr("Connect your hardware wallet first."));
209  } else {
210  ui->sendButton->setEnabled(false);
211  //: "External signer" means using devices such as hardware wallets.
212  ui->sendButton->setToolTip(tr("Set external signer script path in Options -> Wallet"));
213  }
214  } else if (model->wallet().privateKeysDisabled()) {
215  ui->sendButton->setText(tr("Cr&eate Unsigned"));
216  ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
217  }
218 
219  // set the smartfee-sliders default value (wallets default conf.target or last stored value)
220  QSettings settings;
221  if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
222  // migrate nSmartFeeSliderPosition to nConfTarget
223  // nConfTarget is available since 0.15 (replaced nSmartFeeSliderPosition)
224  int nConfirmTarget = 25 - settings.value("nSmartFeeSliderPosition").toInt(); // 25 == old slider range
225  settings.setValue("nConfTarget", nConfirmTarget);
226  settings.remove("nSmartFeeSliderPosition");
227  }
228  if (settings.value("nConfTarget").toInt() == 0)
229  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->wallet().getConfirmTarget()));
230  else
231  ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt()));
232  }
233 }
234 
236 {
237  QSettings settings;
238  settings.setValue("fFeeSectionMinimized", fFeeMinimized);
239  settings.setValue("nFeeRadio", ui->groupFee->checkedId());
240  settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex()));
241  settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
242 
243  delete ui;
244 }
245 
246 bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informative_text, QString& detailed_text)
247 {
248  QList<SendCoinsRecipient> recipients;
249  bool valid = true;
250 
251  for(int i = 0; i < ui->entries->count(); ++i)
252  {
253  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
254  if(entry)
255  {
256  if(entry->validate(model->node()))
257  {
258  recipients.append(entry->getValue());
259  }
260  else if (valid)
261  {
262  ui->scrollArea->ensureWidgetVisible(entry);
263  valid = false;
264  }
265  }
266  }
267 
268  if(!valid || recipients.isEmpty())
269  {
270  return false;
271  }
272 
273  fNewRecipientAllowed = false;
275  if(!ctx.isValid())
276  {
277  // Unlock wallet was cancelled
278  fNewRecipientAllowed = true;
279  return false;
280  }
281 
282  // prepare transaction for getting txFee earlier
283  m_current_transaction = std::make_unique<WalletModelTransaction>(recipients);
284  WalletModel::SendCoinsReturn prepareStatus;
285 
287 
288  CCoinControl coin_control = *m_coin_control;
289  coin_control.m_allow_other_inputs = !coin_control.HasSelected(); // future, could introduce a checkbox to customize this value.
290  prepareStatus = model->prepareTransaction(*m_current_transaction, coin_control);
291 
292  // process prepareStatus and on error generate message shown to user
293  processSendCoinsReturn(prepareStatus,
295 
296  if(prepareStatus.status != WalletModel::OK) {
297  fNewRecipientAllowed = true;
298  return false;
299  }
300 
301  CAmount txFee = m_current_transaction->getTransactionFee();
302  QStringList formatted;
303  for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients())
304  {
305  // generate amount string with wallet name in case of multiwallet
306  QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
307  if (model->isMultiwallet()) {
308  amount.append(tr(" from wallet '%1'").arg(GUIUtil::HtmlEscape(model->getWalletName())));
309  }
310 
311  // generate address string
312  QString address = rcp.address;
313 
314  QString recipientElement;
315 
316  {
317  if(rcp.label.length() > 0) // label with address
318  {
319  recipientElement.append(tr("%1 to '%2'").arg(amount, GUIUtil::HtmlEscape(rcp.label)));
320  recipientElement.append(QString(" (%1)").arg(address));
321  }
322  else // just address
323  {
324  recipientElement.append(tr("%1 to %2").arg(amount, address));
325  }
326  }
327  formatted.append(recipientElement);
328  }
329 
330  /*: Message displayed when attempting to create a transaction. Cautionary text to prompt the user to verify
331  that the displayed transaction details represent the transaction the user intends to create. */
332  question_string.append(tr("Do you want to create this transaction?"));
333  question_string.append("<br /><span style='font-size:10pt;'>");
335  /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
336  a user can only create a PSBT. This string is displayed when private keys are disabled and an external
337  signer is not available. */
338  question_string.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can save or copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
339  } else if (model->getOptionsModel()->getEnablePSBTControls()) {
340  /*: Text to inform a user attempting to create a transaction of their current options. At this stage,
341  a user can send their transaction or create a PSBT. This string is displayed when both private keys
342  and PSBT controls are enabled. */
343  question_string.append(tr("Please, review your transaction. You can create and send this transaction or create a Partially Signed Bitcoin Transaction (PSBT), which you can save or copy and then sign with, e.g., an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
344  } else {
345  /*: Text to prompt a user to review the details of the transaction they are attempting to send. */
346  question_string.append(tr("Please, review your transaction."));
347  }
348  question_string.append("</span>%1");
349 
350  if(txFee > 0)
351  {
352  // append fee string if a fee is required
353  question_string.append("<hr /><b>");
354  question_string.append(tr("Transaction fee"));
355  question_string.append("</b>");
356 
357  // append transaction size
358  question_string.append(" (" + QString::number((double)m_current_transaction->getTransactionSize() / 1000) + " kB): ");
359 
360  // append transaction fee value
361  question_string.append("<span style='color:#aa0000; font-weight:bold;'>");
362  question_string.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
363  question_string.append("</span><br />");
364 
365  // append RBF message according to transaction's signalling
366  question_string.append("<span style='font-size:10pt; font-weight:normal;'>");
367  if (ui->optInRBF->isChecked()) {
368  question_string.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125)."));
369  } else {
370  question_string.append(tr("Not signalling Replace-By-Fee, BIP-125."));
371  }
372  question_string.append("</span>");
373  }
374 
375  // add total amount in all subdivision units
376  question_string.append("<hr />");
377  CAmount totalAmount = m_current_transaction->getTotalTransactionAmount() + txFee;
378  QStringList alternativeUnits;
379  for (const BitcoinUnit u : BitcoinUnits::availableUnits()) {
380  if(u != model->getOptionsModel()->getDisplayUnit())
381  alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
382  }
383  question_string.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
385  question_string.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
386  .arg(alternativeUnits.join(" " + tr("or") + " ")));
387 
388  if (formatted.size() > 1) {
389  question_string = question_string.arg("");
390  informative_text = tr("To review recipient list click \"Show Details…\"");
391  detailed_text = formatted.join("\n\n");
392  } else {
393  question_string = question_string.arg("<br /><br />" + formatted.at(0));
394  }
395 
396  return true;
397 }
398 
400 {
401  // Serialize the PSBT
403  ssTx << psbtx;
404  GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
405  QMessageBox msgBox;
406  msgBox.setText("Unsigned Transaction");
407  msgBox.setInformativeText("The PSBT has been copied to the clipboard. You can also save it.");
408  msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard);
409  msgBox.setDefaultButton(QMessageBox::Discard);
410  switch (msgBox.exec()) {
411  case QMessageBox::Save: {
412  QString selectedFilter;
413  QString fileNameSuggestion = "";
414  bool first = true;
415  for (const SendCoinsRecipient &rcp : m_current_transaction->getRecipients()) {
416  if (!first) {
417  fileNameSuggestion.append(" - ");
418  }
419  QString labelOrAddress = rcp.label.isEmpty() ? rcp.address : rcp.label;
420  QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
421  fileNameSuggestion.append(labelOrAddress + "-" + amount);
422  first = false;
423  }
424  fileNameSuggestion.append(".psbt");
425  QString filename = GUIUtil::getSaveFileName(this,
426  tr("Save Transaction Data"), fileNameSuggestion,
427  //: Expanded name of the binary PSBT file format. See: BIP 174.
428  tr("Partially Signed Transaction (Binary)") + QLatin1String(" (*.psbt)"), &selectedFilter);
429  if (filename.isEmpty()) {
430  return;
431  }
432  std::ofstream out{filename.toLocal8Bit().data(), std::ofstream::out | std::ofstream::binary};
433  out << ssTx.str();
434  out.close();
435  Q_EMIT message(tr("PSBT saved"), "PSBT saved to disk", CClientUIInterface::MSG_INFORMATION);
436  break;
437  }
438  case QMessageBox::Discard:
439  break;
440  default:
441  assert(false);
442  } // msgBox.exec()
443 }
444 
446  TransactionError err;
447  try {
448  err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
449  } catch (const std::runtime_error& e) {
450  QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
451  return false;
452  }
454  //: "External signer" means using devices such as hardware wallets.
455  QMessageBox::critical(nullptr, tr("External signer not found"), "External signer not found");
456  return false;
457  }
459  //: "External signer" means using devices such as hardware wallets.
460  QMessageBox::critical(nullptr, tr("External signer failure"), "External signer failure");
461  return false;
462  }
463  if (err != TransactionError::OK) {
464  tfm::format(std::cerr, "Failed to sign PSBT");
466  return false;
467  }
468  // fillPSBT does not always properly finalize
469  complete = FinalizeAndExtractPSBT(psbtx, mtx);
470  return true;
471 }
472 
473 void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
474 {
475  if(!model || !model->getOptionsModel())
476  return;
477 
478  QString question_string, informative_text, detailed_text;
479  if (!PrepareSendText(question_string, informative_text, detailed_text)) return;
481 
482  const QString confirmation = tr("Confirm send coins");
483  const bool enable_send{!model->wallet().privateKeysDisabled() || model->wallet().hasExternalSigner()};
484  const bool always_show_unsigned{model->getOptionsModel()->getEnablePSBTControls()};
485  auto confirmationDialog = new SendConfirmationDialog(confirmation, question_string, informative_text, detailed_text, SEND_CONFIRM_DELAY, enable_send, always_show_unsigned, this);
486  confirmationDialog->setAttribute(Qt::WA_DeleteOnClose);
487  // TODO: Replace QDialog::exec() with safer QDialog::show().
488  const auto retval = static_cast<QMessageBox::StandardButton>(confirmationDialog->exec());
489 
490  if(retval != QMessageBox::Yes && retval != QMessageBox::Save)
491  {
492  fNewRecipientAllowed = true;
493  return;
494  }
495 
496  bool send_failure = false;
497  if (retval == QMessageBox::Save) {
498  // "Create Unsigned" clicked
500  PartiallySignedTransaction psbtx(mtx);
501  bool complete = false;
502  // Fill without signing
503  TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
504  assert(!complete);
506 
507  // Copy PSBT to clipboard and offer to save
508  presentPSBT(psbtx);
509  } else {
510  // "Send" clicked
512  bool broadcast = true;
513  if (model->wallet().hasExternalSigner()) {
515  PartiallySignedTransaction psbtx(mtx);
516  bool complete = false;
517  // Always fill without signing first. This prevents an external signer
518  // from being called prematurely and is not expensive.
519  TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
520  assert(!complete);
522  send_failure = !signWithExternalSigner(psbtx, mtx, complete);
523  // Don't broadcast when user rejects it on the device or there's a failure:
524  broadcast = complete && !send_failure;
525  if (!send_failure) {
526  // A transaction signed with an external signer is not always complete,
527  // e.g. in a multisig wallet.
528  if (complete) {
529  // Prepare transaction for broadcast transaction if complete
530  const CTransactionRef tx = MakeTransactionRef(mtx);
531  m_current_transaction->setWtx(tx);
532  } else {
533  presentPSBT(psbtx);
534  }
535  }
536  }
537 
538  // Broadcast the transaction, unless an external signer was used and it
539  // failed, or more signatures are needed.
540  if (broadcast) {
541  // now send the prepared transaction
543  Q_EMIT coinsSent(m_current_transaction->getWtx()->GetHash());
544  }
545  }
546  if (!send_failure) {
547  accept();
548  m_coin_control->UnSelectAll();
550  }
551  fNewRecipientAllowed = true;
552  m_current_transaction.reset();
553 }
554 
556 {
557  m_current_transaction.reset();
558 
559  // Clear coin control settings
560  m_coin_control->UnSelectAll();
561  ui->checkBoxCoinControlChange->setChecked(false);
562  ui->lineEditCoinControlChange->clear();
564 
565  // Remove entries until only one left
566  while(ui->entries->count())
567  {
568  ui->entries->takeAt(0)->widget()->deleteLater();
569  }
570  addEntry();
571 
573 }
574 
576 {
577  clear();
578 }
579 
581 {
582  clear();
583 }
584 
586 {
587  SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
588  entry->setModel(model);
589  ui->entries->addWidget(entry);
594 
595  // Focus the field, so that entry can start immediately
596  entry->clear();
597  entry->setFocus();
598  ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
599  qApp->processEvents();
600  QScrollBar* bar = ui->scrollArea->verticalScrollBar();
601  if(bar)
602  bar->setSliderPosition(bar->maximum());
603 
605  return entry;
606 }
607 
609 {
610  setupTabChain(nullptr);
612 }
613 
615 {
616  entry->hide();
617 
618  // If the last entry is about to be removed add an empty one
619  if (ui->entries->count() == 1)
620  addEntry();
621 
622  entry->deleteLater();
623 
625 }
626 
627 QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
628 {
629  for(int i = 0; i < ui->entries->count(); ++i)
630  {
631  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
632  if(entry)
633  {
634  prev = entry->setupTabChain(prev);
635  }
636  }
637  QWidget::setTabOrder(prev, ui->sendButton);
638  QWidget::setTabOrder(ui->sendButton, ui->clearButton);
639  QWidget::setTabOrder(ui->clearButton, ui->addButton);
640  return ui->addButton;
641 }
642 
643 void SendCoinsDialog::setAddress(const QString &address)
644 {
645  SendCoinsEntry *entry = nullptr;
646  // Replace the first entry if it is still unused
647  if(ui->entries->count() == 1)
648  {
649  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
650  if(first->isClear())
651  {
652  entry = first;
653  }
654  }
655  if(!entry)
656  {
657  entry = addEntry();
658  }
659 
660  entry->setAddress(address);
661 }
662 
664 {
666  return;
667 
668  SendCoinsEntry *entry = nullptr;
669  // Replace the first entry if it is still unused
670  if(ui->entries->count() == 1)
671  {
672  SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
673  if(first->isClear())
674  {
675  entry = first;
676  }
677  }
678  if(!entry)
679  {
680  entry = addEntry();
681  }
682 
683  entry->setValue(rv);
685 }
686 
688 {
689  // Just paste the entry, all pre-checks
690  // are done in paymentserver.cpp.
691  pasteEntry(rv);
692  return true;
693 }
694 
696 {
697  if(model && model->getOptionsModel())
698  {
699  CAmount balance = balances.balance;
700  if (model->wallet().hasExternalSigner()) {
701  ui->labelBalanceName->setText(tr("External balance:"));
702  } else if (model->wallet().isLegacy() && model->wallet().privateKeysDisabled()) {
703  balance = balances.watch_only_balance;
704  ui->labelBalanceName->setText(tr("Watch-only balance:"));
705  }
706  ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
707  }
708 }
709 
711 {
713  ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
715 }
716 
717 void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
718 {
719  QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
720  // Default to a warning message, override if error message is needed
721  msgParams.second = CClientUIInterface::MSG_WARNING;
722 
723  // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
724  // All status values are used only in WalletModel::prepareTransaction()
725  switch(sendCoinsReturn.status)
726  {
728  msgParams.first = tr("The recipient address is not valid. Please recheck.");
729  break;
731  msgParams.first = tr("The amount to pay must be larger than 0.");
732  break;
734  msgParams.first = tr("The amount exceeds your balance.");
735  break;
737  msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
738  break;
740  msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
741  break;
743  msgParams.first = tr("Transaction creation failed!");
744  msgParams.second = CClientUIInterface::MSG_ERROR;
745  break;
747  msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->wallet().getDefaultMaxTxFee()));
748  break;
749  // included to prevent a compiler warning.
750  case WalletModel::OK:
751  default:
752  return;
753  }
754 
755  Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
756 }
757 
759 {
760  ui->labelFeeMinimized->setVisible(fMinimize);
761  ui->buttonChooseFee ->setVisible(fMinimize);
762  ui->buttonMinimizeFee->setVisible(!fMinimize);
763  ui->frameFeeSelection->setVisible(!fMinimize);
764  ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
765  fFeeMinimized = fMinimize;
766 }
767 
769 {
770  minimizeFeeSection(false);
771 }
772 
774 {
776  minimizeFeeSection(true);
777 }
778 
780 {
781  // Include watch-only for wallets without private key
783 
784  // Calculate available amount to send.
786  for (int i = 0; i < ui->entries->count(); ++i) {
787  SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
788  if (e && !e->isHidden() && e != entry) {
789  amount -= e->getValue().amount;
790  }
791  }
792 
793  if (amount > 0) {
795  entry->setAmount(amount);
796  } else {
797  entry->setAmount(0);
798  }
799 }
800 
802 {
803  ui->confTargetSelector ->setEnabled(ui->radioSmartFee->isChecked());
804  ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
805  ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
806  ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
807  ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
808  ui->labelCustomFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
809  ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked());
810  ui->customFee ->setEnabled(ui->radioCustomFee->isChecked());
811 }
812 
814 {
815  if(!model || !model->getOptionsModel())
816  return;
817 
818  if (ui->radioSmartFee->isChecked())
819  ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
820  else {
821  ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kvB");
822  }
823 }
824 
826 {
827  if (ui->radioCustomFee->isChecked()) {
828  m_coin_control->m_feerate = CFeeRate(ui->customFee->value());
829  } else {
830  m_coin_control->m_feerate.reset();
831  }
832  // Avoid using global defaults when sending money from the GUI
833  // Either custom fee will be used or if not selected, the confirmation target from dropdown box
834  m_coin_control->m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
835  m_coin_control->m_signal_bip125_rbf = ui->optInRBF->isChecked();
836  // Include watch-only for wallets without private key
838 }
839 
840 void SendCoinsDialog::updateNumberOfBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state) {
841  if (sync_state == SynchronizationState::POST_INIT) {
843  }
844 }
845 
847 {
848  if(!model || !model->getOptionsModel())
849  return;
851  m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels
852  int returned_target;
853  FeeReason reason;
854  CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason));
855 
856  ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kvB");
857 
858  if (reason == FeeReason::FALLBACK) {
859  ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
860  ui->labelFeeEstimation->setText("");
861  ui->fallbackFeeWarningLabel->setVisible(true);
862  int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
863  QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
864  ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
865  ui->fallbackFeeWarningLabel->setIndent(GUIUtil::TextWidth(QFontMetrics(ui->fallbackFeeWarningLabel->font()), "x"));
866  }
867  else
868  {
869  ui->labelSmartFee2->hide();
870  ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target));
871  ui->fallbackFeeWarningLabel->setVisible(false);
872  }
873 
875 }
876 
877 // Coin Control: copy label "Quantity" to clipboard
879 {
880  GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
881 }
882 
883 // Coin Control: copy label "Amount" to clipboard
885 {
886  GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
887 }
888 
889 // Coin Control: copy label "Fee" to clipboard
891 {
892  GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
893 }
894 
895 // Coin Control: copy label "After fee" to clipboard
897 {
898  GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
899 }
900 
901 // Coin Control: copy label "Bytes" to clipboard
903 {
904  GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
905 }
906 
907 // Coin Control: copy label "Dust" to clipboard
909 {
910  GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
911 }
912 
913 // Coin Control: copy label "Change" to clipboard
915 {
916  GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
917 }
918 
919 // Coin Control: settings menu - coin control enabled/disabled by user
921 {
922  ui->frameCoinControl->setVisible(checked);
923 
924  if (!checked && model) { // coin control features disabled
925  m_coin_control = std::make_unique<CCoinControl>();
926  }
927 
929 }
930 
931 // Coin Control: button inputs -> show actual coin control dialog
933 {
935  connect(dlg, &QDialog::finished, this, &SendCoinsDialog::coinControlUpdateLabels);
937 }
938 
939 // Coin Control: checkbox custom change address
941 {
942  if (state == Qt::Unchecked)
943  {
944  m_coin_control->destChange = CNoDestination();
945  ui->labelCoinControlChangeLabel->clear();
946  }
947  else
948  // use this to re-validate an already entered address
949  coinControlChangeEdited(ui->lineEditCoinControlChange->text());
950 
951  ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
952 }
953 
954 // Coin Control: custom change address changed
956 {
957  if (model && model->getAddressTableModel())
958  {
959  // Default to no change address until verified
960  m_coin_control->destChange = CNoDestination();
961  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
962 
963  const CTxDestination dest = DecodeDestination(text.toStdString());
964 
965  if (text.isEmpty()) // Nothing entered
966  {
967  ui->labelCoinControlChangeLabel->setText("");
968  }
969  else if (!IsValidDestination(dest)) // Invalid address
970  {
971  ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
972  }
973  else // Valid address
974  {
975  if (!model->wallet().isSpendable(dest)) {
976  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
977 
978  // confirmation dialog
979  QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm custom change address"), tr("The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?"),
980  QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
981 
982  if(btnRetVal == QMessageBox::Yes)
983  m_coin_control->destChange = dest;
984  else
985  {
986  ui->lineEditCoinControlChange->setText("");
987  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
988  ui->labelCoinControlChangeLabel->setText("");
989  }
990  }
991  else // Known change address
992  {
993  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
994 
995  // Query label
996  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
997  if (!associatedLabel.isEmpty())
998  ui->labelCoinControlChangeLabel->setText(associatedLabel);
999  else
1000  ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
1001 
1002  m_coin_control->destChange = dest;
1003  }
1004  }
1005  }
1006 }
1007 
1008 // Coin Control: update labels
1010 {
1011  if (!model || !model->getOptionsModel())
1012  return;
1013 
1015 
1016  // set pay amounts
1019 
1020  for(int i = 0; i < ui->entries->count(); ++i)
1021  {
1022  SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
1023  if(entry && !entry->isHidden())
1024  {
1025  SendCoinsRecipient rcp = entry->getValue();
1027  if (rcp.fSubtractFeeFromAmount)
1029  }
1030  }
1031 
1032  if (m_coin_control->HasSelected())
1033  {
1034  // actual coin control calculation
1036 
1037  // show coin control stats
1038  ui->labelCoinControlAutomaticallySelected->hide();
1039  ui->widgetCoinControl->show();
1040  }
1041  else
1042  {
1043  // hide coin control stats
1044  ui->labelCoinControlAutomaticallySelected->show();
1045  ui->widgetCoinControl->hide();
1046  ui->labelCoinControlInsuffFunds->hide();
1047  }
1048 }
1049 
1050 SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, bool enable_send, bool always_show_unsigned, QWidget* parent)
1051  : QMessageBox(parent), secDelay(_secDelay), m_enable_send(enable_send)
1052 {
1053  setIcon(QMessageBox::Question);
1054  setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
1055  setText(text);
1056  setInformativeText(informative_text);
1057  setDetailedText(detailed_text);
1058  setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
1059  if (always_show_unsigned || !enable_send) addButton(QMessageBox::Save);
1060  setDefaultButton(QMessageBox::Cancel);
1061  yesButton = button(QMessageBox::Yes);
1062  if (confirmButtonText.isEmpty()) {
1063  confirmButtonText = yesButton->text();
1064  }
1065  m_psbt_button = button(QMessageBox::Save);
1066  updateButtons();
1067  connect(&countDownTimer, &QTimer::timeout, this, &SendConfirmationDialog::countDown);
1068 }
1069 
1071 {
1072  updateButtons();
1073  countDownTimer.start(1s);
1074  return QMessageBox::exec();
1075 }
1076 
1078 {
1079  secDelay--;
1080  updateButtons();
1081 
1082  if(secDelay <= 0)
1083  {
1084  countDownTimer.stop();
1085  }
1086 }
1087 
1089 {
1090  if(secDelay > 0)
1091  {
1092  yesButton->setEnabled(false);
1093  yesButton->setText(confirmButtonText + (m_enable_send ? (" (" + QString::number(secDelay) + ")") : QString("")));
1094  if (m_psbt_button) {
1095  m_psbt_button->setEnabled(false);
1096  m_psbt_button->setText(m_psbt_button_text + " (" + QString::number(secDelay) + ")");
1097  }
1098  }
1099  else
1100  {
1101  yesButton->setEnabled(m_enable_send);
1102  yesButton->setText(confirmButtonText);
1103  if (m_psbt_button) {
1104  m_psbt_button->setEnabled(true);
1106  }
1107  }
1108 }
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
#define PACKAGE_NAME
const CChainParams & Params()
Return the currently selected parameters.
QString labelForAddress(const QString &address) const
Look up label for address in address book, if not found return empty string.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:591
static QString formatHtmlWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as HTML string (with unit)
static QList< Unit > availableUnits()
Get list of units, for drop-down box.
static QString formatWithUnit(Unit unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
Unit
Bitcoin units.
Definition: bitcoinunits.h:42
@ MSG_INFORMATION
Predefined combinations for certain default usage cases.
Definition: interface_ui.h:65
Fee rate in satoshis per kilovirtualbyte: CAmount / kvB.
Definition: feerate.h:33
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
Definition: feerate.h:65
Model for Bitcoin network client.
Definition: clientmodel.h:54
void numBlocksChanged(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType header, SynchronizationState sync_state)
static QList< CAmount > payAmounts
static void updateLabels(wallet::CCoinControl &m_coin_control, WalletModel *, QDialog *)
static bool fSubtractFeeFromAmount
std::string str() const
Definition: streams.h:207
bool getCoinControlFeatures() const
Definition: optionsmodel.h:96
bool getEnablePSBTControls() const
Definition: optionsmodel.h:98
void coinControlFeaturesChanged(bool)
void displayUnitChanged(BitcoinUnit unit)
BitcoinUnit getDisplayUnit() const
Definition: optionsmodel.h:93
QIcon SingleColorIcon(const QString &filename) const
Colorize an icon (given filename) with the icon color.
bool getImagesOnButtons() const
Definition: platformstyle.h:21
Dialog for sending bitcoins.
void useAvailableBalance(SendCoinsEntry *entry)
WalletModel * model
void presentPSBT(PartiallySignedTransaction &psbt)
ClientModel * clientModel
void coinControlChangeEdited(const QString &)
void coinControlChangeChecked(int)
void coinControlClipboardFee()
Ui::SendCoinsDialog * ui
void on_buttonChooseFee_clicked()
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg=QString())
void setClientModel(ClientModel *clientModel)
void updateFeeSectionControls()
SendCoinsEntry * addEntry()
void updateNumberOfBlocks(int count, const QDateTime &blockDate, double nVerificationProgress, SyncType synctype, SynchronizationState sync_state)
void pasteEntry(const SendCoinsRecipient &rv)
void updateFeeMinimizedLabel()
void accept() override
const PlatformStyle * platformStyle
std::unique_ptr< wallet::CCoinControl > m_coin_control
void coinControlClipboardQuantity()
void coinControlButtonClicked()
void coinControlClipboardAfterFee()
bool signWithExternalSigner(PartiallySignedTransaction &psbt, CMutableTransaction &mtx, bool &complete)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
bool PrepareSendText(QString &question_string, QString &informative_text, QString &detailed_text)
void sendButtonClicked(bool checked)
void setModel(WalletModel *model)
void coinControlClipboardLowOutput()
bool handlePaymentRequest(const SendCoinsRecipient &recipient)
void setBalance(const interfaces::WalletBalances &balances)
void coinControlClipboardAmount()
void setAddress(const QString &address)
void coinControlClipboardChange()
std::unique_ptr< WalletModelTransaction > m_current_transaction
void removeEntry(SendCoinsEntry *entry)
void reject() override
void coinControlClipboardBytes()
void message(const QString &title, const QString &message, unsigned int style)
SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent=nullptr)
void coinsSent(const uint256 &txid)
void on_buttonMinimizeFee_clicked()
void coinControlFeatureChanged(bool)
void minimizeFeeSection(bool fMinimize)
A single entry in the dialog for sending bitcoins.
void setFocus()
void setAddress(const QString &address)
bool isClear()
Return whether the entry is still empty and unedited.
void subtractFeeFromAmountChanged()
void useAvailableBalance(SendCoinsEntry *entry)
void setValue(const SendCoinsRecipient &value)
void setModel(WalletModel *model)
void removeEntry(SendCoinsEntry *entry)
void payAmountChanged()
void setAmount(const CAmount &amount)
QWidget * setupTabChain(QWidget *prev)
Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://...
void clear()
bool validate(interfaces::Node &node)
void checkSubtractFeeFromAmount()
SendCoinsRecipient getValue()
SendConfirmationDialog(const QString &title, const QString &text, const QString &informative_text="", const QString &detailed_text="", int secDelay=SEND_CONFIRM_DELAY, bool enable_send=true, bool always_show_unsigned=true, QWidget *parent=nullptr)
QAbstractButton * m_psbt_button
QAbstractButton * yesButton
Interface to Bitcoin wallet from Qt view code.
Definition: walletmodel.h:53
AddressTableModel * getAddressTableModel() const
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const wallet::CCoinControl &coinControl)
void sendCoins(WalletModelTransaction &transaction)
CAmount getAvailableBalance(const wallet::CCoinControl *control)
bool isMultiwallet() const
interfaces::Wallet & wallet() const
Definition: walletmodel.h:145
OptionsModel * getOptionsModel() const
interfaces::Node & node() const
Definition: walletmodel.h:144
UnlockContext requestUnlock()
void balanceChanged(const interfaces::WalletBalances &balances)
interfaces::WalletBalances getCachedBalance() const
QString getWalletName() const
@ AmountWithFeeExceedsBalance
Definition: walletmodel.h:66
@ TransactionCreationFailed
Definition: walletmodel.h:68
@ AmountExceedsBalance
Definition: walletmodel.h:65
@ DuplicateAddress
Definition: walletmodel.h:67
virtual bool isLegacy()=0
Return whether is a legacy wallet.
virtual TransactionError fillPSBT(int sighash_type, bool sign, bool bip32derivs, size_t *n_signed, PartiallySignedTransaction &psbtx, bool &complete)=0
Fill PSBT.
virtual CAmount getRequiredFee(unsigned int tx_bytes)=0
Get required fee.
virtual unsigned int getConfirmTarget()=0
Get tx confirm target.
virtual bool hasExternalSigner()=0
virtual CAmount getDefaultMaxTxFee()=0
Get max tx fee.
virtual bool isSpendable(const CTxDestination &dest)=0
Return whether wallet has private key.
virtual bool privateKeysDisabled()=0
virtual CAmount getMinimumFee(unsigned int tx_bytes, const wallet::CCoinControl &coin_control, int *returned_target, FeeReason *reason)=0
Get minimum fee.
Coin Control Features.
Definition: coincontrol.h:30
bool HasSelected() const
Definition: coincontrol.h:66
bool m_allow_other_inputs
If true, the selection process can add extra unselected inputs from the wallet while requires all sel...
Definition: coincontrol.h:40
SyncType
Definition: clientmodel.h:39
#define ASYMP_UTF8
TransactionError
Definition: error.h:22
@ SIGHASH_ALL
Definition: interpreter.h:28
CTxDestination DecodeDestination(const std::string &str, std::string &error_msg, std::vector< int > *error_locations)
Definition: key_io.cpp:281
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:60
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:240
void ShowModalDialogAsynchronously(QDialog *dialog)
Shows a QDialog instance asynchronously, and deletes it on close.
Definition: guiutil.cpp:989
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedSuffixOut)
Get save filename, mimics QFileDialog::getSaveFileName, except that it appends a default suffix when ...
Definition: guiutil.cpp:304
QString formatNiceTimeOffset(qint64 secs)
Definition: guiutil.cpp:767
constexpr auto dialog_flags
Definition: guiutil.h:60
auto ExceptionSafeConnect(Sender sender, Signal signal, Receiver receiver, Slot method, Qt::ConnectionType type=Qt::AutoConnection)
A drop-in replacement of QObject::connect function (see: https://doc.qt.io/qt-5/qobject....
Definition: guiutil.h:391
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:895
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
Definition: guiutil.cpp:122
void setClipboard(const QString &str)
Definition: guiutil.cpp:652
void format(std::ostream &out, const char *fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1060
constexpr CAmount DEFAULT_PAY_TX_FEE
-paytxfee default
Definition: wallet.h:78
FeeReason
Definition: fees.h:44
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:422
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:421
bool FinalizeAndExtractPSBT(PartiallySignedTransaction &psbtx, CMutableTransaction &result)
Finalizes a PSBT if possible, and extracts it to a CMutableTransaction if it could be finalized.
Definition: psbt.cpp:448
int getConfTargetForIndex(int index)
int getIndexForConfTarget(int target)
static constexpr std::array confTargets
#define SEND_CONFIRM_DELAY
@ SER_NETWORK
Definition: serialize.h:131
bool IsValidDestination(const CTxDestination &dest)
Check whether a CTxDestination is a CNoDestination.
Definition: standard.cpp:356
std::variant< CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessV1Taproot, WitnessUnknown > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:149
A mutable version of CTransaction.
Definition: transaction.h:380
A version of CTransaction with the PSBT format.
Definition: psbt.h:947
Collection of wallet balances.
Definition: wallet.h:366
static secp256k1_context * ctx
Definition: tests.c:35
static int count
Definition: tests.c:34
std::string EncodeBase64(Span< const unsigned char > input)
ArgsManager gArgs
Definition: system.cpp:73
assert(!tx.IsCoinBase())
SynchronizationState
Current sync state passed to tip changed callbacks.
Definition: validation.h:84
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12