Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add memo field in UI #1493

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 46 additions & 7 deletions src/qt/forms/sendcoinsentry.ui
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@
</item>
</layout>
</item>
<item row="4" column="0">
<item row="6" column="0">
<widget class="QLabel" name="messageLabel">
<property name="text">
<string>Message:</string>
Expand All @@ -231,15 +231,54 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="messageTextLabel">
<item row="6" column="1">
<widget class="QLineEdit" name="messageTextLabel">
<property name="toolTip">
<string>A message that was attached to the firo: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Firo network.</string>
<string>Optional message for this transaction</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</widget>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayoutWarning" stretch="0,1">
<property name="spacing">
<number>0</number>
</property>
</widget>
<item>
<widget class="QLabel" name="iconMessageWarning">
<property name="toolTip">
<string></string>
</property>
<property name="text">
<string></string>
</property>
<property name="minimumWidth">
5
</property>
<property name="minimumHeight">
5
</property>
<property name="fixedWidth">
10
</property>
<property name="fixedHeight">
10
</property>
<property name="styleSheet">
<string>margin-left:-30px;margin-right:-10px;margin-top:2px;</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="messageWarning">
<property name="text">
<string></string>
</property>
<property name="styleSheet">
<string>color: #FFA800; margin-left:-10px;</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="2">
<widget class="Line" name="line">
Expand Down
13 changes: 13 additions & 0 deletions src/qt/sendcoinsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ void SendCoinsDialog::on_sendButton_clicked()
}
ctx = dialog->getUnlockContext();
}
recipient.message = entry->getValue().message;
recipients.append(recipient);
}
else
Expand Down Expand Up @@ -532,6 +533,18 @@ void SendCoinsDialog::on_sendButton_clicked()
QString questionString = tr("Are you sure you want to send?");
questionString.append(warningMessage);
questionString.append("<br /><br />%1");
bool firstMessage = true;
for (const auto& rec : recipients) {
if (!rec.message.isEmpty()) {
if (firstMessage) {
questionString.append("<hr><b>" + tr("Messages") + ":</b><br>");
firstMessage = false;
}
QString sanitizedMsg = GUIUtil::HtmlEscape(rec.message, true);
questionString.append("• " + sanitizedMsg + "<br>");
}
}

double txSize;
if ((fAnonymousMode == false) && (recipients.size() == sparkAddressCount) && spark::IsSparkAllowed())
{
Expand Down
39 changes: 39 additions & 0 deletions src/qt/sendcoinsentry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "optionsmodel.h"
#include "platformstyle.h"
#include "walletmodel.h"
#include "../spark/sparkwallet.h"
#include "../wallet/wallet.h"

#include <QApplication>
Expand All @@ -29,6 +30,7 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par
QIcon icon_;
icon_.addFile(QString::fromUtf8(":/icons/ic_warning"), QSize(), QIcon::Normal, QIcon::On);
ui->iconWarning->setPixmap(icon_.pixmap(18, 18));
ui->iconMessageWarning->setPixmap(icon_.pixmap(18, 18));

ui->addressBookButton->setIcon(platformStyle->SingleColorIcon(":/icons/address-book"));
ui->pasteButton->setIcon(platformStyle->SingleColorIcon(":/icons/editpaste"));
Expand All @@ -55,13 +57,43 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par
connect(ui->deleteButton, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked);
connect(ui->deleteButton_is, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked);
connect(ui->deleteButton_s, &QToolButton::clicked, this, &SendCoinsEntry::deleteClicked);
connect(ui->messageTextLabel, &QLineEdit::textChanged, this, &SendCoinsEntry::on_MemoTextChanged);

ui->messageLabel->setVisible(false);
ui->messageTextLabel->setVisible(false);
ui->iconMessageWarning->setVisible(false);
}

SendCoinsEntry::~SendCoinsEntry()
{
delete ui;
}

void SendCoinsEntry::on_MemoTextChanged(const QString &text)
{
const spark::Params* params = spark::Params::get_default();
int maxLength = params->get_memo_bytes();
bool isOverLimit = text.length() > maxLength;

if (isOverLimit) {
ui->messageWarning->setText(QString("Message exceeds %1 bytes limit").arg(maxLength));
ui->messageWarning->setVisible(true);
ui->messageTextLabel->setStyleSheet("border: 1px solid red;");
ui->iconMessageWarning->setVisible(true);
} else {
QString sanitized = text;
sanitized.remove(QRegExp("[\\x00-\\x1F\\x7F]"));
if (sanitized != text) {
ui->messageTextLabel->setText(sanitized);
return;
}
ui->messageWarning->clear();
ui->messageWarning->setVisible(false);
ui->messageTextLabel->setStyleSheet("");
ui->iconMessageWarning->setVisible(false);
}
}
levoncrypto marked this conversation as resolved.
Show resolved Hide resolved

void SendCoinsEntry::on_pasteButton_clicked()
{
// Paste text from clipboard into recipient field
Expand All @@ -85,6 +117,13 @@ void SendCoinsEntry::on_payTo_textChanged(const QString &address)
{
updateLabel(address);
setWarning(fAnonymousMode);

bool isSparkAddress = false;
if (model) {
isSparkAddress = model->validateSparkAddress(address);
}
ui->messageLabel->setVisible(isSparkAddress);
ui->messageTextLabel->setVisible(isSparkAddress);
}

void SendCoinsEntry::setModel(WalletModel *_model)
Expand Down
1 change: 1 addition & 0 deletions src/qt/sendcoinsentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public Q_SLOTS:
private Q_SLOTS:
void deleteClicked();
void on_payTo_textChanged(const QString &address);
void on_MemoTextChanged(const QString &text);
void on_addressBookButton_clicked();
void on_pasteButton_clicked();
void updateDisplayUnit();
Expand Down
42 changes: 38 additions & 4 deletions src/qt/transactiondesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,44 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("Transaction total size") + ":</b> " + QString::number(wtx.tx->GetTotalSize()) + " bytes<br>";
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";

// Message from normal firo:URI (firo:123...?message=example)
for (const PAIRTYPE(std::string, std::string)& r : wtx.vOrderForm)
if (r.first == "Message")
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
isminetype fAllFromMe = ISMINE_SPENDABLE;
bool foundSparkOutput = false;

for (const CTxIn& txin : wtx.tx->vin) {
isminetype mine = wallet->IsMine(txin, *wtx.tx);
fAllFromMe = std::min(fAllFromMe, mine);
}

bool firstMessage = true;
if (fAllFromMe) {
for (const CTxOut& txout : wtx.tx->vout) {
if (wtx.IsChange(txout)) continue;

CSparkOutputTx sparkOutput;
if (wallet->GetSparkOutputTx(txout.scriptPubKey, sparkOutput)) {
if (!sparkOutput.memo.empty()) {
foundSparkOutput = true;
if (firstMessage) {
strHTML += "<hr><b>" + tr("Messages") + ":</b><br>";
firstMessage = false;
}
strHTML += "• " + GUIUtil::HtmlEscape(sparkOutput.memo, true) + "<br>";
}
}
}
}

if (!foundSparkOutput && wallet->sparkWallet) {
for (const auto& [id, meta] : wallet->sparkWallet->getMintMap()) {
if (meta.txid == rec->hash && !meta.memo.empty()) {
if (firstMessage) {
strHTML += "<hr><b>" + tr("Messages") + ":</b><br>";
firstMessage = false;
}
strHTML += "• " + GUIUtil::HtmlEscape(meta.memo, true) + "<br>";
}
}
}

if (wtx.IsCoinBase())
{
Expand Down
4 changes: 2 additions & 2 deletions src/qt/walletmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareMintSparkTransaction(std::vecto
address.decode(rcp.address.toStdString());
spark::MintedCoinData data;
data.address = address;
data.memo = "";
data.memo = rcp.message.toStdString();
data.v = rcp.amount;
outputs.push_back(data);
total += rcp.amount;
Expand Down Expand Up @@ -1481,7 +1481,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareSpendSparkTransaction(WalletMod
address.decode(rcp.address.toStdString());
spark::OutputCoinData data;
data.address = address;
data.memo = "";
data.memo = rcp.message.toStdString();
data.v = rcp.amount;
privateRecipients.push_back(std::make_pair(data, rcp.fSubtractFeeFromAmount));
} else {
Expand Down
11 changes: 11 additions & 0 deletions src/spark/primitives.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ class CSparkOutputTx
{
public:
std::string address;
std::string memo;
int64_t amount;

CSparkOutputTx()
Expand All @@ -97,6 +98,7 @@ class CSparkOutputTx
void SetNull()
{
address = "";
memo = "";
amount = 0;
}

Expand All @@ -105,6 +107,15 @@ class CSparkOutputTx
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(address);
READWRITE(amount);
if (ser_action.ForRead()) {
if (!s.empty()) {
READWRITE(memo);
} else {
memo = "";
}
} else {
READWRITE(memo);
}
}
};

Expand Down
9 changes: 8 additions & 1 deletion src/spark/sparkwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,12 @@ std::vector<CRecipient> CSparkWallet::CreateSparkMintRecipients(
script.insert(script.end(), serializedCoins[i].begin(), serializedCoins[i].end());
unsigned char network = spark::GetNetworkType();
std::string addr = outputs[i].address.encode(network);
CRecipient recipient = {script, CAmount(outputs[i].v), false, addr};
std::string memo = outputs[i].memo;
const std::size_t max_memo_size = outputs[i].address.get_params()->get_memo_bytes();
if (memo.length() > max_memo_size) {
throw std::runtime_error(strprintf("Memo exceeds maximum length of %d bytes", max_memo_size));
}
CRecipient recipient = {script, CAmount(outputs[i].v), false, addr, memo};
levoncrypto marked this conversation as resolved.
Show resolved Hide resolved
results.emplace_back(recipient);
}

Expand Down Expand Up @@ -1093,6 +1098,7 @@ bool CSparkWallet::CreateSparkMintTransactions(
CSparkOutputTx output;
output.address = recipient.address;
output.amount = recipient.nAmount;
output.memo = recipient.memo;
walletdb.WriteSparkOutputTx(recipient.scriptPubKey, output);
break;
}
Expand Down Expand Up @@ -1530,6 +1536,7 @@ CWalletTx CSparkWallet::CreateSparkSpendTransaction(
CSparkOutputTx output;
output.address = privOutputs[i].address.encode(network);
output.amount = privOutputs[i].v;
output.memo = privOutputs[i].memo;
walletdb.WriteSparkOutputTx(script, output);
tx.vout.push_back(CTxOut(0, script));
i++;
Expand Down
1 change: 1 addition & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ struct CRecipient
CAmount nAmount;
bool fSubtractFeeFromAmount;
std::string address;
std::string memo;
levoncrypto marked this conversation as resolved.
Show resolved Hide resolved
};

typedef std::map<std::string, std::string> mapValue_t;
Expand Down
Loading