/ src / qt / signverifymessagedialog.cpp
signverifymessagedialog.cpp
  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  }