Bitcoin ABC  0.24.10
P2P Digital Currency
paymentserver.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 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 <qt/paymentserver.h>
6 
7 #include <cashaddrenc.h>
8 #include <chainparams.h>
9 #include <config.h>
10 #include <interfaces/node.h>
11 #include <key_io.h>
12 #include <node/ui_interface.h>
13 #include <policy/policy.h>
14 #include <qt/bitcoinunits.h>
15 #include <qt/guiutil.h>
16 #include <qt/optionsmodel.h>
17 #include <util/system.h>
18 #include <wallet/wallet.h>
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 <QSslConfiguration>
39 #include <QSslError>
40 #include <QStringList>
41 #include <QTextDocument>
42 #include <QUrlQuery>
43 
44 #include <cstdlib>
45 #include <memory>
46 
47 const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds
48 #ifdef ENABLE_BIP70
49 // BIP70 payment protocol messages
50 const char *BIP70_MESSAGE_PAYMENTACK = "PaymentACK";
51 const char *BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
52 // BIP71 payment protocol media types
53 const char *BIP71_MIMETYPE_PAYMENT = "application/bitcoincash-payment";
54 const char *BIP71_MIMETYPE_PAYMENTACK = "application/bitcoincash-paymentack";
55 const char *BIP71_MIMETYPE_PAYMENTREQUEST =
56  "application/bitcoincash-paymentrequest";
57 #endif
58 
59 //
60 // Create a name that is unique for:
61 // testnet / non-testnet
62 // data directory
63 //
64 static QString ipcServerName() {
65  QString name("BitcoinQt");
66 
67  // Append a simple hash of the datadir
68  // Note that GetDataDir(true) returns a different path for -testnet versus
69  // main net
70  QString ddir(GUIUtil::boostPathToQString(GetDataDir(true)));
71  name.append(QString::number(qHash(ddir)));
72 
73  return name;
74 }
75 
76 //
77 // We store payment URIs and requests received before the main GUI window is up
78 // and ready to ask the user to send payment.
79 //
80 static QSet<QString> savedPaymentRequests;
81 
82 static std::string ipcParseURI(const QString &arg, const CChainParams &params,
83  bool useCashAddr) {
84  const QString scheme = QString::fromStdString(params.CashAddrPrefix());
85  if (!arg.startsWith(scheme + ":", Qt::CaseInsensitive)) {
86  return {};
87  }
88 
90  if (!GUIUtil::parseBitcoinURI(scheme, arg, &r)) {
91  return {};
92  }
93 
94  return r.address.toStdString();
95 }
96 
97 static bool ipcCanParseCashAddrURI(const QString &arg,
98  const std::string &network) {
99  auto tempChainParams = CreateChainParams(network);
100  std::string addr = ipcParseURI(arg, *tempChainParams, true);
101  return IsValidDestinationString(addr, *tempChainParams);
102 }
103 
104 static bool ipcCanParseLegacyURI(const QString &arg,
105  const std::string &network) {
106  auto tempChainParams = CreateChainParams(network);
107  std::string addr = ipcParseURI(arg, *tempChainParams, false);
108  return IsValidDestinationString(addr, *tempChainParams);
109 }
110 
111 //
112 // Sending to the server is done synchronously, at startup.
113 // If the server isn't already running, startup continues, and the items in
114 // savedPaymentRequest will be handled when uiReady() is called.
115 //
116 // Warning: ipcSendCommandLine() is called early in init, so don't use "Q_EMIT
117 // message()", but "QMessageBox::"!
118 //
119 void PaymentServer::ipcParseCommandLine(int argc, char *argv[]) {
120  std::array<const std::string *, 3> networks = {
123 
124  const std::string *chosenNetwork = nullptr;
125 
126  for (int i = 1; i < argc; i++) {
127  QString arg(argv[i]);
128  if (arg.startsWith("-")) {
129  continue;
130  }
131 
132  const std::string *itemNetwork = nullptr;
133 
134  // Try to parse as a URI
135  for (auto net : networks) {
136  if (ipcCanParseCashAddrURI(arg, *net)) {
137  itemNetwork = net;
138  break;
139  }
140 
141  if (ipcCanParseLegacyURI(arg, *net)) {
142  itemNetwork = net;
143  break;
144  }
145  }
146 
147 #ifdef ENABLE_BIP70
148  if (!itemNetwork && QFile::exists(arg)) {
149  // Filename
150  PaymentRequestPlus request;
151  if (readPaymentRequestFromFile(arg, request)) {
152  for (auto net : networks) {
153  if (*net == request.getDetails().network()) {
154  itemNetwork = net;
155  }
156  }
157  }
158  }
159 #endif
160 
161  if (itemNetwork == nullptr) {
162  // Printing to debug.log is about the best we can do here, the GUI
163  // hasn't started yet so we can't pop up a message box.
164  qWarning() << "PaymentServer::ipcSendCommandLine: Payment request "
165  "file or URI does not exist or is invalid: "
166  << arg;
167  continue;
168  }
169 
170 #ifdef ENABLE_BIP70
171  if (chosenNetwork && chosenNetwork != itemNetwork) {
172  qWarning() << "PaymentServer::ipcSendCommandLine: Payment request "
173  "from network "
174  << QString(itemNetwork->c_str())
175  << " does not match already chosen network "
176  << QString(chosenNetwork->c_str());
177  continue;
178  }
179 
180  if (savedPaymentRequests.contains(arg)) {
181  continue;
182  }
183  savedPaymentRequests.insert(arg);
184 #endif
185  chosenNetwork = itemNetwork;
186  }
187 
188  if (chosenNetwork) {
189  SelectParams(*chosenNetwork);
190  }
191 }
192 
193 //
194 // Sending to the server is done synchronously, at startup.
195 // If the server isn't already running, startup continues, and the items in
196 // savedPaymentRequest will be handled when uiReady() is called.
197 //
199  bool fResult = false;
200  for (const QString &r : savedPaymentRequests) {
201  QLocalSocket *socket = new QLocalSocket();
202  socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
203  if (!socket->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT)) {
204  delete socket;
205  socket = nullptr;
206  return false;
207  }
208 
209  QByteArray block;
210  QDataStream out(&block, QIODevice::WriteOnly);
211  out.setVersion(QDataStream::Qt_4_0);
212  out << r;
213  out.device()->seek(0);
214 
215  socket->write(block);
216  socket->flush();
217  socket->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT);
218  socket->disconnectFromServer();
219 
220  delete socket;
221  socket = nullptr;
222  fResult = true;
223  }
224 
225  return fResult;
226 }
227 
228 PaymentServer::PaymentServer(QObject *parent, bool startLocalServer)
229  : QObject(parent), saveURIs(true), uriServer(nullptr), optionsModel(nullptr)
230 // clang-format off
231 #ifdef ENABLE_BIP70
232  ,netManager(nullptr)
233 #endif
234 // clang-format on
235 {
236 #ifdef ENABLE_BIP70
237  // Verify that the version of the library that we linked against is
238  // compatible with the version of the headers we compiled against.
239  GOOGLE_PROTOBUF_VERIFY_VERSION;
240 #endif
241 
242  // Install global event filter to catch QFileOpenEvents
243  // on Mac: sent when you click bitcoincash: links
244  // other OSes: helpful when dealing with payment request files
245  if (parent) {
246  parent->installEventFilter(this);
247  }
248 
249  QString name = ipcServerName();
250 
251  // Clean up old socket leftover from a crash:
252  QLocalServer::removeServer(name);
253 
254  if (startLocalServer) {
255  uriServer = new QLocalServer(this);
256 
257  if (!uriServer->listen(name)) {
258  // constructor is called early in init, so don't use "Q_EMIT
259  // message()" here
260  QMessageBox::critical(nullptr, tr("Payment request error"),
261  tr("Cannot start click-to-pay handler"));
262  } else {
263  connect(uriServer, &QLocalServer::newConnection, this,
265 #ifdef ENABLE_BIP70
266  connect(this, &PaymentServer::receivedPaymentACK, this,
267  &PaymentServer::handlePaymentACK);
268 #endif
269  }
270  }
271 }
272 
274 #ifdef ENABLE_BIP70
275  google::protobuf::ShutdownProtobufLibrary();
276 #endif
277 }
278 
279 //
280 // OSX-specific way of handling bitcoincash: URIs and PaymentRequest mime types.
281 // Also used by paymentservertests.cpp and when opening a payment request file
282 // via "Open URI..." menu entry.
283 //
284 bool PaymentServer::eventFilter(QObject *object, QEvent *event) {
285  if (event->type() == QEvent::FileOpen) {
286  QFileOpenEvent *fileEvent = static_cast<QFileOpenEvent *>(event);
287  if (!fileEvent->file().isEmpty()) {
288  handleURIOrFile(fileEvent->file());
289  } else if (!fileEvent->url().isEmpty()) {
290  handleURIOrFile(fileEvent->url().toString());
291  }
292 
293  return true;
294  }
295 
296  return QObject::eventFilter(object, event);
297 }
298 
300 #ifdef ENABLE_BIP70
301  initNetManager();
302 #endif
303 
304  saveURIs = false;
305  for (const QString &s : savedPaymentRequests) {
306  handleURIOrFile(s);
307  }
308  savedPaymentRequests.clear();
309 }
310 
311 bool PaymentServer::handleURI(const CChainParams &params, const QString &s) {
312  const QString scheme = QString::fromStdString(params.CashAddrPrefix());
313  if (!s.startsWith(scheme + ":", Qt::CaseInsensitive)) {
314  return false;
315  }
316 
317  QUrlQuery uri((QUrl(s)));
318 #ifdef ENABLE_BIP70
319  // payment request URI
320  if (uri.hasQueryItem("r")) {
321  QByteArray temp;
322  temp.append(uri.queryItemValue("r").toUtf8());
323  QString decoded = QUrl::fromPercentEncoding(temp);
324  QUrl fetchUrl(decoded, QUrl::StrictMode);
325 
326  if (fetchUrl.isValid()) {
327  qDebug() << "PaymentServer::handleURIOrFile: fetchRequest("
328  << fetchUrl << ")";
329  fetchRequest(fetchUrl);
330  } else {
331  qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: "
332  << fetchUrl;
333  Q_EMIT message(tr("URI handling"),
334  tr("Payment request fetch URL is invalid: %1")
335  .arg(fetchUrl.toString()),
337  }
338  return true;
339  }
340 #endif
341 
342  // normal URI
343  SendCoinsRecipient recipient;
344  if (GUIUtil::parseBitcoinURI(scheme, s, &recipient)) {
345  if (!IsValidDestinationString(recipient.address.toStdString(),
346  params)) {
347 #ifndef ENABLE_BIP70
348  // payment request
349  if (uri.hasQueryItem("r")) {
350  Q_EMIT message(tr("URI handling"),
351  tr("Cannot process payment request because "
352  "BIP70 support was not compiled in."),
354  }
355 #endif
356  Q_EMIT message(
357  tr("URI handling"),
358  tr("Invalid payment address %1").arg(recipient.address),
360  } else {
361  Q_EMIT receivedPaymentRequest(recipient);
362  }
363  } else {
364  Q_EMIT message(
365  tr("URI handling"),
366  tr("URI cannot be parsed! This can be caused by an invalid "
367  "Bitcoin address or malformed URI parameters."),
369  }
370 
371  return true;
372 }
373 
374 void PaymentServer::handleURIOrFile(const QString &s) {
375  if (saveURIs) {
376  savedPaymentRequests.insert(s);
377  return;
378  }
379 
380  // bitcoincash: CashAddr URI
381  if (handleURI(Params(), s)) {
382  return;
383  }
384 
385  // payment request file
386  if (QFile::exists(s)) {
387 #ifdef ENABLE_BIP70
388  PaymentRequestPlus request;
389  SendCoinsRecipient recipient;
390  if (!readPaymentRequestFromFile(s, request)) {
391  Q_EMIT message(tr("Payment request file handling"),
392  tr("Payment request file cannot be read! This can "
393  "be caused by an invalid payment request file."),
395  } else if (processPaymentRequest(request, recipient)) {
396  Q_EMIT receivedPaymentRequest(recipient);
397  }
398 
399  return;
400 #else
401  Q_EMIT message(tr("Payment request file handling"),
402  tr("Cannot process payment request because BIP70 "
403  "support was not compiled in."),
405 #endif
406  }
407 }
408 
410  QLocalSocket *clientConnection = uriServer->nextPendingConnection();
411 
412  while (clientConnection->bytesAvailable() < (int)sizeof(quint32)) {
413  clientConnection->waitForReadyRead();
414  }
415 
416  connect(clientConnection, &QLocalSocket::disconnected, clientConnection,
417  &QLocalSocket::deleteLater);
418 
419  QDataStream in(clientConnection);
420  in.setVersion(QDataStream::Qt_4_0);
421  if (clientConnection->bytesAvailable() < (int)sizeof(quint16)) {
422  return;
423  }
424  QString msg;
425  in >> msg;
426 
427  handleURIOrFile(msg);
428 }
429 
431  this->optionsModel = _optionsModel;
432 }
433 
434 #ifdef ENABLE_BIP70
435 struct X509StoreDeleter {
436  void operator()(X509_STORE *b) { X509_STORE_free(b); }
437 };
438 
439 struct X509Deleter {
440  void operator()(X509 *b) { X509_free(b); }
441 };
442 
443 // Anon namespace
444 namespace {
445 std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
446 }
447 
448 static void ReportInvalidCertificate(const QSslCertificate &cert) {
449  qDebug() << QString("%1: Payment server found an invalid certificate: ")
450  .arg(__func__)
451  << cert.serialNumber()
452  << cert.subjectInfo(QSslCertificate::CommonName)
453  << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier)
454  << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
455 }
456 
457 //
458 // Load OpenSSL's list of root certificate authorities
459 //
460 void PaymentServer::LoadRootCAs(X509_STORE *_store) {
461  // Unit tests mostly use this, to pass in fake root CAs:
462  if (_store) {
463  certStore.reset(_store);
464  return;
465  }
466 
467  // Normal execution, use either -rootcertificates or system certs:
468  certStore.reset(X509_STORE_new());
469 
470  // Note: use "-system-" default here so that users can pass
471  // -rootcertificates="" and get 'I don't like X.509 certificates, don't
472  // trust anybody' behavior:
473  QString certFile =
474  QString::fromStdString(gArgs.GetArg("-rootcertificates", "-system-"));
475 
476  // Empty store
477  if (certFile.isEmpty()) {
478  qDebug() << QString("PaymentServer::%1: Payment request authentication "
479  "via X.509 certificates disabled.")
480  .arg(__func__);
481  return;
482  }
483 
484  QList<QSslCertificate> certList;
485 
486  if (certFile != "-system-") {
487  qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root "
488  "certificate.")
489  .arg(__func__)
490  .arg(certFile);
491 
492  certList = QSslCertificate::fromPath(certFile);
493  // Use those certificates when fetching payment requests, too:
494  QSslConfiguration::defaultConfiguration().setCaCertificates(certList);
495  } else {
496  certList = QSslConfiguration::systemCaCertificates();
497  }
498 
499  int nRootCerts = 0;
500  const QDateTime currentTime = QDateTime::currentDateTime();
501 
502  for (const QSslCertificate &cert : certList) {
503  // Don't log NULL certificates
504  if (cert.isNull()) {
505  continue;
506  }
507 
508  // Not yet active/valid, or expired certificate
509  if (currentTime < cert.effectiveDate() ||
510  currentTime > cert.expiryDate()) {
511  ReportInvalidCertificate(cert);
512  continue;
513  }
514 
515  // Blacklisted certificate
516  if (cert.isBlacklisted()) {
517  ReportInvalidCertificate(cert);
518  continue;
519  }
520 
521  QByteArray certData = cert.toDer();
522  const uint8_t *data = (const uint8_t *)certData.data();
523 
524  std::unique_ptr<X509, X509Deleter> x509(
525  d2i_X509(0, &data, certData.size()));
526  if (x509 && X509_STORE_add_cert(certStore.get(), x509.get())) {
527  // Note: X509_STORE increases the reference count to the X509
528  // object, we still have to release our reference to it.
529  ++nRootCerts;
530  } else {
531  ReportInvalidCertificate(cert);
532  continue;
533  }
534  }
535  qInfo() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts
536  << " root certificates";
537 
538  // Project for another day:
539  // Fetch certificate revocation lists, and add them to certStore.
540  // Issues to consider:
541  // performance (start a thread to fetch in background?)
542  // privacy (fetch through tor/proxy so IP address isn't revealed)
543  // would it be easier to just use a compiled-in blacklist?
544  // or use Qt's blacklist?
545  // "certificate stapling" with server-side caching is more efficient
546 }
547 
548 void PaymentServer::initNetManager() {
549  if (!optionsModel) {
550  return;
551  }
552  if (netManager != nullptr) {
553  delete netManager;
554  }
555 
556  // netManager is used to fetch paymentrequests given in bitcoincash: URIs
557  netManager = new QNetworkAccessManager(this);
558 
559  QNetworkProxy proxy;
560 
561  // Query active SOCKS5 proxy
562  if (optionsModel->getProxySettings(proxy)) {
563  netManager->setProxy(proxy);
564 
565  qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy"
566  << proxy.hostName() << ":" << proxy.port();
567  } else {
568  qDebug()
569  << "PaymentServer::initNetManager: No active proxy server found.";
570  }
571 
572  connect(netManager, &QNetworkAccessManager::finished, this,
573  &PaymentServer::netRequestFinished);
574  connect(netManager, &QNetworkAccessManager::sslErrors, this,
575  &PaymentServer::reportSslErrors);
576 }
577 
578 //
579 // Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
580 // so don't use "Q_EMIT message()", but "QMessageBox::"!
581 //
582 bool PaymentServer::readPaymentRequestFromFile(const QString &filename,
583  PaymentRequestPlus &request) {
584  QFile f(filename);
585  if (!f.open(QIODevice::ReadOnly)) {
586  qWarning() << QString("PaymentServer::%1: Failed to open %2")
587  .arg(__func__)
588  .arg(filename);
589  return false;
590  }
591 
592  // BIP70 DoS protection
593  if (!verifySize(f.size())) {
594  return false;
595  }
596 
597  QByteArray data = f.readAll();
598 
599  return request.parse(data);
600 }
601 
602 bool PaymentServer::processPaymentRequest(const PaymentRequestPlus &request,
603  SendCoinsRecipient &recipient) {
604  if (!optionsModel) {
605  return false;
606  }
607 
608  if (request.IsInitialized()) {
609  // Payment request network matches client network?
610  if (!verifyNetwork(optionsModel->node(), request.getDetails())) {
611  Q_EMIT message(
612  tr("Payment request rejected"),
613  tr("Payment request network doesn't match client network."),
615 
616  return false;
617  }
618 
619  // Make sure any payment requests involved are still valid.
620  // This is re-checked just before sending coins in
621  // WalletModel::sendCoins().
622  if (verifyExpired(request.getDetails())) {
623  Q_EMIT message(tr("Payment request rejected"),
624  tr("Payment request expired."),
626 
627  return false;
628  }
629  } else {
630  Q_EMIT message(tr("Payment request error"),
631  tr("Payment request is not initialized."),
633 
634  return false;
635  }
636 
637  recipient.paymentRequest = request;
638  recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo());
639 
640  request.getMerchant(certStore.get(), recipient.authenticatedMerchant);
641 
642  QList<std::pair<CScript, Amount>> sendingTos = request.getPayTo();
643  QStringList addresses;
644 
645  for (const std::pair<CScript, Amount> &sendingTo : sendingTos) {
646  // Extract and check destination addresses
647  CTxDestination dest;
648  if (ExtractDestination(sendingTo.first, dest)) {
649  // Append destination address
650  addresses.append(
651  QString::fromStdString(EncodeCashAddr(dest, Params())));
652  } else if (!recipient.authenticatedMerchant.isEmpty()) {
653  // Unauthenticated payment requests to custom bitcoin addresses are
654  // not supported (there is no good way to tell the user where they
655  // are paying in a way they'd have a chance of understanding).
656  Q_EMIT message(tr("Payment request rejected"),
657  tr("Unverified payment requests to custom payment "
658  "scripts are unsupported."),
660  return false;
661  }
662 
663  // Bitcoin amounts are stored as (optional) uint64 in the protobuf
664  // messages (see paymentrequest.proto), but Amount is defined as
665  // int64_t. Because of that we need to verify that amounts are in a
666  // valid range and no overflow has happened.
667  if (!verifyAmount(sendingTo.second)) {
668  Q_EMIT message(tr("Payment request rejected"),
669  tr("Invalid payment request."),
671  return false;
672  }
673 
674  // Extract and check amounts
675  CTxOut txOut(Amount(sendingTo.second), sendingTo.first);
676  if (IsDust(txOut, optionsModel->node().getDustRelayFee())) {
677  Q_EMIT message(
678  tr("Payment request error"),
679  tr("Requested payment amount of %1 is too small (considered "
680  "dust).")
682  optionsModel->getDisplayUnit(), sendingTo.second)),
684 
685  return false;
686  }
687 
688  recipient.amount += sendingTo.second;
689  // Also verify that the final amount is still in a valid range after
690  // adding additional amounts.
691  if (!verifyAmount(recipient.amount)) {
692  Q_EMIT message(tr("Payment request rejected"),
693  tr("Invalid payment request."),
695  return false;
696  }
697  }
698  // Store addresses and format them to fit nicely into the GUI
699  recipient.address = addresses.join("<br />");
700 
701  if (!recipient.authenticatedMerchant.isEmpty()) {
702  qDebug() << "PaymentServer::processPaymentRequest: Secure payment "
703  "request from "
704  << recipient.authenticatedMerchant;
705  } else {
706  qDebug() << "PaymentServer::processPaymentRequest: Insecure payment "
707  "request to "
708  << addresses.join(", ");
709  }
710 
711  return true;
712 }
713 
714 void PaymentServer::fetchRequest(const QUrl &url) {
715  QNetworkRequest netRequest;
716  netRequest.setAttribute(QNetworkRequest::User,
717  BIP70_MESSAGE_PAYMENTREQUEST);
718  netRequest.setUrl(url);
719  netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
720  netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTREQUEST);
721  netManager->get(netRequest);
722 }
723 
724 void PaymentServer::fetchPaymentACK(interfaces::Wallet &wallet,
725  const SendCoinsRecipient &recipient,
726  QByteArray transaction) {
727  const payments::PaymentDetails &details =
728  recipient.paymentRequest.getDetails();
729  if (!details.has_payment_url()) {
730  return;
731  }
732 
733  QNetworkRequest netRequest;
734  netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK);
735  netRequest.setUrl(QString::fromStdString(details.payment_url()));
736  netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
737  BIP71_MIMETYPE_PAYMENT);
738  netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str());
739  netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTACK);
740 
741  payments::Payment payment;
742  payment.set_merchant_data(details.merchant_data());
743  payment.add_transactions(transaction.data(), transaction.size());
744 
745  // Create a new refund address, or re-use:
746  CTxDestination dest;
747  if (wallet.getNewDestination(OutputType::LEGACY, "", dest)) {
748  // BIP70 requests encode the scriptPubKey directly, so we are not
749  // restricted to address types supported by the receiver. As a result,
750  // we choose the address format we also use for change. Despite an
751  // actual payment and not change, this is a close match: it's the output
752  // type we use subject to privacy issues, but not restricted by what
753  // other software supports.
754  std::string label = tr("Refund from %1")
755  .arg(recipient.authenticatedMerchant)
756  .toStdString();
757  wallet.setAddressBook(dest, label, "refund");
758 
760  payments::Output *refund_to = payment.add_refund_to();
761  refund_to->set_script(&s[0], s.size());
762  } else {
763  // This should never happen, because sending coins should have
764  // just unlocked the wallet and refilled the keypool.
765  qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund "
766  "key, refund_to not set";
767  }
768 
769  QVariant length;
770 #ifdef USE_PROTOBUF_MESSAGE_BYTESIZELONG
771  length.setValue(payment.ByteSizeLong());
772 #else
773  length.setValue(payment.ByteSize());
774 #endif
775 
776  netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
777  QByteArray serData(length.toInt(), '\0');
778  if (payment.SerializeToArray(serData.data(), length.toInt())) {
779  netManager->post(netRequest, serData);
780  } else {
781  // This should never happen, either.
782  qWarning() << "PaymentServer::fetchPaymentACK: Error serializing "
783  "payment message";
784  }
785 }
786 
787 void PaymentServer::netRequestFinished(QNetworkReply *reply) {
788  reply->deleteLater();
789 
790  // BIP70 DoS protection
791  if (!verifySize(reply->size())) {
792  Q_EMIT message(
793  tr("Payment request rejected"),
794  tr("Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
795  .arg(reply->request().url().toString())
796  .arg(reply->size())
799  return;
800  }
801 
802  if (reply->error() != QNetworkReply::NoError) {
803  QString msg = tr("Error communicating with %1: %2")
804  .arg(reply->request().url().toString())
805  .arg(reply->errorString());
806 
807  qWarning() << "PaymentServer::netRequestFinished: " << msg;
808  Q_EMIT message(tr("Payment request error"), msg,
810  return;
811  }
812 
813  QByteArray data = reply->readAll();
814 
815  QString requestType =
816  reply->request().attribute(QNetworkRequest::User).toString();
817  if (requestType == BIP70_MESSAGE_PAYMENTREQUEST) {
818  PaymentRequestPlus request;
819  SendCoinsRecipient recipient;
820  if (!request.parse(data)) {
821  qWarning() << "PaymentServer::netRequestFinished: Error parsing "
822  "payment request";
823  Q_EMIT message(tr("Payment request error"),
824  tr("Payment request cannot be parsed!"),
826  } else if (processPaymentRequest(request, recipient)) {
827  Q_EMIT receivedPaymentRequest(recipient);
828  }
829 
830  return;
831  } else if (requestType == BIP70_MESSAGE_PAYMENTACK) {
832  payments::PaymentACK paymentACK;
833  if (!paymentACK.ParseFromArray(data.data(), data.size())) {
834  QString msg = tr("Bad response from server %1")
835  .arg(reply->request().url().toString());
836 
837  qWarning() << "PaymentServer::netRequestFinished: " << msg;
838  Q_EMIT message(tr("Payment request error"), msg,
840  } else {
841  Q_EMIT receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK.memo()));
842  }
843  }
844 }
845 
846 void PaymentServer::reportSslErrors(QNetworkReply *reply,
847  const QList<QSslError> &errs) {
848  Q_UNUSED(reply);
849 
850  QString errString;
851  for (const QSslError &err : errs) {
852  qWarning() << "PaymentServer::reportSslErrors: " << err;
853  errString += err.errorString() + "\n";
854  }
855  Q_EMIT message(tr("Network request error"), errString,
857 }
858 
859 void PaymentServer::handlePaymentACK(const QString &paymentACKMsg) {
860  // currently we don't further process or store the paymentACK message
861  Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg,
864 }
865 
866 bool PaymentServer::verifyNetwork(
867  interfaces::Node &node, const payments::PaymentDetails &requestDetails) {
868  const std::string clientNetwork =
870  bool fVerified = requestDetails.network() == clientNetwork;
871  if (!fVerified) {
872  qWarning() << QString("PaymentServer::%1: Payment request network "
873  "\"%2\" doesn't match client network \"%3\".")
874  .arg(__func__)
875  .arg(QString::fromStdString(requestDetails.network()))
876  .arg(QString::fromStdString(clientNetwork));
877  }
878  return fVerified;
879 }
880 
881 bool PaymentServer::verifyExpired(
882  const payments::PaymentDetails &requestDetails) {
883  bool fVerified = (requestDetails.has_expires() &&
884  (int64_t)requestDetails.expires() < GetTime());
885  if (fVerified) {
886  const QString requestExpires = QString::fromStdString(
887  FormatISO8601DateTime((int64_t)requestDetails.expires()));
888  qWarning() << QString(
889  "PaymentServer::%1: Payment request expired \"%2\".")
890  .arg(__func__)
891  .arg(requestExpires);
892  }
893  return fVerified;
894 }
895 
896 bool PaymentServer::verifySize(qint64 requestSize) {
897  bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE);
898  if (!fVerified) {
899  qWarning() << QString("PaymentServer::%1: Payment request too large "
900  "(%2 bytes, allowed %3 bytes).")
901  .arg(__func__)
902  .arg(requestSize)
904  }
905  return fVerified;
906 }
907 
908 bool PaymentServer::verifyAmount(const Amount requestAmount) {
909  bool fVerified = MoneyRange(Amount(requestAmount));
910  if (!fVerified) {
911  qWarning() << QString("PaymentServer::%1: Payment request amount out "
912  "of allowed range (%2, allowed 0 - %3).")
913  .arg(__func__)
914  .arg(requestAmount / SATOSHI)
915  .arg(MAX_MONEY / SATOSHI);
916  }
917  return fVerified;
918 }
919 
920 X509_STORE *PaymentServer::getCertStore() {
921  return certStore.get();
922 }
923 #endif
policy.h
MoneyRange
bool MoneyRange(const Amount nValue)
Definition: amount.h:176
GUIUtil::boostPathToQString
QString boostPathToQString(const fs::path &path)
Definition: guiutil.cpp:777
CClientUIInterface::MODAL
@ MODAL
Force blocking, modal message box dialog (not just OS notification)
Definition: ui_interface.h:65
fs::exists
static bool exists(const path &p)
Definition: fs.h:94
GetDataDir
const fs::path & GetDataDir(bool fNetSpecific)
Definition: system.cpp:831
PaymentServer::optionsModel
OptionsModel * optionsModel
Definition: paymentserver.h:151
PaymentRequestPlus::IsInitialized
bool IsInitialized() const
Definition: paymentrequestplus.cpp:55
wallet.h
SendCoinsRecipient::amount
Amount amount
Definition: sendcoinsrecipient.h:41
key_io.h
PaymentServer::saveURIs
bool saveURIs
Definition: paymentserver.h:149
OutputType::LEGACY
@ LEGACY
GetScriptForDestination
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:243
PaymentServer::message
void message(const QString &title, const QString &message, unsigned int style)
CClientUIInterface::ICON_WARNING
@ ICON_WARNING
Definition: ui_interface.h:29
PaymentServer::~PaymentServer
~PaymentServer()
Definition: paymentserver.cpp:273
PaymentServer::setOptionsModel
void setOptionsModel(OptionsModel *optionsModel)
Definition: paymentserver.cpp:430
node.h
CBaseChainParams::TESTNET
static const std::string TESTNET
Definition: chainparamsbase.h:22
CChainParams
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:47
PaymentServer::handleURIConnection
void handleURIConnection()
Definition: paymentserver.cpp:409
GetTime
int64_t GetTime()
DEPRECATED Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable)
Definition: time.cpp:27
SendCoinsRecipient::authenticatedMerchant
QString authenticatedMerchant
Definition: sendcoinsrecipient.h:54
PaymentServer::ipcParseCommandLine
static void ipcParseCommandLine(int argc, char *argv[])
Definition: paymentserver.cpp:119
PaymentRequestPlus::parse
bool parse(const QByteArray &data)
Definition: paymentrequestplus.cpp:26
chainparams.h
FormatISO8601DateTime
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition: time.cpp:79
ipcParseURI
static std::string ipcParseURI(const QString &arg, const CChainParams &params, bool useCashAddr)
Definition: paymentserver.cpp:82
PaymentServer::handleURI
bool handleURI(const CChainParams &params, const QString &s)
Definition: paymentserver.cpp:311
PaymentServer::uiReady
void uiReady()
Definition: paymentserver.cpp:299
BIP70_MAX_PAYMENTREQUEST_SIZE
static const QT_END_NAMESPACE qint64 BIP70_MAX_PAYMENTREQUEST_SIZE
Definition: paymentserver.h:61
ipcCanParseCashAddrURI
static bool ipcCanParseCashAddrURI(const QString &arg, const std::string &network)
Definition: paymentserver.cpp:97
OptionsModel::node
interfaces::Node & node() const
Definition: optionsmodel.h:113
url
const char * url
Definition: rpcconsole.cpp:52
PaymentServer::ipcSendCommandLine
static bool ipcSendCommandLine()
Definition: paymentserver.cpp:198
SATOSHI
static constexpr Amount SATOSHI
Definition: amount.h:153
SendCoinsRecipient
Definition: sendcoinsrecipient.h:23
OptionsModel
Interface from Qt to configuration data structure for Bitcoin client.
Definition: optionsmodel.h:49
ipcServerName
static QString ipcServerName()
Definition: paymentserver.cpp:64
OptionsModel::getProxySettings
bool getProxySettings(QNetworkProxy &proxy) const
Definition: optionsmodel.cpp:533
PaymentRequestPlus
Definition: paymentrequestplus.h:28
CreateChainParams
std::unique_ptr< CChainParams > CreateChainParams(const std::string &chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
Definition: chainparams.cpp:513
CTxOut
An output of a transaction.
Definition: transaction.h:130
CLIENT_NAME
const std::string CLIENT_NAME
EncodeCashAddr
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:91
PaymentServer::receivedPaymentRequest
void receivedPaymentRequest(SendCoinsRecipient)
CChainParams::CashAddrPrefix
const std::string & CashAddrPrefix() const
Definition: chainparams.h:91
CBaseChainParams::REGTEST
static const std::string REGTEST
Definition: chainparamsbase.h:23
ipcCanParseLegacyURI
static bool ipcCanParseLegacyURI(const QString &arg, const std::string &network)
Definition: paymentserver.cpp:104
BitcoinUnits::formatWithUnit
static QString formatWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
Definition: bitcoinunits.cpp:161
ArgsManager::GetArg
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:582
CChainParams::NetworkIDString
std::string NetworkIDString() const
Return the BIP70 network string (main, test or regtest)
Definition: chainparams.h:86
guiutil.h
SelectParams
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
Definition: chainparams.cpp:530
interfaces::Wallet
Interface for accessing a wallet.
Definition: wallet.h:59
Amount
Definition: amount.h:19
interfaces::Wallet::getNewDestination
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination &dest)=0
CScript
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:430
ExtractDestination
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
Definition: standard.cpp:161
interfaces::Node
Top-level interface for a bitcoin node (bitcoind process).
Definition: node.h:55
savedPaymentRequests
static QSet< QString > savedPaymentRequests
Definition: paymentserver.cpp:80
CBaseChainParams::MAIN
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
Definition: chainparamsbase.h:21
name
const char * name
Definition: rest.cpp:43
ui_interface.h
interfaces::Node::getDustRelayFee
virtual CFeeRate getDustRelayFee()=0
Get dust relay fee.
system.h
CClientUIInterface::MSG_ERROR
@ MSG_ERROR
Definition: ui_interface.h:73
GUIUtil::HtmlEscape
QString HtmlEscape(const QString &str, bool fMultiLine)
Definition: guiutil.cpp:242
CClientUIInterface::ICON_INFORMATION
@ ICON_INFORMATION
Definition: ui_interface.h:28
OptionsModel::getDisplayUnit
int getDisplayUnit() const
Definition: optionsmodel.h:97
Config::GetChainParams
virtual const CChainParams & GetChainParams() const =0
SendCoinsRecipient::address
QString address
Definition: sendcoinsrecipient.h:39
gArgs
ArgsManager gArgs
Definition: system.cpp:76
prevector::size
size_type size() const
Definition: prevector.h:386
Params
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:508
IsDust
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
Definition: policy.cpp:34
bitcoinunits.h
PaymentRequestPlus::getPayTo
QList< std::pair< CScript, Amount > > getPayTo() const
Definition: paymentrequestplus.cpp:228
IsValidDestinationString
bool IsValidDestinationString(const std::string &str, const CChainParams &params)
Definition: key_io.cpp:186
config.h
PaymentRequestPlus::getMerchant
bool getMerchant(X509_STORE *certStore, QString &merchant) const
Definition: paymentrequestplus.cpp:59
PaymentServer::uriServer
QLocalServer * uriServer
Definition: paymentserver.h:150
interfaces::Wallet::setAddressBook
virtual bool setAddressBook(const CTxDestination &dest, const std::string &name, const std::string &purpose)=0
Add or update address.
PaymentServer::handleURIOrFile
void handleURIOrFile(const QString &s)
Definition: paymentserver.cpp:374
optionsmodel.h
BITCOIN_IPC_CONNECT_TIMEOUT
const int BITCOIN_IPC_CONNECT_TIMEOUT
Definition: paymentserver.cpp:47
MAX_MONEY
static const Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:175
PaymentServer::eventFilter
bool eventFilter(QObject *object, QEvent *event) override
Definition: paymentserver.cpp:284
GUIUtil::parseBitcoinURI
bool parseBitcoinURI(const QString &scheme, const QUrl &uri, SendCoinsRecipient *out)
Definition: guiutil.cpp:135
GetConfig
const Config & GetConfig()
Definition: config.cpp:34
CTxDestination
boost::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:132
PaymentServer::PaymentServer
PaymentServer(QObject *parent, bool startLocalServer=true)
Definition: paymentserver.cpp:228
PaymentRequestPlus::getDetails
const payments::PaymentDetails & getDetails() const
Definition: paymentrequestplus.h:43
cashaddrenc.h
SendCoinsRecipient::message
QString message
Definition: sendcoinsrecipient.h:43
paymentserver.h