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