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.
5 #include "paymentserver.h"
7 #include "bitcoinunits.h"
9 #include "optionsmodel.h"
12 #include "chainparams.h"
13 #include "policy/policy.h"
14 #include "ui_interface.h"
16 #include "wallet/wallet.h"
20 #include <openssl/x509_vfy.h>
22 #include <QApplication>
24 #include <QDataStream>
28 #include <QFileOpenEvent>
31 #include <QLocalServer>
32 #include <QLocalSocket>
33 #include <QNetworkAccessManager>
34 #include <QNetworkProxy>
35 #include <QNetworkReply>
36 #include <QNetworkRequest>
37 #include <QSslCertificate>
40 #include <QStringList>
41 #include <QTextDocument>
43 #if QT_VERSION < 0x050000
49 const int BITCOIN_IPC_CONNECT_TIMEOUT
= 1000; // milliseconds
50 const QString
BITCOIN_IPC_PREFIX("bitcoin:");
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";
59 struct X509StoreDeleter
{
60 void operator()(X509_STORE
* b
) {
66 void operator()(X509
* b
) { X509_free(b
); }
69 namespace // Anon namespace
71 std::unique_ptr
<X509_STORE
, X509StoreDeleter
> certStore
;
75 // Create a name that is unique for:
76 // testnet / non-testnet
79 static QString
ipcServerName()
81 QString
name("BitcoinQt");
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
)));
93 // We store payment URIs and requests received before
94 // the main GUI window is up and ready to ask the user
97 static QList
<QString
> savedPaymentRequests
;
99 static void ReportInvalidCertificate(const QSslCertificate
& cert
)
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
);
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
);
109 // Load OpenSSL's list of root certificate authorities
111 void PaymentServer::LoadRootCAs(X509_STORE
* _store
)
113 // Unit tests mostly use this, to pass in fake root CAs:
116 certStore
.reset(_store
);
120 // Normal execution, use either -rootcertificates or system certs:
121 certStore
.reset(X509_STORE_new());
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(gArgs
.GetArg("-rootcertificates", "-system-"));
128 if (certFile
.isEmpty()) {
129 qDebug() << QString("PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__
);
133 QList
<QSslCertificate
> certList
;
135 if (certFile
!= "-system-") {
136 qDebug() << QString("PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__
).arg(certFile
);
138 certList
= QSslCertificate::fromPath(certFile
);
139 // Use those certificates when fetching payment requests, too:
140 QSslSocket::setDefaultCaCertificates(certList
);
142 certList
= QSslSocket::systemCaCertificates();
145 const QDateTime currentTime
= QDateTime::currentDateTime();
147 for (const QSslCertificate
& cert
: certList
) {
148 // Don't log NULL certificates
152 // Not yet active/valid, or expired certificate
153 if (currentTime
< cert
.effectiveDate() || currentTime
> cert
.expiryDate()) {
154 ReportInvalidCertificate(cert
);
158 #if QT_VERSION >= 0x050000
159 // Blacklisted certificate
160 if (cert
.isBlacklisted()) {
161 ReportInvalidCertificate(cert
);
165 QByteArray certData
= cert
.toDer();
166 const unsigned char *data
= (const unsigned char *)certData
.data();
168 std::unique_ptr
<X509
, X509Deleter
> x509(d2i_X509(0, &data
, certData
.size()));
169 if (x509
&& X509_STORE_add_cert(certStore
.get(), x509
.get()))
171 // Note: X509_STORE increases the reference count to the X509 object,
172 // we still have to release our reference to it.
177 ReportInvalidCertificate(cert
);
181 qWarning() << "PaymentServer::LoadRootCAs: Loaded " << nRootCerts
<< " root certificates";
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
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.
199 // Warning: ipcSendCommandLine() is called early in init,
200 // so don't use "Q_EMIT message()", but "QMessageBox::"!
202 void PaymentServer::ipcParseCommandLine(int argc
, char* argv
[])
204 for (int i
= 1; i
< argc
; i
++)
206 QString
arg(argv
[i
]);
207 if (arg
.startsWith("-"))
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
216 savedPaymentRequests
.append(arg
);
218 SendCoinsRecipient r
;
219 if (GUIUtil::parseBitcoinURI(arg
, &r
) && !r
.address
.isEmpty())
221 auto tempChainParams
= CreateChainParams(CBaseChainParams::MAIN
);
223 if (IsValidDestinationString(r
.address
.toStdString(), *tempChainParams
)) {
224 SelectParams(CBaseChainParams::MAIN
);
226 tempChainParams
= CreateChainParams(CBaseChainParams::TESTNET
);
227 if (IsValidDestinationString(r
.address
.toStdString(), *tempChainParams
)) {
228 SelectParams(CBaseChainParams::TESTNET
);
233 else if (QFile::exists(arg
)) // Filename
235 savedPaymentRequests
.append(arg
);
237 PaymentRequestPlus request
;
238 if (readPaymentRequestFromFile(arg
, request
))
240 if (request
.getDetails().network() == "main")
242 SelectParams(CBaseChainParams::MAIN
);
244 else if (request
.getDetails().network() == "test")
246 SelectParams(CBaseChainParams::TESTNET
);
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
;
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.
265 bool PaymentServer::ipcSendCommandLine()
267 bool fResult
= false;
268 for (const QString
& r
: savedPaymentRequests
)
270 QLocalSocket
* socket
= new QLocalSocket();
271 socket
->connectToServer(ipcServerName(), QIODevice::WriteOnly
);
272 if (!socket
->waitForConnected(BITCOIN_IPC_CONNECT_TIMEOUT
))
280 QDataStream
out(&block
, QIODevice::WriteOnly
);
281 out
.setVersion(QDataStream::Qt_4_0
);
283 out
.device()->seek(0);
285 socket
->write(block
);
287 socket
->waitForBytesWritten(BITCOIN_IPC_CONNECT_TIMEOUT
);
288 socket
->disconnectFromServer();
298 PaymentServer::PaymentServer(QObject
* parent
, bool startLocalServer
) :
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
;
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
313 parent
->installEventFilter(this);
315 QString name
= ipcServerName();
317 // Clean up old socket leftover from a crash:
318 QLocalServer::removeServer(name
);
320 if (startLocalServer
)
322 uriServer
= new QLocalServer(this);
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 bitcoin: click-to-pay handler"));
330 connect(uriServer
, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
331 connect(this, SIGNAL(receivedPaymentACK(QString
)), this, SLOT(handlePaymentACK(QString
)));
336 PaymentServer::~PaymentServer()
338 google::protobuf::ShutdownProtobufLibrary();
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.
346 bool PaymentServer::eventFilter(QObject
*object
, QEvent
*event
)
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());
358 return QObject::eventFilter(object
, event
);
361 void PaymentServer::initNetManager()
365 if (netManager
!= nullptr)
368 // netManager is used to fetch paymentrequests given in bitcoin: URIs
369 netManager
= new QNetworkAccessManager(this);
373 // Query active SOCKS5 proxy
374 if (optionsModel
->getProxySettings(proxy
)) {
375 netManager
->setProxy(proxy
);
377 qDebug() << "PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy
.hostName() << ":" << proxy
.port();
380 qDebug() << "PaymentServer::initNetManager: No active proxy server found.";
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
> &)));
388 void PaymentServer::uiReady()
393 for (const QString
& s
: savedPaymentRequests
)
397 savedPaymentRequests
.clear();
400 void PaymentServer::handleURIOrFile(const QString
& s
)
404 savedPaymentRequests
.append(s
);
408 if (s
.startsWith(BITCOIN_IPC_PREFIX
, Qt::CaseInsensitive
)) // bitcoin: URI
410 #if QT_VERSION < 0x050000
413 QUrlQuery
uri((QUrl(s
)));
415 if (uri
.hasQueryItem("r")) // payment request URI
418 temp
.append(uri
.queryItemValue("r"));
419 QString decoded
= QUrl::fromPercentEncoding(temp
);
420 QUrl
fetchUrl(decoded
, QUrl::StrictMode
);
422 if (fetchUrl
.isValid())
424 qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl
<< ")";
425 fetchRequest(fetchUrl
);
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()),
432 CClientUIInterface::ICON_WARNING
);
439 SendCoinsRecipient recipient
;
440 if (GUIUtil::parseBitcoinURI(s
, &recipient
))
442 if (!IsValidDestinationString(recipient
.address
.toStdString())) {
443 Q_EMIT
message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient
.address
),
444 CClientUIInterface::MSG_ERROR
);
447 Q_EMIT
receivedPaymentRequest(recipient
);
450 Q_EMIT
message(tr("URI handling"),
451 tr("URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
452 CClientUIInterface::ICON_WARNING
);
458 if (QFile::exists(s
)) // payment request file
460 PaymentRequestPlus request
;
461 SendCoinsRecipient recipient
;
462 if (!readPaymentRequestFromFile(s
, request
))
464 Q_EMIT
message(tr("Payment request file handling"),
465 tr("Payment request file cannot be read! This can be caused by an invalid payment request file."),
466 CClientUIInterface::ICON_WARNING
);
468 else if (processPaymentRequest(request
, recipient
))
469 Q_EMIT
receivedPaymentRequest(recipient
);
475 void PaymentServer::handleURIConnection()
477 QLocalSocket
*clientConnection
= uriServer
->nextPendingConnection();
479 while (clientConnection
->bytesAvailable() < (int)sizeof(quint32
))
480 clientConnection
->waitForReadyRead();
482 connect(clientConnection
, SIGNAL(disconnected()),
483 clientConnection
, SLOT(deleteLater()));
485 QDataStream
in(clientConnection
);
486 in
.setVersion(QDataStream::Qt_4_0
);
487 if (clientConnection
->bytesAvailable() < (int)sizeof(quint16
)) {
493 handleURIOrFile(msg
);
497 // Warning: readPaymentRequestFromFile() is used in ipcSendCommandLine()
498 // so don't use "Q_EMIT message()", but "QMessageBox::"!
500 bool PaymentServer::readPaymentRequestFromFile(const QString
& filename
, PaymentRequestPlus
& request
)
503 if (!f
.open(QIODevice::ReadOnly
)) {
504 qWarning() << QString("PaymentServer::%1: Failed to open %2").arg(__func__
).arg(filename
);
508 // BIP70 DoS protection
509 if (!verifySize(f
.size())) {
513 QByteArray data
= f
.readAll();
515 return request
.parse(data
);
518 bool PaymentServer::processPaymentRequest(const PaymentRequestPlus
& request
, SendCoinsRecipient
& recipient
)
523 if (request
.IsInitialized()) {
524 // Payment request network matches client network?
525 if (!verifyNetwork(request
.getDetails())) {
526 Q_EMIT
message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."),
527 CClientUIInterface::MSG_ERROR
);
532 // Make sure any payment requests involved are still valid.
533 // This is re-checked just before sending coins in WalletModel::sendCoins().
534 if (verifyExpired(request
.getDetails())) {
535 Q_EMIT
message(tr("Payment request rejected"), tr("Payment request expired."),
536 CClientUIInterface::MSG_ERROR
);
541 Q_EMIT
message(tr("Payment request error"), tr("Payment request is not initialized."),
542 CClientUIInterface::MSG_ERROR
);
547 recipient
.paymentRequest
= request
;
548 recipient
.message
= GUIUtil::HtmlEscape(request
.getDetails().memo());
550 request
.getMerchant(certStore
.get(), recipient
.authenticatedMerchant
);
552 QList
<std::pair
<CScript
, CAmount
> > sendingTos
= request
.getPayTo();
553 QStringList addresses
;
555 for (const std::pair
<CScript
, CAmount
>& sendingTo
: sendingTos
) {
556 // Extract and check destination addresses
558 if (ExtractDestination(sendingTo
.first
, dest
)) {
559 // Append destination address
560 addresses
.append(QString::fromStdString(EncodeDestination(dest
)));
562 else if (!recipient
.authenticatedMerchant
.isEmpty()) {
563 // Unauthenticated payment requests to custom bitcoin addresses are not supported
564 // (there is no good way to tell the user where they are paying in a way they'd
565 // have a chance of understanding).
566 Q_EMIT
message(tr("Payment request rejected"),
567 tr("Unverified payment requests to custom payment scripts are unsupported."),
568 CClientUIInterface::MSG_ERROR
);
572 // Bitcoin amounts are stored as (optional) uint64 in the protobuf messages (see paymentrequest.proto),
573 // but CAmount is defined as int64_t. Because of that we need to verify that amounts are in a valid range
574 // and no overflow has happened.
575 if (!verifyAmount(sendingTo
.second
)) {
576 Q_EMIT
message(tr("Payment request rejected"), tr("Invalid payment request."), CClientUIInterface::MSG_ERROR
);
580 // Extract and check amounts
581 CTxOut
txOut(sendingTo
.second
, sendingTo
.first
);
582 if (IsDust(txOut
, ::dustRelayFee
)) {
583 Q_EMIT
message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
584 .arg(BitcoinUnits::formatWithUnit(optionsModel
->getDisplayUnit(), sendingTo
.second
)),
585 CClientUIInterface::MSG_ERROR
);
590 recipient
.amount
+= sendingTo
.second
;
591 // Also verify that the final amount is still in a valid range after adding additional amounts.
592 if (!verifyAmount(recipient
.amount
)) {
593 Q_EMIT
message(tr("Payment request rejected"), tr("Invalid payment request."), CClientUIInterface::MSG_ERROR
);
597 // Store addresses and format them to fit nicely into the GUI
598 recipient
.address
= addresses
.join("<br />");
600 if (!recipient
.authenticatedMerchant
.isEmpty()) {
601 qDebug() << "PaymentServer::processPaymentRequest: Secure payment request from " << recipient
.authenticatedMerchant
;
604 qDebug() << "PaymentServer::processPaymentRequest: Insecure payment request to " << addresses
.join(", ");
610 void PaymentServer::fetchRequest(const QUrl
& url
)
612 QNetworkRequest netRequest
;
613 netRequest
.setAttribute(QNetworkRequest::User
, BIP70_MESSAGE_PAYMENTREQUEST
);
614 netRequest
.setUrl(url
);
615 netRequest
.setRawHeader("User-Agent", CLIENT_NAME
.c_str());
616 netRequest
.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTREQUEST
);
617 netManager
->get(netRequest
);
620 void PaymentServer::fetchPaymentACK(CWallet
* wallet
, const SendCoinsRecipient
& recipient
, QByteArray transaction
)
622 const payments::PaymentDetails
& details
= recipient
.paymentRequest
.getDetails();
623 if (!details
.has_payment_url())
626 QNetworkRequest netRequest
;
627 netRequest
.setAttribute(QNetworkRequest::User
, BIP70_MESSAGE_PAYMENTACK
);
628 netRequest
.setUrl(QString::fromStdString(details
.payment_url()));
629 netRequest
.setHeader(QNetworkRequest::ContentTypeHeader
, BIP71_MIMETYPE_PAYMENT
);
630 netRequest
.setRawHeader("User-Agent", CLIENT_NAME
.c_str());
631 netRequest
.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTACK
);
633 payments::Payment payment
;
634 payment
.set_merchant_data(details
.merchant_data());
635 payment
.add_transactions(transaction
.data(), transaction
.size());
637 // Create a new refund address, or re-use:
638 QString account
= tr("Refund from %1").arg(recipient
.authenticatedMerchant
);
639 std::string strAccount
= account
.toStdString();
640 std::set
<CTxDestination
> refundAddresses
= wallet
->GetAccountAddresses(strAccount
);
641 if (!refundAddresses
.empty()) {
642 CScript s
= GetScriptForDestination(*refundAddresses
.begin());
643 payments::Output
* refund_to
= payment
.add_refund_to();
644 refund_to
->set_script(&s
[0], s
.size());
648 if (wallet
->GetKeyFromPool(newKey
)) {
649 CKeyID keyID
= newKey
.GetID();
650 wallet
->SetAddressBook(keyID
, strAccount
, "refund");
652 CScript s
= GetScriptForDestination(keyID
);
653 payments::Output
* refund_to
= payment
.add_refund_to();
654 refund_to
->set_script(&s
[0], s
.size());
657 // This should never happen, because sending coins should have
658 // just unlocked the wallet and refilled the keypool.
659 qWarning() << "PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
663 int length
= payment
.ByteSize();
664 netRequest
.setHeader(QNetworkRequest::ContentLengthHeader
, length
);
665 QByteArray
serData(length
, '\0');
666 if (payment
.SerializeToArray(serData
.data(), length
)) {
667 netManager
->post(netRequest
, serData
);
670 // This should never happen, either.
671 qWarning() << "PaymentServer::fetchPaymentACK: Error serializing payment message";
675 void PaymentServer::netRequestFinished(QNetworkReply
* reply
)
677 reply
->deleteLater();
679 // BIP70 DoS protection
680 if (!verifySize(reply
->size())) {
681 Q_EMIT
message(tr("Payment request rejected"),
682 tr("Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
683 .arg(reply
->request().url().toString())
685 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE
),
686 CClientUIInterface::MSG_ERROR
);
690 if (reply
->error() != QNetworkReply::NoError
) {
691 QString msg
= tr("Error communicating with %1: %2")
692 .arg(reply
->request().url().toString())
693 .arg(reply
->errorString());
695 qWarning() << "PaymentServer::netRequestFinished: " << msg
;
696 Q_EMIT
message(tr("Payment request error"), msg
, CClientUIInterface::MSG_ERROR
);
700 QByteArray data
= reply
->readAll();
702 QString requestType
= reply
->request().attribute(QNetworkRequest::User
).toString();
703 if (requestType
== BIP70_MESSAGE_PAYMENTREQUEST
)
705 PaymentRequestPlus request
;
706 SendCoinsRecipient recipient
;
707 if (!request
.parse(data
))
709 qWarning() << "PaymentServer::netRequestFinished: Error parsing payment request";
710 Q_EMIT
message(tr("Payment request error"),
711 tr("Payment request cannot be parsed!"),
712 CClientUIInterface::MSG_ERROR
);
714 else if (processPaymentRequest(request
, recipient
))
715 Q_EMIT
receivedPaymentRequest(recipient
);
719 else if (requestType
== BIP70_MESSAGE_PAYMENTACK
)
721 payments::PaymentACK paymentACK
;
722 if (!paymentACK
.ParseFromArray(data
.data(), data
.size()))
724 QString msg
= tr("Bad response from server %1")
725 .arg(reply
->request().url().toString());
727 qWarning() << "PaymentServer::netRequestFinished: " << msg
;
728 Q_EMIT
message(tr("Payment request error"), msg
, CClientUIInterface::MSG_ERROR
);
732 Q_EMIT
receivedPaymentACK(GUIUtil::HtmlEscape(paymentACK
.memo()));
737 void PaymentServer::reportSslErrors(QNetworkReply
* reply
, const QList
<QSslError
> &errs
)
742 for (const QSslError
& err
: errs
) {
743 qWarning() << "PaymentServer::reportSslErrors: " << err
;
744 errString
+= err
.errorString() + "\n";
746 Q_EMIT
message(tr("Network request error"), errString
, CClientUIInterface::MSG_ERROR
);
749 void PaymentServer::setOptionsModel(OptionsModel
*_optionsModel
)
751 this->optionsModel
= _optionsModel
;
754 void PaymentServer::handlePaymentACK(const QString
& paymentACKMsg
)
756 // currently we don't further process or store the paymentACK message
757 Q_EMIT
message(tr("Payment acknowledged"), paymentACKMsg
, CClientUIInterface::ICON_INFORMATION
| CClientUIInterface::MODAL
);
760 bool PaymentServer::verifyNetwork(const payments::PaymentDetails
& requestDetails
)
762 bool fVerified
= requestDetails
.network() == Params().NetworkIDString();
764 qWarning() << QString("PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
766 .arg(QString::fromStdString(requestDetails
.network()))
767 .arg(QString::fromStdString(Params().NetworkIDString()));
772 bool PaymentServer::verifyExpired(const payments::PaymentDetails
& requestDetails
)
774 bool fVerified
= (requestDetails
.has_expires() && (int64_t)requestDetails
.expires() < GetTime());
776 const QString requestExpires
= QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", (int64_t)requestDetails
.expires()));
777 qWarning() << QString("PaymentServer::%1: Payment request expired \"%2\".")
779 .arg(requestExpires
);
784 bool PaymentServer::verifySize(qint64 requestSize
)
786 bool fVerified
= (requestSize
<= BIP70_MAX_PAYMENTREQUEST_SIZE
);
788 qWarning() << QString("PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
791 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE
);
796 bool PaymentServer::verifyAmount(const CAmount
& requestAmount
)
798 bool fVerified
= MoneyRange(requestAmount
);
800 qWarning() << QString("PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
808 X509_STORE
* PaymentServer::getCertStore()
810 return certStore
.get();