Merge pull request #3145

395d0d5 rework an ugly hack in processPaymentRequest() (Philip Kaufmann)
952d2cd make processPaymentRequest() use a single SendCoinsRecipient (Philip Kaufmann)
983cef4 payment-request UI: use SendCoinsRecipient.message for memo (Philip Kaufmann)
c6c97e0 [Qt] Rework of payment request UI (mainly for insecure pr) (Philip Kaufmann)
This commit is contained in:
Wladimir J. van der Laan 2013-11-06 16:39:04 +01:00
commit 65d0fc4b73
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
8 changed files with 614 additions and 96 deletions

View File

@ -29,7 +29,16 @@
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <item>

View File

@ -10,13 +10,19 @@
<height>150</height> <height>150</height>
</rect> </rect>
</property> </property>
<property name="focusPolicy">
<enum>Qt::TabFocus</enum>
</property>
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>
<widget class="QFrame" name="SendCoinsInsecure"> <widget class="QFrame" name="SendCoins">
<property name="toolTip">
<string>This is a normal payment.</string>
</property>
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::StyledPanel</enum>
</property> </property>
@ -143,7 +149,495 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QFrame" name="SendCoinsSecure"> <widget class="QFrame" name="SendCoins_InsecurePaymentRequest">
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>191</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>63</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>170</red>
<green>170</green>
<blue>84</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="BrightText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>191</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>220</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>191</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>63</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>170</red>
<green>170</green>
<blue>84</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="BrightText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>191</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>220</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>63</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>191</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>63</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>170</red>
<green>170</green>
<blue>84</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>63</blue>
</color>
</brush>
</colorrole>
<colorrole role="BrightText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>127</red>
<green>127</green>
<blue>63</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>220</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="toolTip">
<string>This is an unverified payment request.</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_is">
<property name="spacing">
<number>12</number>
</property>
<item row="4" column="0">
<widget class="QLabel" name="memoLabel_is">
<property name="text">
<string>Memo:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="amountLabel_is">
<property name="text">
<string>Amount:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="payToLabel_is">
<property name="text">
<string>Pay To:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="BitcoinAmountField" name="payAmount_is">
<property name="acceptDrops">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="2">
<layout class="QHBoxLayout" name="payToLayout_is">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="payTo_is"/>
</item>
</layout>
</item>
<item row="4" column="2">
<widget class="QLabel" name="memoTextLabel_is">
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="SendCoins_SecurePaymentRequest">
<property name="palette"> <property name="palette">
<palette> <palette>
<active> <active>
@ -586,6 +1080,9 @@
</disabled> </disabled>
</palette> </palette>
</property> </property>
<property name="toolTip">
<string>This is a verified payment request.</string>
</property>
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -607,35 +1104,26 @@
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy">
<cstring>addAsLabel</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="5" column="0">
<widget class="QLabel" name="amountLabel_s"> <widget class="QLabel" name="amountLabel_s">
<property name="text"> <property name="text">
<string>A&amp;mount:</string> <string>Amount:</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy">
<cstring>payAmount_s</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="payToLabel_s"> <widget class="QLabel" name="payToLabel_s">
<property name="text"> <property name="text">
<string>Pay &amp;To:</string> <string>Pay To:</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy">
<cstring>payTo_s</cstring>
</property>
</widget> </widget>
</item> </item>
<item row="5" column="2"> <item row="5" column="2">

View File

@ -280,9 +280,6 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString))); connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString)));
} }
} }
// netManager is null until uiReady() is called
netManager = NULL;
} }
PaymentServer::~PaymentServer() PaymentServer::~PaymentServer()
@ -378,6 +375,7 @@ void PaymentServer::handleURIOrFile(const QString& s)
fetchRequest(fetchUrl); fetchRequest(fetchUrl);
else else
qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl; qDebug() << "PaymentServer::handleURIOrFile : Invalid URL: " << fetchUrl;
return; return;
} }
@ -388,18 +386,17 @@ void PaymentServer::handleURIOrFile(const QString& s)
emit message(tr("URI handling"), emit message(tr("URI handling"),
tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."), tr("URI can not be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
CClientUIInterface::ICON_WARNING); CClientUIInterface::ICON_WARNING);
return; return;
} }
if (QFile::exists(s)) if (QFile::exists(s))
{ {
PaymentRequestPlus request; PaymentRequestPlus request;
QList<SendCoinsRecipient> recipients; SendCoinsRecipient recipient;
if (readPaymentRequest(s, request) && processPaymentRequest(request, recipients)) { if (readPaymentRequest(s, request) && processPaymentRequest(request, recipient))
foreach (const SendCoinsRecipient& recipient, recipients){ emit receivedPaymentRequest(recipient);
emit receivedPaymentRequest(recipient);
}
}
return; return;
} }
} }
@ -445,14 +442,37 @@ bool PaymentServer::readPaymentRequest(const QString& filename, PaymentRequestPl
return request.parse(data); return request.parse(data);
} }
bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<SendCoinsRecipient>& recipients) bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient)
{ {
if (!optionsModel) if (!optionsModel)
return false; return false;
QList<std::pair<CScript,qint64> > sendingTos = request.getPayTo(); recipient.paymentRequest = request;
qint64 totalAmount = 0; recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo());
request.getMerchant(PaymentServer::certStore, recipient.authenticatedMerchant);
QList<std::pair<CScript, qint64> > sendingTos = request.getPayTo();
QStringList addresses;
foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) { foreach(const PAIRTYPE(CScript, qint64)& sendingTo, sendingTos) {
// Extract and check destination addresses
CTxDestination dest;
if (ExtractDestination(sendingTo.first, dest)) {
// Append destination address
addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString()));
}
else if (!recipient.authenticatedMerchant.isEmpty()){
// Insecure payments to custom bitcoin addresses are not supported
// (there is no good way to tell the user where they are paying in a way
// they'd have a chance of understanding).
emit message(tr("Payment request error"),
tr("Unverified payment requests to custom payment scripts are unsupported."),
CClientUIInterface::MSG_ERROR);
return false;
}
// Extract and check amounts
CTxOut txOut(sendingTo.second, sendingTo.first); CTxOut txOut(sendingTo.second, sendingTo.first);
if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { if (txOut.IsDust(CTransaction::nMinRelayTxFee)) {
QString msg = tr("Requested payment amount of %1 is too small (considered dust).") QString msg = tr("Requested payment amount of %1 is too small (considered dust).")
@ -463,43 +483,16 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, QList<Sen
return false; return false;
} }
totalAmount += sendingTo.second; recipient.amount += sendingTo.second;
} }
// Store addresses and format them to fit nicely into the GUI
recipient.address = addresses.join("<br />");
recipients.append(SendCoinsRecipient()); if (!recipient.authenticatedMerchant.isEmpty()) {
qDebug() << "PaymentServer::processPaymentRequest : Secure payment request from " << recipient.authenticatedMerchant;
if (request.getMerchant(PaymentServer::certStore, recipients[0].authenticatedMerchant)) {
recipients[0].paymentRequest = request;
recipients[0].amount = totalAmount;
qDebug() << "PaymentServer::processPaymentRequest : Payment request from " << recipients[0].authenticatedMerchant;
} }
else { else {
recipients.clear(); qDebug() << "PaymentServer::processPaymentRequest : Insecure payment request to " << addresses.join(", ");
// Insecure payment requests may turn into more than one recipient if
// the merchant is requesting payment to more than one address.
for (int i = 0; i < sendingTos.size(); i++) {
std::pair<CScript, qint64>& sendingTo = sendingTos[i];
recipients.append(SendCoinsRecipient());
recipients[i].amount = sendingTo.second;
QString memo = QString::fromStdString(request.getDetails().memo());
recipients[i].label = GUIUtil::HtmlEscape(memo);
CTxDestination dest;
if (ExtractDestination(sendingTo.first, dest)) {
if (i == 0) // Tie request to first pay-to, we don't want multiple ACKs
recipients[i].paymentRequest = request;
recipients[i].address = QString::fromStdString(CBitcoinAddress(dest).ToString());
qDebug() << "PaymentServer::processPaymentRequest : Payment request, insecure " << recipients[i].address;
}
else {
// Insecure payments to custom bitcoin addresses are not supported
// (there is no good way to tell the user where they are paying in a way
// they'd have a chance of understanding).
emit message(tr("Payment request error"),
tr("Insecure requests to custom payment scripts unsupported"),
CClientUIInterface::MSG_ERROR);
return false;
}
}
} }
return true; return true;
@ -590,12 +583,9 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply)
if (requestType == "PaymentRequest") if (requestType == "PaymentRequest")
{ {
PaymentRequestPlus request; PaymentRequestPlus request;
QList<SendCoinsRecipient> recipients; SendCoinsRecipient recipient;
if (request.parse(data) && processPaymentRequest(request, recipients)) { if (request.parse(data) && processPaymentRequest(request, recipient))
foreach (const SendCoinsRecipient& recipient, recipients) { emit receivedPaymentRequest(recipient);
emit receivedPaymentRequest(recipient);
}
}
else else
qDebug() << "PaymentServer::netRequestFinished : Error processing payment request"; qDebug() << "PaymentServer::netRequestFinished : Error processing payment request";

View File

@ -113,7 +113,7 @@ private slots:
private: private:
static bool readPaymentRequest(const QString& filename, PaymentRequestPlus& request); static bool readPaymentRequest(const QString& filename, PaymentRequestPlus& request);
bool processPaymentRequest(PaymentRequestPlus& request, QList<SendCoinsRecipient>& recipients); bool processPaymentRequest(PaymentRequestPlus& request, SendCoinsRecipient& recipient);
void handleURIOrFile(const QString& s); void handleURIOrFile(const QString& s);
void fetchRequest(const QUrl& url); void fetchRequest(const QUrl& url);

View File

@ -106,7 +106,7 @@ void SendCoinsDialog::on_sendButton_clicked()
QString recipientElement; QString recipientElement;
if (rcp.authenticatedMerchant.isEmpty()) if (!rcp.paymentRequest.IsInitialized()) // normal payment
{ {
if(rcp.label.length() > 0) // label with address if(rcp.label.length() > 0) // label with address
{ {
@ -118,10 +118,14 @@ void SendCoinsDialog::on_sendButton_clicked()
recipientElement = tr("%1 to %2").arg(amount, address); recipientElement = tr("%1 to %2").arg(amount, address);
} }
} }
else // just merchant else if(!rcp.authenticatedMerchant.isEmpty()) // secure payment request
{ {
recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
} }
else // insecure payment request
{
recipientElement = tr("%1 to %2").arg(amount, address);
}
formatted.append(recipientElement); formatted.append(recipientElement);
} }
@ -317,7 +321,7 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
{ {
QString strSendCoins = tr("Send Coins"); QString strSendCoins = tr("Send Coins");
if (!rv.authenticatedMerchant.isEmpty()) { if (rv.paymentRequest.IsInitialized()) {
// Expired payment request? // Expired payment request?
const payments::PaymentDetails& details = rv.paymentRequest.getDetails(); const payments::PaymentDetails& details = rv.paymentRequest.getDetails();
if (details.has_expires() && (int64)details.expires() < GetTime()) if (details.has_expires() && (int64)details.expires() < GetTime())

View File

@ -22,7 +22,7 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
setCurrentWidget(ui->SendCoinsInsecure); setCurrentWidget(ui->SendCoins);
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
ui->payToLayout->setSpacing(4); ui->payToLayout->setSpacing(4);
@ -32,10 +32,12 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)")); ui->payTo->setPlaceholderText(tr("Enter a Bitcoin address (e.g. 1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L)"));
#endif #endif
setFocusPolicy(Qt::TabFocus);
setFocusProxy(ui->payTo); setFocusProxy(ui->payTo);
// normal bitcoin address field
GUIUtil::setupAddressWidget(ui->payTo, this); GUIUtil::setupAddressWidget(ui->payTo, this);
// just a label for displaying bitcoin address(es)
ui->payTo_is->setFont(GUIUtil::bitcoinAddressFont());
} }
SendCoinsEntry::~SendCoinsEntry() SendCoinsEntry::~SendCoinsEntry()
@ -71,7 +73,7 @@ void SendCoinsEntry::setModel(WalletModel *model)
{ {
this->model = model; this->model = model;
if(model && model->getOptionsModel()) if (model && model->getOptionsModel())
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
clear(); clear();
@ -84,11 +86,15 @@ void SendCoinsEntry::setRemoveEnabled(bool enabled)
void SendCoinsEntry::clear() void SendCoinsEntry::clear()
{ {
// clear UI elements for insecure payments // clear UI elements for normal payment
ui->payTo->clear(); ui->payTo->clear();
ui->addAsLabel->clear(); ui->addAsLabel->clear();
ui->payAmount->clear(); ui->payAmount->clear();
// and the ones for secure payments just to be sure // clear UI elements for insecure payment request
ui->payTo_is->clear();
ui->memoTextLabel_is->clear();
ui->payAmount_is->clear();
// clear UI elements for secure payment request
ui->payTo_s->clear(); ui->payTo_s->clear();
ui->memoTextLabel_s->clear(); ui->memoTextLabel_s->clear();
ui->payAmount_s->clear(); ui->payAmount_s->clear();
@ -106,20 +112,23 @@ void SendCoinsEntry::on_deleteButton_clicked()
bool SendCoinsEntry::validate() bool SendCoinsEntry::validate()
{ {
if (!model)
return false;
// Check input validity // Check input validity
bool retval = true; bool retval = true;
if (!recipient.authenticatedMerchant.isEmpty()) // Skip checks for payment request
if (recipient.paymentRequest.IsInitialized())
return retval; return retval;
if (!ui->payTo->hasAcceptableInput() || if (!ui->payTo->hasAcceptableInput() || !model->validateAddress(ui->payTo->text()))
(model && !model->validateAddress(ui->payTo->text())))
{ {
ui->payTo->setValid(false); ui->payTo->setValid(false);
retval = false; retval = false;
} }
if(!ui->payAmount->validate()) if (!ui->payAmount->validate())
{ {
retval = false; retval = false;
} }
@ -135,10 +144,11 @@ bool SendCoinsEntry::validate()
SendCoinsRecipient SendCoinsEntry::getValue() SendCoinsRecipient SendCoinsEntry::getValue()
{ {
if (!recipient.authenticatedMerchant.isEmpty()) // Payment request
if (recipient.paymentRequest.IsInitialized())
return recipient; return recipient;
// User-entered or non-authenticated: // Normal payment
recipient.address = ui->payTo->text(); recipient.address = ui->payTo->text();
recipient.label = ui->addAsLabel->text(); recipient.label = ui->addAsLabel->text();
recipient.amount = ui->payAmount->value(); recipient.amount = ui->payAmount->value();
@ -160,22 +170,31 @@ void SendCoinsEntry::setValue(const SendCoinsRecipient &value)
{ {
recipient = value; recipient = value;
if (recipient.authenticatedMerchant.isEmpty()) if (recipient.paymentRequest.IsInitialized()) // payment request
{
if (recipient.authenticatedMerchant.isEmpty()) // insecure
{
ui->payTo_is->setText(recipient.address);
ui->memoTextLabel_is->setText(recipient.message);
ui->payAmount_is->setValue(recipient.amount);
ui->payAmount_is->setReadOnly(true);
setCurrentWidget(ui->SendCoins_InsecurePaymentRequest);
}
else // secure
{
ui->payTo_s->setText(recipient.authenticatedMerchant);
ui->memoTextLabel_s->setText(recipient.message);
ui->payAmount_s->setValue(recipient.amount);
ui->payAmount_s->setReadOnly(true);
setCurrentWidget(ui->SendCoins_SecurePaymentRequest);
}
}
else // normal payment
{ {
ui->payTo->setText(recipient.address); ui->payTo->setText(recipient.address);
ui->addAsLabel->setText(recipient.label); ui->addAsLabel->setText(recipient.label);
ui->payAmount->setValue(recipient.amount); ui->payAmount->setValue(recipient.amount);
} }
else
{
const payments::PaymentDetails& details = recipient.paymentRequest.getDetails();
ui->payTo_s->setText(recipient.authenticatedMerchant);
ui->memoTextLabel_s->setText(QString::fromStdString(details.memo()));
ui->payAmount_s->setValue(recipient.amount);
ui->payAmount_s->setReadOnly(true);
setCurrentWidget(ui->SendCoinsSecure);
}
} }
void SendCoinsEntry::setAddress(const QString &address) void SendCoinsEntry::setAddress(const QString &address)
@ -186,7 +205,7 @@ void SendCoinsEntry::setAddress(const QString &address)
bool SendCoinsEntry::isClear() bool SendCoinsEntry::isClear()
{ {
return ui->payTo->text().isEmpty() && ui->payTo_s->text().isEmpty(); return ui->payTo->text().isEmpty() && ui->payTo_is->text().isEmpty() && ui->payTo_s->text().isEmpty();
} }
void SendCoinsEntry::setFocus() void SendCoinsEntry::setFocus()
@ -200,6 +219,7 @@ void SendCoinsEntry::updateDisplayUnit()
{ {
// Update payAmount with the current unit // Update payAmount with the current unit
ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); ui->payAmount->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
ui->payAmount_is->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
ui->payAmount_s->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); ui->payAmount_s->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
} }
} }

View File

@ -262,8 +262,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
// and emit coinsSent signal for each recipient // and emit coinsSent signal for each recipient
foreach(const SendCoinsRecipient &rcp, transaction.getRecipients()) foreach(const SendCoinsRecipient &rcp, transaction.getRecipients())
{ {
// Don't touch the address book when we have a secure payment-request // Don't touch the address book when we have a payment request
if (rcp.authenticatedMerchant.isEmpty()) if (!rcp.paymentRequest.IsInitialized())
{ {
std::string strAddress = rcp.address.toStdString(); std::string strAddress = rcp.address.toStdString();
CTxDestination dest = CBitcoinAddress(strAddress).Get(); CTxDestination dest = CBitcoinAddress(strAddress).Get();

View File

@ -29,14 +29,21 @@ public:
explicit SendCoinsRecipient(const QString &addr, const QString &label, quint64 amount, const QString &message): explicit SendCoinsRecipient(const QString &addr, const QString &label, quint64 amount, const QString &message):
address(addr), label(label), amount(amount), message(message) {} address(addr), label(label), amount(amount), message(message) {}
// If from an insecure payment request, this is used for storing
// the addresses, e.g. address-A<br />address-B<br />address-C.
// Info: As we don't need to process addresses in here when using
// payment requests, we can abuse it for displaying an address list.
// Todo: This is a hack, should be replaced with a cleaner solution!
QString address; QString address;
QString label; QString label;
qint64 amount; qint64 amount;
// If from a payment request, this is used for storing the memo
QString message; QString message;
// If from a payment request, paymentRequest.IsInitialized() will be true // If from a payment request, paymentRequest.IsInitialized() will be true
PaymentRequestPlus paymentRequest; PaymentRequestPlus paymentRequest;
QString authenticatedMerchant; // Empty if no authentication or invalid signature/cert/etc. // Empty if no authentication or invalid signature/cert/etc.
QString authenticatedMerchant;
}; };
/** Interface to Bitcoin wallet from Qt view code. */ /** Interface to Bitcoin wallet from Qt view code. */
@ -164,7 +171,7 @@ signals:
// this means that the unlocking failed or was cancelled. // this means that the unlocking failed or was cancelled.
void requireUnlock(); void requireUnlock();
// Asynchronous message notification // Fired when a message should be reported to the user
void message(const QString &title, const QString &message, unsigned int style); void message(const QString &title, const QString &message, unsigned int style);
// Coins sent: from wallet, to recipient, in (serialized) transaction: // Coins sent: from wallet, to recipient, in (serialized) transaction: