From 2bc4fd609ca00d5a5cb0b6b3eba5f35cb334b967 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 23 Dec 2011 10:14:57 -0500 Subject: [PATCH] Bitcoin-Qt signmessage GUI (pull request #582) --- bitcoin-qt.pro | 9 ++ src/bitcoinrpc.cpp | 2 - src/main.cpp | 3 + src/main.h | 1 + src/qt/addressbookpage.cpp | 22 +++++ src/qt/addressbookpage.h | 1 + src/qt/bitcoingui.cpp | 47 +++++++++ src/qt/bitcoingui.h | 6 ++ src/qt/forms/addressbookpage.ui | 14 +++ src/qt/forms/messagepage.ui | 170 ++++++++++++++++++++++++++++++++ src/qt/messagepage.cpp | 107 ++++++++++++++++++++ src/qt/messagepage.h | 38 +++++++ 12 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 src/qt/forms/messagepage.ui create mode 100644 src/qt/messagepage.cpp create mode 100644 src/qt/messagepage.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index cd68720fe..4913d0fec 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -68,6 +68,12 @@ contains(USE_SSL, 1) { DEFINES += USE_SSL } +# use: qmake "FIRST_CLASS_MESSAGING=1" +contains(FIRST_CLASS_MESSAGING, 1) { + message(Building with first-class messaging) + DEFINES += FIRST_CLASS_MESSAGING +} + contains(BITCOIN_NEED_QT_PLUGINS, 1) { DEFINES += BITCOIN_NEED_QT_PLUGINS QTPLUGIN += qcncodecs qjpcodecs qtwcodecs qkrcodecs @@ -91,6 +97,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/optionsdialog.h \ src/qt/sendcoinsdialog.h \ src/qt/addressbookpage.h \ + src/qt/messagepage.h \ src/qt/aboutdialog.h \ src/qt/editaddressdialog.h \ src/qt/bitcoinaddressvalidator.h \ @@ -154,6 +161,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/optionsdialog.cpp \ src/qt/sendcoinsdialog.cpp \ src/qt/addressbookpage.cpp \ + src/qt/messagepage.cpp \ src/qt/aboutdialog.cpp \ src/qt/editaddressdialog.cpp \ src/qt/bitcoinaddressvalidator.cpp \ @@ -204,6 +212,7 @@ RESOURCES += \ FORMS += \ src/qt/forms/sendcoinsdialog.ui \ src/qt/forms/addressbookpage.ui \ + src/qt/forms/messagepage.ui \ src/qt/forms/aboutdialog.ui \ src/qt/forms/editaddressdialog.ui \ src/qt/forms/transactiondescdialog.ui \ diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 3063abd1f..03c01f7b2 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -589,8 +589,6 @@ Value sendtoaddress(const Array& params, bool fHelp) return wtx.GetHash().GetHex(); } -static const string strMessageMagic = "Bitcoin Signed Message:\n"; - Value signmessage(const Array& params, bool fHelp) { if (fHelp || params.size() != 2) diff --git a/src/main.cpp b/src/main.cpp index 1dbd50f4e..9e13f7a36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -53,6 +53,9 @@ map mapOrphanTransactions; multimap mapOrphanTransactionsByPrev; +const string strMessageMagic = "Bitcoin Signed Message:\n"; + + double dHashesPerSec; int64 nHPSTimerStart; diff --git a/src/main.h b/src/main.h index 124b228ec..825c81e48 100644 --- a/src/main.h +++ b/src/main.h @@ -72,6 +72,7 @@ extern uint64 nPooledTx; extern unsigned int nTransactionsUpdated; extern uint64 nLastBlockTx; extern uint64 nLastBlockSize; +extern const std::string strMessageMagic; extern double dHashesPerSec; extern int64 nHPSTimerStart; extern int64 nTimeBestReceived; diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index 91412fcc1..b5a798ca3 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -2,6 +2,7 @@ #include "ui_addressbookpage.h" #include "addresstablemodel.h" +#include "bitcoingui.h" #include "editaddressdialog.h" #include "csvmodelwriter.h" #include "guiutil.h" @@ -156,6 +157,24 @@ void AddressBookPage::onEditAction() dlg.exec(); } +void AddressBookPage::on_signMessage_clicked() +{ + QTableView *table = ui->tableView; + QModelIndexList indexes = table->selectionModel()->selectedRows(AddressTableModel::Address); + QString addr; + + foreach (QModelIndex index, indexes) + { + QVariant address = index.data(); + addr = address.toString(); + } + + QObject *qoGUI = parent()->parent(); + BitcoinGUI *gui = qobject_cast(qoGUI); + if (gui) + gui->gotoMessagePage(addr); +} + void AddressBookPage::on_newAddressButton_clicked() { if(!model) @@ -207,11 +226,13 @@ void AddressBookPage::selectionChanged() // In sending tab, allow deletion of selection ui->deleteButton->setEnabled(true); deleteAction->setEnabled(true); + ui->signMessage->setEnabled(false); break; case ReceivingTab: // Deleting receiving addresses, however, is not allowed ui->deleteButton->setEnabled(false); deleteAction->setEnabled(false); + ui->signMessage->setEnabled(true); break; } ui->copyToClipboard->setEnabled(true); @@ -222,6 +243,7 @@ void AddressBookPage::selectionChanged() ui->deleteButton->setEnabled(false); ui->showQRCode->setEnabled(false); ui->copyToClipboard->setEnabled(false); + ui->signMessage->setEnabled(false); } } diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 6b08f8415..b2cf2db97 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -57,6 +57,7 @@ private slots: void on_newAddressButton_clicked(); /** Copy address of currently selected address entry to clipboard */ void on_copyToClipboard_clicked(); + void on_signMessage_clicked(); void selectionChanged(); void on_showQRCode_clicked(); /** Spawn contextual menu (right mouse menu) for address book entry */ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f9f92f0f0..3e0a12b9c 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -8,6 +8,7 @@ #include "transactiontablemodel.h" #include "addressbookpage.h" #include "sendcoinsdialog.h" +#include "messagepage.h" #include "optionsdialog.h" #include "aboutdialog.h" #include "clientmodel.h" @@ -99,12 +100,17 @@ BitcoinGUI::BitcoinGUI(QWidget *parent): sendCoinsPage = new SendCoinsDialog(this); + messagePage = new MessagePage(this); + centralWidget = new QStackedWidget(this); centralWidget->addWidget(overviewPage); centralWidget->addWidget(transactionsPage); centralWidget->addWidget(addressBookPage); centralWidget->addWidget(receiveCoinsPage); centralWidget->addWidget(sendCoinsPage); +#ifdef FIRST_CLASS_MESSAGING + centralWidget->addWidget(messagePage); +#endif setCentralWidget(centralWidget); // Create status bar @@ -192,6 +198,13 @@ void BitcoinGUI::createActions() sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); tabGroup->addAction(sendCoinsAction); + messageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message"), this); + messageAction->setToolTip(tr("Prove you control an address")); +#ifdef FIRST_CLASS_MESSAGING + messageAction->setCheckable(true); +#endif + tabGroup->addAction(messageAction); + connect(overviewAction, SIGNAL(triggered()), this, SLOT(show())); connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); connect(historyAction, SIGNAL(triggered()), this, SLOT(show())); @@ -202,6 +215,8 @@ void BitcoinGUI::createActions() connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(show())); connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + connect(messageAction, SIGNAL(triggered()), this, SLOT(show())); + connect(messageAction, SIGNAL(triggered()), this, SLOT(gotoMessagePage())); quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); quitAction->setToolTip(tr("Quit application")); @@ -247,6 +262,10 @@ void BitcoinGUI::createMenuBar() // Configure the menus QMenu *file = appMenuBar->addMenu(tr("&File")); +#ifndef FIRST_CLASS_MESSAGING + file->addAction(messageAction); + file->addSeparator(); +#endif file->addAction(quitAction); QMenu *settings = appMenuBar->addMenu(tr("&Settings")); @@ -269,6 +288,9 @@ void BitcoinGUI::createToolBars() toolbar->addAction(receiveCoinsAction); toolbar->addAction(historyAction); toolbar->addAction(addressBookAction); +#ifdef FIRST_CLASS_MESSAGING + toolbar->addAction(messageAction); +#endif QToolBar *toolbar2 = addToolBar(tr("Actions toolbar")); toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); @@ -323,6 +345,7 @@ void BitcoinGUI::setWalletModel(WalletModel *walletModel) addressBookPage->setModel(walletModel->getAddressTableModel()); receiveCoinsPage->setModel(walletModel->getAddressTableModel()); sendCoinsPage->setModel(walletModel); + messagePage->setModel(walletModel); setEncryptionStatus(walletModel->getEncryptionStatus()); connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); @@ -358,6 +381,10 @@ void BitcoinGUI::createTrayIcon() // Configuration of the tray icon (or dock icon) icon menu trayIconMenu->addAction(openBitcoinAction); trayIconMenu->addSeparator(); + trayIconMenu->addAction(messageAction); +#ifndef FIRST_CLASS_MESSAGING + trayIconMenu->addSeparator(); +#endif trayIconMenu->addAction(receiveCoinsAction); trayIconMenu->addAction(sendCoinsAction); trayIconMenu->addSeparator(); @@ -648,6 +675,26 @@ void BitcoinGUI::gotoSendCoinsPage() disconnect(exportAction, SIGNAL(triggered()), 0, 0); } +void BitcoinGUI::gotoMessagePage() +{ +#ifdef FIRST_CLASS_MESSAGING + messageAction->setChecked(true); + centralWidget->setCurrentWidget(messagePage); + + exportAction->setEnabled(false); + disconnect(exportAction, SIGNAL(triggered()), 0, 0); +#else + messagePage->show(); + messagePage->setFocus(); +#endif +} + +void BitcoinGUI::gotoMessagePage(QString addr) +{ + gotoMessagePage(); + messagePage->setAddress(addr); +} + void BitcoinGUI::dragEnterEvent(QDragEnterEvent *event) { // Accept only URLs diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index d54dc9fcd..09ad89a89 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -11,6 +11,7 @@ class TransactionView; class OverviewPage; class AddressBookPage; class SendCoinsDialog; +class MessagePage; class Notificator; QT_BEGIN_NAMESPACE @@ -62,6 +63,7 @@ private: AddressBookPage *addressBookPage; AddressBookPage *receiveCoinsPage; SendCoinsDialog *sendCoinsPage; + MessagePage *messagePage; QLabel *labelEncryptionIcon; QLabel *labelConnectionsIcon; @@ -75,6 +77,7 @@ private: QAction *quitAction; QAction *sendCoinsAction; QAction *addressBookAction; + QAction *messageAction; QAction *aboutAction; QAction *receiveCoinsAction; QAction *optionsAction; @@ -125,6 +128,9 @@ public slots: void askFee(qint64 nFeeRequired, bool *payFee); void handleURL(QString strURL); + void gotoMessagePage(); + void gotoMessagePage(QString); + private slots: /** Switch to overview (home) page */ void gotoOverviewPage(); diff --git a/src/qt/forms/addressbookpage.ui b/src/qt/forms/addressbookpage.ui index 9b301cbbf..b31a9ce99 100644 --- a/src/qt/forms/addressbookpage.ui +++ b/src/qt/forms/addressbookpage.ui @@ -90,6 +90,20 @@ + + + + Sign a message to prove you own this address + + + &Sign Message + + + + :/icons/edit:/icons/edit + + + diff --git a/src/qt/forms/messagepage.ui b/src/qt/forms/messagepage.ui new file mode 100644 index 000000000..8afa4b59d --- /dev/null +++ b/src/qt/forms/messagepage.ui @@ -0,0 +1,170 @@ + + + MessagePage + + + + 0 + 0 + 627 + 380 + + + + Message + + + + + + You can sign messages with your addresses to prove you own them. Be careful to only sign statement you agree to with full details, as phishing attacks may try to trick you into signing access to them. + + + Qt::AutoText + + + true + + + + + + + 0 + + + + + The address to send the payment to (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L) + + + 34 + + + + + + + Choose adress from address book + + + + + + + :/icons/address-book:/icons/address-book + + + Alt+A + + + false + + + false + + + + + + + Paste address from clipboard + + + + + + + :/icons/editpaste:/icons/editpaste + + + Alt+P + + + false + + + + + + + + + Enter the message you want to sign here + + + + + + + + true + + + + Click "Sign Message" to get signature + + + true + + + + + + + + + Sign a message to prove you own this address + + + &Sign Message + + + + :/icons/edit:/icons/edit + + + + + + + Copy the currently selected address to the system clipboard + + + &Copy to Clipboard + + + + :/icons/editcopy:/icons/editcopy + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + QValidatedLineEdit + QLineEdit +
qvalidatedlineedit.h
+
+
+ + + + +
diff --git a/src/qt/messagepage.cpp b/src/qt/messagepage.cpp new file mode 100644 index 000000000..dee1837ed --- /dev/null +++ b/src/qt/messagepage.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include "main.h" +#include "wallet.h" +#include "init.h" +#include "util.h" + +#include "messagepage.h" +#include "ui_messagepage.h" + +#include "addressbookpage.h" +#include "guiutil.h" +#include "walletmodel.h" + +#include +#include +#include +#include +#include + +MessagePage::MessagePage(QWidget *parent) : + QDialog(parent), + ui(new Ui::MessagePage) +{ + ui->setupUi(this); + + GUIUtil::setupAddressWidget(ui->signFrom, this); +} + +MessagePage::~MessagePage() +{ + delete ui; +} + +void MessagePage::setModel(WalletModel *model) +{ + this->model = model; +} + +void MessagePage::setAddress(QString addr) +{ + ui->signFrom->setText(addr); + ui->message->setFocus(); +} + +void MessagePage::on_pasteButton_clicked() +{ + setAddress(QApplication::clipboard()->text()); +} + +void MessagePage::on_addressBookButton_clicked() +{ + AddressBookPage dlg(AddressBookPage::ForSending, AddressBookPage::ReceivingTab, this); + dlg.setModel(model->getAddressTableModel()); + if(dlg.exec()) + { + setAddress(dlg.getReturnValue()); + } +} + +void MessagePage::on_copyToClipboard_clicked() +{ + QApplication::clipboard()->setText(ui->signature->text()); +} + +void MessagePage::on_signMessage_clicked() +{ + QString address = ui->signFrom->text(); + + CBitcoinAddress addr(address.toStdString()); + if (!addr.IsValid()) + { + QMessageBox::critical(this, tr("Error signing"), tr("%1 is not a valid address.").arg(address), + QMessageBox::Abort, QMessageBox::Abort); + return; + } + + WalletModel::UnlockContext ctx(model->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet was cancelled + return; + } + + CKey key; + if (!pwalletMain->GetKey(addr, key)) + { + QMessageBox::critical(this, tr("Error signing"), tr("Private key for %1 is not available.").arg(address), + QMessageBox::Abort, QMessageBox::Abort); + return; + } + + CDataStream ss(SER_GETHASH); + ss << strMessageMagic; + ss << ui->message->document()->toPlainText().toStdString(); + + std::vector vchSig; + if (!key.SignCompact(Hash(ss.begin(), ss.end()), vchSig)) + { + QMessageBox::critical(this, tr("Error signing"), tr("Sign failed"), + QMessageBox::Abort, QMessageBox::Abort); + } + + ui->signature->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); + ui->signature->setFont(GUIUtil::bitcoinAddressFont()); +} diff --git a/src/qt/messagepage.h b/src/qt/messagepage.h new file mode 100644 index 000000000..55e622812 --- /dev/null +++ b/src/qt/messagepage.h @@ -0,0 +1,38 @@ +#ifndef MESSAGEPAGE_H +#define MESSAGEPAGE_H + +#include + +namespace Ui { + class MessagePage; +} +class WalletModel; + +QT_BEGIN_NAMESPACE +QT_END_NAMESPACE + +class MessagePage : public QDialog +{ + Q_OBJECT + +public: + explicit MessagePage(QWidget *parent = 0); + ~MessagePage(); + + void setModel(WalletModel *model); + + void setAddress(QString); + +private: + Ui::MessagePage *ui; + WalletModel *model; + +private slots: + void on_pasteButton_clicked(); + void on_addressBookButton_clicked(); + + void on_signMessage_clicked(); + void on_copyToClipboard_clicked(); +}; + +#endif // MESSAGEPAGE_H