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