Changeset View
Changeset View
Standalone View
Standalone View
src/qt/paymentserver.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "paymentserver.h" | #include <qt/paymentserver.h> | ||||
#include "bitcoinunits.h" | #include <chainparams.h> | ||||
#include "guiutil.h" | #include <config.h> | ||||
#include "optionsmodel.h" | #include <dstencode.h> | ||||
#include <interfaces/node.h> | |||||
#include "chainparams.h" | #include <policy/policy.h> | ||||
#include "config.h" | #include <qt/bitcoinunits.h> | ||||
#include "dstencode.h" | #include <qt/guiutil.h> | ||||
#include "policy/policy.h" | #include <qt/optionsmodel.h> | ||||
#include "ui_interface.h" | #include <ui_interface.h> | ||||
#include "util.h" | #include <util.h> | ||||
#include "wallet/wallet.h" | #include <wallet/wallet.h> | ||||
#include <cstdlib> | |||||
#include <openssl/x509_vfy.h> | #include <openssl/x509_vfy.h> | ||||
#include <QApplication> | #include <QApplication> | ||||
#include <QByteArray> | #include <QByteArray> | ||||
#include <QDataStream> | #include <QDataStream> | ||||
#include <QDateTime> | #include <QDateTime> | ||||
#include <QDebug> | #include <QDebug> | ||||
Show All 9 Lines | |||||
#include <QNetworkRequest> | #include <QNetworkRequest> | ||||
#include <QSslCertificate> | #include <QSslCertificate> | ||||
#include <QSslError> | #include <QSslError> | ||||
#include <QSslSocket> | #include <QSslSocket> | ||||
#include <QStringList> | #include <QStringList> | ||||
#include <QTextDocument> | #include <QTextDocument> | ||||
#include <QUrlQuery> | #include <QUrlQuery> | ||||
#include <cstdlib> | |||||
const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds | const int BITCOIN_IPC_CONNECT_TIMEOUT = 1000; // milliseconds | ||||
// BIP70 payment protocol messages | // BIP70 payment protocol messages | ||||
const char *BIP70_MESSAGE_PAYMENTACK = "PaymentACK"; | const char *BIP70_MESSAGE_PAYMENTACK = "PaymentACK"; | ||||
const char *BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; | const char *BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest"; | ||||
// BIP71 payment protocol media types | // BIP71 payment protocol media types | ||||
const char *BIP71_MIMETYPE_PAYMENT = "application/bitcoincash-payment"; | const char *BIP71_MIMETYPE_PAYMENT = "application/bitcoincash-payment"; | ||||
const char *BIP71_MIMETYPE_PAYMENTACK = "application/bitcoincash-paymentack"; | const char *BIP71_MIMETYPE_PAYMENTACK = "application/bitcoincash-paymentack"; | ||||
const char *BIP71_MIMETYPE_PAYMENTREQUEST = | const char *BIP71_MIMETYPE_PAYMENTREQUEST = | ||||
▲ Show 20 Lines • Show All 165 Lines • ▼ Show 20 Lines | |||||
// | // | ||||
// Sending to the server is done synchronously, at startup. | // Sending to the server is done synchronously, at startup. | ||||
// If the server isn't already running, startup continues, and the items in | // If the server isn't already running, startup continues, and the items in | ||||
// savedPaymentRequest will be handled when uiReady() is called. | // savedPaymentRequest will be handled when uiReady() is called. | ||||
// | // | ||||
// Warning: ipcSendCommandLine() is called early in init, so don't use "Q_EMIT | // Warning: ipcSendCommandLine() is called early in init, so don't use "Q_EMIT | ||||
// message()", but "QMessageBox::"! | // message()", but "QMessageBox::"! | ||||
// | // | ||||
void PaymentServer::ipcParseCommandLine(int argc, char *argv[]) { | void PaymentServer::ipcParseCommandLine(interfaces::Node &node, int argc, | ||||
char *argv[]) { | |||||
std::array<const std::string *, 3> networks = { | std::array<const std::string *, 3> networks = { | ||||
{&CBaseChainParams::MAIN, &CBaseChainParams::TESTNET, | {&CBaseChainParams::MAIN, &CBaseChainParams::TESTNET, | ||||
&CBaseChainParams::REGTEST}}; | &CBaseChainParams::REGTEST}}; | ||||
const std::string *chosenNetwork = nullptr; | const std::string *chosenNetwork = nullptr; | ||||
for (int i = 1; i < argc; i++) { | for (int i = 1; i < argc; i++) { | ||||
QString arg(argv[i]); | QString arg(argv[i]); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | for (int i = 1; i < argc; i++) { | ||||
continue; | continue; | ||||
} | } | ||||
savedPaymentRequests.append(arg); | savedPaymentRequests.append(arg); | ||||
chosenNetwork = itemNetwork; | chosenNetwork = itemNetwork; | ||||
} | } | ||||
if (chosenNetwork) { | if (chosenNetwork) { | ||||
SelectParams(*chosenNetwork); | node.selectParams(*chosenNetwork); | ||||
} | } | ||||
} | } | ||||
// | // | ||||
// Sending to the server is done synchronously, at startup. | // Sending to the server is done synchronously, at startup. | ||||
// If the server isn't already running, startup continues, and the items in | // If the server isn't already running, startup continues, and the items in | ||||
// savedPaymentRequest will be handled when uiReady() is called. | // savedPaymentRequest will be handled when uiReady() is called. | ||||
// | // | ||||
▲ Show 20 Lines • Show All 263 Lines • ▼ Show 20 Lines | |||||
bool PaymentServer::processPaymentRequest(const PaymentRequestPlus &request, | bool PaymentServer::processPaymentRequest(const PaymentRequestPlus &request, | ||||
SendCoinsRecipient &recipient) { | SendCoinsRecipient &recipient) { | ||||
if (!optionsModel) { | if (!optionsModel) { | ||||
return false; | return false; | ||||
} | } | ||||
if (request.IsInitialized()) { | if (request.IsInitialized()) { | ||||
// Payment request network matches client network? | // Payment request network matches client network? | ||||
if (!verifyNetwork(request.getDetails())) { | if (!verifyNetwork(optionsModel->node(), request.getDetails())) { | ||||
Q_EMIT message( | Q_EMIT message( | ||||
tr("Payment request rejected"), | tr("Payment request rejected"), | ||||
tr("Payment request network doesn't match client network."), | tr("Payment request network doesn't match client network."), | ||||
CClientUIInterface::MSG_ERROR); | CClientUIInterface::MSG_ERROR); | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | for (const std::pair<CScript, Amount> &sendingTo : sendingTos) { | ||||
Q_EMIT message(tr("Payment request rejected"), | Q_EMIT message(tr("Payment request rejected"), | ||||
tr("Invalid payment request."), | tr("Invalid payment request."), | ||||
CClientUIInterface::MSG_ERROR); | CClientUIInterface::MSG_ERROR); | ||||
return false; | return false; | ||||
} | } | ||||
// Extract and check amounts | // Extract and check amounts | ||||
CTxOut txOut(Amount(sendingTo.second), sendingTo.first); | CTxOut txOut(Amount(sendingTo.second), sendingTo.first); | ||||
if (txOut.IsDust(dustRelayFee)) { | if (txOut.IsDust(optionsModel->node().getDustRelayFee())) { | ||||
Q_EMIT message( | Q_EMIT message( | ||||
tr("Payment request error"), | tr("Payment request error"), | ||||
tr("Requested payment amount of %1 is too small (considered " | tr("Requested payment amount of %1 is too small (considered " | ||||
"dust).") | "dust).") | ||||
.arg(BitcoinUnits::formatWithUnit( | .arg(BitcoinUnits::formatWithUnit( | ||||
optionsModel->getDisplayUnit(), sendingTo.second)), | optionsModel->getDisplayUnit(), sendingTo.second)), | ||||
CClientUIInterface::MSG_ERROR); | CClientUIInterface::MSG_ERROR); | ||||
Show All 31 Lines | void PaymentServer::fetchRequest(const QUrl &url) { | ||||
netRequest.setAttribute(QNetworkRequest::User, | netRequest.setAttribute(QNetworkRequest::User, | ||||
BIP70_MESSAGE_PAYMENTREQUEST); | BIP70_MESSAGE_PAYMENTREQUEST); | ||||
netRequest.setUrl(url); | netRequest.setUrl(url); | ||||
netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); | netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); | ||||
netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTREQUEST); | netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTREQUEST); | ||||
netManager->get(netRequest); | netManager->get(netRequest); | ||||
} | } | ||||
void PaymentServer::fetchPaymentACK(CWallet *wallet, | void PaymentServer::fetchPaymentACK(WalletModel *walletModel, | ||||
const SendCoinsRecipient &recipient, | const SendCoinsRecipient &recipient, | ||||
QByteArray transaction) { | QByteArray transaction) { | ||||
const payments::PaymentDetails &details = | const payments::PaymentDetails &details = | ||||
recipient.paymentRequest.getDetails(); | recipient.paymentRequest.getDetails(); | ||||
if (!details.has_payment_url()) { | if (!details.has_payment_url()) { | ||||
return; | return; | ||||
} | } | ||||
QNetworkRequest netRequest; | QNetworkRequest netRequest; | ||||
netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK); | netRequest.setAttribute(QNetworkRequest::User, BIP70_MESSAGE_PAYMENTACK); | ||||
netRequest.setUrl(QString::fromStdString(details.payment_url())); | netRequest.setUrl(QString::fromStdString(details.payment_url())); | ||||
netRequest.setHeader(QNetworkRequest::ContentTypeHeader, | netRequest.setHeader(QNetworkRequest::ContentTypeHeader, | ||||
BIP71_MIMETYPE_PAYMENT); | BIP71_MIMETYPE_PAYMENT); | ||||
netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); | netRequest.setRawHeader("User-Agent", CLIENT_NAME.c_str()); | ||||
netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTACK); | netRequest.setRawHeader("Accept", BIP71_MIMETYPE_PAYMENTACK); | ||||
payments::Payment payment; | payments::Payment payment; | ||||
payment.set_merchant_data(details.merchant_data()); | payment.set_merchant_data(details.merchant_data()); | ||||
payment.add_transactions(transaction.data(), transaction.size()); | payment.add_transactions(transaction.data(), transaction.size()); | ||||
// Create a new refund address, or re-use: | // Create a new refund address, or re-use: | ||||
std::string label = | std::string label = | ||||
tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString(); | tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString(); | ||||
std::set<CTxDestination> refundAddresses = wallet->GetLabelAddresses(label); | std::set<CTxDestination> refundAddresses = | ||||
walletModel->wallet().getLabelAddresses(label); | |||||
if (!refundAddresses.empty()) { | if (!refundAddresses.empty()) { | ||||
CScript s = GetScriptForDestination(*refundAddresses.begin()); | CScript s = GetScriptForDestination(*refundAddresses.begin()); | ||||
payments::Output *refund_to = payment.add_refund_to(); | payments::Output *refund_to = payment.add_refund_to(); | ||||
refund_to->set_script(&s[0], s.size()); | refund_to->set_script(&s[0], s.size()); | ||||
} else { | } else { | ||||
CPubKey newKey; | CPubKey newKey; | ||||
if (wallet->GetKeyFromPool(newKey)) { | if (walletModel->wallet().getKeyFromPool(false /* internal */, | ||||
newKey)) { | |||||
CKeyID keyID = newKey.GetID(); | CKeyID keyID = newKey.GetID(); | ||||
wallet->SetAddressBook(keyID, label, "refund"); | walletModel->wallet().setAddressBook(keyID, label, "refund"); | ||||
CScript s = GetScriptForDestination(keyID); | CScript s = GetScriptForDestination(keyID); | ||||
payments::Output *refund_to = payment.add_refund_to(); | payments::Output *refund_to = payment.add_refund_to(); | ||||
refund_to->set_script(&s[0], s.size()); | refund_to->set_script(&s[0], s.size()); | ||||
} else { | } else { | ||||
// This should never happen, because sending coins should have just | // This should never happen, because sending coins should have just | ||||
// unlocked the wallet and refilled the keypool. | // unlocked the wallet and refilled the keypool. | ||||
qWarning() << "PaymentServer::fetchPaymentACK: Error getting " | qWarning() << "PaymentServer::fetchPaymentACK: Error getting " | ||||
▲ Show 20 Lines • Show All 92 Lines • ▼ Show 20 Lines | |||||
void PaymentServer::handlePaymentACK(const QString &paymentACKMsg) { | void PaymentServer::handlePaymentACK(const QString &paymentACKMsg) { | ||||
// currently we don't further process or store the paymentACK message | // currently we don't further process or store the paymentACK message | ||||
Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, | Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, | ||||
CClientUIInterface::ICON_INFORMATION | | CClientUIInterface::ICON_INFORMATION | | ||||
CClientUIInterface::MODAL); | CClientUIInterface::MODAL); | ||||
} | } | ||||
bool PaymentServer::verifyNetwork( | bool PaymentServer::verifyNetwork( | ||||
const payments::PaymentDetails &requestDetails) { | interfaces::Node &node, const payments::PaymentDetails &requestDetails) { | ||||
bool fVerified = requestDetails.network() == Params().NetworkIDString(); | bool fVerified = requestDetails.network() == node.getNetwork(); | ||||
if (!fVerified) { | if (!fVerified) { | ||||
qWarning() << QString("PaymentServer::%1: Payment request network " | qWarning() << QString("PaymentServer::%1: Payment request network " | ||||
"\"%2\" doesn't match client network \"%3\".") | "\"%2\" doesn't match client network \"%3\".") | ||||
.arg(__func__) | .arg(__func__) | ||||
.arg(QString::fromStdString(requestDetails.network())) | .arg(QString::fromStdString(requestDetails.network())) | ||||
.arg(QString::fromStdString( | .arg(QString::fromStdString(node.getNetwork())); | ||||
Params().NetworkIDString())); | |||||
} | } | ||||
return fVerified; | return fVerified; | ||||
} | } | ||||
bool PaymentServer::verifyExpired( | bool PaymentServer::verifyExpired( | ||||
const payments::PaymentDetails &requestDetails) { | const payments::PaymentDetails &requestDetails) { | ||||
bool fVerified = (requestDetails.has_expires() && | bool fVerified = (requestDetails.has_expires() && | ||||
(int64_t)requestDetails.expires() < GetTime()); | (int64_t)requestDetails.expires() < GetTime()); | ||||
Show All 38 Lines |