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