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