From ef079e183bf1be9f5a61a05018ee4480db86bc45 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 30 Jun 2011 18:05:29 +0200 Subject: [PATCH] Split off WalletModel from ClientModel, to be able to support multi-wallets in future --- bitcoin-qt.pro | 6 +- src/qt/bitcoin.cpp | 7 ++- src/qt/bitcoingui.cpp | 69 ++++++++++++--------- src/qt/bitcoingui.h | 7 ++- src/qt/clientmodel.cpp | 93 +--------------------------- src/qt/clientmodel.h | 31 +++------- src/qt/sendcoinsdialog.cpp | 14 ++--- src/qt/sendcoinsdialog.h | 6 +- src/qt/walletmodel.cpp | 124 +++++++++++++++++++++++++++++++++++++ src/qt/walletmodel.h | 62 +++++++++++++++++++ 10 files changed, 259 insertions(+), 160 deletions(-) create mode 100644 src/qt/walletmodel.cpp create mode 100644 src/qt/walletmodel.h diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 76841f84..539c3264 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -74,7 +74,8 @@ HEADERS += src/qt/bitcoingui.h \ src/wallet.h \ src/keystore.h \ src/qt/transactionfilterproxy.h \ - src/qt/transactionview.h + src/qt/transactionview.h \ + src/qt/walletmodel.h SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ @@ -109,7 +110,8 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/wallet.cpp \ src/keystore.cpp \ src/qt/transactionfilterproxy.cpp \ - src/qt/transactionview.cpp + src/qt/transactionview.cpp \ + src/qt/walletmodel.cpp RESOURCES += \ src/qt/bitcoin.qrc diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index c31be1b6..397af5fd 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -3,6 +3,7 @@ */ #include "bitcoingui.h" #include "clientmodel.h" +#include "walletmodel.h" #include "headers.h" #include "init.h" @@ -113,9 +114,11 @@ int main(int argc, char *argv[]) if(AppInit2(argc, argv)) { BitcoinGUI window; - ClientModel model(pwalletMain); + ClientModel clientModel(pwalletMain); + WalletModel walletModel(pwalletMain); guiref = &window; - window.setModel(&model); + window.setClientModel(&clientModel); + window.setWalletModel(&walletModel); window.show(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index f5885dbe..2b1990b9 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -10,6 +10,7 @@ #include "optionsdialog.h" #include "aboutdialog.h" #include "clientmodel.h" +#include "walletmodel.h" #include "guiutil.h" #include "editaddressdialog.h" #include "optionsmodel.h" @@ -42,7 +43,10 @@ #include BitcoinGUI::BitcoinGUI(QWidget *parent): - QMainWindow(parent), trayIcon(0) + QMainWindow(parent), + clientModel(0), + walletModel(0), + trayIcon(0) { resize(850, 550); setWindowTitle(tr("Bitcoin")); @@ -174,34 +178,43 @@ void BitcoinGUI::createActions() connect(openBitcoin, SIGNAL(triggered()), this, SLOT(show())); } -void BitcoinGUI::setModel(ClientModel *model) +void BitcoinGUI::setClientModel(ClientModel *clientModel) { - this->model = model; + this->clientModel = clientModel; // Keep up to date with client - setBalance(model->getBalance()); - connect(model, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); + setNumConnections(clientModel->getNumConnections()); + connect(clientModel, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumConnections(model->getNumConnections()); - connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); + setNumBlocks(clientModel->getNumBlocks()); + connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); - setNumTransactions(model->getNumTransactions()); - connect(model, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); + // Report errors from network/worker thread + connect(clientModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); +} + +void BitcoinGUI::setWalletModel(WalletModel *walletModel) +{ + this->walletModel = walletModel; - setNumBlocks(model->getNumBlocks()); - connect(model, SIGNAL(numBlocksChanged(int)), this, SLOT(setNumBlocks(int))); + // Keep up to date with wallet + setBalance(walletModel->getBalance()); + connect(walletModel, SIGNAL(balanceChanged(qint64)), this, SLOT(setBalance(qint64))); - setAddress(model->getAddressTableModel()->getDefaultAddress()); - connect(model->getAddressTableModel(), SIGNAL(defaultAddressChanged(QString)), this, SLOT(setAddress(QString))); + setNumTransactions(walletModel->getNumTransactions()); + connect(walletModel, SIGNAL(numTransactionsChanged(int)), this, SLOT(setNumTransactions(int))); - // Report errors from network/worker thread - connect(model, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); + setAddress(walletModel->getAddressTableModel()->getDefaultAddress()); + connect(walletModel->getAddressTableModel(), SIGNAL(defaultAddressChanged(QString)), this, SLOT(setAddress(QString))); + + // Report errors from wallet thread + connect(walletModel, SIGNAL(error(QString,QString)), this, SLOT(error(QString,QString))); // Put transaction list in tabs - transactionView->setModel(model->getTransactionTableModel()); + transactionView->setModel(walletModel->getTransactionTableModel()); // Balloon popup for new transaction - connect(model->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + connect(walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(incomingTransaction(const QModelIndex &, int, int))); } @@ -234,14 +247,14 @@ void BitcoinGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason) void BitcoinGUI::sendcoinsClicked() { SendCoinsDialog dlg; - dlg.setModel(model); + dlg.setModel(walletModel); dlg.exec(); } void BitcoinGUI::addressbookClicked() { AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(model->getAddressTableModel()); + dlg.setModel(walletModel->getAddressTableModel()); dlg.setTab(AddressBookDialog::SendingTab); dlg.exec(); } @@ -249,7 +262,7 @@ void BitcoinGUI::addressbookClicked() void BitcoinGUI::receivingAddressesClicked() { AddressBookDialog dlg(AddressBookDialog::ForEditing); - dlg.setModel(model->getAddressTableModel()); + dlg.setModel(walletModel->getAddressTableModel()); dlg.setTab(AddressBookDialog::ReceivingTab); dlg.exec(); } @@ -257,7 +270,7 @@ void BitcoinGUI::receivingAddressesClicked() void BitcoinGUI::optionsClicked() { OptionsDialog dlg; - dlg.setModel(model->getOptionsModel()); + dlg.setModel(clientModel->getOptionsModel()); dlg.exec(); } @@ -270,7 +283,7 @@ void BitcoinGUI::aboutClicked() void BitcoinGUI::newAddressClicked() { EditAddressDialog dlg(EditAddressDialog::NewReceivingAddress); - dlg.setModel(model->getAddressTableModel()); + dlg.setModel(walletModel->getAddressTableModel()); if(dlg.exec()) { QString newAddress = dlg.saveCurrentRow(); @@ -310,7 +323,7 @@ void BitcoinGUI::setNumConnections(int count) void BitcoinGUI::setNumBlocks(int count) { - int total = model->getTotalBlocksEstimate(); + int total = clientModel->getTotalBlocksEstimate(); if(count < total) { progressBarLabel->setVisible(true); @@ -353,7 +366,7 @@ void BitcoinGUI::changeEvent(QEvent *e) { if (e->type() == QEvent::WindowStateChange) { - if(model->getOptionsModel()->getMinimizeToTray()) + if(clientModel->getOptionsModel()->getMinimizeToTray()) { if (isMinimized()) { @@ -371,8 +384,8 @@ void BitcoinGUI::changeEvent(QEvent *e) void BitcoinGUI::closeEvent(QCloseEvent *event) { - if(!model->getOptionsModel()->getMinimizeToTray() && - !model->getOptionsModel()->getMinimizeOnClose()) + if(!clientModel->getOptionsModel()->getMinimizeToTray() && + !clientModel->getOptionsModel()->getMinimizeOnClose()) { qApp->quit(); } @@ -400,10 +413,10 @@ void BitcoinGUI::transactionDetails(const QModelIndex& idx) void BitcoinGUI::incomingTransaction(const QModelIndex & parent, int start, int end) { - TransactionTableModel *ttm = model->getTransactionTableModel(); + TransactionTableModel *ttm = walletModel->getTransactionTableModel(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent) .data(Qt::EditRole).toULongLong(); - if(amount>0 && !model->inInitialBlockDownload()) + if(amount>0 && !clientModel->inInitialBlockDownload()) { // On incoming transaction, make an info balloon // Unless the initial block download is in progress, to prevent balloon-spam diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index f6241a47..41b665c2 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -6,6 +6,7 @@ class TransactionTableModel; class ClientModel; +class WalletModel; class TransactionView; QT_BEGIN_NAMESPACE @@ -22,7 +23,8 @@ class BitcoinGUI : public QMainWindow Q_OBJECT public: explicit BitcoinGUI(QWidget *parent = 0); - void setModel(ClientModel *model); + void setClientModel(ClientModel *clientModel); + void setWalletModel(WalletModel *walletModel); /* Transaction table tab indices */ enum { @@ -37,7 +39,8 @@ protected: void closeEvent(QCloseEvent *event); private: - ClientModel *model; + ClientModel *clientModel; + WalletModel *walletModel; QLineEdit *address; QLabel *labelBalance; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index b70b71ee..125cb910 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -9,8 +9,7 @@ #include ClientModel::ClientModel(CWallet *wallet, QObject *parent) : - QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), - transactionTableModel(0) + QObject(parent), wallet(wallet), optionsModel(0) { // Until signal notifications is built into the bitcoin core, // simply update everything after polling using a timer. @@ -19,13 +18,6 @@ ClientModel::ClientModel(CWallet *wallet, QObject *parent) : timer->start(MODEL_UPDATE_DELAY); optionsModel = new OptionsModel(wallet, this); - addressTableModel = new AddressTableModel(wallet, this); - transactionTableModel = new TransactionTableModel(wallet, this); -} - -qint64 ClientModel::getBalance() const -{ - return wallet->GetBalance(); } int ClientModel::getNumConnections() const @@ -38,86 +30,13 @@ int ClientModel::getNumBlocks() const return nBestHeight; } -int ClientModel::getNumTransactions() const -{ - int numTransactions = 0; - CRITICAL_BLOCK(wallet->cs_mapWallet) - { - numTransactions = wallet->mapWallet.size(); - } - return numTransactions; -} - void ClientModel::update() { // Plainly emit all signals for now. To be more efficient this should check // whether the values actually changed first, although it'd be even better if these // were events coming in from the bitcoin core. - emit balanceChanged(getBalance()); emit numConnectionsChanged(getNumConnections()); emit numBlocksChanged(getNumBlocks()); - emit numTransactionsChanged(getNumTransactions()); - - addressTableModel->update(); -} - -ClientModel::StatusCode ClientModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) -{ - uint160 hash160 = 0; - bool valid = false; - - if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) - { - return InvalidAddress; - } - - if(payAmount <= 0) - { - return InvalidAmount; - } - - if(payAmount > getBalance()) - { - return AmountExceedsBalance; - } - - if((payAmount + nTransactionFee) > getBalance()) - { - return AmountWithFeeExceedsBalance; - } - - CRITICAL_BLOCK(cs_main) - { - // Send to bitcoin address - CWalletTx wtx; - CScript scriptPubKey; - scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; - - std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); - if (strError == "") - { - // OK - } - else if (strError == "ABORTED") - { - return Aborted; - } - else - { - emit error(tr("Sending..."), QString::fromStdString(strError)); - return MiscError; - } - } - - // Add addresses that we've sent to to the address book - std::string strAddress = payTo.toStdString(); - CRITICAL_BLOCK(wallet->cs_mapAddressBook) - { - if (!wallet->mapAddressBook.count(strAddress)) - wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); - } - - return OK; } bool ClientModel::inInitialBlockDownload() const @@ -130,18 +49,8 @@ int ClientModel::getTotalBlocksEstimate() const return GetTotalBlocksEstimate(); } - OptionsModel *ClientModel::getOptionsModel() { return optionsModel; } -AddressTableModel *ClientModel::getAddressTableModel() -{ - return addressTableModel; -} - -TransactionTableModel *ClientModel::getTransactionTableModel() -{ - return transactionTableModel; -} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 9c23a14a..a5028ff3 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -8,52 +8,35 @@ class AddressTableModel; class TransactionTableModel; class CWallet; +// Interface to Bitcoin network client class ClientModel : public QObject { Q_OBJECT public: + // The only reason that this constructor takes a wallet is because + // the global client settings are stored in the main wallet. explicit ClientModel(CWallet *wallet, QObject *parent = 0); - enum StatusCode - { - OK, - InvalidAmount, - InvalidAddress, - AmountExceedsBalance, - AmountWithFeeExceedsBalance, - Aborted, - MiscError - }; - OptionsModel *getOptionsModel(); - AddressTableModel *getAddressTableModel(); - TransactionTableModel *getTransactionTableModel(); - qint64 getBalance() const; int getNumConnections() const; int getNumBlocks() const; - int getNumTransactions() const; - /* Return true if core is doing initial block download */ + // Return true if core is doing initial block download bool inInitialBlockDownload() const; - /* Return conservative estimate of total number of blocks, or 0 if unknown */ + // Return conservative estimate of total number of blocks, or 0 if unknown int getTotalBlocksEstimate() const; - /* Send coins */ - StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); private: CWallet *wallet; OptionsModel *optionsModel; - AddressTableModel *addressTableModel; - TransactionTableModel *transactionTableModel; signals: - void balanceChanged(qint64 balance); void numConnectionsChanged(int count); void numBlocksChanged(int count); - void numTransactionsChanged(int count); - /* Asynchronous error notification */ + + // Asynchronous error notification void error(const QString &title, const QString &message); public slots: diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index d83962d1..5c889b23 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,6 +1,6 @@ #include "sendcoinsdialog.h" #include "ui_sendcoinsdialog.h" -#include "clientmodel.h" +#include "walletmodel.h" #include "guiutil.h" #include "addressbookdialog.h" @@ -29,7 +29,7 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent, const QString &address) : } } -void SendCoinsDialog::setModel(ClientModel *model) +void SendCoinsDialog::setModel(WalletModel *model) { this->model = model; } @@ -64,32 +64,32 @@ void SendCoinsDialog::on_sendButton_clicked() switch(model->sendCoins(ui->payTo->text(), payAmountParsed, label)) { - case ClientModel::InvalidAddress: + case WalletModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), tr("The recepient address is not valid, please recheck."), QMessageBox::Ok, QMessageBox::Ok); ui->payTo->setFocus(); break; - case ClientModel::InvalidAmount: + case WalletModel::InvalidAmount: QMessageBox::warning(this, tr("Send Coins"), tr("The amount to pay must be larger than 0."), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; - case ClientModel::AmountExceedsBalance: + case WalletModel::AmountExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Amount exceeds your balance"), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; - case ClientModel::AmountWithFeeExceedsBalance: + case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("Total exceeds your balance when the %1 transaction fee is included"). arg(GUIUtil::formatMoney(model->getOptionsModel()->getTransactionFee())), QMessageBox::Ok, QMessageBox::Ok); ui->payAmount->setFocus(); break; - case ClientModel::OK: + case WalletModel::OK: accept(); break; } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 206a854e..bbb6a5fc 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -6,7 +6,7 @@ namespace Ui { class SendCoinsDialog; } -class ClientModel; +class WalletModel; class SendCoinsDialog : public QDialog { @@ -16,11 +16,11 @@ public: explicit SendCoinsDialog(QWidget *parent = 0, const QString &address = ""); ~SendCoinsDialog(); - void setModel(ClientModel *model); + void setModel(WalletModel *model); private: Ui::SendCoinsDialog *ui; - ClientModel *model; + WalletModel *model; private slots: void on_addToAddressBook_toggled(bool checked); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp new file mode 100644 index 00000000..0fb7d210 --- /dev/null +++ b/src/qt/walletmodel.cpp @@ -0,0 +1,124 @@ +#include "walletmodel.h" +#include "guiconstants.h" +#include "optionsmodel.h" +#include "addresstablemodel.h" +#include "transactiontablemodel.h" + +#include "headers.h" + +#include + +WalletModel::WalletModel(CWallet *wallet, QObject *parent) : + QObject(parent), wallet(wallet), optionsModel(0), addressTableModel(0), + transactionTableModel(0) +{ + // Until signal notifications is built into the bitcoin core, + // simply update everything after polling using a timer. + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(MODEL_UPDATE_DELAY); + + optionsModel = new OptionsModel(wallet, this); + addressTableModel = new AddressTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(wallet, this); +} + +qint64 WalletModel::getBalance() const +{ + return wallet->GetBalance(); +} + +int WalletModel::getNumTransactions() const +{ + int numTransactions = 0; + CRITICAL_BLOCK(wallet->cs_mapWallet) + { + numTransactions = wallet->mapWallet.size(); + } + return numTransactions; +} + +void WalletModel::update() +{ + // Plainly emit all signals for now. To be more efficient this should check + // whether the values actually changed first, although it'd be even better if these + // were events coming in from the bitcoin core. + emit balanceChanged(getBalance()); + emit numTransactionsChanged(getNumTransactions()); + + addressTableModel->update(); +} + +WalletModel::StatusCode WalletModel::sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs) +{ + uint160 hash160 = 0; + bool valid = false; + + if(!AddressToHash160(payTo.toUtf8().constData(), hash160)) + { + return InvalidAddress; + } + + if(payAmount <= 0) + { + return InvalidAmount; + } + + if(payAmount > getBalance()) + { + return AmountExceedsBalance; + } + + if((payAmount + nTransactionFee) > getBalance()) + { + return AmountWithFeeExceedsBalance; + } + + CRITICAL_BLOCK(cs_main) + { + // Send to bitcoin address + CWalletTx wtx; + CScript scriptPubKey; + scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG; + + std::string strError = wallet->SendMoney(scriptPubKey, payAmount, wtx, true); + if (strError == "") + { + // OK + } + else if (strError == "ABORTED") + { + return Aborted; + } + else + { + emit error(tr("Sending..."), QString::fromStdString(strError)); + return MiscError; + } + } + + // Add addresses that we've sent to to the address book + std::string strAddress = payTo.toStdString(); + CRITICAL_BLOCK(wallet->cs_mapAddressBook) + { + if (!wallet->mapAddressBook.count(strAddress)) + wallet->SetAddressBookName(strAddress, addToAddressBookAs.toStdString()); + } + + return OK; +} + +OptionsModel *WalletModel::getOptionsModel() +{ + return optionsModel; +} + +AddressTableModel *WalletModel::getAddressTableModel() +{ + return addressTableModel; +} + +TransactionTableModel *WalletModel::getTransactionTableModel() +{ + return transactionTableModel; +} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h new file mode 100644 index 00000000..5b46dfb6 --- /dev/null +++ b/src/qt/walletmodel.h @@ -0,0 +1,62 @@ +#ifndef WALLETMODEL_H +#define WALLETMODEL_H + +#include + +class OptionsModel; +class AddressTableModel; +class TransactionTableModel; +class CWallet; + +// Interface to a Bitcoin wallet +class WalletModel : public QObject +{ + Q_OBJECT +public: + explicit WalletModel(CWallet *wallet, QObject *parent = 0); + + enum StatusCode + { + OK, + InvalidAmount, + InvalidAddress, + AmountExceedsBalance, + AmountWithFeeExceedsBalance, + Aborted, + MiscError + }; + + OptionsModel *getOptionsModel(); + AddressTableModel *getAddressTableModel(); + TransactionTableModel *getTransactionTableModel(); + + qint64 getBalance() const; + int getNumTransactions() const; + + /* Send coins */ + StatusCode sendCoins(const QString &payTo, qint64 payAmount, const QString &addToAddressBookAs=QString()); +private: + CWallet *wallet; + + // Wallet has an options model for wallet-specific options + // (transaction fee, for example) + OptionsModel *optionsModel; + + AddressTableModel *addressTableModel; + TransactionTableModel *transactionTableModel; + +signals: + void balanceChanged(qint64 balance); + void numTransactionsChanged(int count); + + // Asynchronous error notification + void error(const QString &title, const QString &message); + +public slots: + +private slots: + void update(); +}; + + +#endif // WALLETMODEL_H