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