Dash Core  0.12.2.1
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Copyright (c) 2014-2017 The Dash Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include "paymentserver.h"
7 
8 #include "bitcoinunits.h"
9 #include "guiutil.h"
10 #include "optionsmodel.h"
11 
12 #include "base58.h"
13 #include "chainparams.h"
14 #include "validation.h" // For minRelayTxFee
15 #include "ui_interface.h"
16 #include "util.h"
17 #include "wallet/wallet.h"
18 
19 #include <cstdlib>
20 
21 #include <openssl/x509_vfy.h>
22 
23 #include <QApplication>
24 #include <QByteArray>
25 #include <QDataStream>
26 #include <QDateTime>
27 #include <QDebug>
28 #include <QFile>
29 #include <QFileOpenEvent>
30 #include <QHash>
31 #include <QList>
32 #include <QLocalServer>
33 #include <QLocalSocket>
34 #include <QNetworkAccessManager>
35 #include <QNetworkProxy>
36 #include <QNetworkReply>
37 #include <QNetworkRequest>
38 #include <QSslCertificate>
39 #include <QSslError>
40 #include <QSslSocket>
41 #include <QStringList>
42 #include <QTextDocument>
43 
44 #if QT_VERSION < 0x050000
45 #include <QUrl>
46 #else
47 #include <QUrlQuery>
48 #endif
49 
50 const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
51 const QString BITCOIN_IPC_PREFIX("dash:");
52 // BIP70 payment protocol messages
53 const char* BIP70_MESSAGE_PAYMENTACK = "PaymentACK";
54 const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
55 // BIP71 payment protocol media types
56 const char* BIP71_MIMETYPE_PAYMENT = "application/dash-payment";
57 const char* BIP71_MIMETYPE_PAYMENTACK = "application/dash-paymentack";
58 const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/dash-paymentrequest";
59 // BIP70 max payment request size in bytes (DoS protection)
60 const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000;
61 
63  void operator()(X509_STORE* b) {
64  X509_STORE_free(b);
65  }
66 };
67 
68 struct X509Deleter {
69  void operator()(X509* b) { X509_free(b); }
70 };
71 
72 namespace // Anon namespace
73 {
74  std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
75 }
76 
77 //
78 // Create a name that is unique for:
79 // testnet / non-testnet
80 // data directory
81 //
82 static QString ipcServerName()
83 {
84  QString name("DashQt");
85 
86  // Append a simple hash of the datadir
87  // Note that GetDataDir(true) returns a different path
88  // for -testnet versus main net
89  QString ddir(QString::fromStdString(GetDataDir(true).string()));
90  name.append(QString::number(qHash(ddir)));
91 
92  return name;
93 }
94 
95 //
96 // We store payment URIs and requests received before
97 // the main GUI window is up and ready to ask the user
98 // to send payment.
99 
100 static QList<QString> savedPaymentRequests;
101 
102 static void ReportInvalidCertificate(const QSslCertificate& cert)
103 {
104 #if QT_VERSION < 0x050000
105  qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
106 #else
107  qDebug() << QString("%1: Payment server found an invalid certificate: ").arg(__func__) << cert.serialNumber() << cert.subjectInfo(QSslCertificate::CommonName) << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier) << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
108 #endif
109 }
110 
111 //
112 // Load OpenSSL's list of root certificate authorities
113 //
114 void PaymentServer::LoadRootCAs(X509_STORE* _store)
115 {
116  // Unit tests mostly use this, to pass in fake root CAs:
117  if (_store)
118  {
119  certStore.reset(_store);
120  return;
121  }
122 
123  // Normal execution, use either -rootcertificates or system certs:
124  certStore.reset(X509_STORE_new());
125 
126  // Note: use "-system-" default here so that users can pass -rootcertificates=""
127  // and get 'I don't like X.509 certificates, don't trust anybody' behavior:
128  QString certFile = QString::fromStdString(GetArg("-rootcertificates", "-system-"));
129 
130  // Empty store
131  if (certFile.isEmpty()) {
132  qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
133  return;
134  }
135 
136  QList<QSslCertificate> certList;
137 
138  if (certFile != "-system-") {
139  qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
140 
141  certList = QSslCertificate::fromPath(certFile);
142  // Use those certificates when fetching payment requests, too:
143  QSslSocket::setDefaultCaCertificates(certList);
144  } else
145  certList = QSslSocket::systemCaCertificates();
146 
147  int nRootCerts = 0;
148  const QDateTime currentTime = QDateTime::currentDateTime();
149 
150  Q_FOREACH (const QSslCertificate& cert, certList) {
151  // Don't log NULL certificates
152  if (cert.isNull())
153  continue;
154 
155  // Not yet active/valid, or expired certificate
156  if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
158  continue;
159  }
160 
161 #if QT_VERSION >= 0x050000
162  // Blacklisted certificate
163  if (cert.isBlacklisted()) {
165  continue;
166  }
167 #endif
168  QByteArray certData = cert.toDer();
169  const unsigned char *data = (const unsigned char *)certData.data();
170 
171  std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
172  if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
173  {
174  // Note: X509_STORE increases the reference count to the X509 object,
175  // we still have to release our reference to it.
176  ++nRootCerts;
177  }
178  else
179  {
181  continue;
182  }
183  }
184  qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts << " root certificates";
185 
186  // Project for another day:
187  // Fetch certificate revocation lists, and add them to certStore.
188  // Issues to consider:
189  // performance (start a thread to fetch in background?)
190  // privacy (fetch through tor/proxy so IP address isn't revealed)
191  // would it be easier to just use a compiled-in blacklist?
192  // or use Qt's blacklist?
193  // "certificate stapling" with server-side caching is more efficient
194 }
195 
196 //
197 // Sending to the server is done synchronously, at startup.
198 // If the server isn't already running, startup continues,
199 // and the items in savedPaymentRequest will be handled
200 // when uiReady() is called.
201 //
202 // Warning: ipcSendCommandLine() is called early in init,
203 // so don't use "Q_EMIT message()", but "QMessageBox::"!
204 //
205 void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
206 {
207  for (int i = 1; i < argc; i++)
208  {
209  QString arg(argv[i]);
210  if (arg.startsWith("-"))
211  continue;
212 
213  // If the dash: URI contains a payment request, we are not able to detect the
214  // network as that would require fetching and parsing the payment request.
215  // That means clicking such an URI which contains a testnet payment request
216  // will start a mainnet instance and throw a "wrong network" error.
217  if (arg.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // dash: URI
218  {
219  savedPaymentRequests.append(arg);
220 
222  if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
223  {
224  CBitcoinAddress address(r.address.toStdString());
225 
226  if (address.IsValid(Params(CBaseChainParams::MAIN)))
227  {
229  }
230  else if (address.IsValid(Params(CBaseChainParams::TESTNET)))
231  {
233  }
234  }
235  }
236  else if (QFile::exists(arg)) // Filename
237  {
238  savedPaymentRequests.append(arg);
239 
240  PaymentRequestPlus request;
241  if (readPaymentRequestFromFile(arg, request))
242  {
243  if (request.getDetails().network() == "main")
244  {
246  }
247  else if (request.getDetails().network() == "test")
248  {
250  }
251  }
252  }
253  else
254  {
255  // Printing to debug.log is about the best we can do here, the
256  // GUI hasn't started yet so we can't pop up a message box.
257  qWarning() << "PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
258  }
259  }
260 }
261 
262 //
263 // Sending to the server is done synchronously, at startup.
264 // If the server isn't already running, startup continues,
265 // and the items in savedPaymentRequest will be handled
266 // when uiReady() is called.
267 //
269 {
270  bool fResult = false;
271  Q_FOREACH (const QString& r, savedPaymentRequests)
272  {
273  QLocalSocket* socket = new QLocalSocket();
274  socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
275  if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT))
276  {
277  delete socket;
278  socket = NULL;
279  return false;
280  }
281 
282  QByteArray block;
283  QDataStream out(&block, QIODevice::WriteOnly);
284  out.setVersion(QDataStream::Qt_4_0);
285  out << r;
286  out.device()->seek(0);
287 
288  socket->write(block);
289  socket->flush();
290  socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
291  socket->disconnectFromServer();
292 
293  delete socket;
294  socket = NULL;
295  fResult = true;
296  }
297 
298  return fResult;
299 }
300 
301 PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
302  QObject(parent),
303  saveURIs(true),
304  uriServer(0),
305  netManager(0),
306  optionsModel(0)
307 {
308  // Verify that the version of the library that we linked against is
309  // compatible with the version of the headers we compiled against.
310  GOOGLE_PROTOBUF_VERIFY_VERSION;
311 
312  // Install global event filter to catch QFileOpenEvents
313  // on Mac: sent when you click dash: links
314  // other OSes: helpful when dealing with payment request files
315  if (parent)
316  parent->installEventFilter(this);
317 
318  QString name = ipcServerName();
319 
320  // Clean up old socket leftover from a crash:
321  QLocalServer::removeServer(name);
322 
323  if (startLocalServer)
324  {
325  uriServer = new QLocalServer(this);
326 
327  if (!uriServer->listen(name)) {
328  // constructor is called early in init, so don't use "Q_EMIT message()" here
329  QMessageBox::critical(0, tr("Payment request error"),
330  tr("Cannot start dash: click-to-pay handler"));
331  }
332  else {
333  connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
334  connect(this, SIGNAL(receivedPaymentACK(QString)), this, SLOT(handlePaymentACK(QString)));
335  }
336  }
337 }
338 
340 {
341  google::protobuf::ShutdownProtobufLibrary();
342 }
343 
344 //
345 // OSX-specific way of handling dash: URIs and PaymentRequest mime types.
346 // Also used by paymentservertests.cpp and when opening a payment request file
347 // via "Open URI..." menu entry.
348 //
349 bool PaymentServer::eventFilter(QObject *object, QEvent *event)
350 {
351  if (event->type() == QEvent::FileOpen) {
352  QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent*>(event);
353  if (!fileEvent->file().isEmpty())
354  handleURIOrFile(fileEvent->file());
355  else if (!fileEvent->url().isEmpty())
356  handleURIOrFile(fileEvent->url().toString());
357 
358  return true;
359  }
360 
361  return QObject::eventFilter(object, event);
362 }
363 
365 {
366  if (!optionsModel)
367  return;
368  if (netManager != NULL)
369  delete netManager;
370 
371  // netManager is used to fetch paymentrequests given in dash: URIs
372  netManager = new QNetworkAccessManager(this);
373 
374  QNetworkProxy proxy;
375 
376  // Query active SOCKS5 proxy
377  if (optionsModel->getProxySettings(proxy)) {
378  netManager->setProxy(proxy);
379 
380  qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
381  }
382  else
383  qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
384 
385  connect(netManager, SIGNAL(finished(QNetworkReply*)),
386  this, SLOT(netRequestFinished(QNetworkReply*)));
387  connect(netManager, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> &)),
388  this, SLOT(reportSslErrors(QNetworkReply*, const QList<QSslError> &)));
389 }
390 
392 {
393  initNetManager();
394 
395  saveURIs = false;
396  Q_FOREACH (const QString& s, savedPaymentRequests)
397  {
398  handleURIOrFile(s);
399  }
400  savedPaymentRequests.clear();
401 }
402 
403 void PaymentServer::handleURIOrFile(const QString& s)
404 {
405  if (saveURIs)
406  {
407  savedPaymentRequests.append(s);
408  return;
409  }
410 
411  if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // dash: URI
412  {
413 #if QT_VERSION < 0x050000
414  QUrl uri(s);
415 #else
416  QUrlQuery uri((QUrl(s)));
417 #endif
418  if (uri.hasQueryItem("r")) // payment request URI
419  {
420  QByteArray temp;
421  temp.append(uri.queryItemValue("r"));
422  QString decoded = QUrl::fromPercentEncoding(temp);
423  QUrl fetchUrl(decoded, QUrl::StrictMode);
424 
425  if (fetchUrl.isValid())
426  {
427  qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl << ")";
428  fetchRequest(fetchUrl);
429  }
430  else
431  {
432  qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
433  Q_EMIT message(tr("URI handling"),
434  tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
436  }
437 
438  return;
439  }
440  else // normal URI
441  {
442  SendCoinsRecipient recipient;
443  if (GUIUtil::parseBitcoinURI(s, &recipient))
444  {
445  CBitcoinAddress address(recipient.address.toStdString());
446  if (!address.IsValid()) {
447  Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
449  }
450  else
451  Q_EMIT receivedPaymentRequest(recipient);
452  }
453  else
454  Q_EMIT message(tr("URI handling"),
455  tr("URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters."),
457 
458  return;
459  }
460  }
461 
462  if (QFile::exists(s)) // payment request file
463  {
464  PaymentRequestPlus request;
465  SendCoinsRecipient recipient;
466  if (!readPaymentRequestFromFile(s, request))
467  {
468  Q_EMIT message(tr("Payment request file handling"),
469  tr("Payment request file cannot be read! This can be caused by an invalid payment request file."),
471  }
472  else if (processPaymentRequest(request, recipient))
473  Q_EMIT receivedPaymentRequest(recipient);
474 
475  return;
476  }
477 }
478 
480 {
481  QLocalSocket *clientConnection = uriServer->nextPendingConnection();
482 
483  while (clientConnection->bytesAvailable() < (int)sizeof(quint32))
484  clientConnection->waitForReadyRead();
485 
486  connect(clientConnection, SIGNAL(disconnected()),
487  clientConnection, SLOT(deleteLater()));
488 
489  QDataStream in(clientConnection);
490  in.setVersion(QDataStream::Qt_4_0);
491  if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
492  return;
493  }
494  QString msg;
495  in >> msg;
496 
498 }
499 
500 //
501 // Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
502 // so don't use "Q_EMIT message()", but "QMessageBox::"!
503 //
504 bool PaymentServer::readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request)
505 {
506  QFile f(filename);
507  if (!f.open(QIODevice::ReadOnly)) {
508  qWarning() << QString("PaymentServer::%1: Failed to open %2").arg(__func__).arg(filename);
509  return false;
510  }
511 
512  // BIP70 DoS protection
513  if (!verifySize(f.size())) {
514  return false;
515  }
516 
517  QByteArray data = f.readAll();
518 
519  return request.parse(data);
520 }
521 
523 {
524  if (!optionsModel)
525  return false;
526 
527  if (request.IsInitialized()) {
528  // Payment request network matches client network?
529  if (!verifyNetwork(request.getDetails())) {
530  Q_EMIT message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."),
532 
533  return false;
534  }
535 
536  // Make sure any payment requests involved are still valid.
537  // This is re-checked just before sending coins in WalletModel::sendCoins().
538  if (verifyExpired(request.getDetails())) {
539  Q_EMIT message(tr("Payment request rejected"), tr("Payment request expired."),
541 
542  return false;
543  }
544  } else {
545  Q_EMIT message(tr("Payment request error"), tr("Payment request is not initialized."),
547 
548  return false;
549  }
550 
551  recipient.paymentRequest = request;
552  recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo());
553 
554  request.getMerchant(certStore.get(), recipient.authenticatedMerchant);
555 
556  QList<std::pair<CScript, CAmount> > sendingTos = request.getPayTo();
557  QStringList addresses;
558 
559  Q_FOREACH(const PAIRTYPE(CScript, CAmount)& sendingTo, sendingTos) {
560  // Extract and check destination addresses
561  CTxDestination dest;
562  if (ExtractDestination(sendingTo.first, dest)) {
563  // Append destination address
564  addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString()));
565  }
566  else if (!recipient.authenticatedMerchant.isEmpty()) {
567  // Unauthenticated payment requests to custom dash addresses are not supported
568  // (there is no good way to tell the user where they are paying in a way they'd
569  // have a chance of understanding).
570  Q_EMIT message(tr("Payment request rejected"),
571  tr("Unverified payment requests to custom payment scripts are unsupported."),
573  return false;
574  }
575 
576  // Dash amounts are stored as (optional) uint64 in the protobuf messages (see paymentrequest.proto),
577  // but CAmount is defined as int64_t. Because of that we need to verify that amounts are in a valid range
578  // and no overflow has happened.
579  if (!verifyAmount(sendingTo.second)) {
580  Q_EMIT message(tr("Payment request rejected"), tr("Invalid payment request."), CClientUIInterface::MSG_ERROR);
581  return false;
582  }
583 
584  // Extract and check amounts
585  CTxOut txOut(sendingTo.second, sendingTo.first);
586  if (txOut.IsDust(::minRelayTxFee)) {
587  Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
588  .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
590 
591  return false;
592  }
593 
594  recipient.amount += sendingTo.second;
595  // Also verify that the final amount is still in a valid range after adding additional amounts.
596  if (!verifyAmount(recipient.amount)) {
597  Q_EMIT message(tr("Payment request rejected"), tr("Invalid payment request."), CClientUIInterface::MSG_ERROR);
598  return false;
599  }
600  }
601  // Store addresses and format them to fit nicely into the GUI
602  recipient.address = addresses.join("<br />");
603 
604  if (!recipient.authenticatedMerchant.isEmpty()) {
605  qDebug() << "PaymentServer::processPaymentRequest: Secure payment request from " << recipient.authenticatedMerchant;
606  }
607  else {
608  qDebug() << "PaymentServer::processPaymentRequest: Insecure payment request to " << addresses.join(", ");
609  }
610 
611  return true;
612 }
613 
615 {
616  QNetworkRequest netRequest;
617  netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTREQUEST);
618  netRequest.setUrl(url);
619  netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
620  netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTREQUEST);
621  netManager->get(netRequest);
622 }
623 
624 void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction)
625 {
626  const payments::PaymentDetails& details = recipient.paymentRequest.getDetails();
627  if (!details.has_payment_url())
628  return;
629 
630  QNetworkRequest netRequest;
631  netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK);
632  netRequest.setUrl(QString::fromStdString(details.payment_url()));
633  netRequest.setHeader(QNetworkRequest::ContentTypeHeader, BIP71_MIMETYPE_PAYMENT);
634  netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
635  netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTACK);
636 
637  payments::Payment payment;
638  payment.set_merchant_data(details.merchant_data());
639  payment.add_transactions(transaction.data(), transaction.size());
640 
641  // Create a new refund address, or re-use:
642  QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant);
643  std::string strAccount = account.toStdString();
644  std::set<CTxDestination> refundAddresses = wallet->GetAccountAddresses(strAccount);
645  if (!refundAddresses.empty()) {
646  CScript s = GetScriptForDestination(*refundAddresses.begin());
647  payments::Output* refund_to = payment.add_refund_to();
648  refund_to->set_script(&s[0], s.size());
649  }
650  else {
651  CPubKey newKey;
652  if (wallet->GetKeyFromPool(newKey, false)) {
653  CKeyID keyID = newKey.GetID();
654  wallet->SetAddressBook(keyID, strAccount, "refund");
655 
656  CScript s = GetScriptForDestination(keyID);
657  payments::Output* refund_to = payment.add_refund_to();
658  refund_to->set_script(&s[0], s.size());
659  }
660  else {
661  // This should never happen, because sending coins should have
662  // just unlocked the wallet and refilled the keypool.
663  qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
664  }
665  }
666 
667  int length = payment.ByteSize();
668  netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
669  QByteArray serData(length, '\0');
670  if (payment.SerializeToArray(serData.data(), length)) {
671  netManager->post(netRequest, serData);
672  }
673  else {
674  // This should never happen, either.
675  qWarning() << "PaymentServer::fetchPaymentACK: Error serializing payment message";
676  }
677 }
678 
679 void PaymentServer::netRequestFinished(QNetworkReply* reply)
680 {
681  reply->deleteLater();
682 
683  // BIP70 DoS protection
684  if (!verifySize(reply->size())) {
685  Q_EMIT message(tr("Payment request rejected"),
686  tr("Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
687  .arg(reply->request().url().toString())
688  .arg(reply->size())
691  return;
692  }
693 
694  if (reply->error() != QNetworkReply::NoError) {
695  QString msg = tr("Error communicating with %1: %2")
696  .arg(reply->request().url().toString())
697  .arg(reply->errorString());
698 
699  qWarning() << "PaymentServer::netRequestFinished: " << msg;
700  Q_EMIT message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR);
701  return;
702  }
703 
704  QByteArray data = reply->readAll();
705 
706  QString requestType = reply->request().attribute(QNetworkRequest::User).toString();
707  if (requestType == BIP70_MESSAGE_PAYMENTREQUEST)
708  {
709  PaymentRequestPlus request;
710  SendCoinsRecipient recipient;
711  if (!request.parse(data))
712  {
713  qWarning() << "PaymentServer::netRequestFinished: Error parsing payment request";
714  Q_EMIT message(tr("Payment request error"),
715  tr("Payment request cannot be parsed!"),
717  }
718  else if (processPaymentRequest(request, recipient))
719  Q_EMIT receivedPaymentRequest(recipient);
720 
721  return;
722  }
723  else if (requestType == BIP70_MESSAGE_PAYMENTACK)
724  {
725  payments::PaymentACK paymentACK;
726  if (!paymentACK.ParseFromArray(data.data(), data.size()))
727  {
728  QString msg = tr("Bad response from server %1")
729  .arg(reply->request().url().toString());
730 
731  qWarning() << "PaymentServer::netRequestFinished: " << msg;
732  Q_EMIT message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR);
733  }
734  else
735  {
736  Q_EMIT receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo()));
737  }
738  }
739 }
740 
741 void PaymentServer::reportSslErrors(QNetworkReply* reply, const QList<QSslError> &errs)
742 {
743  Q_UNUSED(reply);
744 
745  QString errString;
746  Q_FOREACH (const QSslError& err, errs) {
747  qWarning() << "PaymentServer::reportSslErrors: " << err;
748  errString += err.errorString() + "\n";
749  }
750  Q_EMIT message(tr("Network request error"), errString, CClientUIInterface::MSG_ERROR);
751 }
752 
754 {
755  this->optionsModel = optionsModel;
756 }
757 
758 void PaymentServer::handlePaymentACK(const QString& paymentACKMsg)
759 {
760  // currently we don't further process or store the paymentACK message
761  Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL);
762 }
763 
765 {
766  bool fVerified = requestDetails.network() == Params().NetworkIDString();
767  if (!fVerified) {
768  qWarning() << QString("PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
769  .arg(__func__)
770  .arg(QString::fromStdString(requestDetails.network()))
771  .arg(QString::fromStdString(Params().NetworkIDString()));
772  }
773  return fVerified;
774 }
775 
777 {
778  bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() < GetTime());
779  if (fVerified) {
780  const QString requestExpires = QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", (int64_t)requestDetails.expires()));
781  qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".")
782  .arg(__func__)
783  .arg(requestExpires);
784  }
785  return fVerified;
786 }
787 
788 bool PaymentServer::verifySize(qint64 requestSize)
789 {
790  bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE);
791  if (!fVerified) {
792  qWarning() << QString("PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
793  .arg(__func__)
794  .arg(requestSize)
796  }
797  return fVerified;
798 }
799 
800 bool PaymentServer::verifyAmount(const CAmount& requestAmount)
801 {
802  bool fVerified = MoneyRange(requestAmount);
803  if (!fVerified) {
804  qWarning() << QString("PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
805  .arg(__func__)
806  .arg(requestAmount)
807  .arg(MAX_MONEY);
808  }
809  return fVerified;
810 }
811 
813 {
814  return certStore.get();
815 }
void netRequestFinished(QNetworkReply *)
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:547
void handlePaymentACK(const QString &paymentACKMsg)
void receivedPaymentACK(const QString &paymentACKMsg)
boost::variant< CNoDestination, CKeyID, CScriptID > CTxDestination
Definition: standard.h:69
bool eventFilter(QObject *object, QEvent *event)
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Definition: standard.cpp:164
const ::std::string & payment_url() const
bool processPaymentRequest(const PaymentRequestPlus &request, SendCoinsRecipient &recipient)
const char * BIP71_MIMETYPE_PAYMENT
std::string NetworkIDString() const
Definition: chainparams.h:75
void set_script(const ::std::string &value)
static const CAmount MAX_MONEY
Definition: amount.h:30
static bool verifyNetwork(const payments::PaymentDetails &requestDetails)
void handleURIConnection()
PaymentServer(QObject *parent, bool startLocalServer=true)
std::string DateTimeStrFormat(const char *pszFormat, int64_t nTime)
Definition: utiltime.cpp:81
static bool verifySize(qint64 requestSize)
bool MoneyRange(const CAmount &nValue)
Definition: amount.h:31
static bool verifyExpired(const payments::PaymentDetails &requestDetails)
bool parse(const QByteArray &data)
const ::std::string & merchant_data() const
void setOptionsModel(OptionsModel *optionsModel)
static const std::string TESTNET
static bool readPaymentRequestFromFile(const QString &filename, PaymentRequestPlus &request)
Definition: pubkey.h:27
static X509_STORE * getCertStore()
const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE
void handleURIOrFile(const QString &s)
OptionsModel * optionsModel
QLocalServer * uriServer
static QString formatWithUnit(int unit, const CAmount &amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
CKeyID GetID() const
Get the KeyID of this public key (hash of its serialization)
Definition: pubkey.h:144
static bool ipcSendCommandLine()
inline ::std::string * add_transactions()
static bool verifyAmount(const CAmount &requestAmount)
static void ReportInvalidCertificate(const QSslCertificate &cert)
int64_t CAmount
Definition: amount.h:14
QNetworkAccessManager * netManager
void fetchPaymentACK(CWallet *wallet, SendCoinsRecipient recipient, QByteArray transaction)
const char * url
Definition: rpcconsole.cpp:62
const char * BIP71_MIMETYPE_PAYMENTACK
bool getMerchant(X509_STORE *certStore, QString &merchant) const
const int BITCOIN_IPC_CONNECT_TIMEOUT
CFeeRate minRelayTxFee
Definition: validation.cpp:94
const char * name
Definition: rest.cpp:37
bool getProxySettings(QNetworkProxy &proxy) const
void SelectParams(const std::string &network)
const char * BIP70_MESSAGE_PAYMENTREQUEST
inline ::google::protobuf::uint64 expires() const
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:261
void receivedPaymentRequest(SendCoinsRecipient)
CScript GetScriptForDestination(const CTxDestination &dest)
Definition: standard.cpp:262
const std::string CLIENT_NAME
Definition: wallet.py:1
static QList< QString > savedPaymentRequests
void set_merchant_data(const ::std::string &value)
void message(const QString &title, const QString &message, unsigned int style)
PaymentRequestPlus paymentRequest
Definition: walletmodel.h:59
const char * BIP71_MIMETYPE_PAYMENTREQUEST
static void LoadRootCAs(X509_STORE *store=NULL)
const char * BIP70_MESSAGE_PAYMENTACK
bool IsDust(const CFeeRate &minRelayTxFee) const
Definition: transaction.h:185
const ::std::string & memo() const
void operator()(X509 *b)
void fetchRequest(const QUrl &url)
static void ipcParseCommandLine(int argc, char *argv[])
const CChainParams & Params()
QString authenticatedMerchant
Definition: walletmodel.h:61
const QString BITCOIN_IPC_PREFIX("dash:")
static QString ipcServerName()
const ::std::string & network() const
static const std::string MAIN
Definition: pubkey.h:37
size_type size() const
Definition: prevector.h:262
int getDisplayUnit()
Definition: optionsmodel.h:73
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Definition: util.cpp:441
QList< std::pair< CScript, CAmount > > getPayTo() const
int64_t GetTime()
For unit testing.
Definition: utiltime.cpp:20
inline ::payments::Output * add_refund_to()
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:135
#define PAIRTYPE(t1, t2)
void reportSslErrors(QNetworkReply *, const QList< QSslError > &)
void operator()(X509_STORE *b)
const ::std::string & memo() const
const payments::PaymentDetails & getDetails() const