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