8#include <chainparams.h>
22#include <openssl/x509_vfy.h>
25#include <QApplication>
31#include <QFileOpenEvent>
34#include <QLocalServer>
35#include <QLocalSocket>
37#include <QNetworkAccessManager>
38#include <QNetworkProxy>
39#include <QNetworkReply>
40#include <QNetworkRequest>
41#include <QSslCertificate>
42#include <QSslConfiguration>
46#include <QTextDocument>
55const char *BIP70_MESSAGE_PAYMENTACK =
"PaymentACK";
56const char *BIP70_MESSAGE_PAYMENTREQUEST =
"PaymentRequest";
58const char *BIP71_MIMETYPE_PAYMENT =
"application/ecash-payment";
59const char *BIP71_MIMETYPE_PAYMENTACK =
"application/ecash-paymentack";
60const char *BIP71_MIMETYPE_PAYMENTREQUEST =
"application/ecash-paymentrequest";
69 QString
name(
"BitcoinQt");
75 name.append(QString::number(qHash(ddir)));
88 const QString scheme = QString::fromStdString(params.
CashAddrPrefix());
89 if (!arg.startsWith(scheme +
":", Qt::CaseInsensitive)) {
104 std::string addr =
ipcParseURI(arg, *tempChainParams,
true);
111 std::string addr =
ipcParseURI(arg, *tempChainParams,
false);
124 std::array<const ChainType, 3> networks = {
127 const ChainType *chosenNetwork =
nullptr;
129 for (
int i = 1; i < argc; i++) {
130 QString arg(argv[i]);
131 if (arg.startsWith(
"-")) {
138 for (
auto net : networks) {
154 if (readPaymentRequestFromFile(arg, request)) {
155 for (
auto net : networks) {
165 if (itemNetwork ==
nullptr) {
168 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request "
169 "file or URI does not exist or is invalid: "
175 if (chosenNetwork && chosenNetwork != itemNetwork) {
176 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request "
179 <<
" does not match already chosen network "
189 chosenNetwork = itemNetwork;
203 bool fResult =
false;
205 QLocalSocket *socket =
new QLocalSocket();
206 socket->connectToServer(
ipcServerName(), QIODevice::WriteOnly);
214 QDataStream out(&block, QIODevice::WriteOnly);
215 out.setVersion(QDataStream::Qt_4_0);
217 out.device()->seek(0);
219 socket->write(block);
222 socket->disconnectFromServer();
233 : QObject(parent), saveURIs(true), uriServer(nullptr), optionsModel(nullptr)
243 GOOGLE_PROTOBUF_VERIFY_VERSION;
250 parent->installEventFilter(
this);
256 QLocalServer::removeServer(
name);
258 if (startLocalServer) {
264 QMessageBox::critical(
nullptr, tr(
"Payment request error"),
265 tr(
"Cannot start click-to-pay handler"));
267 connect(
uriServer, &QLocalServer::newConnection,
this,
270 connect(
this, &PaymentServer::receivedPaymentACK,
this,
271 &PaymentServer::handlePaymentACK);
279 google::protobuf::ShutdownProtobufLibrary();
289 if (event->type() == QEvent::FileOpen) {
290 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent *
>(event);
291 if (!fileEvent->file().isEmpty()) {
293 }
else if (!fileEvent->url().isEmpty()) {
300 return QObject::eventFilter(
object, event);
316 const QString scheme = QString::fromStdString(params.
CashAddrPrefix());
317 if (!s.startsWith(scheme +
":", Qt::CaseInsensitive)) {
321 QUrlQuery uri((QUrl(s)));
324 if (uri.hasQueryItem(
"r")) {
326 temp.append(uri.queryItemValue(
"r").toUtf8());
327 QString decoded = QUrl::fromPercentEncoding(temp);
328 QUrl fetchUrl(decoded, QUrl::StrictMode);
330 if (fetchUrl.isValid()) {
331 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest("
333 fetchRequest(fetchUrl);
335 qWarning() <<
"PaymentServer::handleURIOrFile: Invalid URL: "
337 Q_EMIT
message(tr(
"URI handling"),
338 tr(
"Payment request fetch URL is invalid: %1")
339 .arg(fetchUrl.toString()),
353 if (uri.hasQueryItem(
"r")) {
354 Q_EMIT
message(tr(
"URI handling"),
355 tr(
"Cannot process payment request because "
356 "BIP70 support was not compiled in."),
362 tr(
"Invalid payment address %1").arg(recipient.
address),
370 tr(
"URI cannot be parsed! This can be caused by an invalid "
371 "Bitcoin address or malformed URI parameters."),
394 if (!readPaymentRequestFromFile(s, request)) {
395 Q_EMIT
message(tr(
"Payment request file handling"),
396 tr(
"Payment request file cannot be read! This can "
397 "be caused by an invalid payment request file."),
399 }
else if (processPaymentRequest(request, recipient)) {
405 Q_EMIT
message(tr(
"Payment request file handling"),
406 tr(
"Cannot process payment request because BIP70 "
407 "support was not compiled in."),
414 QLocalSocket *clientConnection =
uriServer->nextPendingConnection();
416 while (clientConnection->bytesAvailable() < (
int)
sizeof(quint32)) {
417 clientConnection->waitForReadyRead();
420 connect(clientConnection, &QLocalSocket::disconnected, clientConnection,
421 &QLocalSocket::deleteLater);
423 QDataStream in(clientConnection);
424 in.setVersion(QDataStream::Qt_4_0);
425 if (clientConnection->bytesAvailable() < (
int)
sizeof(quint16)) {
439struct X509StoreDeleter {
440 void operator()(X509_STORE *b) { X509_STORE_free(b); }
444 void operator()(X509 *b) { X509_free(b); }
449std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
452static void ReportInvalidCertificate(
const QSslCertificate &cert) {
453 qDebug() << QString(
"%1: Payment server found an invalid certificate: ")
455 << cert.serialNumber()
456 << cert.subjectInfo(QSslCertificate::CommonName)
457 << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier)
458 << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
464void PaymentServer::LoadRootCAs(X509_STORE *_store) {
467 certStore.reset(_store);
472 certStore.reset(X509_STORE_new());
478 QString::fromStdString(
gArgs.
GetArg(
"-rootcertificates",
"-system-"));
481 if (certFile.isEmpty()) {
482 qDebug() << QString(
"PaymentServer::%1: Payment request authentication "
483 "via X.509 certificates disabled.")
488 QList<QSslCertificate> certList;
490 if (certFile !=
"-system-") {
491 qDebug() << QString(
"PaymentServer::%1: Using \"%2\" as trusted root "
496 certList = QSslCertificate::fromPath(certFile);
498 QSslConfiguration::defaultConfiguration().setCaCertificates(certList);
500 certList = QSslConfiguration::systemCaCertificates();
504 const QDateTime currentTime = QDateTime::currentDateTime();
506 for (
const QSslCertificate &cert : certList) {
513 if (currentTime < cert.effectiveDate() ||
514 currentTime > cert.expiryDate()) {
515 ReportInvalidCertificate(cert);
520 if (cert.isBlacklisted()) {
521 ReportInvalidCertificate(cert);
525 QByteArray certData = cert.toDer();
526 const uint8_t *data = (
const uint8_t *)certData.data();
528 std::unique_ptr<X509, X509Deleter> x509(
529 d2i_X509(0, &data, certData.size()));
530 if (x509 && X509_STORE_add_cert(certStore.get(), x509.get())) {
535 ReportInvalidCertificate(cert);
539 qInfo() <<
"PaymentServer::LoadRootCAs: Loaded " << nRootCerts
540 <<
" root certificates";
552void PaymentServer::initNetManager() {
556 if (netManager !=
nullptr) {
561 netManager =
new QNetworkAccessManager(
this);
567 netManager->setProxy(proxy);
569 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy"
570 << proxy.hostName() <<
":" << proxy.port();
573 <<
"PaymentServer::initNetManager: No active proxy server found.";
576 connect(netManager, &QNetworkAccessManager::finished,
this,
577 &PaymentServer::netRequestFinished);
578 connect(netManager, &QNetworkAccessManager::sslErrors,
this,
579 &PaymentServer::reportSslErrors);
586bool PaymentServer::readPaymentRequestFromFile(
const QString &filename,
589 if (!f.open(QIODevice::ReadOnly)) {
590 qWarning() << QString(
"PaymentServer::%1: Failed to open %2")
597 if (!verifySize(f.size())) {
601 QByteArray data = f.readAll();
603 return request.
parse(data);
616 tr(
"Payment request rejected"),
617 tr(
"Payment request network doesn't match client network."),
627 Q_EMIT
message(tr(
"Payment request rejected"),
628 tr(
"Payment request expired."),
634 Q_EMIT
message(tr(
"Payment request error"),
635 tr(
"Payment request is not initialized."),
641 recipient.paymentRequest = request;
646 QList<std::pair<CScript, Amount>> sendingTos = request.
getPayTo();
647 QStringList addresses;
649 for (
const std::pair<CScript, Amount> &sendingTo : sendingTos) {
660 Q_EMIT
message(tr(
"Payment request rejected"),
661 tr(
"Unverified payment requests to custom payment "
662 "scripts are unsupported."),
671 if (!verifyAmount(sendingTo.second)) {
672 Q_EMIT
message(tr(
"Payment request rejected"),
673 tr(
"Invalid payment request."),
679 CTxOut txOut(
Amount(sendingTo.second), sendingTo.first);
682 tr(
"Payment request error"),
683 tr(
"Requested payment amount of %1 is too small (considered "
692 recipient.
amount += sendingTo.second;
695 if (!verifyAmount(recipient.
amount)) {
696 Q_EMIT
message(tr(
"Payment request rejected"),
697 tr(
"Invalid payment request."),
703 recipient.
address = addresses.join(
"<br />");
706 qDebug() <<
"PaymentServer::processPaymentRequest: Secure payment "
710 qDebug() <<
"PaymentServer::processPaymentRequest: Insecure payment "
712 << addresses.join(
", ");
718void PaymentServer::dataDownloaded() {
720 qWarning() <<
"PaymentServer::dataDownloaded: Payment request size "
721 "bigger than expected - aborting";
725 paymentRequestReply->finished();
729void PaymentServer::fetchRequest(
const QUrl &
url) {
730 QNetworkRequest netRequest;
731 netRequest.setAttribute(QNetworkRequest::User,
732 BIP70_MESSAGE_PAYMENTREQUEST);
733 netRequest.setUrl(
url);
734 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
735 netRequest.setRawHeader(
"Accept", BIP71_MIMETYPE_PAYMENTREQUEST);
738 paymentRequestReply = netManager->get(netRequest);
739 assert(paymentRequestReply);
746 connect(paymentRequestReply, &QIODevice::readyRead,
this,
747 &PaymentServer::dataDownloaded);
752 QByteArray transaction) {
753 const payments::PaymentDetails &details =
754 recipient.paymentRequest.getDetails();
755 if (!details.has_payment_url()) {
759 QNetworkRequest netRequest;
760 netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK);
761 netRequest.setUrl(QString::fromStdString(details.payment_url()));
762 netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
763 BIP71_MIMETYPE_PAYMENT);
764 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
765 netRequest.setRawHeader(
"Accept", BIP71_MIMETYPE_PAYMENTACK);
767 payments::Payment payment;
768 payment.set_merchant_data(details.merchant_data());
769 payment.add_transactions(transaction.data(), transaction.size());
780 std::string label = tr(
"Refund from %1")
783 wallet.setAddressBook(*op_dest, label,
"refund");
786 payments::Output *refund_to = payment.add_refund_to();
787 refund_to->set_script(&s[0], s.size());
791 qWarning() <<
"PaymentServer::fetchPaymentACK: Error getting refund "
792 "key, refund_to not set";
796#ifdef USE_PROTOBUF_MESSAGE_BYTESIZELONG
797 length.setValue(payment.ByteSizeLong());
799 length.setValue(payment.ByteSize());
802 netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
803 QByteArray serData(length.toInt(),
'\0');
804 if (payment.SerializeToArray(serData.data(), length.toInt())) {
805 netManager->post(netRequest, serData);
808 qWarning() <<
"PaymentServer::fetchPaymentACK: Error serializing "
813void PaymentServer::netRequestFinished(QNetworkReply *reply) {
814 reply->deleteLater();
821 if (!verifySize(reply->size())) {
823 tr(
"Payment request rejected"),
824 tr(
"Payment request %1 is larger than the max allowed %2 bytes).")
825 .arg(reply->request().url().toString())
831 if (reply->error() != QNetworkReply::NoError) {
832 QString msg = tr(
"Error communicating with %1: %2")
833 .arg(reply->request().url().toString())
834 .arg(reply->errorString());
836 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
837 Q_EMIT
message(tr(
"Payment request error"), msg,
842 QByteArray data = reply->readAll();
844 QString requestType =
845 reply->request().attribute(QNetworkRequest::User).toString();
846 if (requestType == BIP70_MESSAGE_PAYMENTREQUEST) {
849 if (!request.
parse(data)) {
850 qWarning() <<
"PaymentServer::netRequestFinished: Error parsing "
852 Q_EMIT
message(tr(
"Payment request error"),
853 tr(
"Payment request cannot be parsed!"),
855 }
else if (processPaymentRequest(request, recipient)) {
860 }
else if (requestType == BIP70_MESSAGE_PAYMENTACK) {
861 payments::PaymentACK paymentACK;
862 if (!paymentACK.ParseFromArray(data.data(), data.size())) {
863 QString msg = tr(
"Bad response from server %1")
864 .arg(reply->request().url().toString());
866 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
867 Q_EMIT
message(tr(
"Payment request error"), msg,
875void PaymentServer::reportSslErrors(QNetworkReply *reply,
876 const QList<QSslError> &errs) {
880 for (
const QSslError &err : errs) {
881 qWarning() <<
"PaymentServer::reportSslErrors: " << err;
882 errString += err.errorString() +
"\n";
884 Q_EMIT
message(tr(
"Network request error"), errString,
888void PaymentServer::handlePaymentACK(
const QString &paymentACKMsg) {
890 Q_EMIT
message(tr(
"Payment acknowledged"), paymentACKMsg,
895bool PaymentServer::verifyNetwork(
897 const std::string clientNetwork =
899 bool fVerified = requestDetails.network() == clientNetwork;
901 qWarning() << QString(
"PaymentServer::%1: Payment request network "
902 "\"%2\" doesn't match client network \"%3\".")
904 .arg(QString::fromStdString(requestDetails.network()))
905 .arg(QString::fromStdString(clientNetwork));
910bool PaymentServer::verifyExpired(
911 const payments::PaymentDetails &requestDetails) {
912 bool fVerified = (requestDetails.has_expires() &&
913 (int64_t)requestDetails.expires() <
GetTime());
915 const QString requestExpires = QString::fromStdString(
917 qWarning() << QString(
918 "PaymentServer::%1: Payment request expired \"%2\".")
920 .arg(requestExpires);
925bool PaymentServer::verifySize(qint64 requestSize) {
928 qWarning() << QString(
"PaymentServer::%1: Payment request too large "
929 "(downloaded %2 bytes, max allowed %3 bytes).")
937bool PaymentServer::verifyAmount(
const Amount requestAmount) {
940 qWarning() << QString(
"PaymentServer::%1: Payment request amount out "
941 "of allowed range (%2, allowed 0 - %3).")
949X509_STORE *PaymentServer::getCertStore() {
950 return certStore.get();
bool MoneyRange(const Amount nValue)
static constexpr Amount SATOSHI
static constexpr Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams ¶ms)
std::unique_ptr< const CChainParams > CreateChainParams(const ArgsManager &args, const ChainType chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given BIP70 chain name.
const CChainParams & Params()
Return the currently selected parameters.
std::string ChainTypeToString(ChainType chain)
fs::path GetDataDirNet() const
Get data directory path with appended network identifier.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
static QString formatWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD)
Format as string (with unit)
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
std::string GetChainTypeString() const
Return the chain type string.
const std::string & CashAddrPrefix() const
@ MODAL
Force blocking, modal message box dialog (not just OS notification)
An output of a transaction.
virtual const CChainParams & GetChainParams() const =0
Interface from Qt to configuration data structure for Bitcoin client.
int getDisplayUnit() const
bool getProxySettings(QNetworkProxy &proxy) const
interfaces::Node & node() const
QList< std::pair< CScript, Amount > > getPayTo() const
bool getMerchant(X509_STORE *certStore, QString &merchant) const
bool IsInitialized() const
bool parse(const QByteArray &data)
const payments::PaymentDetails & getDetails() const
static bool ipcSendCommandLine()
void setOptionsModel(OptionsModel *optionsModel)
PaymentServer(QObject *parent, bool startLocalServer=true)
void message(const QString &title, const QString &message, unsigned int style)
void handleURIConnection()
static void ipcParseCommandLine(int argc, char *argv[])
void receivedPaymentRequest(SendCoinsRecipient)
bool eventFilter(QObject *object, QEvent *event) override
void handleURIOrFile(const QString &s)
bool handleURI(const CChainParams ¶ms, const QString &s)
OptionsModel * optionsModel
QString authenticatedMerchant
Top-level interface for a bitcoin node (bitcoind process).
virtual CFeeRate getDustRelayFee()=0
Get dust relay fee.
Interface for accessing a wallet.
const std::string CLIENT_NAME
const Config & GetConfig()
bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms)
bool parseBitcoinURI(const QString &scheme, const QUrl &uri, SendCoinsRecipient *out)
QString HtmlEscape(const QString &str, bool fMultiLine)
QString boostPathToQString(const fs::path &path)
Convert OS specific boost path to QString through UTF-8.
static bool exists(const path &p)
static QString ipcServerName()
static bool ipcCanParseCashAddrURI(const QString &arg, const ChainType chain_type)
const int BITCOIN_IPC_CONNECT_TIMEOUT
static std::string ipcParseURI(const QString &arg, const CChainParams ¶ms, bool useCashAddr)
static bool ipcCanParseLegacyURI(const QString &arg, const ChainType chain_type)
static QSet< QString > savedPaymentRequests
static QT_END_NAMESPACE const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.