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