Skip to content

Commit

Permalink
TX Comment
Browse files Browse the repository at this point in the history
  • Loading branch information
Bushstar committed Oct 23, 2018
1 parent 30463e7 commit dc310d6
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 19 deletions.
2 changes: 2 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ static const int MAX_UNCONNECTING_HEADERS = 10;

static const bool DEFAULT_PEERBLOOMFILTERS = true;

static const unsigned int MAX_TX_COMMENT_LEN = 140;

struct BlockHasher
{
size_t operator()(const uint256& hash) const { return hash.GetCheapHash(); }
Expand Down
6 changes: 6 additions & 0 deletions src/policy/policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
return false;
}

// Disallow large transaction comments
if (tx.strTxComment.length() > MAX_TX_COMMENT_LEN) {
reason = "comment-size";
return false;
}

// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
Expand Down
27 changes: 27 additions & 0 deletions src/qt/forms/sendcoinsdialog.ui
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,33 @@
<property name="bottomMargin">
<number>8</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayoutTxComment">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelTxComment">
<property name="text">
<string>Transaction comment: </string>
</property>
<property name="buddy">
<cstring>editTxComment</cstring>
</property>
</widget>
</item>
<item>
<widget class="QValidatedLineEdit" name="editTxComment">
<property name="enabled">
<bool>true</bool>
</property>
<property name="toolTip">
<string>Enter a transaction comment up to 140 characters (ATTENTION: this information is public)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="frameCoinControl">
<property name="sizePolicy">
Expand Down
15 changes: 13 additions & 2 deletions src/qt/sendcoinsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *platformStyle, QWidget *pa
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));

ui->editTxComment->setPlaceholderText(tr("Enter a transaction comment up to 140 characters (NOTE: this information is public)"));

// Coin Control
connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
Expand Down Expand Up @@ -132,6 +134,8 @@ void SendCoinsDialog::setModel(WalletModel *model)
{
this->model = model;

QString txcomment = ui->editTxComment->text();

if(model && model->getOptionsModel())
{
for(int i = 0; i < ui->entries->count(); ++i)
Expand Down Expand Up @@ -199,6 +203,8 @@ void SendCoinsDialog::on_sendButton_clicked()
QList<SendCoinsRecipient> recipients;
bool valid = true;

QString txcomment = ui->editTxComment->text();

for(int i = 0; i < ui->entries->count(); ++i)
{
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
Expand Down Expand Up @@ -233,9 +239,9 @@ void SendCoinsDialog::on_sendButton_clicked()
WalletModelTransaction currentTransaction(recipients);
WalletModel::SendCoinsReturn prepareStatus;
if (model->getOptionsModel()->getCoinControlFeatures()) // coin control enabled
prepareStatus = model->prepareTransaction(currentTransaction, CoinControlDialog::coinControl);
prepareStatus = model->prepareTransaction(txcomment, currentTransaction, CoinControlDialog::coinControl);
else
prepareStatus = model->prepareTransaction(currentTransaction);
prepareStatus = model->prepareTransaction(txcomment, currentTransaction);

// process prepareStatus and on error generate message shown to user
processSendCoinsReturn(prepareStatus,
Expand Down Expand Up @@ -341,6 +347,8 @@ void SendCoinsDialog::on_sendButton_clicked()

void SendCoinsDialog::clear()
{
ui->editTxComment->clear();

// Remove entries until only one left
while(ui->entries->count())
{
Expand Down Expand Up @@ -404,6 +412,9 @@ void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)

QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
{
QWidget::setTabOrder(prev, ui->editTxComment);
prev = ui->editTxComment;

for(int i = 0; i < ui->entries->count(); ++i)
{
SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
Expand Down
4 changes: 3 additions & 1 deletion src/qt/transactiondesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "<br>";
if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty())
strHTML += "<br><b>" + tr("Comment") + ":</b><br>" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
if (!wtx.strTxComment.empty())
strHTML += "<b>" + tr("Transaction comment") + ":</b><br>" + wtx.strTxComment.c_str() + "<br>";

strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>";
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
Expand All @@ -263,7 +265,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
}
}

if (wtx.IsCoinBase())
if (wtx.IsCoinBase() || wtx.IsCoinStake())
{
quint32 numBlocksToMaturity = COINBASE_MATURITY + 1;
strHTML += "<br>" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "<br>";
Expand Down
7 changes: 5 additions & 2 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ bool WalletModel::validateAddress(const QString &address)
return addressParsed.IsValid();
}

WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl)
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(const QString &txcomment, WalletModelTransaction &transaction, const CCoinControl *coinControl)
{
CAmount total = 0;
bool fSubtractFeeFromAmount = false;
Expand Down Expand Up @@ -282,7 +282,10 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact

CWalletTx *newTx = transaction.getTransaction();
CReserveKey *keyChange = transaction.getPossibleKeyChange();
bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, true);
std::string strTxComment = txcomment.toStdString();
if (!strTxComment.empty())
strTxComment = "text:" + strTxComment;
bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, true, strTxComment);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && fCreated)
transaction.reassignAmounts(nChangePosRet);
Expand Down
2 changes: 1 addition & 1 deletion src/qt/walletmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class WalletModel : public QObject
};

// prepare transaction for getting txfee before sending coins
SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl = NULL);
SendCoinsReturn prepareTransaction(const QString &txcomment, WalletModelTransaction &transaction, const CCoinControl *coinControl = NULL);

// Send coins to a list of recipients
SendCoinsReturn sendCoins(WalletModelTransaction &transaction);
Expand Down
38 changes: 30 additions & 8 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
conflicts.push_back(conflict.GetHex());
entry.push_back(Pair("walletconflicts", conflicts));
entry.push_back(Pair("time", wtx.GetTxTime()));
entry.push_back(Pair("tx-comment", wtx.strTxComment));
entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));

// Add opt-in RBF status
Expand Down Expand Up @@ -336,7 +337,7 @@ UniValue getaddressesbyaccount(const UniValue& params, bool fHelp)
return ret;
}

static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew)
static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew, std::string strTxComment)
{
CAmount curBalance = pwalletMain->GetBalance();

Expand All @@ -358,7 +359,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) {
if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, NULL, true, strTxComment)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance())
strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
Expand All @@ -372,9 +373,9 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;

if (fHelp || params.size() < 2 || params.size() > 5)
if (fHelp || params.size() < 2 || params.size() > 6)
throw runtime_error(
"sendtoaddress \"trezarcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n"
"sendtoaddress \"trezarcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount \"tx-comment\")\n"
"\nSend an amount to a given address.\n"
+ HelpRequiringPassphrase() +
"\nArguments:\n"
Expand All @@ -387,6 +388,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
" transaction, just kept in your wallet.\n"
"5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
" The recipient will receive less trezarcoin than you enter in the amount field.\n"
"6. \"tx-comment\" (string, optional) A comment to send with the transaction \n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id.\n"
"\nExamples:\n"
Expand Down Expand Up @@ -418,9 +420,19 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp)
if (params.size() > 4)
fSubtractFeeFromAmount = params[4].get_bool();

// Transaction comment
std::string txcomment;
if(params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) {
txcomment = params[5].get_str();
if (!txcomment.empty())
txcomment = "text:" + txcomment;
if (txcomment.length() > MAX_TX_COMMENT_LEN)
txcomment.resize(MAX_TX_COMMENT_LEN);
}

EnsureWalletIsUnlocked();

SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx);
SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx, txcomment);

return wtx.GetHash().GetHex();
}
Expand Down Expand Up @@ -790,9 +802,9 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;

if (fHelp || params.size() < 3 || params.size() > 6)
if (fHelp || params.size() < 3 || params.size() > 7)
throw runtime_error(
"sendfrom \"fromaccount\" \"totrezarcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n"
"sendfrom \"fromaccount\" \"totrezarcoinaddress\" amount ( minconf \"comment\" \"comment-to\" \"tx-comment\" )\n"
"\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a trezarcoin address."
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
Expand All @@ -805,6 +817,7 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
"6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n"
" to which you're sending the transaction. This is not part of the transaction, \n"
" it is just kept in your wallet.\n"
"7. \"tx-comment\" (string, optional) A comment to send with the transaction \n"
"\nResult:\n"
"\"transactionid\" (string) The transaction id.\n"
"\nExamples:\n"
Expand Down Expand Up @@ -836,14 +849,23 @@ UniValue sendfrom(const UniValue& params, bool fHelp)
if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty())
wtx.mapValue["to"] = params[5].get_str();

std::string txcomment;
if ((params.size() > 6) && !params[6].isNull() && !params[6].get_str().empty()) {
txcomment = params[6].get_str();
if (!txcomment.empty())
txcomment = "text:" + txcomment;
if (txcomment.length() > MAX_TX_COMMENT_LEN)
txcomment.resize(MAX_TX_COMMENT_LEN);
}

EnsureWalletIsUnlocked();

// Check funds
CAmount nBalance = pwalletMain->GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");

SendMoney(address.Get(), nAmount, false, wtx);
SendMoney(address.Get(), nAmount, false, wtx, txcomment);

return wtx.GetHash().GetHex();
}
Expand Down
28 changes: 25 additions & 3 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2189,7 +2189,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
}

bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, std::string strTxComment)
{
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
Expand Down Expand Up @@ -2217,6 +2217,11 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
wtxNew.BindWallet(this);
CMutableTransaction txNew;

// transaction comment
txNew.strTxComment = strTxComment;
if (txNew.strTxComment.length() > MAX_TX_COMMENT_LEN)
txNew.strTxComment.resize(MAX_TX_COMMENT_LEN);

// Discourage fee sniping.
//
// For a large miner the value of the transactions in the best block and
Expand Down Expand Up @@ -2257,6 +2262,15 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
AvailableCoins(vAvailableCoins, true, coinControl);

nFeeRet = 0;
/* Transactions with comments require additional fees to deal with spam */
unsigned int nCommentLength = txNew.strTxComment.length();
if (nCommentLength) {
if (nCommentLength > 15) // Long comment, high fee
nFeeRet += 10 * CENT;
else // Short comment, low fee
nFeeRet += CENT;
}

// Start with no fee and loop until there is enough fee
while (true)
{
Expand Down Expand Up @@ -2478,7 +2492,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
break;
}

CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool, txNew.strTxComment);
if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
Expand Down Expand Up @@ -2590,10 +2604,18 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
}

CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, std::string strTxComment)
{
// payTxFee is user-set "I want to pay this much"
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
/* Transactions with comments require additional fees to deal with spam */
unsigned int nCommentLength = strTxComment.length();
if (nCommentLength) {
if (nCommentLength > 15) // Long comment, high fee
nFeeNeeded += 10 * CENT;
else // Short comment, low fee
nFeeNeeded += CENT;
}
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0) {
int estimateFoundTarget = nConfirmTarget;
Expand Down
4 changes: 2 additions & 2 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
* @note passing nChangePosInOut as -1 will result in setting a random position
*/
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, std::string strTxComment = "");
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);

bool AddAccountingEntry(const CAccountingEntry&, CWalletDB & pwalletdb);
Expand All @@ -776,7 +776,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, std::string strTxComment = "");
/**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee
Expand Down

0 comments on commit dc310d6

Please sign in to comment.