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