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