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> 38 #include <QSslConfiguration> 40 #include <QStringList> 41 #include <QTextDocument> 50 const char *BIP70_MESSAGE_PAYMENTACK =
"PaymentACK";
51 const char *BIP70_MESSAGE_PAYMENTREQUEST =
"PaymentRequest";
53 const char *BIP71_MIMETYPE_PAYMENT =
"application/bitcoincash-payment";
54 const char *BIP71_MIMETYPE_PAYMENTACK =
"application/bitcoincash-paymentack";
55 const char *BIP71_MIMETYPE_PAYMENTREQUEST =
56 "application/bitcoincash-paymentrequest";
65 QString
name(
"BitcoinQt");
71 name.append(QString::number(qHash(ddir)));
84 const QString scheme = QString::fromStdString(params.
CashAddrPrefix());
85 if (!arg.startsWith(scheme +
":", Qt::CaseInsensitive)) {
98 const std::string &network) {
100 std::string addr =
ipcParseURI(arg, *tempChainParams,
true);
105 const std::string &network) {
107 std::string addr =
ipcParseURI(arg, *tempChainParams,
false);
120 std::array<const std::string *, 3> networks = {
124 const std::string *chosenNetwork =
nullptr;
126 for (
int i = 1; i < argc; i++) {
127 QString arg(argv[i]);
128 if (arg.startsWith(
"-")) {
132 const std::string *itemNetwork =
nullptr;
135 for (
auto net : networks) {
148 if (!itemNetwork && QFile::exists(arg)) {
151 if (readPaymentRequestFromFile(arg, request)) {
152 for (
auto net : networks) {
161 if (itemNetwork ==
nullptr) {
164 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request " 165 "file or URI does not exist or is invalid: " 171 if (chosenNetwork && chosenNetwork != itemNetwork) {
172 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request " 174 << QString(itemNetwork->c_str())
175 <<
" does not match already chosen network " 176 << QString(chosenNetwork->c_str());
182 chosenNetwork = itemNetwork;
196 bool fResult =
false;
198 QLocalSocket *socket =
new QLocalSocket();
199 socket->connectToServer(
ipcServerName(), QIODevice::WriteOnly);
207 QDataStream out(&block, QIODevice::WriteOnly);
208 out.setVersion(QDataStream::Qt_4_0);
210 out.device()->seek(0);
212 socket->write(block);
215 socket->disconnectFromServer();
236 GOOGLE_PROTOBUF_VERIFY_VERSION;
243 parent->installEventFilter(
this);
249 QLocalServer::removeServer(name);
251 if (startLocalServer) {
257 QMessageBox::critical(
nullptr, tr(
"Payment request error"),
258 tr(
"Cannot start click-to-pay handler"));
260 connect(
uriServer, &QLocalServer::newConnection,
this,
263 connect(
this, &PaymentServer::receivedPaymentACK,
this,
264 &PaymentServer::handlePaymentACK);
272 google::protobuf::ShutdownProtobufLibrary();
282 if (event->type() == QEvent::FileOpen) {
283 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent *
>(event);
284 if (!fileEvent->file().isEmpty()) {
286 }
else if (!fileEvent->url().isEmpty()) {
293 return QObject::eventFilter(
object, event);
305 savedPaymentRequests.clear();
309 const QString scheme = QString::fromStdString(params.
CashAddrPrefix());
310 if (!s.startsWith(scheme +
":", Qt::CaseInsensitive)) {
314 QUrlQuery uri((QUrl(s)));
316 if (uri.hasQueryItem(
"r")) {
319 temp.append(uri.queryItemValue(
"r").toUtf8());
320 QString decoded = QUrl::fromPercentEncoding(temp);
321 QUrl fetchUrl(decoded, QUrl::StrictMode);
323 if (fetchUrl.isValid()) {
324 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest(" 326 fetchRequest(fetchUrl);
328 qWarning() <<
"PaymentServer::handleURIOrFile: Invalid URL: " 330 Q_EMIT
message(tr(
"URI handling"),
331 tr(
"Payment request fetch URL is invalid: %1")
332 .arg(fetchUrl.toString()),
337 Q_EMIT
message(tr(
"URI handling"),
338 tr(
"Cannot process payment request because BIP70 " 339 "support was not compiled in."),
352 tr(
"Invalid payment address %1").arg(recipient.
address),
360 tr(
"URI cannot be parsed! This can be caused by an invalid " 361 "Bitcoin address or malformed URI parameters."),
381 if (QFile::exists(s)) {
384 if (!readPaymentRequestFromFile(s, request)) {
385 Q_EMIT
message(tr(
"Payment request file handling"),
386 tr(
"Payment request file cannot be read! This can " 387 "be caused by an invalid payment request file."),
389 }
else if (processPaymentRequest(request, recipient)) {
399 QLocalSocket *clientConnection =
uriServer->nextPendingConnection();
401 while (clientConnection->bytesAvailable() < (int)
sizeof(quint32)) {
402 clientConnection->waitForReadyRead();
405 connect(clientConnection, &QLocalSocket::disconnected, clientConnection,
406 &QLocalSocket::deleteLater);
408 QDataStream in(clientConnection);
409 in.setVersion(QDataStream::Qt_4_0);
410 if (clientConnection->bytesAvailable() < (int)
sizeof(quint16)) {
424 struct X509StoreDeleter {
425 void operator()(X509_STORE *b) { X509_STORE_free(b); }
429 void operator()(X509 *b) { X509_free(b); }
434 std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
437 static void ReportInvalidCertificate(
const QSslCertificate &cert) {
438 qDebug() << QString(
"%1: Payment server found an invalid certificate: ")
440 << cert.serialNumber()
441 << cert.subjectInfo(QSslCertificate::CommonName)
442 << cert.subjectInfo(QSslCertificate::DistinguishedNameQualifier)
443 << cert.subjectInfo(QSslCertificate::OrganizationalUnitName);
449 void PaymentServer::LoadRootCAs(X509_STORE *_store) {
452 certStore.reset(_store);
457 certStore.reset(X509_STORE_new());
463 QString::fromStdString(
gArgs.
GetArg(
"-rootcertificates",
"-system-"));
466 if (certFile.isEmpty()) {
467 qDebug() << QString(
"PaymentServer::%1: Payment request authentication " 468 "via X.509 certificates disabled.")
473 QList<QSslCertificate> certList;
475 if (certFile !=
"-system-") {
476 qDebug() << QString(
"PaymentServer::%1: Using \"%2\" as trusted root " 481 certList = QSslCertificate::fromPath(certFile);
483 QSslConfiguration::defaultConfiguration().setCaCertificates(certList);
485 certList = QSslConfiguration::systemCaCertificates();
489 const QDateTime currentTime = QDateTime::currentDateTime();
491 for (
const QSslCertificate &cert : certList) {
498 if (currentTime < cert.effectiveDate() ||
499 currentTime > cert.expiryDate()) {
500 ReportInvalidCertificate(cert);
505 if (cert.isBlacklisted()) {
506 ReportInvalidCertificate(cert);
510 QByteArray certData = cert.toDer();
511 const uint8_t *data = (
const uint8_t *)certData.data();
513 std::unique_ptr<X509, X509Deleter> x509(
514 d2i_X509(0, &data, certData.size()));
515 if (x509 && X509_STORE_add_cert(certStore.get(), x509.get())) {
520 ReportInvalidCertificate(cert);
524 qInfo() <<
"PaymentServer::LoadRootCAs: Loaded " << nRootCerts
525 <<
" root certificates";
537 void PaymentServer::initNetManager() {
541 if (netManager !=
nullptr) {
546 netManager =
new QNetworkAccessManager(
this);
552 netManager->setProxy(proxy);
554 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy" 555 << proxy.hostName() <<
":" << proxy.port();
558 <<
"PaymentServer::initNetManager: No active proxy server found.";
561 connect(netManager, &QNetworkAccessManager::finished,
this,
562 &PaymentServer::netRequestFinished);
563 connect(netManager, &QNetworkAccessManager::sslErrors,
this,
564 &PaymentServer::reportSslErrors);
571 bool PaymentServer::readPaymentRequestFromFile(
const QString &filename,
574 if (!f.open(QIODevice::ReadOnly)) {
575 qWarning() << QString(
"PaymentServer::%1: Failed to open %2")
582 if (!verifySize(f.size())) {
586 QByteArray data = f.readAll();
588 return request.
parse(data);
601 tr(
"Payment request rejected"),
602 tr(
"Payment request network doesn't match client network."),
612 Q_EMIT
message(tr(
"Payment request rejected"),
613 tr(
"Payment request expired."),
619 Q_EMIT
message(tr(
"Payment request error"),
620 tr(
"Payment request is not initialized."),
626 recipient.paymentRequest = request;
631 QList<std::pair<CScript, Amount>> sendingTos = request.
getPayTo();
632 QStringList addresses;
634 for (
const std::pair<CScript, Amount> &sendingTo : sendingTos) {
645 Q_EMIT
message(tr(
"Payment request rejected"),
646 tr(
"Unverified payment requests to custom payment " 647 "scripts are unsupported."),
656 if (!verifyAmount(sendingTo.second)) {
657 Q_EMIT
message(tr(
"Payment request rejected"),
658 tr(
"Invalid payment request."),
664 CTxOut txOut(
Amount(sendingTo.second), sendingTo.first);
667 tr(
"Payment request error"),
668 tr(
"Requested payment amount of %1 is too small (considered " 677 recipient.
amount += sendingTo.second;
680 if (!verifyAmount(recipient.
amount)) {
681 Q_EMIT
message(tr(
"Payment request rejected"),
682 tr(
"Invalid payment request."),
688 recipient.
address = addresses.join(
"<br />");
691 qDebug() <<
"PaymentServer::processPaymentRequest: Secure payment " 695 qDebug() <<
"PaymentServer::processPaymentRequest: Insecure payment " 697 << addresses.join(
", ");
703 void PaymentServer::fetchRequest(
const QUrl &
url) {
704 QNetworkRequest netRequest;
705 netRequest.setAttribute(QNetworkRequest::User,
706 BIP70_MESSAGE_PAYMENTREQUEST);
707 netRequest.setUrl(url);
708 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
709 netRequest.setRawHeader(
"Accept", BIP71_MIMETYPE_PAYMENTREQUEST);
710 netManager->get(netRequest);
715 QByteArray transaction) {
716 const payments::PaymentDetails &details =
717 recipient.paymentRequest.getDetails();
718 if (!details.has_payment_url()) {
722 QNetworkRequest netRequest;
723 netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK);
724 netRequest.setUrl(QString::fromStdString(details.payment_url()));
725 netRequest.setHeader(QNetworkRequest::ContentTypeHeader,
726 BIP71_MIMETYPE_PAYMENT);
727 netRequest.setRawHeader(
"User-Agent",
CLIENT_NAME.c_str());
728 netRequest.setRawHeader(
"Accept", BIP71_MIMETYPE_PAYMENTACK);
730 payments::Payment payment;
731 payment.set_merchant_data(details.merchant_data());
732 payment.add_transactions(transaction.data(), transaction.size());
747 std::string label = tr(
"Refund from %1")
753 payments::Output *refund_to = payment.add_refund_to();
754 refund_to->set_script(&s[0], s.
size());
758 qWarning() <<
"PaymentServer::fetchPaymentACK: Error getting refund " 759 "key, refund_to not set";
763 #ifdef USE_PROTOBUF_MESSAGE_BYTESIZELONG 764 length.setValue(payment.ByteSizeLong());
766 length.setValue(payment.ByteSize());
769 netRequest.setHeader(QNetworkRequest::ContentLengthHeader, length);
770 QByteArray serData(length.toInt(),
'\0');
771 if (payment.SerializeToArray(serData.data(), length.toInt())) {
772 netManager->post(netRequest, serData);
775 qWarning() <<
"PaymentServer::fetchPaymentACK: Error serializing " 780 void PaymentServer::netRequestFinished(QNetworkReply *reply) {
781 reply->deleteLater();
784 if (!verifySize(reply->size())) {
786 tr(
"Payment request rejected"),
787 tr(
"Payment request %1 is too large (%2 bytes, allowed %3 bytes).")
788 .arg(reply->request().url().toString())
795 if (reply->error() != QNetworkReply::NoError) {
796 QString msg = tr(
"Error communicating with %1: %2")
797 .arg(reply->request().url().toString())
798 .arg(reply->errorString());
800 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
801 Q_EMIT
message(tr(
"Payment request error"), msg,
806 QByteArray data = reply->readAll();
808 QString requestType =
809 reply->request().attribute(QNetworkRequest::User).toString();
810 if (requestType == BIP70_MESSAGE_PAYMENTREQUEST) {
813 if (!request.
parse(data)) {
814 qWarning() <<
"PaymentServer::netRequestFinished: Error parsing " 816 Q_EMIT
message(tr(
"Payment request error"),
817 tr(
"Payment request cannot be parsed!"),
819 }
else if (processPaymentRequest(request, recipient)) {
824 }
else if (requestType == BIP70_MESSAGE_PAYMENTACK) {
825 payments::PaymentACK paymentACK;
826 if (!paymentACK.ParseFromArray(data.data(), data.size())) {
827 QString msg = tr(
"Bad response from server %1")
828 .arg(reply->request().url().toString());
830 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
831 Q_EMIT
message(tr(
"Payment request error"), msg,
839 void PaymentServer::reportSslErrors(QNetworkReply *reply,
840 const QList<QSslError> &errs) {
844 for (
const QSslError &err : errs) {
845 qWarning() <<
"PaymentServer::reportSslErrors: " << err;
846 errString += err.errorString() +
"\n";
848 Q_EMIT
message(tr(
"Network request error"), errString,
852 void PaymentServer::handlePaymentACK(
const QString &paymentACKMsg) {
854 Q_EMIT
message(tr(
"Payment acknowledged"), paymentACKMsg,
859 bool PaymentServer::verifyNetwork(
861 const std::string clientNetwork =
863 bool fVerified = requestDetails.network() == clientNetwork;
865 qWarning() << QString(
"PaymentServer::%1: Payment request network " 866 "\"%2\" doesn't match client network \"%3\".")
868 .arg(QString::fromStdString(requestDetails.network()))
869 .arg(QString::fromStdString(clientNetwork));
874 bool PaymentServer::verifyExpired(
875 const payments::PaymentDetails &requestDetails) {
876 bool fVerified = (requestDetails.has_expires() &&
877 (int64_t)requestDetails.expires() <
GetTime());
879 const QString requestExpires = QString::fromStdString(
881 qWarning() << QString(
882 "PaymentServer::%1: Payment request expired \"%2\".")
884 .arg(requestExpires);
889 bool PaymentServer::verifySize(qint64 requestSize) {
892 qWarning() << QString(
"PaymentServer::%1: Payment request too large " 893 "(%2 bytes, allowed %3 bytes).")
901 bool PaymentServer::verifyAmount(
const Amount requestAmount) {
904 qWarning() << QString(
"PaymentServer::%1: Payment request amount out " 905 "of allowed range (%2, allowed 0 - %3).")
913 X509_STORE *PaymentServer::getCertStore() {
914 return certStore.get();
static QString formatWithUnit(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string (with unit)
std::string NetworkIDString() const
Return the BIP70 network string (main, test or regtest)
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
static std::string ipcParseURI(const QString &arg, const CChainParams ¶ms, bool useCashAddr)
void message(const QString &title, const QString &message, unsigned int style)
bool getProxySettings(QNetworkProxy &proxy) const
virtual OutputType getDefaultAddressType()=0
void setOptionsModel(OptionsModel *optionsModel)
static const std::string REGTEST
bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms)
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
static constexpr Amount SATOSHI
void receivedPaymentRequest(SendCoinsRecipient)
static const Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system...
QString HtmlEscape(const QString &str, bool fMultiLine)
bool IsInitialized() const
static bool ipcCanParseLegacyURI(const QString &arg, const std::string &network)
bool eventFilter(QObject *object, QEvent *event) override
static bool ipcCanParseCashAddrURI(const QString &arg, const std::string &network)
virtual CFeeRate getDustRelayFee()=0
Get dust relay fee.
static void ipcParseCommandLine(int argc, char *argv[])
virtual OutputType getDefaultChangeType()=0
virtual const CChainParams & GetChainParams() const =0
static QT_END_NAMESPACE const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE
int getDisplayUnit() const
void handleURIOrFile(const QString &s)
Force blocking, modal message box dialog (not just OS notification)
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
static bool ipcSendCommandLine()
interfaces::Node & node() const
bool MoneyRange(const Amount nValue)
const int BITCOIN_IPC_CONNECT_TIMEOUT
bool getMerchant(X509_STORE *certStore, QString &merchant) const
std::unique_ptr< CChainParams > CreateChainParams(const std::string &chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
bool handleURI(const CChainParams ¶ms, const QString &s)
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
const fs::path & GetDataDir(bool fNetSpecific)
const payments::PaymentDetails & getDetails() const
Interface for accessing a wallet.
bool parseBitcoinURI(const QString &scheme, const QUrl &uri, SendCoinsRecipient *out)
const Config & GetConfig()
An output of a transaction.
PaymentServer(QObject *parent, bool startLocalServer=true)
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
const std::string CLIENT_NAME
static QList< QString > savedPaymentRequests
Special output type for change outputs only.
bool parse(const QByteArray &data)
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination &dest)=0
Interface from Qt to configuration data structure for Bitcoin client.
const CChainParams & Params()
Return the currently selected parameters.
Serialized script, used inside transaction inputs and outputs.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
virtual bool setAddressBook(const CTxDestination &dest, const std::string &name, const std::string &purpose)=0
Add or update address.
static QString ipcServerName()
void handleURIConnection()
const std::string & CashAddrPrefix() const
static const std::string TESTNET
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams ¶ms)
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
OptionsModel * optionsModel
QString boostPathToQString(const fs::path &path)
int64_t GetTime()
Return system time (or mocked time, if set)
QString authenticatedMerchant
Top-level interface for a bitcoin node (bitcoind process).
boost::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
QList< std::pair< CScript, Amount > > getPayTo() const