signverifymessagedialog.cpp raw

   1  // Copyright (c) 2011-present 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  #include <qt/signverifymessagedialog.h>
   6  #include <qt/forms/ui_signverifymessagedialog.h>
   7  
   8  #include <qt/addressbookpage.h>
   9  #include <qt/guiutil.h>
  10  #include <qt/platformstyle.h>
  11  #include <qt/walletmodel.h>
  12  
  13  #include <common/signmessage.h>
  14  #include <bitcoin-build-config.h> // IWYU pragma: keep
  15  #include <key_io.h>
  16  #include <wallet/wallet.h>
  17  
  18  #include <string>
  19  #include <variant>
  20  #include <vector>
  21  
  22  #include <QClipboard>
  23  
  24  SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
  25      QDialog(parent, GUIUtil::dialog_flags),
  26      ui(new Ui::SignVerifyMessageDialog),
  27      platformStyle(_platformStyle)
  28  {
  29      ui->setupUi(this);
  30  
  31      ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/address-book"));
  32      ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste"));
  33      ui->copySignatureButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/editcopy"));
  34      ui->signMessageButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/edit"));
  35      ui->clearButton_SM->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
  36      ui->addressBookButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/address-book"));
  37      ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/transaction_0"));
  38      ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
  39  
  40      GUIUtil::setupAddressWidget(ui->addressIn_SM, this);
  41      GUIUtil::setupAddressWidget(ui->addressIn_VM, this);
  42  
  43      ui->addressIn_SM->installEventFilter(this);
  44      ui->messageIn_SM->installEventFilter(this);
  45      ui->signatureOut_SM->installEventFilter(this);
  46      ui->addressIn_VM->installEventFilter(this);
  47      ui->messageIn_VM->installEventFilter(this);
  48      ui->signatureIn_VM->installEventFilter(this);
  49  
  50      ui->signatureOut_SM->setFont(GUIUtil::fixedPitchFont());
  51      ui->signatureIn_VM->setFont(GUIUtil::fixedPitchFont());
  52  
  53      GUIUtil::handleCloseWindowShortcut(this);
  54  }
  55  
  56  SignVerifyMessageDialog::~SignVerifyMessageDialog()
  57  {
  58      delete ui;
  59  }
  60  
  61  void SignVerifyMessageDialog::setModel(WalletModel *_model)
  62  {
  63      this->model = _model;
  64  }
  65  
  66  void SignVerifyMessageDialog::setAddress_SM(const QString &address)
  67  {
  68      ui->addressIn_SM->setText(address);
  69      ui->messageIn_SM->setFocus();
  70  }
  71  
  72  void SignVerifyMessageDialog::setAddress_VM(const QString &address)
  73  {
  74      ui->addressIn_VM->setText(address);
  75      ui->messageIn_VM->setFocus();
  76  }
  77  
  78  void SignVerifyMessageDialog::showTab_SM(bool fShow)
  79  {
  80      ui->tabWidget->setCurrentIndex(0);
  81      if (fShow)
  82          this->show();
  83  }
  84  
  85  void SignVerifyMessageDialog::showTab_VM(bool fShow)
  86  {
  87      ui->tabWidget->setCurrentIndex(1);
  88      if (fShow)
  89          this->show();
  90  }
  91  
  92  void SignVerifyMessageDialog::on_addressBookButton_SM_clicked()
  93  {
  94      if (model && model->getAddressTableModel())
  95      {
  96          model->refresh(/*pk_hash_only=*/true);
  97          AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::ReceivingTab, this);
  98          dlg.setModel(model->getAddressTableModel());
  99          if (dlg.exec())
 100          {
 101              setAddress_SM(dlg.getReturnValue());
 102          }
 103      }
 104  }
 105  
 106  void SignVerifyMessageDialog::on_pasteButton_SM_clicked()
 107  {
 108      setAddress_SM(QApplication::clipboard()->text());
 109  }
 110  
 111  void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
 112  {
 113      if (!model)
 114          return;
 115  
 116      /* Clear old signature to ensure users don't get confused on error with an old signature displayed */
 117      ui->signatureOut_SM->clear();
 118  
 119      CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString());
 120      if (!IsValidDestination(destination)) {
 121          ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
 122          ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
 123          return;
 124      }
 125      const PKHash* pkhash = std::get_if<PKHash>(&destination);
 126      if (!pkhash) {
 127          ui->addressIn_SM->setValid(false);
 128          ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
 129          ui->statusLabel_SM->setText(tr("The entered address does not refer to a legacy (P2PKH) key. Message signing for SegWit and other non-P2PKH address types is not supported in this version of %1. Please check the address and try again.").arg(CLIENT_NAME));
 130          return;
 131      }
 132  
 133      WalletModel::UnlockContext ctx(model->requestUnlock());
 134      if (!ctx.isValid())
 135      {
 136          ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
 137          ui->statusLabel_SM->setText(tr("Wallet unlock was cancelled."));
 138          return;
 139      }
 140  
 141      const std::string& message = ui->messageIn_SM->document()->toPlainText().toStdString();
 142      std::string signature;
 143      SigningResult res = model->wallet().signMessage(message, *pkhash, signature);
 144  
 145      QString error;
 146      switch (res) {
 147          case SigningResult::OK:
 148              error = tr("No error");
 149              break;
 150          case SigningResult::PRIVATE_KEY_NOT_AVAILABLE:
 151              error = tr("Private key for the entered address is not available.");
 152              break;
 153          case SigningResult::SIGNING_FAILED:
 154              error = tr("Message signing failed.");
 155              break;
 156      } // no default case, so the compiler can warn about missing cases
 157  
 158      if (res != SigningResult::OK) {
 159          ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
 160          ui->statusLabel_SM->setText(QString("<nobr>") + error + QString("</nobr>"));
 161          return;
 162      }
 163  
 164      ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }");
 165      ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") + QString("</nobr>"));
 166  
 167      ui->signatureOut_SM->setText(QString::fromStdString(signature));
 168  }
 169  
 170  void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked()
 171  {
 172      GUIUtil::setClipboard(ui->signatureOut_SM->text());
 173  }
 174  
 175  void SignVerifyMessageDialog::on_clearButton_SM_clicked()
 176  {
 177      ui->addressIn_SM->clear();
 178      ui->messageIn_SM->clear();
 179      ui->signatureOut_SM->clear();
 180      ui->statusLabel_SM->clear();
 181  
 182      ui->addressIn_SM->setFocus();
 183  }
 184  
 185  void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()
 186  {
 187      if (model && model->getAddressTableModel())
 188      {
 189          AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::SendingTab, this);
 190          dlg.setModel(model->getAddressTableModel());
 191          if (dlg.exec())
 192          {
 193              setAddress_VM(dlg.getReturnValue());
 194          }
 195      }
 196  }
 197  
 198  void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
 199  {
 200      const std::string& address = ui->addressIn_VM->text().toStdString();
 201      const std::string& signature = ui->signatureIn_VM->text().toStdString();
 202      const std::string& message = ui->messageIn_VM->document()->toPlainText().toStdString();
 203  
 204      const auto result = MessageVerify(address, signature, message);
 205  
 206      if (result == MessageVerificationResult::OK) {
 207          ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }");
 208      } else {
 209          ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
 210      }
 211  
 212      switch (result) {
 213      case MessageVerificationResult::OK:
 214          ui->statusLabel_VM->setText(
 215              QString("<nobr>") + tr("Message verified.") + QString("</nobr>")
 216          );
 217          return;
 218      case MessageVerificationResult::ERR_INVALID_ADDRESS:
 219          ui->statusLabel_VM->setText(
 220              tr("The entered address is invalid.") + QString(" ") +
 221              tr("Please check the address and try again.")
 222          );
 223          return;
 224      case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
 225          ui->addressIn_VM->setValid(false);
 226          ui->statusLabel_VM->setText(tr("The entered address does not refer to a legacy (P2PKH) key. Message signing for SegWit and other non-P2PKH address types is not supported in this version of %1. Please check the address and try again.").arg(CLIENT_NAME));
 227          return;
 228      case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
 229          ui->signatureIn_VM->setValid(false);
 230          ui->statusLabel_VM->setText(
 231              tr("The signature could not be decoded.") + QString(" ") +
 232              tr("Please check the signature and try again.")
 233          );
 234          return;
 235      case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
 236          ui->signatureIn_VM->setValid(false);
 237          ui->statusLabel_VM->setText(
 238              tr("The signature did not match the message digest.") + QString(" ") +
 239              tr("Please check the signature and try again.")
 240          );
 241          return;
 242      case MessageVerificationResult::ERR_NOT_SIGNED:
 243          ui->statusLabel_VM->setText(
 244              QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")
 245          );
 246          return;
 247      }
 248  }
 249  
 250  void SignVerifyMessageDialog::on_clearButton_VM_clicked()
 251  {
 252      ui->addressIn_VM->clear();
 253      ui->signatureIn_VM->clear();
 254      ui->messageIn_VM->clear();
 255      ui->statusLabel_VM->clear();
 256  
 257      ui->addressIn_VM->setFocus();
 258  }
 259  
 260  bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event)
 261  {
 262      if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::FocusIn)
 263      {
 264          if (ui->tabWidget->currentIndex() == 0)
 265          {
 266              /* Clear status message on focus change */
 267              ui->statusLabel_SM->clear();
 268  
 269              /* Select generated signature */
 270              if (object == ui->signatureOut_SM)
 271              {
 272                  ui->signatureOut_SM->selectAll();
 273                  return true;
 274              }
 275          }
 276          else if (ui->tabWidget->currentIndex() == 1)
 277          {
 278              /* Clear status message on focus change */
 279              ui->statusLabel_VM->clear();
 280          }
 281      }
 282      return QDialog::eventFilter(object, event);
 283  }
 284  
 285  void SignVerifyMessageDialog::changeEvent(QEvent* e)
 286  {
 287      if (e->type() == QEvent::PaletteChange) {
 288          ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
 289          ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste")));
 290          ui->copySignatureButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editcopy")));
 291          ui->signMessageButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/edit")));
 292          ui->clearButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
 293          ui->addressBookButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
 294          ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/transaction_0")));
 295          ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
 296      }
 297  
 298      QDialog::changeEvent(e);
 299  }
 300