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/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index df3ff5c9a9..809eff9c27 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -97,9 +97,18 @@ void SendCoinsDialog::on_sendButton_clicked() QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); if (rcp.authenticatedMerchant.isEmpty()) { - QString address = rcp.address; - QString to = GUIUtil::HtmlEscape(rcp.label); - formatted.append(tr("%1 to %2 (%3)").arg(amount, to, address)); + QString recipientElement = QString("%1 ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount)); + recipientElement.append(tr("to")); + + if(rcp.label.length() > 0) + { + recipientElement.append(QString(" %1 %2
").arg(GUIUtil::HtmlEscape(rcp.label), rcp.address)); // add address with label + } + else + { + recipientElement.append(QString(" %1
").arg(rcp.address)); // add address WITHOUT label + } + formatted.append(recipientElement); } else { @@ -110,16 +119,6 @@ void SendCoinsDialog::on_sendButton_clicked() 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()) @@ -129,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"), @@ -150,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(model->getOptionsModel()->getDisplayUnit(), sendstatus.fee)), + arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee())), QMessageBox::Ok, QMessageBox::Ok); break; case WalletModel::DuplicateAddress: @@ -163,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("

%1"); + + if(txFee > 0) + { + // append fee string if a fee is required + questionString.append("
"); + questionString.append(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + questionString.append(" "); + 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("
"); + 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("
")), + 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."), @@ -173,6 +220,8 @@ void SendCoinsDialog::on_sendButton_clicked() case WalletModel::OK: accept(); break; + default: + break; } fNewRecipientAllowed = true; } 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/walletmodel.cpp b/src/qt/walletmodel.cpp index 82dc075a7b..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" @@ -125,11 +124,11 @@ bool WalletModel::validateAddress(const QString &address) return addressParsed.IsValid(); } -WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList &recipients) +WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction) { qint64 total = 0; + QList recipients = transaction.getRecipients(); std::vector > vecSend; - QByteArray transaction; if(recipients.empty()) { @@ -193,58 +192,70 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList 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(); @@ -264,10 +275,10 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QListSetAddressBook(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() diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ff7ded7b42..6abcdaf8cb 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -4,12 +4,15 @@ #include #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 &recipients); + SendCoinsReturn sendCoins(WalletModelTransaction &transaction); // Wallet encryption bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); 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 &recipients) : + recipients(recipients), + walletTransaction(0), + keyChange(0), + fee(0) +{ + walletTransaction = new CWalletTx(); +} + +WalletModelTransaction::~WalletModelTransaction() +{ + delete keyChange; + delete walletTransaction; +} + +QList 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 &recipients); + ~WalletModelTransaction(); + + QList getRecipients(); + + CWalletTx *getTransaction(); + + void setTransactionFee(qint64 newFee); + qint64 getTransactionFee(); + + qint64 getTotalTransactionAmount(); + + void newPossibleKeyChange(CWallet *wallet); + CReserveKey *getPossibleKeyChange(); + +private: + const QList recipients; + CWalletTx *walletTransaction; + CReserveKey *keyChange; + qint64 fee; + +public slots: + +}; + +#endif // WALLETMODELTRANSACTION_H