5 #if defined(HAVE_CONFIG_H) 26 #include <openssl/x509_vfy.h> 28 #include <QApplication> 30 #include <QDataStream> 34 #include <QFileOpenEvent> 37 #include <QLocalServer> 38 #include <QLocalSocket> 39 #include <QNetworkAccessManager> 40 #include <QNetworkProxy> 41 #include <QNetworkReply> 42 #include <QNetworkRequest> 43 #include <QSslCertificate> 46 #include <QStringList> 47 #include <QTextDocument> 54 const char* BIP70_MESSAGE_PAYMENTACK =
"PaymentACK";
55 const char* BIP70_MESSAGE_PAYMENTREQUEST =
"PaymentRequest";
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";
67 static QString ipcServerName()
69 QString
name(
"BitcoinQt");
75 name.append(QString::number(qHash(ddir)));
85 static QList<QString> savedPaymentRequests;
98 for (
int i = 1; i < argc; i++)
100 QString arg(argv[i]);
101 if (arg.startsWith(
"-"))
110 savedPaymentRequests.append(arg);
128 else if (QFile::exists(arg))
130 savedPaymentRequests.append(arg);
133 if (readPaymentRequestFromFile(arg, request))
139 else if (request.
getDetails().network() ==
"test")
149 qWarning() <<
"PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
163 bool fResult =
false;
164 for (
const QString& r : savedPaymentRequests)
166 QLocalSocket* socket =
new QLocalSocket();
167 socket->connectToServer(ipcServerName(), QIODevice::WriteOnly);
176 QDataStream out(&block, QIODevice::WriteOnly);
177 out.setVersion(QDataStream::Qt_4_0);
179 out.device()->seek(0);
181 socket->write(block);
184 socket->disconnectFromServer();
206 GOOGLE_PROTOBUF_VERIFY_VERSION;
213 parent->installEventFilter(
this);
215 QString
name = ipcServerName();
218 QLocalServer::removeServer(
name);
220 if (startLocalServer)
226 QMessageBox::critical(0, tr(
"Payment request error"),
227 tr(
"Cannot start bitcoin: click-to-pay handler"));
232 connect(
this, &PaymentServer::receivedPaymentACK,
this, &PaymentServer::handlePaymentACK);
241 google::protobuf::ShutdownProtobufLibrary();
252 if (event->type() == QEvent::FileOpen) {
253 QFileOpenEvent *fileEvent =
static_cast<QFileOpenEvent*
>(event);
254 if (!fileEvent->file().isEmpty())
256 else if (!fileEvent->url().isEmpty())
262 return QObject::eventFilter(
object, event);
272 for (
const QString& s : savedPaymentRequests)
276 savedPaymentRequests.clear();
283 savedPaymentRequests.append(s);
287 if (s.startsWith(
"bitcoin://", Qt::CaseInsensitive))
289 Q_EMIT
message(tr(
"URI handling"), tr(
"'bitcoin://' is not a valid URI. Use 'bitcoin:' instead."),
294 QUrlQuery uri((QUrl(s)));
295 if (uri.hasQueryItem(
"r"))
298 Q_EMIT
message(tr(
"URI handling"),
299 tr(
"You are using a BIP70 URL which will be unsupported in the future."),
302 temp.append(uri.queryItemValue(
"r"));
303 QString decoded = QUrl::fromPercentEncoding(temp);
304 QUrl fetchUrl(decoded, QUrl::StrictMode);
306 if (fetchUrl.isValid())
308 qDebug() <<
"PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl <<
")";
309 fetchRequest(fetchUrl);
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()),
319 Q_EMIT
message(tr(
"URI handling"),
320 tr(
"Cannot process payment request because BIP70 support was not compiled in."),
331 Q_EMIT
message(tr(
"URI handling"), tr(
"Invalid payment address %1").arg(recipient.
address),
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."),
347 if (QFile::exists(s))
351 if (!readPaymentRequestFromFile(s, request))
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."),
357 else if (processPaymentRequest(request, recipient))
367 QLocalSocket *clientConnection =
uriServer->nextPendingConnection();
369 while (clientConnection->bytesAvailable() < (int)
sizeof(quint32))
370 clientConnection->waitForReadyRead();
372 connect(clientConnection, &QLocalSocket::disconnected, clientConnection, &QLocalSocket::deleteLater);
374 QDataStream in(clientConnection);
375 in.setVersion(QDataStream::Qt_4_0);
376 if (clientConnection->bytesAvailable() < (int)
sizeof(quint16)) {
391 struct X509StoreDeleter {
392 void operator()(X509_STORE* b) {
398 void operator()(X509* b) { X509_free(b); }
403 std::unique_ptr<X509_STORE, X509StoreDeleter> certStore;
406 static void ReportInvalidCertificate(
const QSslCertificate& cert)
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);
414 void PaymentServer::LoadRootCAs(X509_STORE* _store)
419 certStore.reset(_store);
424 certStore.reset(X509_STORE_new());
428 QString certFile = QString::fromStdString(
gArgs.
GetArg(
"-rootcertificates",
"-system-"));
431 if (certFile.isEmpty()) {
432 qDebug() << QString(
"PaymentServer::%1: Payment request authentication via X.509 certificates disabled.").arg(__func__);
436 QList<QSslCertificate> certList;
438 if (certFile !=
"-system-") {
439 qDebug() << QString(
"PaymentServer::%1: Using \"%2\" as trusted root certificate.").arg(__func__).arg(certFile);
441 certList = QSslCertificate::fromPath(certFile);
443 QSslSocket::setDefaultCaCertificates(certList);
445 certList = QSslSocket::systemCaCertificates();
448 const QDateTime currentTime = QDateTime::currentDateTime();
450 for (
const QSslCertificate& cert : certList) {
456 if (currentTime < cert.effectiveDate() || currentTime > cert.expiryDate()) {
457 ReportInvalidCertificate(cert);
462 if (cert.isBlacklisted()) {
463 ReportInvalidCertificate(cert);
467 QByteArray certData = cert.toDer();
468 const unsigned char *data = (
const unsigned char *)certData.data();
470 std::unique_ptr<X509, X509Deleter> x509(d2i_X509(0, &data, certData.size()));
471 if (x509 && X509_STORE_add_cert(certStore.get(), x509.get()))
479 ReportInvalidCertificate(cert);
483 qWarning() <<
"PaymentServer::LoadRootCAs: Loaded " << nRootCerts <<
" root certificates";
495 void PaymentServer::initNetManager()
502 netManager =
new QNetworkAccessManager(
this);
508 netManager->setProxy(proxy);
510 qDebug() <<
"PaymentServer::initNetManager: Using SOCKS5 proxy" << proxy.hostName() <<
":" << proxy.port();
513 qDebug() <<
"PaymentServer::initNetManager: No active proxy server found.";
515 connect(netManager, &QNetworkAccessManager::finished,
this, &PaymentServer::netRequestFinished);
516 connect(netManager, &QNetworkAccessManager::sslErrors,
this, &PaymentServer::reportSslErrors);
523 bool PaymentServer::readPaymentRequestFromFile(
const QString& filename,
PaymentRequestPlus& request)
526 if (!f.open(QIODevice::ReadOnly)) {
527 qWarning() << QString(
"PaymentServer::%1: Failed to open %2").arg(__func__).arg(filename);
532 if (!verifySize(f.size())) {
536 QByteArray data = f.readAll();
538 return request.
parse(data);
549 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request network doesn't match client network."),
558 Q_EMIT
message(tr(
"Payment request rejected"), tr(
"Payment request expired."),
564 Q_EMIT
message(tr(
"Payment request error"), tr(
"Payment request is not initialized."),
570 recipient.paymentRequest = request;
575 QList<std::pair<CScript, CAmount> > sendingTos = request.
getPayTo();
576 QStringList addresses;
578 for (
const std::pair<CScript, CAmount>& sendingTo : sendingTos) {
589 Q_EMIT
message(tr(
"Payment request rejected"),
590 tr(
"Unverified payment requests to custom payment scripts are unsupported."),
598 if (!verifyAmount(sendingTo.second)) {
604 CTxOut txOut(sendingTo.second, sendingTo.first);
606 Q_EMIT
message(tr(
"Payment request error"), tr(
"Requested payment amount of %1 is too small (considered dust).")
613 recipient.
amount += sendingTo.second;
615 if (!verifyAmount(recipient.
amount)) {
621 recipient.
address = addresses.join(
"<br />");
624 qDebug() <<
"PaymentServer::processPaymentRequest: Secure payment request from " << recipient.
authenticatedMerchant;
627 qDebug() <<
"PaymentServer::processPaymentRequest: Insecure payment request to " << addresses.join(
", ");
633 void PaymentServer::fetchRequest(
const QUrl&
url)
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);
645 const payments::PaymentDetails& details = recipient.paymentRequest.getDetails();
646 if (!details.has_payment_url())
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);
656 payments::Payment payment;
657 payment.set_merchant_data(details.merchant_data());
658 payment.add_transactions(transaction.data(), transaction.size());
675 payments::Output* refund_to = payment.add_refund_to();
676 refund_to->set_script(&s[0], s.
size());
680 qWarning() <<
"PaymentServer::fetchPaymentACK: Error getting refund key, refund_to not set";
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);
691 qWarning() <<
"PaymentServer::fetchPaymentACK: Error serializing payment message";
695 void PaymentServer::netRequestFinished(QNetworkReply* reply)
697 reply->deleteLater();
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())
705 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE),
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());
715 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
720 QByteArray data = reply->readAll();
722 QString requestType = reply->request().attribute(QNetworkRequest::User).toString();
723 if (requestType == BIP70_MESSAGE_PAYMENTREQUEST)
727 if (!request.
parse(data))
729 qWarning() <<
"PaymentServer::netRequestFinished: Error parsing payment request";
730 Q_EMIT
message(tr(
"Payment request error"),
731 tr(
"Payment request cannot be parsed!"),
734 else if (processPaymentRequest(request, recipient))
739 else if (requestType == BIP70_MESSAGE_PAYMENTACK)
741 payments::PaymentACK paymentACK;
742 if (!paymentACK.ParseFromArray(data.data(), data.size()))
744 QString msg = tr(
"Bad response from server %1")
745 .arg(reply->request().url().toString());
747 qWarning() <<
"PaymentServer::netRequestFinished: " << msg;
757 void PaymentServer::reportSslErrors(QNetworkReply* reply,
const QList<QSslError> &errs)
762 for (
const QSslError& err : errs) {
763 qWarning() <<
"PaymentServer::reportSslErrors: " << err;
764 errString += err.errorString() +
"\n";
769 void PaymentServer::handlePaymentACK(
const QString& paymentACKMsg)
775 bool PaymentServer::verifyNetwork(
interfaces::Node& node,
const payments::PaymentDetails& requestDetails)
777 bool fVerified = requestDetails.network() == node.
getNetwork();
779 qWarning() << QString(
"PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".")
781 .arg(QString::fromStdString(requestDetails.network()))
782 .arg(QString::fromStdString(node.
getNetwork()));
787 bool PaymentServer::verifyExpired(
const payments::PaymentDetails& requestDetails)
789 bool fVerified = (requestDetails.has_expires() && (int64_t)requestDetails.expires() <
GetTime());
791 const QString requestExpires = QString::fromStdString(
FormatISO8601DateTime((int64_t)requestDetails.expires()));
792 qWarning() << QString(
"PaymentServer::%1: Payment request expired \"%2\".")
794 .arg(requestExpires);
799 bool PaymentServer::verifySize(qint64 requestSize)
801 bool fVerified = (requestSize <= BIP70_MAX_PAYMENTREQUEST_SIZE);
803 qWarning() << QString(
"PaymentServer::%1: Payment request too large (%2 bytes, allowed %3 bytes).")
806 .arg(BIP70_MAX_PAYMENTREQUEST_SIZE);
811 bool PaymentServer::verifyAmount(
const CAmount& requestAmount)
815 qWarning() << QString(
"PaymentServer::%1: Payment request amount out of allowed range (%2, allowed 0 - %3).")
823 X509_STORE* PaymentServer::getCertStore()
825 return certStore.get();
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
bool ExtractDestination(const CScript &scriptPubKey, CTxDestination &addressRet)
Parse a standard scriptPubKey for the destination address.
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 ¶ms)
static void ipcParseCommandLine(interfaces::Node &node, int argc, char *argv[])
bool eventFilter(QObject *object, QEvent *event)
bool MoneyRange(const CAmount &nValue)
void receivedPaymentRequest(SendCoinsRecipient)
QString HtmlEscape(const QString &str, bool fMultiLine)
bool IsInitialized() const
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
virtual CFeeRate getDustRelayFee()=0
Get dust relay fee.
virtual OutputType getDefaultChangeType()=0
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()
int64_t CAmount
Amount in satoshis (Can be negative)
interfaces::Node & node() const
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.
An encapsulated public key.
const payments::PaymentDetails & getDetails() const
QList< std::pair< CScript, CAmount > > getPayTo() const
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
Special output type for change outputs only.
bool parse(const QByteArray &data)
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.
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Serialized script, used inside transaction inputs and outputs.
Interface to Bitcoin wallet from Qt view code.
const QString BITCOIN_IPC_PREFIX("bitcoin:")
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.
void handleURIConnection()
CTxDestination GetDestinationForKey(const CPubKey &key, OutputType type)
Get a destination of the requested type (if possible) to the specified key.
static const std::string TESTNET
std::string EncodeDestination(const CTxDestination &dest)
bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFeeIn)
OptionsModel * optionsModel
const fs::path & GetDataDir(bool fNetSpecific)
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...
QString boostPathToQString(const fs::path &path)
QString authenticatedMerchant
Top-level interface for a bitcoin node (bsha3d process).