diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -19,6 +19,7 @@ #include class ClientModel; +class Config; class NetworkStyle; class Notificator; class OptionsModel; @@ -31,7 +32,6 @@ class HelpMessageDialog; class ModalOverlay; -class Config; class CWallet; QT_BEGIN_NAMESPACE diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -285,7 +285,7 @@ tr("&Receive"), this); receiveCoinsAction->setStatusTip( tr("Request payments (generates QR codes and %1: URIs)") - .arg(GUIUtil::URI_SCHEME)); + .arg(GUIUtil::bitcoinURIScheme(cfg))); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); @@ -410,8 +410,8 @@ openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); - openAction->setStatusTip( - tr("Open a %1: URI or payment request").arg(GUIUtil::URI_SCHEME)); + openAction->setStatusTip(tr("Open a %1: URI or payment request") + .arg(GUIUtil::bitcoinURIScheme(cfg))); showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -35,7 +35,6 @@ /** Utility functions used by the Bitcoin Qt UI. */ namespace GUIUtil { -extern const QString URI_SCHEME; // Create human-readable string from date QString dateTimeStr(const QDateTime &datetime); @@ -51,10 +50,13 @@ void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent); void setupAmountWidget(QLineEdit *widget, QWidget *parent); +QString bitcoinURIScheme(const Config &); // Parse "bitcoincash:" URI into recipient object, return true on successful // parsing -bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out); -bool parseBitcoinURI(QString uri, SendCoinsRecipient *out); +bool parseBitcoinURI(const QString &scheme, const QUrl &uri, + SendCoinsRecipient *out); +bool parseBitcoinURI(const QString &scheme, QString uri, + SendCoinsRecipient *out); QString formatBitcoinURI(const Config &cfg, const SendCoinsRecipient &info); // Returns true if given address+amount meets "dust" definition diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -91,7 +91,6 @@ #endif namespace GUIUtil { -const QString URI_SCHEME("bitcoincash"); QString dateTimeStr(const QDateTime &date) { return date.date().toString(Qt::SystemLocaleShortDate) + QString(" ") + @@ -166,15 +165,24 @@ widget->setAlignment(Qt::AlignRight | Qt::AlignVCenter); } +QString bitcoinURIScheme(const Config &cfg) { + if (!cfg.UseCashAddrEncoding()) return "bitcoincash"; + + return QString::fromStdString(cfg.GetChainParams().CashAddrPrefix()); +} + static bool IsCashAddrEncoded(const QUrl &uri) { const std::string addr = (uri.scheme() + ":" + uri.path()).toStdString(); auto decoded = cashaddr::Decode(addr); return !decoded.first.empty(); } -bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out) { - // return if URI is not valid or is no bitcoincash: URI - if (!uri.isValid() || uri.scheme() != URI_SCHEME) return false; +bool parseBitcoinURI(const QString &scheme, const QUrl &uri, + SendCoinsRecipient *out) { + // return if URI has wrong scheme. + if (!uri.isValid() || uri.scheme() != scheme) { + return false; + } SendCoinsRecipient rv; if (IsCashAddrEncoded(uri)) { @@ -230,24 +238,24 @@ return true; } -bool parseBitcoinURI(QString uri, SendCoinsRecipient *out) { - // Convert bitcoincash:// to bitcoincash: +bool parseBitcoinURI(const QString &scheme, QString uri, + SendCoinsRecipient *out) { // // Cannot handle this later, because bitcoincash:// // will cause Qt to see the part after // as host, // which will lower-case it (and thus invalidate the address). - if (uri.startsWith(URI_SCHEME + "://", Qt::CaseInsensitive)) { - uri.replace(0, URI_SCHEME.length() + 3, URI_SCHEME + ":"); + if (uri.startsWith(scheme + "://", Qt::CaseInsensitive)) { + uri.replace(0, scheme.length() + 3, scheme + ":"); } QUrl uriInstance(uri); - return parseBitcoinURI(uriInstance, out); + return parseBitcoinURI(scheme, uriInstance, out); } QString formatBitcoinURI(const Config &cfg, const SendCoinsRecipient &info) { QString ret = info.address; if (!cfg.UseCashAddrEncoding()) { // prefix address with uri scheme for base58 encoded addresses. - ret = (URI_SCHEME + ":%1").arg(ret); + ret = (bitcoinURIScheme(cfg) + ":%1").arg(ret); } int paramCount = 0; diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -14,7 +14,7 @@ : QDialog(parent), ui(new Ui::OpenURIDialog), cfg(cfg) { ui->setupUi(this); #if QT_VERSION >= 0x040700 - ui->uriEdit->setPlaceholderText(GUIUtil::URI_SCHEME + ":"); + ui->uriEdit->setPlaceholderText(GUIUtil::bitcoinURIScheme(cfg) + ":"); #endif } @@ -28,7 +28,8 @@ void OpenURIDialog::accept() { SendCoinsRecipient rcp; - if (GUIUtil::parseBitcoinURI(getURI(), &rcp)) { + QString uriScheme = GUIUtil::bitcoinURIScheme(cfg); + if (GUIUtil::parseBitcoinURI(uriScheme, getURI(), &rcp)) { /* Only accept value URIs */ QDialog::accept(); } else { @@ -41,6 +42,6 @@ this, tr("Select payment request file to open"), "", "", nullptr); if (filename.isEmpty()) return; QUrl fileUri = QUrl::fromLocalFile(filename); - ui->uriEdit->setText(GUIUtil::URI_SCHEME + ":?r=" + + ui->uriEdit->setText(GUIUtil::bitcoinURIScheme(cfg) + ":?r=" + QUrl::toPercentEncoding(fileUri.toString())); } diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -199,6 +199,25 @@ // "certificate stapling" with server-side caching is more efficient } +static bool ipcParseURI(const QString &arg) { + + std::vector networks = {CBaseChainParams::MAIN, + CBaseChainParams::TESTNET, + CBaseChainParams::REGTEST}; + + for (std::string net : networks) { + const QString scheme = + QString::fromStdString(Params(net).CashAddrPrefix()); + + if (arg.startsWith(scheme + ":", Qt::CaseInsensitive)) { + savedPaymentRequests.append(arg); + SelectParams(net); + return true; + } + } + return false; +} + // // Sending to the server is done synchronously, at startup. // If the server isn't already running, startup continues, and the items in @@ -212,28 +231,11 @@ QString arg(argv[i]); if (arg.startsWith("-")) continue; - // If the bitcoincash: URI contains a payment request, we are not able - // to detect the network as that would require fetching and parsing the - // payment request. That means clicking such an URI which contains a - // testnet payment request will start a mainnet instance and throw a - // "wrong network" error. - if (arg.startsWith(GUIUtil::URI_SCHEME + ":", - Qt::CaseInsensitive)) // bitcoincash: URI - { - savedPaymentRequests.append(arg); + if (ipcParseURI(arg)) { + continue; + } - SendCoinsRecipient r; - if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) { - if (IsValidDestinationString(r.address.toStdString(), - Params(CBaseChainParams::MAIN))) { - SelectParams(CBaseChainParams::MAIN); - } else if (IsValidDestinationString( - r.address.toStdString(), - Params(CBaseChainParams::TESTNET))) { - SelectParams(CBaseChainParams::TESTNET); - } - } - } else if (QFile::exists(arg)) { + if (QFile::exists(arg)) { // Filename savedPaymentRequests.append(arg); @@ -314,8 +316,7 @@ // constructor is called early in init, so don't use "Q_EMIT // message()" here QMessageBox::critical(0, tr("Payment request error"), - tr("Cannot start %1: click-to-pay handler") - .arg(GUIUtil::URI_SCHEME)); + tr("Cannot start click-to-pay handler")); } else { connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection())); @@ -390,8 +391,9 @@ return; } - // bitcoincash: URI - if (s.startsWith(GUIUtil::URI_SCHEME + ":", Qt::CaseInsensitive)) { + // URI + const QString uriScheme = QString::fromStdString(Params().CashAddrPrefix()); + if (s.startsWith(uriScheme + ":", Qt::CaseInsensitive)) { #if QT_VERSION < 0x050000 QUrl uri(s); #else @@ -421,7 +423,7 @@ } else { // normal URI SendCoinsRecipient recipient; - if (GUIUtil::parseBitcoinURI(s, &recipient)) { + if (GUIUtil::parseBitcoinURI(uriScheme, s, &recipient)) { if (!IsValidDestinationString( recipient.address.toStdString())) { Q_EMIT message( diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -7,6 +7,7 @@ #include "addressbookpage.h" #include "addresstablemodel.h" +#include "config.h" #include "guiutil.h" #include "optionsmodel.h" #include "platformstyle.h" @@ -35,7 +36,7 @@ tr("A message that was attached to the %1 URI which will be" " stored with the transaction for your reference. Note: " "This message will not be sent over the Bitcoin network.") - .arg(GUIUtil::URI_SCHEME)); + .arg(GUIUtil::bitcoinURIScheme(GetConfig()))); setCurrentWidget(ui->SendCoins); diff --git a/src/qt/test/uritests.cpp b/src/qt/test/uritests.cpp --- a/src/qt/test/uritests.cpp +++ b/src/qt/test/uritests.cpp @@ -1,9 +1,11 @@ // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2017 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "uritests.h" +#include "chainparams.h" #include "config.h" #include "guiutil.h" #include "walletmodel.h" @@ -12,35 +14,36 @@ void URITests::uriTestsBase58() { SendCoinsRecipient rv; + QString scheme = + QString::fromStdString(Params(CBaseChainParams::MAIN).CashAddrPrefix()); QUrl uri; - uri.setUrl(QString( - "bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-dontexist=")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + uri.setUrl(QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-dontexist=")); + QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv)); uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?dontexist=")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString()); QVERIFY(rv.amount == 0); uri.setUrl(QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=" "Wikipedia Example Address")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString("Wikipedia Example Address")); QVERIFY(rv.amount == 0); uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=0.001")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString()); QVERIFY(rv.amount == 100000); uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1.001")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString()); QVERIFY(rv.amount == 100100000); @@ -48,18 +51,19 @@ uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&" "label=Wikipedia Example")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.amount == 10000000000LL); QVERIFY(rv.label == QString("Wikipedia Example")); uri.setUrl(QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=" "Wikipedia Example Address")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W")); QVERIFY(rv.label == QString()); - QVERIFY(GUIUtil::parseBitcoinURI("bitcoincash://" + QVERIFY(GUIUtil::parseBitcoinURI(scheme, + "bitcoincash://" "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?" "message=Wikipedia Example Address", &rv)); @@ -69,29 +73,31 @@ uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=" "Wikipedia Example Address")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1," "000&label=Wikipedia Example")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv)); uri.setUrl( QString("bitcoincash:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1," "000.0&label=Wikipedia Example")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv)); } void URITests::uriTestsCashAddr() { SendCoinsRecipient rv; QUrl uri; + QString scheme = + QString::fromStdString(Params(CBaseChainParams::MAIN).CashAddrPrefix()); uri.setUrl(QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?" "req-dontexist=")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv)); uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?dontexist=")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.label == QString()); @@ -100,7 +106,7 @@ uri.setUrl( QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?label=" "Wikipedia Example Address")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.label == QString("Wikipedia Example Address")); @@ -108,7 +114,7 @@ uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=0.001")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.label == QString()); @@ -116,7 +122,7 @@ uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1.001")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.label == QString()); @@ -125,7 +131,7 @@ uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=100&" "label=Wikipedia Example")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.amount == 10000000000LL); @@ -134,16 +140,16 @@ uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?message=" "Wikipedia Example Address")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.label == QString()); - QVERIFY( - GUIUtil::parseBitcoinURI("bitcoincash://" - "qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?" - "message=Wikipedia Example Address", - &rv)); + QVERIFY(GUIUtil::parseBitcoinURI( + scheme, "bitcoincash://" + "qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?" + "message=Wikipedia Example Address", + &rv)); QVERIFY(rv.address == QString("bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a")); QVERIFY(rv.label == QString()); @@ -151,17 +157,17 @@ uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?req-message=" "Wikipedia Example Address")); - QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv)); uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1," "000&label=Wikipedia Example")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv)); uri.setUrl(QString( "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1," "000.0&label=Wikipedia Example")); - QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv)); + QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv)); } namespace {