/ 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  
159      if (res != SigningResult::OK) {
160          ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
161          ui->statusLabel_SM->setText(QString("<nobr>") + error + QString("</nobr>"));
162          return;
163      }
164  
165      ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }");
166      ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") + QString("</nobr>"));
167  
168      ui->signatureOut_SM->setText(QString::fromStdString(signature));
169  }
170  
171  void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked()
172  {
173      GUIUtil::setClipboard(ui->signatureOut_SM->text());
174  }
175  
176  void SignVerifyMessageDialog::on_clearButton_SM_clicked()
177  {
178      ui->addressIn_SM->clear();
179      ui->messageIn_SM->clear();
180      ui->signatureOut_SM->clear();
181      ui->statusLabel_SM->clear();
182  
183      ui->addressIn_SM->setFocus();
184  }
185  
186  void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()
187  {
188      if (model && model->getAddressTableModel())
189      {
190          AddressBookPage dlg(platformStyle, AddressBookPage::ForSelection, AddressBookPage::SendingTab, this);
191          dlg.setModel(model->getAddressTableModel());
192          if (dlg.exec())
193          {
194              setAddress_VM(dlg.getReturnValue());
195          }
196      }
197  }
198  
199  void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
200  {
201      const std::string& address = ui->addressIn_VM->text().toStdString();
202      const std::string& signature = ui->signatureIn_VM->text().toStdString();
203      const std::string& message = ui->messageIn_VM->document()->toPlainText().toStdString();
204  
205      const auto result = MessageVerify(address, signature, message);
206  
207      if (result == MessageVerificationResult::OK) {
208          ui->statusLabel_VM->setStyleSheet("QLabel { color: green; }");
209      } else {
210          ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
211      }
212  
213      switch (result) {
214      case MessageVerificationResult::OK:
215          ui->statusLabel_VM->setText(
216              QString("<nobr>") + tr("Message verified.") + QString("</nobr>")
217          );
218          return;
219      case MessageVerificationResult::ERR_INVALID_ADDRESS:
220          ui->statusLabel_VM->setText(
221              tr("The entered address is invalid.") + QString(" ") +
222              tr("Please check the address and try again.")
223          );
224          return;
225      case MessageVerificationResult::ERR_ADDRESS_NO_KEY:
226          ui->addressIn_VM->setValid(false);
227          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));
228          return;
229      case MessageVerificationResult::ERR_MALFORMED_SIGNATURE:
230          ui->signatureIn_VM->setValid(false);
231          ui->statusLabel_VM->setText(
232              tr("The signature could not be decoded.") + QString(" ") +
233              tr("Please check the signature and try again.")
234          );
235          return;
236      case MessageVerificationResult::ERR_PUBKEY_NOT_RECOVERED:
237          ui->signatureIn_VM->setValid(false);
238          ui->statusLabel_VM->setText(
239              tr("The signature did not match the message digest.") + QString(" ") +
240              tr("Please check the signature and try again.")
241          );
242          return;
243      case MessageVerificationResult::ERR_NOT_SIGNED:
244          ui->statusLabel_VM->setText(
245              QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")
246          );
247          return;
248      }
249  }
250  
251  void SignVerifyMessageDialog::on_clearButton_VM_clicked()
252  {
253      ui->addressIn_VM->clear();
254      ui->signatureIn_VM->clear();
255      ui->messageIn_VM->clear();
256      ui->statusLabel_VM->clear();
257  
258      ui->addressIn_VM->setFocus();
259  }
260  
261  bool SignVerifyMessageDialog::eventFilter(QObject *object, QEvent *event)
262  {
263      if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::FocusIn)
264      {
265          if (ui->tabWidget->currentIndex() == 0)
266          {
267              /* Clear status message on focus change */
268              ui->statusLabel_SM->clear();
269  
270              /* Select generated signature */
271              if (object == ui->signatureOut_SM)
272              {
273                  ui->signatureOut_SM->selectAll();
274                  return true;
275              }
276          }
277          else if (ui->tabWidget->currentIndex() == 1)
278          {
279              /* Clear status message on focus change */
280              ui->statusLabel_VM->clear();
281          }
282      }
283      return QDialog::eventFilter(object, event);
284  }
285  
286  void SignVerifyMessageDialog::changeEvent(QEvent* e)
287  {
288      if (e->type() == QEvent::PaletteChange) {
289          ui->addressBookButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
290          ui->pasteButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editpaste")));
291          ui->copySignatureButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/editcopy")));
292          ui->signMessageButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/edit")));
293          ui->clearButton_SM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
294          ui->addressBookButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/address-book")));
295          ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/transaction_0")));
296          ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/remove")));
297      }
298  
299      QDialog::changeEvent(e);
300  }