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