Merge pull request #2958 from laanwj/2013_08_txfee2

[Qt] Display txfee in first sendCoinsDialog message box
This commit is contained in:
Wladimir J. van der Laan 2013-09-02 09:48:10 -07:00
commit f4f048ee58
7 changed files with 207 additions and 47 deletions

View file

@ -240,7 +240,8 @@ HEADERS += src/qt/bitcoingui.h \
src/threadsafety.h \ src/threadsafety.h \
src/limitedmap.h \ src/limitedmap.h \
src/qt/splashscreen.h \ src/qt/splashscreen.h \
src/qt/intro.h src/qt/intro.h \
src/qt/walletmodeltransaction.h
SOURCES += src/qt/bitcoin.cpp \ SOURCES += src/qt/bitcoin.cpp \
src/qt/bitcoingui.cpp \ src/qt/bitcoingui.cpp \
@ -313,7 +314,8 @@ SOURCES += src/qt/bitcoin.cpp \
src/leveldb.cpp \ src/leveldb.cpp \
src/txdb.cpp \ src/txdb.cpp \
src/qt/splashscreen.cpp \ src/qt/splashscreen.cpp \
src/qt/intro.cpp src/qt/intro.cpp \
src/qt/walletmodeltransaction.cpp
RESOURCES += src/qt/bitcoin.qrc RESOURCES += src/qt/bitcoin.qrc

View file

@ -97,9 +97,18 @@ void SendCoinsDialog::on_sendButton_clicked()
QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount); QString amount = BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
if (rcp.authenticatedMerchant.isEmpty()) if (rcp.authenticatedMerchant.isEmpty())
{ {
QString address = rcp.address; QString recipientElement = QString("<b>%1</b> ").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount));
QString to = GUIUtil::HtmlEscape(rcp.label); recipientElement.append(tr("to"));
formatted.append(tr("<b>%1</b> to %2 (%3)").arg(amount, to, address));
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 else
{ {
@ -110,16 +119,6 @@ void SendCoinsDialog::on_sendButton_clicked()
fNewRecipientAllowed = false; 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()); WalletModel::UnlockContext ctx(model->requestUnlock());
if(!ctx.isValid()) if(!ctx.isValid())
@ -129,8 +128,11 @@ void SendCoinsDialog::on_sendButton_clicked()
return; return;
} }
WalletModel::SendCoinsReturn sendstatus = model->sendCoins(recipients); // prepare transaction for getting txFee earlier
switch(sendstatus.status) WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus = model->prepareTransaction(currentTransaction);
switch(prepareStatus.status)
{ {
case WalletModel::InvalidAddress: case WalletModel::InvalidAddress:
QMessageBox::warning(this, tr("Send Coins"), QMessageBox::warning(this, tr("Send Coins"),
@ -150,7 +152,7 @@ void SendCoinsDialog::on_sendButton_clicked()
case WalletModel::AmountWithFeeExceedsBalance: case WalletModel::AmountWithFeeExceedsBalance:
QMessageBox::warning(this, tr("Send Coins"), QMessageBox::warning(this, tr("Send Coins"),
tr("The total exceeds your balance when the %1 transaction fee is included."). 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); QMessageBox::Ok, QMessageBox::Ok);
break; break;
case WalletModel::DuplicateAddress: case WalletModel::DuplicateAddress:
@ -163,6 +165,51 @@ void SendCoinsDialog::on_sendButton_clicked()
tr("Error: Transaction creation failed!"), tr("Error: Transaction creation failed!"),
QMessageBox::Ok, QMessageBox::Ok); QMessageBox::Ok, QMessageBox::Ok);
break; 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: case WalletModel::TransactionCommitFailed:
QMessageBox::warning(this, tr("Send Coins"), 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."), 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: case WalletModel::OK:
accept(); accept();
break; break;
default:
break;
} }
fNewRecipientAllowed = true; fNewRecipientAllowed = true;
} }

View file

@ -10,6 +10,7 @@ namespace Ui {
class WalletModel; class WalletModel;
class SendCoinsEntry; class SendCoinsEntry;
class SendCoinsRecipient; class SendCoinsRecipient;
class OptionsModel;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QUrl; class QUrl;

View file

@ -5,7 +5,6 @@
#include "transactiontablemodel.h" #include "transactiontablemodel.h"
#include "ui_interface.h" #include "ui_interface.h"
#include "wallet.h"
#include "walletdb.h" // for BackupWallet #include "walletdb.h" // for BackupWallet
#include "base58.h" #include "base58.h"
@ -125,11 +124,11 @@ bool WalletModel::validateAddress(const QString &address)
return addressParsed.IsValid(); return addressParsed.IsValid();
} }
WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipient> &recipients) WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction)
{ {
qint64 total = 0; qint64 total = 0;
QList<SendCoinsRecipient> recipients = transaction.getRecipients();
std::vector<std::pair<CScript, int64> > vecSend; std::vector<std::pair<CScript, int64> > vecSend;
QByteArray transaction;
if(recipients.empty()) if(recipients.empty())
{ {
@ -193,58 +192,70 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
if((total + nTransactionFee) > getBalance()) if((total + nTransactionFee) > getBalance())
{ {
return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); transaction.setTransactionFee(nTransactionFee);
return SendCoinsReturn(AmountWithFeeExceedsBalance);
} }
{ {
LOCK2(cs_main, wallet->cs_wallet); LOCK2(cs_main, wallet->cs_wallet);
CReserveKey keyChange(wallet); transaction.newPossibleKeyChange(wallet);
int64 nFeeRequired = 0; int64 nFeeRequired = 0;
std::string strFailReason; 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(!fCreated)
{ {
if((total + nFeeRequired) > wallet->GetBalance()) if((total + nFeeRequired) > wallet->GetBalance())
{ {
return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); return SendCoinsReturn(AmountWithFeeExceedsBalance);
} }
emit message(tr("Send Coins"), QString::fromStdString(strFailReason), emit message(tr("Send Coins"), QString::fromStdString(strFailReason),
CClientUIInterface::MSG_ERROR); CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed; 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. // Store PaymentRequests in wtx.vOrderForm in wallet.
foreach(const SendCoinsRecipient &rcp, recipients) foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
{ {
if (rcp.paymentRequest.IsInitialized()) if (rcp.paymentRequest.IsInitialized())
{ {
std::string key("PaymentRequest"); std::string key("PaymentRequest");
std::string value; std::string value;
rcp.paymentRequest.SerializeToString(&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; return TransactionCommitFailed;
}
CTransaction* t = (CTransaction*)&wtx; CTransaction* t = (CTransaction*)newTx;
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << *t; 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, // Add addresses / update labels that we've sent to to the address book,
// and emit coinsSent signal // and emit coinsSent signal for each recipient
foreach(const SendCoinsRecipient &rcp, recipients) foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
{ {
std::string strAddress = rcp.address.toStdString(); std::string strAddress = rcp.address.toStdString();
CTxDestination dest = CBitcoinAddress(strAddress).Get(); CTxDestination dest = CBitcoinAddress(strAddress).Get();
@ -264,10 +275,10 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose 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() OptionsModel *WalletModel::getOptionsModel()

View file

@ -4,12 +4,15 @@
#include <QObject> #include <QObject>
#include "allocators.h" /* for SecureString */ #include "allocators.h" /* for SecureString */
#include "wallet.h"
#include "walletmodeltransaction.h"
#include "paymentrequestplus.h" #include "paymentrequestplus.h"
class OptionsModel; class OptionsModel;
class AddressTableModel; class AddressTableModel;
class TransactionTableModel; class TransactionTableModel;
class CWallet; class CWallet;
class WalletModelTransaction;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QTimer; class QTimer;
@ -74,15 +77,16 @@ public:
// Return status record for SendCoins, contains error id + information // Return status record for SendCoins, contains error id + information
struct SendCoinsReturn struct SendCoinsReturn
{ {
SendCoinsReturn(StatusCode status, SendCoinsReturn(StatusCode status):
qint64 fee=0): status(status) {}
status(status), fee(fee) {}
StatusCode 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 // Send coins to a list of recipients
SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients); SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
// Wallet encryption // Wallet encryption
bool setWalletEncrypted(bool encrypted, const SecureString &passphrase); bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);

View file

@ -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;
}

View file

@ -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