diff options
-rw-r--r-- | bitcoin-qt.pro | 6 | ||||
-rw-r--r-- | contrib/misc/example-linearize.cfg | 12 | ||||
-rwxr-xr-x | contrib/misc/linearize.py | 129 | ||||
-rw-r--r-- | src/init.cpp | 5 | ||||
-rw-r--r-- | src/qt/addresstablemodel.cpp | 65 | ||||
-rw-r--r-- | src/qt/addresstablemodel.h | 2 | ||||
-rw-r--r-- | src/qt/bitcoin.cpp | 22 | ||||
-rw-r--r-- | src/qt/bitcoinaddressvalidator.h | 4 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 5 | ||||
-rw-r--r-- | src/qt/intro.cpp | 7 | ||||
-rw-r--r-- | src/qt/intro.h | 2 | ||||
-rw-r--r-- | src/qt/paymentserver.cpp | 24 | ||||
-rw-r--r-- | src/qt/paymentserver.h | 18 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.cpp | 113 | ||||
-rw-r--r-- | src/qt/sendcoinsdialog.h | 1 | ||||
-rw-r--r-- | src/qt/test/paymentservertests.cpp | 3 | ||||
-rw-r--r-- | src/qt/transactiondesc.cpp | 26 | ||||
-rw-r--r-- | src/qt/transactiondesc.h | 2 | ||||
-rw-r--r-- | src/qt/transactiontablemodel.cpp | 24 | ||||
-rw-r--r-- | src/qt/walletmodel.cpp | 76 | ||||
-rw-r--r-- | src/qt/walletmodel.h | 16 | ||||
-rw-r--r-- | src/qt/walletmodeltransaction.cpp | 56 | ||||
-rw-r--r-- | src/qt/walletmodeltransaction.h | 37 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 4 | ||||
-rw-r--r-- | src/wallet.cpp | 17 | ||||
-rw-r--r-- | src/wallet.h | 10 |
26 files changed, 510 insertions, 176 deletions
diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 9baa03a4e3..00dd02da58 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -240,7 +240,8 @@ HEADERS += src/qt/bitcoingui.h \ src/threadsafety.h \ src/limitedmap.h \ src/qt/splashscreen.h \ - src/qt/intro.h + src/qt/intro.h \ + src/qt/walletmodeltransaction.h SOURCES += src/qt/bitcoin.cpp \ src/qt/bitcoingui.cpp \ @@ -313,7 +314,8 @@ SOURCES += src/qt/bitcoin.cpp \ src/leveldb.cpp \ src/txdb.cpp \ src/qt/splashscreen.cpp \ - src/qt/intro.cpp + src/qt/intro.cpp \ + src/qt/walletmodeltransaction.cpp RESOURCES += src/qt/bitcoin.qrc diff --git a/contrib/misc/example-linearize.cfg b/contrib/misc/example-linearize.cfg new file mode 100644 index 0000000000..9e5aa404c2 --- /dev/null +++ b/contrib/misc/example-linearize.cfg @@ -0,0 +1,12 @@ + +# bitcoind RPC settings +rpcuser=someuser +rpcpass=somepassword +host=127.0.0.1 +port=8332 + +# bootstrap.dat settings +netmagic=f9beb4d9 +max_height=250000 +output=bootstrap.dat + diff --git a/contrib/misc/linearize.py b/contrib/misc/linearize.py new file mode 100755 index 0000000000..2d8509f83c --- /dev/null +++ b/contrib/misc/linearize.py @@ -0,0 +1,129 @@ +#!/usr/bin/python +# +# linearize.py: Construct a linear, no-fork, best version of the blockchain. +# +# +# Copyright (c) 2013 The Bitcoin developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# + +import json +import struct +import re +import base64 +import httplib +import sys + +ERR_SLEEP = 15 +MAX_NONCE = 1000000L + +settings = {} + +class BitcoinRPC: + OBJID = 1 + + def __init__(self, host, port, username, password): + authpair = "%s:%s" % (username, password) + self.authhdr = "Basic %s" % (base64.b64encode(authpair)) + self.conn = httplib.HTTPConnection(host, port, False, 30) + def rpc(self, method, params=None): + self.OBJID += 1 + obj = { 'version' : '1.1', + 'method' : method, + 'id' : self.OBJID } + if params is None: + obj['params'] = [] + else: + obj['params'] = params + self.conn.request('POST', '/', json.dumps(obj), + { 'Authorization' : self.authhdr, + 'Content-type' : 'application/json' }) + + resp = self.conn.getresponse() + if resp is None: + print "JSON-RPC: no response" + return None + + body = resp.read() + resp_obj = json.loads(body) + if resp_obj is None: + print "JSON-RPC: cannot JSON-decode body" + return None + if 'error' in resp_obj and resp_obj['error'] != None: + return resp_obj['error'] + if 'result' not in resp_obj: + print "JSON-RPC: no result in object" + return None + + return resp_obj['result'] + def getblock(self, hash, verbose=True): + return self.rpc('getblock', [hash, verbose]) + def getblockhash(self, index): + return self.rpc('getblockhash', [index]) + +def getblock(rpc, settings, n): + hash = rpc.getblockhash(n) + hexdata = rpc.getblock(hash, False) + data = hexdata.decode('hex') + + return data + +def get_blocks(settings): + rpc = BitcoinRPC(settings['host'], settings['port'], + settings['rpcuser'], settings['rpcpass']) + + outf = open(settings['output'], 'wb') + + for height in xrange(settings['max_height']+1): + data = getblock(rpc, settings, height) + + outhdr = settings['netmagic'] + outhdr += struct.pack("<i", len(data)) + + outf.write(outhdr) + outf.write(data) + + if (height % 1000) == 0: + sys.stdout.write("Wrote block " + str(height) + "\n") + +if __name__ == '__main__': + if len(sys.argv) != 2: + print "Usage: linearize.py CONFIG-FILE" + sys.exit(1) + + f = open(sys.argv[1]) + for line in f: + # skip comment lines + m = re.search('^\s*#', line) + if m: + continue + + # parse key=value lines + m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + if m is None: + continue + settings[m.group(1)] = m.group(2) + f.close() + + if 'netmagic' not in settings: + settings['netmagic'] = 'f9beb4d9' + if 'output' not in settings: + settings['output'] = 'bootstrap.dat' + if 'host' not in settings: + settings['host'] = '127.0.0.1' + if 'port' not in settings: + settings['port'] = 8332 + if 'max_height' not in settings: + settings['max_height'] = 250000 + if 'rpcuser' not in settings or 'rpcpass' not in settings: + print "Missing username and/or password in cfg file" + sys.exit(1) + + settings['netmagic'] = settings['netmagic'].decode('hex') + settings['port'] = int(settings['port']) + settings['max_height'] = int(settings['max_height']) + + get_blocks(settings) + + diff --git a/src/init.cpp b/src/init.cpp index db368c7f53..57e20523b1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -377,8 +377,6 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 2: parameter interactions - Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); - if (mapArgs.count("-bind")) { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified @@ -427,6 +425,7 @@ bool AppInit2(boost::thread_group& threadGroup) fDebug = GetBoolArg("-debug", false); fBenchmark = GetBoolArg("-benchmark", false); mempool.fChecks = GetBoolArg("-checkmempool", RegTest()); + Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", 0); @@ -898,7 +897,7 @@ bool AppInit2(boost::thread_group& threadGroup) RandAddSeedPerfmon(); CPubKey newDefaultKey; - if (pwalletMain->GetKeyFromPool(newDefaultKey, false)) { + if (pwalletMain->GetKeyFromPool(newDefaultKey)) { pwalletMain->SetDefaultKey(newDefaultKey); if (!pwalletMain->SetAddressBook(pwalletMain->vchDefaultKey.GetID(), "", "receive")) strErrors << _("Cannot write default address") << "\n"; diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index dcc70222cc..be31b03749 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -15,7 +15,8 @@ struct AddressTableEntry { enum Type { Sending, - Receiving + Receiving, + Hidden /* QSortFilterProxyModel will filter these out */ }; Type type; @@ -43,6 +44,20 @@ struct AddressTableEntryLessThan } }; +/* Determine address type from address purpose */ +static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine) +{ + AddressTableEntry::Type addressType = AddressTableEntry::Hidden; + // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all. + if (strPurpose == "send") + addressType = AddressTableEntry::Sending; + else if (strPurpose == "receive") + addressType = AddressTableEntry::Receiving; + else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess + addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending); + return addressType; +} + // Private implementation class AddressTablePriv { @@ -62,17 +77,9 @@ public: BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook) { const CBitcoinAddress& address = item.first; - - AddressTableEntry::Type addressType; - const std::string& strPurpose = item.second.purpose; - if (strPurpose == "send") addressType = AddressTableEntry::Sending; - else if (strPurpose == "receive") addressType = AddressTableEntry::Receiving; - else if (strPurpose == "unknown") { - bool fMine = IsMine(*wallet, address.Get()); - addressType = (fMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending); - } - else continue; // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all. - + bool fMine = IsMine(*wallet, address.Get()); + AddressTableEntry::Type addressType = translateTransactionType( + QString::fromStdString(item.second.purpose), fMine); const std::string& strName = item.second.name; cachedAddressTable.append(AddressTableEntry(addressType, QString::fromStdString(strName), @@ -80,10 +87,12 @@ public: } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order + // Even though the map is already sorted this re-sorting step is needed because the originating map + // is sorted by binary address, not by base58() address. qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan()); } - void updateEntry(const QString &address, const QString &label, bool isMine, int status) + void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status) { // Find address / label in model QList<AddressTableEntry>::iterator lower = qLowerBound( @@ -93,7 +102,7 @@ public: int lowerIndex = (lower - cachedAddressTable.begin()); int upperIndex = (upper - cachedAddressTable.begin()); bool inModel = (lower != upper); - AddressTableEntry::Type newEntryType = isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending; + AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine); switch(status) { @@ -322,10 +331,11 @@ QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &par } } -void AddressTableModel::updateEntry(const QString &address, const QString &label, bool isMine, int status) +void AddressTableModel::updateEntry(const QString &address, + const QString &label, bool isMine, const QString &purpose, int status) { // Update address book model from Bitcoin core - priv->updateEntry(address, label, isMine, status); + priv->updateEntry(address, label, isMine, purpose, status); } QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address) @@ -355,18 +365,21 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con else if(type == Receive) { // Generate a new address to associate with given label - WalletModel::UnlockContext ctx(walletModel->requestUnlock()); - if(!ctx.isValid()) - { - // Unlock wallet failed or was cancelled - editStatus = WALLET_UNLOCK_FAILURE; - return QString(); - } CPubKey newKey; - if(!wallet->GetKeyFromPool(newKey, true)) + if(!wallet->GetKeyFromPool(newKey)) { - editStatus = KEY_GENERATION_FAILURE; - return QString(); + WalletModel::UnlockContext ctx(walletModel->requestUnlock()); + if(!ctx.isValid()) + { + // Unlock wallet failed or was cancelled + editStatus = WALLET_UNLOCK_FAILURE; + return QString(); + } + if(!wallet->GetKeyFromPool(newKey)) + { + editStatus = KEY_GENERATION_FAILURE; + return QString(); + } } strAddress = CBitcoinAddress(newKey.GetID()).ToString(); } diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 48baff5e54..6f532087fe 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -85,7 +85,7 @@ signals: public slots: /* Update address list from core. */ - void updateEntry(const QString &address, const QString &label, bool isMine, int status); + void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); friend class AddressTablePriv; }; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index e7cf440044..a4d589e167 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -155,12 +155,12 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans #if QT_VERSION < 0x050000 void DebugMessageHandler(QtMsgType type, const char * msg) { - OutputDebugStringF("%s\n", msg); + OutputDebugStringF("Bitcoin-Qt: %s\n", msg); } #else void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg) { - OutputDebugStringF("%s\n", qPrintable(msg)); + OutputDebugStringF("Bitcoin-Qt: %s\n", qPrintable(msg)); } #endif @@ -232,10 +232,16 @@ int main(int argc, char *argv[]) PaymentServer* paymentServer = new PaymentServer(&app); // User language is set up: pick a data directory - Intro::pickDataDirectory(); + Intro::pickDataDirectory(TestNet()); // Install global event filter that makes sure that long tooltips can be word-wrapped app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); + // Install qDebug() message handler to route to debug.log +#if QT_VERSION < 0x050000 + qInstallMsgHandler(DebugMessageHandler); +#else + qInstallMessageHandler(DebugMessageHandler); +#endif // ... now GUI settings: OptionsModel optionsModel; @@ -255,13 +261,6 @@ int main(int argc, char *argv[]) return 1; } - // Install qDebug() message handler to route to debug.log: -#if QT_VERSION < 0x050000 - qInstallMsgHandler(DebugMessageHandler); -#else - qInstallMessageHandler(DebugMessageHandler); -#endif - SplashScreen splash(QPixmap(), 0); if (GetBoolArg("-splash", true) && !GetBoolArg("-min", false)) { @@ -300,7 +299,8 @@ int main(int argc, char *argv[]) optionsModel.Upgrade(); // Must be done after AppInit2 PaymentServer::LoadRootCAs(); - paymentServer->initNetManager(optionsModel); + paymentServer->setOptionsModel(&optionsModel); + paymentServer->initNetManager(); if (splashref) splash.finish(&window); diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index 2b6a59367d..b7f4dfee96 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -3,8 +3,8 @@ #include <QValidator> -/** Base48 entry widget validator. - Corrects near-miss characters and refuses characters that are no part of base48. +/** Base58 entry widget validator. + Corrects near-miss characters and refuses characters that are not part of base58. */ class BitcoinAddressValidator : public QValidator { diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index ad32c9ea68..bb9eb60e5b 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -670,9 +670,12 @@ void BitcoinGUI::closeEvent(QCloseEvent *event) void BitcoinGUI::askFee(qint64 nFeeRequired, bool *payFee) { + if (!clientModel || !clientModel->getOptionsModel()) + return; + QString strMessage = tr("This transaction is over the size limit. You can still send it for a fee of %1, " "which goes to the nodes that process your transaction and helps to support the network. " - "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nFeeRequired)); + "Do you want to pay the fee?").arg(BitcoinUnits::formatWithUnit(clientModel->getOptionsModel()->getDisplayUnit(), nFeeRequired)); QMessageBox::StandardButton retval = QMessageBox::question( this, tr("Confirm transaction fee"), strMessage, QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Yes); diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 99db141c94..4a02ff89e7 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -142,7 +142,7 @@ QString Intro::getDefaultDataDirectory() return QString::fromStdString(GetDefaultDataDir().string()); } -void Intro::pickDataDirectory() +void Intro::pickDataDirectory(bool fIsTestnet) { namespace fs = boost::filesystem;; QSettings settings; @@ -160,6 +160,11 @@ void Intro::pickDataDirectory() /* If current default data directory does not exist, let the user choose one */ Intro intro; intro.setDataDirectory(dataDir); + if (!fIsTestnet) + intro.setWindowIcon(QIcon(":icons/bitcoin")); + else + intro.setWindowIcon(QIcon(":icons/bitcoin_testnet")); + while(true) { if(!intro.exec()) diff --git a/src/qt/intro.h b/src/qt/intro.h index 788799b7b0..8b09847abd 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -31,7 +31,7 @@ public: * @note do NOT call global GetDataDir() before calling this function, this * will cause the wrong path to be cached. */ - static void pickDataDirectory(); + static void pickDataDirectory(bool fIsTestnet); /** * Determine default data directory for operating system. diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index a9f71315a9..c7c6f6706a 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -92,7 +92,7 @@ static void ReportInvalidCertificate(const QSslCertificate& cert) } // -// Load openSSL's list of root certificate authorities +// Load OpenSSL's list of root certificate authorities // void PaymentServer::LoadRootCAs(X509_STORE* _store) { @@ -147,7 +147,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) const unsigned char *data = (const unsigned char *)certData.data(); X509* x509 = d2i_X509(0, &data, certData.size()); - if (x509 && X509_STORE_add_cert( PaymentServer::certStore, x509)) + if (x509 && X509_STORE_add_cert(PaymentServer::certStore, x509)) { // Note: X509_STORE_free will free the X509* objects when // the PaymentServer is destroyed @@ -303,18 +303,20 @@ bool PaymentServer::eventFilter(QObject *, QEvent *event) return false; } -void PaymentServer::initNetManager(const OptionsModel& options) +void PaymentServer::initNetManager() { + if (!optionsModel) + return; if (netManager != NULL) delete netManager; // netManager is used to fetch paymentrequests given in bitcoin: URI's netManager = new QNetworkAccessManager(this); - // Use proxy settings from options: + // Use proxy settings from optionsModel: QString proxyIP; quint16 proxyPort; - if (options.getProxySettings(proxyIP, proxyPort)) + if (optionsModel->getProxySettings(proxyIP, proxyPort)) { QNetworkProxy proxy; proxy.setType(QNetworkProxy::Socks5Proxy); @@ -435,13 +437,16 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<SendCoinsRecipient>& recipients) { + if (!optionsModel) + return false; + QList<std::pair<CScript,qint64> > sendingTos = request.getPayTo(); qint64 totalAmount = 0; foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) { CTxOut txOut(sendingTo.second, sendingTo.first); if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { QString message = QObject::tr("Requested payment amount (%1) too small") - .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendingTo.second)); + .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)); qDebug() << message; emit reportError(tr("Payment request error"), message, CClientUIInterface::MODAL); return false; @@ -531,7 +536,7 @@ PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QB } else { CPubKey newKey; - if (wallet->GetKeyFromPool(newKey, false)) { + if (wallet->GetKeyFromPool(newKey)) { CKeyID keyID = newKey.GetID(); wallet->SetAddressBook(keyID, strAccount, "refund"); @@ -614,3 +619,8 @@ PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &err } emit reportError(tr("Network request error"), errString, CClientUIInterface::MODAL); } + +void PaymentServer::setOptionsModel(OptionsModel *optionsModel) +{ + this->optionsModel = optionsModel; +} diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 7c6b2eabf0..131ede518e 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -17,7 +17,7 @@ // received at or during startup in a list. // // When startup is finished and the main window is -// show, a signal is sent to slot uiReady(), which +// shown, a signal is sent to slot uiReady(), which // emits a receivedURL() signal for any payment // requests that happened during startup. // @@ -70,13 +70,16 @@ public: // Return certificate store static X509_STORE* getCertStore() { return certStore; } - // Setup networking (options is used to get proxy settings) - void initNetManager(const OptionsModel& options); + // Setup networking + void initNetManager(); // Constructor registers this on the parent QApplication to // receive QEvent::FileOpen events bool eventFilter(QObject *object, QEvent *event); + // OptionsModel is used for getting proxy settings and display unit + void setOptionsModel(OptionsModel *optionsModel); + signals: // Fired when a valid payment request is received void receivedPaymentRequest(SendCoinsRecipient); @@ -106,12 +109,15 @@ private: void handleURIOrFile(const QString& s); void fetchRequest(const QUrl& url); - bool saveURIs; // true during startup + bool saveURIs; // true during startup QLocalServer* uriServer; - static X509_STORE* certStore; // Trusted root certificates + + static X509_STORE* certStore; // Trusted root certificates static void freeCertStore(); - QNetworkAccessManager* netManager; // Used to fetch payment requests + QNetworkAccessManager* netManager; // Used to fetch payment requests + + OptionsModel *optionsModel; }; #endif // PAYMENTSERVER_H diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 9086f6614e..809eff9c27 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -63,12 +63,12 @@ SendCoinsDialog::~SendCoinsDialog() void SendCoinsDialog::on_sendButton_clicked() { + if(!model || !model->getOptionsModel()) + return; + QList<SendCoinsRecipient> recipients; bool valid = true; - if(!model) - return; - for(int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); @@ -94,40 +94,31 @@ void SendCoinsDialog::on_sendButton_clicked() QStringList formatted; foreach(const SendCoinsRecipient &rcp, recipients) { - QString amount = BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount); + QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); if (rcp.authenticatedMerchant.isEmpty()) { - QString address = rcp.address; -#if QT_VERSION < 0x050000 - QString to = Qt::escape(rcp.label); -#else - QString to = rcp.label.toHtmlEscaped(); -#endif - formatted.append(tr("<b>%1</b> to %2 (%3)").arg(amount, to, address)); + QString recipientElement = QString("<b>%1</b> ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount)); + recipientElement.append(tr("to")); + + if(rcp.label.length() > 0) + { + recipientElement.append(QString(" %1 <span style='font-size:8px;'>%2</span><br />").arg(GUIUtil::HtmlEscape(rcp.label), rcp.address)); // add address with label + } + else + { + recipientElement.append(QString(" %1<br />").arg(rcp.address)); // add address WITHOUT label + } + formatted.append(recipientElement); } else { -#if QT_VERSION < 0x050000 - QString merchant = Qt::escape(rcp.authenticatedMerchant); -#else - QString merchant = rcp.authenticatedMerchant.toHtmlEscaped(); -#endif + QString merchant = GUIUtil::HtmlEscape(rcp.authenticatedMerchant); formatted.append(tr("<b>%1</b> to %2").arg(amount, merchant)); } } fNewRecipientAllowed = false; - QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), - tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))), - QMessageBox::Yes|QMessageBox::Cancel, - QMessageBox::Cancel); - - if(retval != QMessageBox::Yes) - { - fNewRecipientAllowed = true; - return; - } WalletModel::UnlockContext ctx(model->requestUnlock()); if(!ctx.isValid()) @@ -137,8 +128,11 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients); - switch(sendstatus.status) + // prepare transaction for getting txFee earlier + WalletModelTransaction currentTransaction(recipients); + WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction); + + switch(prepareStatus.status) { case WalletModel::InvalidAddress: QMessageBox::warning(this, tr("Send Coins"), @@ -158,7 +152,7 @@ void SendCoinsDialog::on_sendButton_clicked() case WalletModel::AmountWithFeeExceedsBalance: QMessageBox::warning(this, tr("Send Coins"), tr("The total exceeds your balance when the %1 transaction fee is included."). - arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, sendstatus.fee)), + arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::DuplicateAddress: @@ -171,6 +165,51 @@ void SendCoinsDialog::on_sendButton_clicked() tr("Error: Transaction creation failed!"), QMessageBox::Ok, QMessageBox::Ok); break; + case WalletModel::Aborted: // User aborted, nothing to do + case WalletModel::OK: + case WalletModel::TransactionCommitFailed: + break; + } + + if(prepareStatus.status != WalletModel::OK) { + fNewRecipientAllowed = true; + return; + } + + qint64 txFee = currentTransaction.getTransactionFee(); + QString questionString = tr("Are you sure you want to send?"); + questionString.append("<br /><br />%1"); + + if(txFee > 0) + { + // append fee string if a fee is required + questionString.append("<hr /><span style='color:#aa0000;'>"); + questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + questionString.append("</span> "); + questionString.append(tr("added as transaction fee")); + } + if(txFee > 0 || recipients.count() > 1) + { + // add total amount string if there are more then one recipients or a fee is required + questionString.append("<hr />"); + questionString.append(tr("Total Amount %1").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTotalTransactionAmount()+txFee))); + } + + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"), + questionString.arg(formatted.join("<br />")), + QMessageBox::Yes|QMessageBox::Cancel, + QMessageBox::Cancel); + + if(retval != QMessageBox::Yes) + { + fNewRecipientAllowed = true; + return; + } + + // now send the prepared transaction + WalletModel::SendCoinsReturn sendstatus = model->sendCoins(currentTransaction); + switch(sendstatus.status) + { case WalletModel::TransactionCommitFailed: QMessageBox::warning(this, tr("Send Coins"), tr("Error: The transaction was rejected. This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."), @@ -181,6 +220,8 @@ void SendCoinsDialog::on_sendButton_clicked() case WalletModel::OK: accept(); break; + default: + break; } fNewRecipientAllowed = true; } @@ -338,18 +379,14 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint { Q_UNUSED(unconfirmedBalance); Q_UNUSED(immatureBalance); - if(!model || !model->getOptionsModel()) - return; - int unit = model->getOptionsModel()->getDisplayUnit(); - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); + if(model && model->getOptionsModel()) + { + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance)); + } } void SendCoinsDialog::updateDisplayUnit() { - if(model && model->getOptionsModel()) - { - // Update labelBalance with the current balance and the current unit - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance())); - } + setBalance(model->getBalance(), 0, 0); } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index e75a003ba1..f4bffedc9b 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -10,6 +10,7 @@ namespace Ui { class WalletModel; class SendCoinsEntry; class SendCoinsRecipient; +class OptionsModel; QT_BEGIN_NAMESPACE class QUrl; diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 2e26ab0c9b..6c8ad62b2b 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -58,7 +58,8 @@ void PaymentServerTests::paymentServerTests() X509_STORE* caStore = X509_STORE_new(); X509_STORE_add_cert(caStore, parse_b64der_cert(caCert_BASE64)); PaymentServer::LoadRootCAs(caStore); - server->initNetManager(optionsModel); + server->setOptionsModel(&optionsModel); + server->initNetManager(); server->uiReady(); // Now feed PaymentRequests to server, and observe signals it produces: diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 25ff3623c0..55875c2e46 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -32,7 +32,7 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) } } -QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) +QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int unit) { QString strHTML; @@ -129,7 +129,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) nUnmatured += wallet->GetCredit(txout); strHTML += "<b>" + tr("Credit") + ":</b> "; if (wtx.IsInMainChain()) - strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; + strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; else strHTML += "(" + tr("not accepted") + ")"; strHTML += "<br>"; @@ -139,7 +139,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // // Credit // - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet) + "<br>"; } else { @@ -175,7 +175,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) } } - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -txout.nValue) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>"; } if (fAllToMe) @@ -183,13 +183,13 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // Payment to self int64 nChange = wtx.GetChange(); int64 nValue = nCredit - nChange; - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nValue) + "<br>"; - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nValue) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>"; } int64 nTxFee = nDebit - GetValueOut(wtx); if (nTxFee > 0) - strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -nTxFee) + "<br>"; + strHTML += "<b>" + tr("Transaction fee") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nTxFee) + "<br>"; } else { @@ -198,14 +198,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // BOOST_FOREACH(const CTxIn& txin, wtx.vin) if (wallet->IsMine(txin)) - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>"; } } - strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nNet, true) + "<br>"; + strHTML += "<b>" + tr("Net amount") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nNet, true) + "<br>"; // // Message @@ -243,10 +243,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "<hr><br>" + tr("Debug information") + "<br><br>"; BOOST_FOREACH(const CTxIn& txin, wtx.vin) if(wallet->IsMine(txin)) - strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "<br>"; + strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "<br>"; + strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>"; strHTML += "<br><b>" + tr("Transaction") + ":</b><br>"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); @@ -274,7 +274,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } - strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, vout.nValue); + strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + "</li>"; } } diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index cb0dda5b58..a659281dd2 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -14,7 +14,7 @@ class TransactionDesc: public QObject Q_OBJECT public: - static QString toHTML(CWallet *wallet, CWalletTx &wtx); + static QString toHTML(CWallet *wallet, CWalletTx &wtx, int unit); private: TransactionDesc() {} diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index baf1e16483..9c040d5b63 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -20,11 +20,11 @@ // Amount column is right-aligned it contains numbers static int column_alignments[] = { - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignLeft|Qt::AlignVCenter, - Qt::AlignRight|Qt::AlignVCenter + Qt::AlignLeft|Qt::AlignVCenter, /* status */ + Qt::AlignLeft|Qt::AlignVCenter, /* date */ + Qt::AlignLeft|Qt::AlignVCenter, /* type */ + Qt::AlignLeft|Qt::AlignVCenter, /* address */ + Qt::AlignRight|Qt::AlignVCenter /* amount */ }; // Comparison operator for sort/binary search of model tx list @@ -48,11 +48,12 @@ struct TxLessThan class TransactionTablePriv { public: - TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent): - wallet(wallet), - parent(parent) + TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent) : + wallet(wallet), + parent(parent) { } + CWallet *wallet; TransactionTableModel *parent; @@ -200,19 +201,18 @@ public: } } - QString describe(TransactionRecord *rec) + QString describe(TransactionRecord *rec, int unit) { { LOCK(wallet->cs_wallet); std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash); if(mi != wallet->mapWallet.end()) { - return TransactionDesc::toHTML(wallet, mi->second); + return TransactionDesc::toHTML(wallet, mi->second, unit); } } return QString(""); } - }; TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *parent): @@ -561,7 +561,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case DateRole: return QDateTime::fromTime_t(static_cast<uint>(rec->time)); case LongDescriptionRole: - return priv->describe(rec); + return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit()); case AddressRole: return QString::fromStdString(rec->address); case LabelRole: diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 61357647b7..2d3a6975e4 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -5,7 +5,6 @@ #include "transactiontablemodel.h" #include "ui_interface.h" -#include "wallet.h" #include "walletdb.h" // for BackupWallet #include "base58.h" @@ -112,10 +111,11 @@ void WalletModel::updateTransaction(const QString &hash, int status) } } -void WalletModel::updateAddressBook(const QString &address, const QString &label, bool isMine, int status) +void WalletModel::updateAddressBook(const QString &address, const QString &label, + bool isMine, const QString &purpose, int status) { if(addressTableModel) - addressTableModel->updateEntry(address, label, isMine, status); + addressTableModel->updateEntry(address, label, isMine, purpose, status); } bool WalletModel::validateAddress(const QString &address) @@ -124,11 +124,11 @@ bool WalletModel::validateAddress(const QString &address) return addressParsed.IsValid(); } -WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients) +WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction) { qint64 total = 0; + QList<SendCoinsRecipient> recipients = transaction.getRecipients(); std::vector<std::pair<CScript, int64> > vecSend; - QByteArray transaction; if(recipients.empty()) { @@ -192,58 +192,70 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie if((total + nTransactionFee) > getBalance()) { - return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); + transaction.setTransactionFee(nTransactionFee); + return SendCoinsReturn(AmountWithFeeExceedsBalance); } { LOCK2(cs_main, wallet->cs_wallet); - CReserveKey keyChange(wallet); + transaction.newPossibleKeyChange(wallet); int64 nFeeRequired = 0; std::string strFailReason; - CWalletTx wtx; - bool fCreated = wallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, strFailReason); + + CWalletTx *newTx = transaction.getTransaction(); + CReserveKey *keyChange = transaction.getPossibleKeyChange(); + bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, strFailReason); + transaction.setTransactionFee(nFeeRequired); if(!fCreated) { if((total + nFeeRequired) > wallet->GetBalance()) { - return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + return SendCoinsReturn(AmountWithFeeExceedsBalance); } emit message(tr("Send Coins"), QString::fromStdString(strFailReason), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } + } + + return SendCoinsReturn(OK); +} + +WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &transaction) +{ + QByteArray transaction_array; /* store serialized transaction */ + + { + LOCK2(cs_main, wallet->cs_wallet); + CWalletTx *newTx = transaction.getTransaction(); + // Store PaymentRequests in wtx.vOrderForm in wallet. - foreach(const SendCoinsRecipient &rcp, recipients) + foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) { std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); - wtx.vOrderForm.push_back(make_pair(key, value)); + newTx->vOrderForm.push_back(make_pair(key, value)); } - } - - if(!uiInterface.ThreadSafeAskFee(nFeeRequired)) - { - return Aborted; } - if(!wallet->CommitTransaction(wtx, keyChange)) - { + + CReserveKey *keyChange = transaction.getPossibleKeyChange(); + if(!wallet->CommitTransaction(*newTx, *keyChange)) return TransactionCommitFailed; - } - CTransaction* t = (CTransaction*)&wtx; + CTransaction* t = (CTransaction*)newTx; CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << *t; - transaction.append(&(ssTx[0]), ssTx.size()); + transaction_array.append(&(ssTx[0]), ssTx.size()); } // Add addresses / update labels that we've sent to to the address book, - // and emit coinsSent signal - foreach(const SendCoinsRecipient &rcp, recipients) + // and emit coinsSent signal for each recipient + foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) { std::string strAddress = rcp.address.toStdString(); CTxDestination dest = CBitcoinAddress(strAddress).Get(); @@ -263,10 +275,10 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose } } - emit coinsSent(wallet, rcp, transaction); + emit coinsSent(wallet, rcp, transaction_array); } - return SendCoinsReturn(OK, 0); + return SendCoinsReturn(OK); } OptionsModel *WalletModel::getOptionsModel() @@ -351,13 +363,17 @@ static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStor QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); } -static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, ChangeType status) +static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, + const CTxDestination &address, const std::string &label, bool isMine, + const std::string &purpose, ChangeType status) { - OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i status=%i\n", CBitcoinAddress(address).ToString().c_str(), label.c_str(), isMine, status); + OutputDebugStringF("NotifyAddressBookChanged %s %s isMine=%i purpose=%s status=%i\n", + CBitcoinAddress(address).ToString().c_str(), label.c_str(), isMine, purpose.c_str(), status); QMetaObject::invokeMethod(walletmodel, "updateAddressBook", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(CBitcoinAddress(address).ToString())), Q_ARG(QString, QString::fromStdString(label)), Q_ARG(bool, isMine), + Q_ARG(QString, QString::fromStdString(purpose)), Q_ARG(int, status)); } @@ -373,7 +389,7 @@ void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); - wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); } @@ -381,7 +397,7 @@ void WalletModel::unsubscribeFromCoreSignals() { // Disconnect signals from wallet wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); - wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 8cba10f5d2..6abcdaf8cb 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -4,12 +4,15 @@ #include <QObject> #include "allocators.h" /* for SecureString */ +#include "wallet.h" +#include "walletmodeltransaction.h" #include "paymentrequestplus.h" class OptionsModel; class AddressTableModel; class TransactionTableModel; class CWallet; +class WalletModelTransaction; QT_BEGIN_NAMESPACE class QTimer; @@ -74,15 +77,16 @@ public: // Return status record for SendCoins, contains error id + information struct SendCoinsReturn { - SendCoinsReturn(StatusCode status, - qint64 fee=0): - status(status), fee(fee) {} + SendCoinsReturn(StatusCode status): + status(status) {} StatusCode status; - qint64 fee; // is used in case status is "AmountWithFeeExceedsBalance" }; + // prepare transaction for getting txfee before sending coins + SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction); + // Send coins to a list of recipients - SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients); + SendCoinsReturn sendCoins(WalletModelTransaction &transaction); // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); @@ -165,7 +169,7 @@ public slots: /* New transaction, or transaction changed status */ void updateTransaction(const QString &hash, int status); /* New, updated or removed address book entry */ - void updateAddressBook(const QString &address, const QString &label, bool isMine, int status); + void updateAddressBook(const QString &address, const QString &label, bool isMine, const QString &purpose, int status); /* Current, immature or unconfirmed balance might have changed - emit 'balanceChanged' if so */ void pollBalanceChanged(); }; diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp new file mode 100644 index 0000000000..96fc3edbb2 --- /dev/null +++ b/src/qt/walletmodeltransaction.cpp @@ -0,0 +1,56 @@ +#include "walletmodeltransaction.h" + +WalletModelTransaction::WalletModelTransaction(const QList<SendCoinsRecipient> &recipients) : + recipients(recipients), + walletTransaction(0), + keyChange(0), + fee(0) +{ + walletTransaction = new CWalletTx(); +} + +WalletModelTransaction::~WalletModelTransaction() +{ + delete keyChange; + delete walletTransaction; +} + +QList<SendCoinsRecipient> WalletModelTransaction::getRecipients() +{ + return recipients; +} + +CWalletTx *WalletModelTransaction::getTransaction() +{ + return walletTransaction; +} + +qint64 WalletModelTransaction::getTransactionFee() +{ + return fee; +} + +void WalletModelTransaction::setTransactionFee(qint64 newFee) +{ + fee=newFee; +} + +qint64 WalletModelTransaction::getTotalTransactionAmount() +{ + qint64 totalTransactionAmount = 0; + foreach(const SendCoinsRecipient &rcp, recipients) + { + totalTransactionAmount+=rcp.amount; + } + return totalTransactionAmount; +} + +void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet) +{ + keyChange = new CReserveKey(wallet); +} + +CReserveKey *WalletModelTransaction::getPossibleKeyChange() +{ + return keyChange; +} diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h new file mode 100644 index 0000000000..c4848fb12d --- /dev/null +++ b/src/qt/walletmodeltransaction.h @@ -0,0 +1,37 @@ +#ifndef WALLETMODELTRANSACTION_H +#define WALLETMODELTRANSACTION_H + +#include "walletmodel.h" + +class SendCoinsRecipient; + +/** Data model for a walletmodel transaction. */ +class WalletModelTransaction +{ +public: + explicit WalletModelTransaction(const QList<SendCoinsRecipient> &recipients); + ~WalletModelTransaction(); + + QList<SendCoinsRecipient> getRecipients(); + + CWalletTx *getTransaction(); + + void setTransactionFee(qint64 newFee); + qint64 getTransactionFee(); + + qint64 getTotalTransactionAmount(); + + void newPossibleKeyChange(CWallet *wallet); + CReserveKey *getPossibleKeyChange(); + +private: + const QList<SendCoinsRecipient> recipients; + CWalletTx *walletTransaction; + CReserveKey *keyChange; + qint64 fee; + +public slots: + +}; + +#endif // WALLETMODELTRANSACTION_H diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 978c44e1a9..14b4956a15 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -110,7 +110,7 @@ Value getnewaddress(const Array& params, bool fHelp) // Generate a new key that is added to wallet CPubKey newKey; - if (!pwalletMain->GetKeyFromPool(newKey, false)) + if (!pwalletMain->GetKeyFromPool(newKey)) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); CKeyID keyID = newKey.GetID(); @@ -148,7 +148,7 @@ CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) // Generate a new key if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) { - if (!pwalletMain->GetKeyFromPool(account.vchPubKey, false)) + if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); diff --git a/src/wallet.cpp b/src/wallet.cpp index ddfd71efda..54ede12a50 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -493,7 +493,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) if (txout.scriptPubKey == scriptDefaultKey) { CPubKey newDefaultKey; - if (GetKeyFromPool(newDefaultKey, false)) + if (GetKeyFromPool(newDefaultKey)) { SetDefaultKey(newDefaultKey); SetAddressBook(vchDefaultKey.GetID(), "", "receive"); @@ -1461,7 +1461,11 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam { std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address); mapAddressBook[address].name = strName; - NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED); + if (!strPurpose.empty()) /* update purpose only if requested */ + mapAddressBook[address].purpose = strPurpose; + NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), + mapAddressBook[address].purpose, + (mi == mapAddressBook.end()) ? CT_NEW : CT_UPDATED); if (!fFileBacked) return false; if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) @@ -1472,7 +1476,7 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const string& strNam bool CWallet::DelAddressBook(const CTxDestination& address) { mapAddressBook.erase(address); - NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), CT_DELETED); + NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address), "", CT_DELETED); if (!fFileBacked) return false; CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString()); @@ -1647,7 +1651,7 @@ void CWallet::ReturnKey(int64 nIndex) printf("keypool return %"PRI64d"\n", nIndex); } -bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse) +bool CWallet::GetKeyFromPool(CPubKey& result) { int64 nIndex = 0; CKeyPool keypool; @@ -1656,11 +1660,6 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool fAllowReuse) ReserveKeyFromKeyPool(nIndex, keypool); if (nIndex == -1) { - if (fAllowReuse && vchDefaultKey.IsValid()) - { - result = vchDefaultKey; - return true; - } if (IsLocked()) return false; result = GenerateNewKey(); return true; diff --git a/src/wallet.h b/src/wallet.h index d47416d272..b529d5f28b 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -220,7 +220,7 @@ public: void ReserveKeyFromKeyPool(int64& nIndex, CKeyPool& keypool); void KeepKey(int64 nIndex); void ReturnKey(int64 nIndex); - bool GetKeyFromPool(CPubKey &key, bool fAllowReuse=true); + bool GetKeyFromPool(CPubKey &key); int64 GetOldestKeyPoolTime(); void GetAllReserveKeys(std::set<CKeyID>& setAddress) const; @@ -335,12 +335,16 @@ public: /** Address book entry changed. * @note called with lock cs_wallet held. */ - boost::signals2::signal<void (CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, ChangeType status)> NotifyAddressBookChanged; + boost::signals2::signal<void (CWallet *wallet, const CTxDestination + &address, const std::string &label, bool isMine, + const std::string &purpose, + ChangeType status)> NotifyAddressBookChanged; /** Wallet transaction added, removed or updated. * @note called with lock cs_wallet held. */ - boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx, ChangeType status)> NotifyTransactionChanged; + boost::signals2::signal<void (CWallet *wallet, const uint256 &hashTx, + ChangeType status)> NotifyTransactionChanged; }; /** A key allocated from the key pool. */ |