diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 6543455d6..fdca851b9 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -173,7 +173,7 @@ public: #ifdef ENABLE_WALLET /// Create payment server - // void createPaymentServer(); + void createPaymentServer(); #endif /// Create options model void createOptionsModel(); @@ -212,7 +212,7 @@ private: BitcoinGUI *window; QTimer *pollShutdownTimer; #ifdef ENABLE_WALLET - // PaymentServer* paymentServer; + PaymentServer* paymentServer; WalletModel *walletModel; #endif int returnValue; @@ -279,7 +279,7 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): window(0), pollShutdownTimer(0), #ifdef ENABLE_WALLET - // paymentServer(0), + paymentServer(0), walletModel(0), #endif returnValue(0) @@ -298,20 +298,18 @@ BitcoinApplication::~BitcoinApplication() delete window; window = 0; #ifdef ENABLE_WALLET - // delete paymentServer; - // paymentServer = 0; + delete paymentServer; + paymentServer = 0; #endif delete optionsModel; optionsModel = 0; } #ifdef ENABLE_WALLET -/* void BitcoinApplication::createPaymentServer() { paymentServer = new PaymentServer(this); } -*/ #endif void BitcoinApplication::createOptionsModel() @@ -394,12 +392,10 @@ void BitcoinApplication::initializeResult(int retval) returnValue = retval ? 0 : 1; if(retval) { -/* #ifdef ENABLE_WALLET PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); #endif -*/ emit splashFinished(window); @@ -414,10 +410,8 @@ void BitcoinApplication::initializeResult(int retval) window->addWallet("~Default", walletModel); window->setCurrentWallet("~Default"); -/* connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); -*/ } #endif @@ -431,11 +425,8 @@ void BitcoinApplication::initializeResult(int retval) window->show(); } #ifdef ENABLE_WALLET - // Payment server disabled pending future work on specifications - // Now that initialization/startup is done, process any command-line // dogecoin: URIs or payment requests: - /* connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), window, SLOT(handlePaymentRequest(SendCoinsRecipient))); connect(window, SIGNAL(receivedURI(QString)), @@ -443,7 +434,6 @@ void BitcoinApplication::initializeResult(int retval) connect(paymentServer, SIGNAL(message(QString,QString,unsigned int)), window, SLOT(message(QString,QString,unsigned int))); QTimer::singleShot(100, paymentServer, SLOT(uiReady())); - */ #endif } else { quit(); // Exit main loop @@ -578,7 +568,7 @@ int main(int argc, char *argv[]) // Start up the payment server early, too, so impatient users that click on // dogecoin: links repeatedly have their payment requests routed to this process: - // app.createPaymentServer(); + app.createPaymentServer(); #endif /// 9. Main GUI initialization diff --git a/src/qt/paymentrequest.proto b/src/qt/paymentrequest.proto index b2281c4c7..964d6e173 100644 --- a/src/qt/paymentrequest.proto +++ b/src/qt/paymentrequest.proto @@ -1,22 +1,23 @@ // -// Simple Bitcoin Payment Protocol messages +// Simple Dogecoin Payment Protocol messages +// Derived fromthe Bitcoin Payment Protocol // // Use fields 100+ for extensions; -// to avoid conflicts, register extensions at: -// https://en.bitcoin.it/wiki/Payment_Request +// to avoid conflicts, register extensions via pull-req at: +// https://github.com/dogecoin/dips // package payments; -option java_package = "org.bitcoin.protocols.payments"; +option java_package = "com.dogecoin.protocols.payments"; option java_outer_classname = "Protos"; -// Generalized form of "send payment to this/these bitcoin addresses" +// Generalized form of "send payment to this/these dogecoin addresses" message Output { - optional uint64 amount = 1 [default = 0]; // amount is integer-number-of-satoshis - required bytes script = 2; // usually one of the standard Script forms + optional uint64 amount = 1 [default = 0]; // amount is integer-number-of-satoshis + required bytes script = 2; // usually one of the standard Script forms } message PaymentDetails { - optional string network = 1 [default = "main"]; // "main" or "test" + optional string genesis = 1 [default = "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691"]; // Hash of the network genesis block repeated Output outputs = 2; // Where payment should be sent required uint64 time = 3; // Timestamp; when payment request created optional uint64 expires = 4; // Timestamp; when this request should be considered invalid diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 98130062f..d0428c144 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -178,6 +178,9 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) // and the items in savedPaymentRequest will be handled // when uiReady() is called. // +// Warning: ipcSendCommandLine() is called early in init, +// so don't use "emit message()", but "QMessageBox::"! +// bool PaymentServer::ipcParseCommandLine(int argc, char* argv[]) { for (int i = 1; i < argc; i++) @@ -205,14 +208,19 @@ bool PaymentServer::ipcParseCommandLine(int argc, char* argv[]) else if (QFile::exists(arg)) // Filename { savedPaymentRequests.append(arg); - + PaymentRequestPlus request; + if (readPaymentRequest(arg, request)) { - if (request.getDetails().network() == "main") + if (request.getDetails().genesis() == "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691") + { SelectParams(CChainParams::MAIN); - else + } + else if (request.getDetails().genesis() == "bb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e") + { SelectParams(CChainParams::TESTNET); + } } } else @@ -411,7 +419,15 @@ void PaymentServer::handleURIOrFile(const QString& s) { SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) - emit receivedPaymentRequest(recipient); + { + CBitcoinAddress address(recipient.address.toStdString()); + if (!address.IsValid()) { + emit message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), + CClientUIInterface::MSG_ERROR); + } + else + emit receivedPaymentRequest(recipient); + } else emit message(tr("URI handling"), tr("URI can not be parsed! This can be caused by an invalid Dogecoin address or malformed URI parameters."), @@ -425,12 +441,14 @@ void PaymentServer::handleURIOrFile(const QString& s) { PaymentRequestPlus request; SendCoinsRecipient recipient; - if (readPaymentRequest(s, request) && processPaymentRequest(request, recipient)) - emit receivedPaymentRequest(recipient); - else + if (!readPaymentRequest(s, request)) + { emit message(tr("Payment request file handling"), - tr("Payment request file can not be read or processed! This can be caused by an invalid payment request file."), + tr("Payment request file can not be read! This can be caused by an invalid payment request file."), CClientUIInterface::ICON_WARNING); + } + else if (processPaymentRequest(request, recipient)) + emit receivedPaymentRequest(recipient); return; } @@ -482,6 +500,35 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins if (!optionsModel) return false; + if (request.IsInitialized()) { + const payments::PaymentDetails& details = request.getDetails(); + + // Payment request network matches client network? + if ((details.genesis() == "1a91e3dace36e2be3bf030a65679fe821aa1d6ef92e7c9902eb318182c355691" && TestNet()) || + (details.genesis() == "bb0a78264637406b6360aad926284d544d7049f45189db5664f3c4d07350559e" && !TestNet())) + { + emit message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."), + CClientUIInterface::MSG_ERROR); + + return false; + } + + // Expired payment request? + if (details.has_expires() && (int64_t)details.expires() < GetTime()) + { + emit message(tr("Payment request rejected"), tr("Payment request has expired."), + CClientUIInterface::MSG_ERROR); + + return false; + } + } + else { + emit message(tr("Payment request error"), tr("Payment request is not initialized."), + CClientUIInterface::MSG_ERROR); + + return false; + } + recipient.paymentRequest = request; recipient.message = GUIUtil::HtmlEscape(request.getDetails().memo()); @@ -497,11 +544,11 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins // Append destination address addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); } - else if (!recipient.authenticatedMerchant.isEmpty()){ + else if (!recipient.authenticatedMerchant.isEmpty()) { // Insecure payments to custom bitcoin addresses are not supported // (there is no good way to tell the user where they are paying in a way // they'd have a chance of understanding). - emit message(tr("Payment request error"), + emit message(tr("Payment request rejected"), tr("Unverified payment requests to custom payment scripts are unsupported."), CClientUIInterface::MSG_ERROR); return false; @@ -510,11 +557,10 @@ bool PaymentServer::processPaymentRequest(PaymentRequestPlus& request, SendCoins // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); if (txOut.IsDust(CTransaction::nMinRelayTxFee)) { - QString msg = tr("Requested payment amount of %1 is too small (considered dust).") - .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)); + emit message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") + .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), + CClientUIInterface::MSG_ERROR); - qDebug() << "PaymentServer::processPaymentRequest : " << msg; - emit message(tr("Payment request error"), msg, CClientUIInterface::MSG_ERROR); return false; } @@ -581,8 +627,8 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien refund_to->set_script(&s[0], s.size()); } else { - // This should never happen, because sending coins should have just unlocked the wallet - // and refilled the keypool + // This should never happen, because sending coins should have + // just unlocked the wallet and refilled the keypool. qDebug() << "PaymentServer::fetchPaymentACK : Error getting refund key, refund_to not set"; } } @@ -594,7 +640,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien netManager->post(netRequest, serData); } else { - // This should never happen, either: + // This should never happen, either. qDebug() << "PaymentServer::fetchPaymentACK : Error serializing payment message"; } } @@ -620,17 +666,15 @@ void PaymentServer::netRequestFinished(QNetworkReply* reply) { PaymentRequestPlus request; SendCoinsRecipient recipient; - if (request.parse(data) && processPaymentRequest(request, recipient)) + if (!request.parse(data)) { - emit receivedPaymentRequest(recipient); - } - else - { - qDebug() << "PaymentServer::netRequestFinished : Error processing payment request"; + qDebug() << "PaymentServer::netRequestFinished : Error parsing payment request"; emit message(tr("Payment request error"), - tr("Payment request can not be parsed or processed!"), + tr("Payment request can not be parsed!"), CClientUIInterface::MSG_ERROR); } + else if (processPaymentRequest(request, recipient)) + emit receivedPaymentRequest(recipient); return; } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 1d1b30fca..a01db3cac 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -377,26 +377,8 @@ void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv) bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) { - QString strSendCoins = tr("Send Coins"); - if (rv.paymentRequest.IsInitialized()) { - // Expired payment request? - const payments::PaymentDetails& details = rv.paymentRequest.getDetails(); - if (details.has_expires() && (int64_t)details.expires() < GetTime()) - { - emit message(strSendCoins, tr("Payment request expired"), - CClientUIInterface::MSG_WARNING); - return false; - } - } - else { - CBitcoinAddress address(rv.address.toStdString()); - if (!address.IsValid()) { - emit message(strSendCoins, tr("Invalid payment address %1").arg(rv.address), - CClientUIInterface::MSG_WARNING); - return false; - } - } - + // Just paste the entry, all pre-checks + // are done in paymentserver.cpp. pasteEntry(rv); return true; }