Add CashAddr Address Format

Ported from Bitcoin Unlimited, Bitcoin ABC
This commit is contained in:
lateminer
2018-01-14 22:32:08 +03:00
parent 7cd5894690
commit 323a6750c2
85 changed files with 3107 additions and 780 deletions

View File

@@ -7,7 +7,7 @@
#include "guiutil.h"
#include "walletmodel.h"
#include "base58.h"
#include "dstencode.h"
#include "wallet/wallet.h"
#include <boost/foreach.hpp>
@@ -81,16 +81,16 @@ public:
cachedAddressTable.clear();
{
LOCK(wallet->cs_wallet);
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook)
{
const CBitcoinAddress& address = item.first;
bool fMine = IsMine(*wallet, address.Get());
const CTxDestination& address = item.first;
bool fMine = IsMine(*wallet, address);
AddressTableEntry::Type addressType = translateTransactionType(
QString::fromStdString(item.second.purpose), fMine);
const std::string& strName = item.second.name;
cachedAddressTable.append(AddressTableEntry(addressType,
QString::fromStdString(strName),
QString::fromStdString(address.ToString())));
QString::fromStdString(EncodeDestination(address))));
}
}
// qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
@@ -247,7 +247,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
if(role == Qt::EditRole)
{
LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
CTxDestination curAddress = DecodeDestination(rec->address.toStdString());
if(index.column() == Label)
{
// Do nothing, if old label == new label
@@ -258,7 +258,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value,
}
wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
} else if(index.column() == Address) {
CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
CTxDestination newAddress = DecodeDestination(value.toString().toStdString());
// Refuse to set invalid address, set error status and return false
if(boost::get<CNoDestination>(&newAddress))
{
@@ -359,7 +359,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// Check for duplicate addresses
{
LOCK(wallet->cs_wallet);
if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
if(wallet->mapAddressBook.count(DecodeDestination(strAddress)))
{
editStatus = DUPLICATE_ADDRESS;
return QString();
@@ -385,7 +385,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
return QString();
}
}
strAddress = CBitcoinAddress(newKey.GetID()).ToString();
strAddress = EncodeDestination(newKey.GetID());
}
else
{
@@ -395,7 +395,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con
// Add entry
{
LOCK(wallet->cs_wallet);
wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
wallet->SetAddressBook(DecodeDestination(strAddress), strLabel,
(type == Send ? "send" : "receive"));
}
return QString::fromStdString(strAddress);
@@ -413,7 +413,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent
}
{
LOCK(wallet->cs_wallet);
wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
wallet->DelAddressBook(DecodeDestination(rec->address.toStdString()));
}
return true;
}
@@ -424,8 +424,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const
{
{
LOCK(wallet->cs_wallet);
CBitcoinAddress address_parsed(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
CTxDestination destination = DecodeDestination(address.toStdString());
std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination);
if (mi != wallet->mapAddressBook.end())
{
return QString::fromStdString(mi->second.name);

View File

@@ -10,6 +10,7 @@
#include "chainparams.h"
#include "clientmodel.h"
#include "config.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "intro.h"
@@ -81,10 +82,10 @@ Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin);
Q_DECLARE_METATYPE(bool*)
Q_DECLARE_METATYPE(CAmount)
static void InitMessage(const std::string &message)
{
LogPrintf("init message: %s\n", message);
}
// Config is non-copyable so we can only register pointers to it
Q_DECLARE_METATYPE(Config *)
static void InitMessage(const std::string &message) { LogPrintf("init message: %s\n", message); }
/*
Translate string to current locale using Qt.
@@ -173,7 +174,7 @@ public:
explicit BitcoinCore();
public Q_SLOTS:
void initialize();
void initialize(Config *config);
void shutdown();
Q_SIGNALS:
@@ -206,12 +207,12 @@ public:
/// Create options model
void createOptionsModel(bool resetSettings);
/// Create main window
void createWindow(const NetworkStyle *networkStyle);
void createWindow(const Config *, const NetworkStyle *networkStyle);
/// Create splash screen
void createSplashScreen(const NetworkStyle *networkStyle);
/// Request core initialization
void requestInitialize();
void requestInitialize(Config &config);
/// Request core shutdown
void requestShutdown();
@@ -228,7 +229,7 @@ public Q_SLOTS:
void handleRunawayException(const QString &message);
Q_SIGNALS:
void requestedInitialize();
void requestedInitialize(Config *config);
void requestedShutdown();
void stopThread();
void splashFinished(QWidget *window);
@@ -262,12 +263,13 @@ void BitcoinCore::handleRunawayException(const std::exception *e)
Q_EMIT runawayException(QString::fromStdString(strMiscWarning));
}
void BitcoinCore::initialize()
void BitcoinCore::initialize(Config *cfg)
{
Config &config(*cfg);
try
{
qDebug() << __func__ << ": Running AppInit2 in thread";
int rv = AppInit2(threadGroup, scheduler);
int rv = AppInit2(config, threadGroup, scheduler);
Q_EMIT initializeResult(rv);
} catch (const std::exception& e) {
handleRunawayException(&e);
@@ -353,9 +355,9 @@ void BitcoinApplication::createOptionsModel(bool resetSettings)
optionsModel = new OptionsModel(NULL, resetSettings);
}
void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
void BitcoinApplication::createWindow(const Config *config, const NetworkStyle *networkStyle)
{
window = new BitcoinGUI(platformStyle, networkStyle, 0);
window = new BitcoinGUI(config, platformStyle, networkStyle, 0);
pollShutdownTimer = new QTimer(window);
connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown()));
@@ -384,7 +386,7 @@ void BitcoinApplication::startThread()
connect(executor, SIGNAL(initializeResult(int)), this, SLOT(initializeResult(int)));
connect(executor, SIGNAL(shutdownResult(int)), this, SLOT(shutdownResult(int)));
connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString)));
connect(this, SIGNAL(requestedInitialize()), executor, SLOT(initialize()));
connect(this, SIGNAL(requestedInitialize(Config *)), executor, SLOT(initialize(Config *)));
connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown()));
/* make sure executor object is deleted in its own thread */
connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater()));
@@ -399,11 +401,11 @@ void BitcoinApplication::parameterSetup()
InitParameterInteraction();
}
void BitcoinApplication::requestInitialize()
void BitcoinApplication::requestInitialize(Config &config)
{
qDebug() << __func__ << ": Requesting initialize";
startThread();
Q_EMIT requestedInitialize();
Q_EMIT requestedInitialize(&config);
}
void BitcoinApplication::requestShutdown()
@@ -472,7 +474,7 @@ void BitcoinApplication::initializeResult(int retval)
#ifdef ENABLE_WALLET
// Now that initialization/startup is done, process any command-line
// bitcoin: URIs or payment requests:
// blackcoin: URIs or payment requests:
connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)),
window, SLOT(handlePaymentRequest(SendCoinsRecipient)));
connect(window, SIGNAL(receivedURI(QString)),
@@ -547,7 +549,9 @@ int main(int argc, char *argv[])
qRegisterMetaType< bool* >();
// Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType)
// IMPORTANT if it is no longer a typedef use the normal variant above
qRegisterMetaType< CAmount >("CAmount");
qRegisterMetaType<CAmount>("CAmount");
// Config is non-copyable so we can't register as a non pointer type
qRegisterMetaType<Config *>();
/// 3. Application identification
// must be set before OptionsModel is initialized or translations are loaded,
@@ -629,7 +633,8 @@ int main(int argc, char *argv[])
exit(0);
// Start up the payment server early, too, so impatient users that click on
// bitcoin: links repeatedly have their payment requests routed to this process:
// blackcoin: links repeatedly have their payment requests routed to this
// process:
app.createPaymentServer();
#endif
@@ -658,10 +663,13 @@ int main(int argc, char *argv[])
if (GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !GetBoolArg("-min", false))
app.createSplashScreen(networkStyle.data());
// Get global config
Config &config = const_cast<Config &>(GetConfig());
try
{
app.createWindow(networkStyle.data());
app.requestInitialize();
app.createWindow(&config, networkStyle.data());
app.requestInitialize(config);
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("Bitcoin Core didn't yet exit safely..."), (HWND)app.getMainWinId());
#endif

View File

@@ -1,10 +1,13 @@
// Copyright (c) 2011-2014 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited 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 "bitcoinaddressvalidator.h"
#include "base58.h"
#include "cashaddr.h"
#include "dstencode.h"
/* Base58 characters are:
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
@@ -14,9 +17,49 @@
- All upper-case letters except for 'I' and 'O'
- All lower-case letters except for 'l'
*/
static bool ValidLegacyInput(const QString &input)
{
// Alphanumeric and not a 'forbidden' character
for (QChar ch : input)
{
if (!(((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) && ch != 'l' &&
ch != 'I' && ch != '0' && ch != 'O'))
return false;
}
return true;
}
BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(QObject *parent) :
QValidator(parent)
static bool ValidCashaddrInput(const QString &prefix, const QString &input)
{
std::vector<uint8_t> charset = cashaddr::EncodingCharset();
// Input may be incomplete. We're checking if it so far looks good.
for (int i = 0; i < input.size(); ++i)
{
char ch = std::tolower(input[i].toLatin1());
// Does the input have the right prefix?
if (i < prefix.size())
{
if (ch != prefix[i].toLatin1())
{
return false;
}
continue;
}
// Payload, must use cashaddr charset.
if (std::find(begin(charset), end(charset), ch) == end(charset))
{
return false;
}
}
return true;
}
BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(const std::string &cashaddrprefix, QObject *parent)
: QValidator(parent), cashaddrprefix(cashaddrprefix)
{
}
@@ -59,25 +102,9 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po
}
// Validation
QValidator::State state = QValidator::Acceptable;
for (int idx = 0; idx < input.size(); ++idx)
{
int ch = input.at(idx).unicode();
if (((ch >= '0' && ch<='9') ||
(ch >= 'a' && ch<='z') ||
(ch >= 'A' && ch<='Z')) &&
ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
{
// Alphanumeric and not a 'forbidden' character
}
else
{
state = QValidator::Invalid;
}
}
return state;
const QString cashPrefix = QString::fromStdString(cashaddrprefix) + ":";
return (ValidLegacyInput(input) || ValidCashaddrInput(cashPrefix, input)) ? QValidator::Acceptable :
QValidator::Invalid;
}
BitcoinAddressCheckValidator::BitcoinAddressCheckValidator(QObject *parent) :
@@ -89,9 +116,10 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po
{
Q_UNUSED(pos);
// Validate the passed Bitcoin address
CBitcoinAddress addr(input.toStdString());
if (addr.IsValid())
if (IsValidDestinationString(input.toStdString()))
{
return QValidator::Acceptable;
}
return QValidator::Invalid;
}

View File

@@ -1,4 +1,6 @@
// Copyright (c) 2011-2014 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited 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.
@@ -7,7 +9,8 @@
#include <QValidator>
/** Base58 entry widget validator, checks for valid characters and
/**
* Bitcoin address entry widget validator, checks for valid characters and
* removes some whitespace.
*/
class BitcoinAddressEntryValidator : public QValidator
@@ -15,9 +18,12 @@ class BitcoinAddressEntryValidator : public QValidator
Q_OBJECT
public:
explicit BitcoinAddressEntryValidator(QObject *parent);
explicit BitcoinAddressEntryValidator(const std::string &cashaddrprefix, QObject *parent);
State validate(QString &input, int &pos) const;
private:
std::string cashaddrprefix;
};
/** Bitcoin address widget validator, checks for a valid bitcoin address.

View File

@@ -75,7 +75,7 @@ const QString BitcoinGUI::DEFAULT_WALLET = "~Default";
extern int64_t nLastCoinStakeSearchInterval;
double GetPoSKernelPS();
BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
BitcoinGUI::BitcoinGUI(const Config *cfg, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent) :
QMainWindow(parent),
clientModel(0),
walletFrame(0),
@@ -118,7 +118,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
helpMessageDialog(0),
prevBlocks(0),
spinnerFrame(0),
platformStyle(platformStyle)
platformStyle(platformStyle),
cfg(cfg)
{
GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this);
@@ -156,7 +157,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *n
if(enableWallet)
{
/** Create wallet frame and make it the central widget */
walletFrame = new WalletFrame(platformStyle, this);
walletFrame = new WalletFrame(platformStyle, cfg, this);
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
@@ -297,7 +298,7 @@ void BitcoinGUI::createActions()
sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip());
receiveCoinsAction = new QAction(platformStyle->SingleColorIcon(":/icons/receiving_addresses"), tr("&Receive"), this);
receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and bitcoin: URIs)"));
receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and %1: URIs)").arg(GUIUtil::bitcoinURIScheme(*cfg)));
receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip());
receiveCoinsAction->setCheckable(true);
receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3));
@@ -376,7 +377,7 @@ void BitcoinGUI::createActions()
usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels"));
openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this);
openAction->setStatusTip(tr("Open a bitcoin: URI or payment request"));
openAction->setStatusTip(tr("Open a %1: URI or payment request").arg(GUIUtil::bitcoinURIScheme(*cfg)));
showHelpMessageAction = new QAction(platformStyle->TextColorIcon(":/icons/info"), tr("&Command-line options"), this);
showHelpMessageAction->setMenuRole(QAction::NoRole);
@@ -670,8 +671,8 @@ void BitcoinGUI::showHelpMessageClicked()
#ifdef ENABLE_WALLET
void BitcoinGUI::openClicked()
{
OpenURIDialog dlg(this);
if(dlg.exec())
OpenURIDialog dlg(cfg, this);
if (dlg.exec())
{
Q_EMIT receivedURI(dlg.getURI());
}

View File

@@ -30,6 +30,7 @@ class WalletFrame;
class WalletModel;
class HelpMessageDialog;
class Config;
class CWallet;
QT_BEGIN_NAMESPACE
@@ -50,7 +51,10 @@ public:
static const QString DEFAULT_WALLET;
static const std::string DEFAULT_UIPLATFORM;
explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0);
explicit BitcoinGUI(const Config *,
const PlatformStyle *platformStyle,
const NetworkStyle *networkStyle,
QWidget *parent = 0);
~BitcoinGUI();
/** Set the client model.
@@ -129,6 +133,7 @@ private:
uint64_t nWeight;
const PlatformStyle *platformStyle;
const Config *cfg;
/** Create the main UI actions. */
void createActions();

View File

@@ -14,6 +14,7 @@
#include "walletmodel.h"
#include "coincontrol.h"
#include "dstencode.h"
#include "init.h"
#include "main.h" // For minRelayTxFee
#include "wallet/wallet.h"
@@ -734,9 +735,10 @@ void CoinControlDialog::updateView()
QString sAddress = "";
if(ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress))
{
sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString());
sAddress = QString::fromStdString(EncodeDestination(outputAddress));
// if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs
// if listMode or change => show bitcoin address. In tree mode,
// address is not shown again for direct wallet address outputs
if (!treeMode || (!(sAddress == sWalletAddress)))
itemOutput->setText(COLUMN_ADDRESS, sAddress);

View File

@@ -47,7 +47,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80;
static const int MAX_URI_LENGTH = 255;
/* QRCodeDialog -- size of exported QR Code image */
#define QR_IMAGE_SIZE 300
#define QR_IMAGE_SIZE 350
/* Number of frames in spinner animation */
#define SPINNER_FRAMES 36

View File

@@ -10,12 +10,16 @@
#include "walletmodel.h"
#include "primitives/transaction.h"
#include "cashaddr.h"
#include "config.h"
#include "dstencode.h"
#include "init.h"
#include "main.h" // For minRelayTxFee
#include "protocol.h"
#include "script/script.h"
#include "script/standard.h"
#include "util.h"
#include "utilstrencodings.h"
#ifdef WIN32
#ifdef _WIN32_WINNT
@@ -107,35 +111,46 @@ QFont fixedPitchFont()
#endif
}
// Just some dummy data to generate an convincing random-looking (but consistent) address
static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47};
// Generate a dummy address with invalid CRC, starting with the network prefix.
static std::string DummyAddress(const CChainParams &params)
static std::string MakeAddrInvalid(std::string addr)
{
std::vector<unsigned char> sourcedata = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata));
for(int i=0; i<256; ++i) { // Try every trailing byte
std::string s = EncodeBase58(begin_ptr(sourcedata), end_ptr(sourcedata));
if (!CBitcoinAddress(s).IsValid())
return s;
sourcedata[sourcedata.size()-1] += 1;
if (addr.size() < 2)
{
return "";
}
// Checksum is at the end of the address. Swapping chars to make it invalid.
std::swap(addr[addr.size() - 1], addr[addr.size() - 2]);
if (!IsValidDestinationString(addr))
{
return addr;
}
return "";
}
std::string DummyAddress(const CChainParams &params, const Config &cfg)
{
// Just some dummy data to generate an convincing random-looking (but
// consistent) address
static const std::vector<uint8_t> dummydata = {0xeb, 0x15, 0x23, 0x1d, 0xfc, 0xeb, 0x60, 0x92, 0x58, 0x86, 0xb6,
0x7d, 0x06, 0x52, 0x99, 0x92, 0x59, 0x15, 0xae, 0xb1};
const CTxDestination dstKey = CKeyID(uint160(dummydata));
return MakeAddrInvalid(EncodeDestination(dstKey, params, cfg));
}
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent)
{
parent->setFocusProxy(widget);
widget->setFont(fixedPitchFont());
const CChainParams &params = Params();
#if QT_VERSION >= 0x040700
// We don't want translators to use own addresses in translations
// and this is the only place, where this address is supplied.
widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)").arg(
QString::fromStdString(DummyAddress(Params()))));
widget->setPlaceholderText(QObject::tr("Enter a Bitcoin address (e.g. %1)")
.arg(QString::fromStdString(DummyAddress(params, GetConfig()))));
#endif
widget->setValidator(new BitcoinAddressEntryValidator(parent));
widget->setValidator(new BitcoinAddressEntryValidator(params.CashAddrPrefix(), parent));
widget->setCheckValidator(new BitcoinAddressCheckValidator(parent));
}
@@ -148,16 +163,48 @@ void setupAmountWidget(QLineEdit *widget, QWidget *parent)
widget->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
}
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
QString bitcoinURIScheme(const CChainParams &params, bool useCashAddr)
{
// return if URI is not valid or is no bitcoin: URI
if(!uri.isValid() || uri.scheme() != QString("bitcoin"))
if (!useCashAddr)
{
return "blackcoin";
}
return QString::fromStdString(params.CashAddrPrefix());
}
QString bitcoinURIScheme(const Config &cfg)
{
return bitcoinURIScheme(cfg.GetChainParams(), cfg.UseCashAddrEncoding());
}
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 QString &scheme, const QUrl &uri, SendCoinsRecipient *out)
{
// return if URI has wrong scheme.
if (!uri.isValid() || uri.scheme() != scheme)
{
return false;
}
SendCoinsRecipient rv;
rv.address = uri.path();
if (IsCashAddrEncoded(uri))
{
rv.address = uri.scheme() + ":" + uri.path();
}
else
{
// strip out uri scheme for base58 encoded addresses
rv.address = uri.path();
}
// Trim any following forward slash which may have been added by the OS
if (rv.address.endsWith("/")) {
if (rv.address.endsWith("/"))
{
rv.address.truncate(rv.address.length() - 1);
}
rv.amount = 0;
@@ -189,9 +236,9 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
}
else if (i->first == "amount")
{
if(!i->second.isEmpty())
if (!i->second.isEmpty())
{
if(!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
if (!BitcoinUnits::parse(BitcoinUnits::BTC, i->second, &rv.amount))
{
return false;
}
@@ -202,30 +249,35 @@ bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out)
if (fShouldReturnFalse)
return false;
}
if(out)
if (out)
{
*out = rv;
}
return true;
}
bool parseBitcoinURI(QString uri, SendCoinsRecipient *out)
bool parseBitcoinURI(const QString &scheme, QString uri, SendCoinsRecipient *out)
{
// Convert bitcoin:// to bitcoin:
//
// Cannot handle this later, because bitcoin:// will cause Qt to see the part after // as host,
// Cannot handle this later, because blackcoin://
// will cause Qt to see the part after // as host,
// which will lower-case it (and thus invalidate the address).
if(uri.startsWith("bitcoin://", Qt::CaseInsensitive))
if (uri.startsWith(scheme + "://", Qt::CaseInsensitive))
{
uri.replace(0, 10, "bitcoin:");
uri.replace(0, scheme.length() + 3, scheme + ":");
}
QUrl uriInstance(uri);
return parseBitcoinURI(uriInstance, out);
return parseBitcoinURI(scheme, uriInstance, out);
}
QString formatBitcoinURI(const SendCoinsRecipient &info)
QString formatBitcoinURI(const Config &cfg, const SendCoinsRecipient &info)
{
QString ret = QString("blackcoin:%1").arg(info.address);
QString ret = info.address;
if (!cfg.UseCashAddrEncoding())
{
// prefix address with uri scheme for base58 encoded addresses.
ret = (bitcoinURIScheme(cfg) + ":%1").arg(ret);
}
int paramCount = 0;
if (info.amount)
@@ -253,7 +305,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info)
bool isDust(const QString& address, const CAmount& amount)
{
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
CTxDestination dest = DecodeDestination(address.toStdString());
CScript script = GetScriptForDestination(dest);
CTxOut txOut(amount, script);
return txOut.IsDust(::minRelayTxFee);

View File

@@ -19,6 +19,8 @@
class QValidatedLineEdit;
class SendCoinsRecipient;
class CChainParams;
class Config;
QT_BEGIN_NAMESPACE
class QAbstractItemView;
@@ -40,14 +42,20 @@ namespace GUIUtil
// Return a monospace font
QFont fixedPitchFont();
// Generate an invalid, but convincing address.
std::string DummyAddress(const CChainParams &params, const Config &cfg);
// Set up widgets for address and amounts
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent);
void setupAmountWidget(QLineEdit *widget, QWidget *parent);
// Parse "bitcoin:" URI into recipient object, return true on successful parsing
bool parseBitcoinURI(const QUrl &uri, SendCoinsRecipient *out);
bool parseBitcoinURI(QString uri, SendCoinsRecipient *out);
QString formatBitcoinURI(const SendCoinsRecipient &info);
QString bitcoinURIScheme(const CChainParams &, bool useCashAddr);
QString bitcoinURIScheme(const Config &);
// Parse "blackcoin:" URI into recipient object, return true on successful
// parsing
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
bool isDust(const QString& address, const CAmount& amount);

View File

@@ -10,9 +10,10 @@
#include <QUrl>
OpenURIDialog::OpenURIDialog(QWidget *parent) :
OpenURIDialog::OpenURIDialog(const Config *cfg, QWidget *parent) :
QDialog(parent),
ui(new Ui::OpenURIDialog)
ui(new Ui::OpenURIDialog),
cfg(cfg)
{
ui->setupUi(this);
#if QT_VERSION >= 0x040700
@@ -33,7 +34,8 @@ QString OpenURIDialog::getURI()
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();
@@ -48,5 +50,5 @@ void OpenURIDialog::on_selectFileButton_clicked()
if(filename.isEmpty())
return;
QUrl fileUri = QUrl::fromLocalFile(filename);
ui->uriEdit->setText("blackcoin:?r=" + QUrl::toPercentEncoding(fileUri.toString()));
ui->uriEdit->setText(GUIUtil::bitcoinURIScheme(*cfg) + ":?r=" + QUrl::toPercentEncoding(fileUri.toString()));
}

View File

@@ -7,6 +7,8 @@
#include <QDialog>
class Config;
namespace Ui {
class OpenURIDialog;
}
@@ -16,7 +18,7 @@ class OpenURIDialog : public QDialog
Q_OBJECT
public:
explicit OpenURIDialog(QWidget *parent);
explicit OpenURIDialog(const Config *cfg, QWidget *parent);
~OpenURIDialog();
QString getURI();
@@ -29,6 +31,7 @@ private Q_SLOTS:
private:
Ui::OpenURIDialog *ui;
const Config *cfg;
};
#endif // BITCOIN_QT_OPENURIDIALOG_H

View File

@@ -8,8 +8,9 @@
#include "guiutil.h"
#include "optionsmodel.h"
#include "base58.h"
#include "chainparams.h"
#include "config.h"
#include "dstencode.h"
#include "main.h" // For minRelayTxFee
#include "ui_interface.h"
#include "util.h"
@@ -52,9 +53,9 @@ const QString BITCOIN_IPC_PREFIX("blackcoin:");
const char* BIP70_MESSAGE_PAYMENTACK = "PaymentACK";
const char* BIP70_MESSAGE_PAYMENTREQUEST = "PaymentRequest";
// BIP71 payment protocol media types
const char* BIP71_MIMETYPE_PAYMENT = "application/bitcoin-payment";
const char* BIP71_MIMETYPE_PAYMENTACK = "application/bitcoin-paymentack";
const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/bitcoin-paymentrequest";
const char* BIP71_MIMETYPE_PAYMENT = "application/blackcoin-payment";
const char* BIP71_MIMETYPE_PAYMENTACK = "application/blackcoin-paymentack";
const char* BIP71_MIMETYPE_PAYMENTREQUEST = "application/blackcoin-paymentrequest";
// BIP70 max payment request size in bytes (DoS protection)
const qint64 BIP70_MAX_PAYMENTREQUEST_SIZE = 50000;
@@ -192,6 +193,37 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
// "certificate stapling" with server-side caching is more efficient
}
static std::string ipcParseURI(const QString &arg, const CChainParams &params, bool useCashAddr)
{
const QString scheme = GUIUtil::bitcoinURIScheme(params, useCashAddr);
if (!arg.startsWith(scheme + ":", Qt::CaseInsensitive))
{
return {};
}
SendCoinsRecipient r;
if (!GUIUtil::parseBitcoinURI(scheme, arg, &r))
{
return {};
}
return r.address.toStdString();
}
static bool ipcCanParseCashAddrURI(const QString &arg, const std::string &network)
{
const CChainParams &params(Params(network));
std::string addr = ipcParseURI(arg, params, true);
return IsValidDestinationString(addr, params);
}
static bool ipcCanParseLegacyURI(const QString &arg, const std::string &network)
{
const CChainParams &params(Params(network));
std::string addr = ipcParseURI(arg, params, false);
return IsValidDestinationString(addr, params);
}
//
// Sending to the server is done synchronously, at startup.
// If the server isn't already running, startup continues,
@@ -203,58 +235,77 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store)
//
void PaymentServer::ipcParseCommandLine(int argc, char* argv[])
{
std::array<const std::string *, 3> networks = {
&CBaseChainParams::MAIN, &CBaseChainParams::TESTNET, &CBaseChainParams::REGTEST};
const std::string *chosenNetwork = nullptr;
for (int i = 1; i < argc; i++)
{
QString arg(argv[i]);
if (arg.startsWith("-"))
continue;
// If the bitcoin: 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(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
const std::string *itemNetwork = nullptr;
// Try to parse as a URI
for (auto net : networks)
{
savedPaymentRequests.append(arg);
SendCoinsRecipient r;
if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty())
if (ipcCanParseCashAddrURI(arg, *net))
{
CBitcoinAddress address(r.address.toStdString());
itemNetwork = net;
break;
}
if (address.IsValid(Params(CBaseChainParams::MAIN)))
{
SelectParams(CBaseChainParams::MAIN);
}
else if (address.IsValid(Params(CBaseChainParams::TESTNET)))
{
SelectParams(CBaseChainParams::TESTNET);
}
if (ipcCanParseLegacyURI(arg, *net))
{
itemNetwork = net;
break;
}
}
else if (QFile::exists(arg)) // Filename
{
savedPaymentRequests.append(arg);
if (!itemNetwork && QFile::exists(arg))
{
// Filename
PaymentRequestPlus request;
if (readPaymentRequestFromFile(arg, request))
{
if (request.getDetails().network() == "main")
for (auto net : networks)
{
SelectParams(CBaseChainParams::MAIN);
}
else if (request.getDetails().network() == "test")
{
SelectParams(CBaseChainParams::TESTNET);
if (*net == request.getDetails().network())
{
itemNetwork = net;
}
}
}
}
else
if (itemNetwork == nullptr)
{
// Printing to debug.log is about the best we can do here, the
// GUI hasn't started yet so we can't pop up a message box.
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request file does not exist: " << arg;
// Printing to debug.log is about the best we can do here, the GUI
// hasn't started yet so we can't pop up a message box.
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request "
"file or URI does not exist or is invalid: "
<< arg;
continue;
}
if (chosenNetwork && chosenNetwork != itemNetwork)
{
qWarning() << "PaymentServer::ipcSendCommandLine: Payment request "
"from network "
<< QString(itemNetwork->c_str()) << " does not match already chosen network "
<< QString(chosenNetwork->c_str());
continue;
}
savedPaymentRequests.append(arg);
chosenNetwork = itemNetwork;
}
if (chosenNetwork)
{
SelectParams(*chosenNetwork);
}
}
@@ -309,7 +360,7 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
GOOGLE_PROTOBUF_VERIFY_VERSION;
// Install global event filter to catch QFileOpenEvents
// on Mac: sent when you click bitcoin: links
// on Mac: sent when you click blackcoin: links
// other OSes: helpful when dealing with payment request files
if (parent)
parent->installEventFilter(this);
@@ -323,10 +374,11 @@ PaymentServer::PaymentServer(QObject* parent, bool startLocalServer) :
{
uriServer = new QLocalServer(this);
if (!uriServer->listen(name)) {
// constructor is called early in init, so don't use "Q_EMIT message()" here
QMessageBox::critical(0, tr("Payment request error"),
tr("Cannot start blackcoin: click-to-pay handler"));
if (!uriServer->listen(name))
{
// constructor is called early in init, so don't use "Q_EMIT
// message()" here
QMessageBox::critical(0, tr("Payment request error"), tr("Cannot start click-to-pay handler"));
}
else {
connect(uriServer, SIGNAL(newConnection()), this, SLOT(handleURIConnection()));
@@ -341,7 +393,7 @@ PaymentServer::~PaymentServer()
}
//
// OSX-specific way of handling bitcoin: URIs and PaymentRequest mime types.
// OSX-specific way of handling blackcoin: URIs and PaymentRequest mime types.
// Also used by paymentservertests.cpp and when opening a payment request file
// via "Open URI..." menu entry.
//
@@ -367,7 +419,7 @@ void PaymentServer::initNetManager()
if (netManager != NULL)
delete netManager;
// netManager is used to fetch paymentrequests given in bitcoin: URIs
// netManager is used to fetch paymentrequests given in blackcoin: URIs
netManager = new QNetworkAccessManager(this);
QNetworkProxy proxy;
@@ -399,7 +451,65 @@ void PaymentServer::uiReady()
savedPaymentRequests.clear();
}
void PaymentServer::handleURIOrFile(const QString& s)
bool PaymentServer::handleURI(const QString &scheme, const QString &s)
{
if (!s.startsWith(scheme + ":", Qt::CaseInsensitive))
{
return false;
}
#if QT_VERSION < 0x050000
QUrl uri(s);
#else
QUrlQuery uri((QUrl(s)));
#endif
if (uri.hasQueryItem("r"))
{
// payment request URI
QByteArray temp;
temp.append(uri.queryItemValue("r"));
QString decoded = QUrl::fromPercentEncoding(temp);
QUrl fetchUrl(decoded, QUrl::StrictMode);
if (fetchUrl.isValid())
{
qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl << ")";
fetchRequest(fetchUrl);
}
else
{
qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
Q_EMIT message(tr("URI handling"), tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
CClientUIInterface::ICON_WARNING);
}
return true;
}
// normal URI
SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(scheme, s, &recipient))
{
if (!IsValidDestinationString(recipient.address.toStdString()))
{
Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
CClientUIInterface::MSG_ERROR);
}
else
{
Q_EMIT receivedPaymentRequest(recipient);
}
}
else
{
Q_EMIT message(tr("URI handling"), tr("URI cannot be parsed! This can be caused by an invalid "
"Bitcoin address or malformed URI parameters."),
CClientUIInterface::ICON_WARNING);
}
return true;
}
void PaymentServer::handleURIOrFile(const QString &s)
{
if (saveURIs)
{
@@ -407,55 +517,18 @@ void PaymentServer::handleURIOrFile(const QString& s)
return;
}
if (s.startsWith(BITCOIN_IPC_PREFIX, Qt::CaseInsensitive)) // bitcoin: URI
// blackcoin: CashAddr URI
QString schemeCash = GUIUtil::bitcoinURIScheme(Params(), true);
if (handleURI(schemeCash, s))
{
#if QT_VERSION < 0x050000
QUrl uri(s);
#else
QUrlQuery uri((QUrl(s)));
#endif
if (uri.hasQueryItem("r")) // payment request URI
{
QByteArray temp;
temp.append(uri.queryItemValue("r"));
QString decoded = QUrl::fromPercentEncoding(temp);
QUrl fetchUrl(decoded, QUrl::StrictMode);
return;
}
if (fetchUrl.isValid())
{
qDebug() << "PaymentServer::handleURIOrFile: fetchRequest(" << fetchUrl << ")";
fetchRequest(fetchUrl);
}
else
{
qWarning() << "PaymentServer::handleURIOrFile: Invalid URL: " << fetchUrl;
Q_EMIT message(tr("URI handling"),
tr("Payment request fetch URL is invalid: %1").arg(fetchUrl.toString()),
CClientUIInterface::ICON_WARNING);
}
return;
}
else // normal URI
{
SendCoinsRecipient recipient;
if (GUIUtil::parseBitcoinURI(s, &recipient))
{
CBitcoinAddress address(recipient.address.toStdString());
if (!address.IsValid()) {
Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address),
CClientUIInterface::MSG_ERROR);
}
else
Q_EMIT receivedPaymentRequest(recipient);
}
else
Q_EMIT message(tr("URI handling"),
tr("URI cannot be parsed! This can be caused by an invalid Bitcoin address or malformed URI parameters."),
CClientUIInterface::ICON_WARNING);
return;
}
// blackcoin: Legacy URI
QString schemeLegacy = GUIUtil::bitcoinURIScheme(Params(), false);
if (handleURI(schemeLegacy, s))
{
return;
}
if (QFile::exists(s)) // payment request file
@@ -560,12 +633,13 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
CTxDestination dest;
if (ExtractDestination(sendingTo.first, dest)) {
// Append destination address
addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString()));
addresses.append(QString::fromStdString(EncodeDestination(dest)));
}
else if (!recipient.authenticatedMerchant.isEmpty()) {
// Unauthenticated payment requests 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).
else if (!recipient.authenticatedMerchant.isEmpty())
{
// Unauthenticated payment requests 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).
Q_EMIT message(tr("Payment request rejected"),
tr("Unverified payment requests to custom payment scripts are unsupported."),
CClientUIInterface::MSG_ERROR);

View File

@@ -6,7 +6,7 @@
#define BITCOIN_QT_PAYMENTSERVER_H
// This class handles payment requests from clicking on
// bitcoin: URIs
// blackcoin: URIs
//
// This is somewhat tricky, because we have to deal with
// the situation where the user clicks on a link during
@@ -131,6 +131,7 @@ protected:
private:
static bool readPaymentRequestFromFile(const QString& filename, PaymentRequestPlus& request);
bool handleURI(const QString &scheme, const QString &s);
bool processPaymentRequest(const PaymentRequestPlus& request, SendCoinsRecipient& recipient);
void fetchRequest(const QUrl& url);

View File

@@ -8,6 +8,7 @@
#include "addressbookpage.h"
#include "addresstablemodel.h"
#include "bitcoinunits.h"
#include "config.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
@@ -22,12 +23,13 @@
#include <QScrollBar>
#include <QTextDocument>
ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent) :
ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, const Config *cfg, QWidget *parent) :
QDialog(parent),
ui(new Ui::ReceiveCoinsDialog),
columnResizingFixer(0),
model(0),
platformStyle(platformStyle)
platformStyle(platformStyle),
cfg(cfg)
{
ui->setupUi(this);
@@ -44,18 +46,21 @@ ReceiveCoinsDialog::ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidg
}
// context menu actions
QAction *copyURIAction = new QAction(tr("Copy URI"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyMessageAction = new QAction(tr("Copy message"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
// context menu
contextMenu = new QMenu(this);
contextMenu->addAction(copyURIAction);
contextMenu->addAction(copyLabelAction);
contextMenu->addAction(copyMessageAction);
contextMenu->addAction(copyAmountAction);
// context menu signals
connect(ui->recentRequestsView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showMenu(QPoint)));
connect(copyURIAction, SIGNAL(triggered()), this, SLOT(copyURI()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
connect(copyMessageAction, SIGNAL(triggered()), this, SLOT(copyMessage()));
connect(copyAmountAction, SIGNAL(triggered()), this, SLOT(copyAmount()));
@@ -153,7 +158,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
}
SendCoinsRecipient info(address, label,
ui->reqAmount->value(), ui->reqMessage->text());
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(cfg, this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setModel(model->getOptionsModel());
dialog->setInfo(info);
@@ -167,7 +172,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked()
void ReceiveCoinsDialog::on_recentRequestsView_doubleClicked(const QModelIndex &index)
{
const RecentRequestsTableModel *submodel = model->getRecentRequestsTableModel();
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(this);
ReceiveRequestDialog *dialog = new ReceiveRequestDialog(cfg, this);
dialog->setModel(model->getOptionsModel());
dialog->setInfo(submodel->entry(index.row()).recipient);
dialog->setAttribute(Qt::WA_DeleteOnClose);
@@ -229,30 +234,55 @@ void ReceiveCoinsDialog::keyPressEvent(QKeyEvent *event)
this->QDialog::keyPressEvent(event);
}
QModelIndex ReceiveCoinsDialog::selectedRow()
{
if (!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
return QModelIndex();
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
if (selection.empty())
return QModelIndex();
// correct for selection mode ContiguousSelection
QModelIndex firstIndex = selection.at(0);
return firstIndex;
}
// copy column of selected row to clipboard
void ReceiveCoinsDialog::copyColumnToClipboard(int column)
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
QModelIndex firstIndex = selectedRow();
if (!firstIndex.isValid())
{
return;
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
if(selection.empty())
return;
// correct for selection mode ContiguousSelection
QModelIndex firstIndex = selection.at(0);
GUIUtil::setClipboard(model->getRecentRequestsTableModel()->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole).toString());
}
GUIUtil::setClipboard(model->getRecentRequestsTableModel()
->data(firstIndex.child(firstIndex.row(), column), Qt::EditRole)
.toString());
}
// context menu
void ReceiveCoinsDialog::showMenu(const QPoint &point)
{
if(!model || !model->getRecentRequestsTableModel() || !ui->recentRequestsView->selectionModel())
return;
QModelIndexList selection = ui->recentRequestsView->selectionModel()->selectedRows();
if(selection.empty())
if (!selectedRow().isValid())
{
return;
}
contextMenu->exec(QCursor::pos());
}
// context menu action: copy URI
void ReceiveCoinsDialog::copyURI()
{
QModelIndex sel = selectedRow();
if (!sel.isValid())
{
return;
}
const RecentRequestsTableModel *const submodel = model->getRecentRequestsTableModel();
const QString uri = GUIUtil::formatBitcoinURI(*cfg, submodel->entry(sel.row()).recipient);
GUIUtil::setClipboard(uri);
}
// context menu action: copy label
void ReceiveCoinsDialog::copyLabel()
{

View File

@@ -15,6 +15,7 @@
#include <QPoint>
#include <QVariant>
class Config;
class OptionsModel;
class PlatformStyle;
class WalletModel;
@@ -40,7 +41,7 @@ public:
MINIMUM_COLUMN_WIDTH = 130
};
explicit ReceiveCoinsDialog(const PlatformStyle *platformStyle, QWidget *parent = 0);
explicit ReceiveCoinsDialog(const PlatformStyle *platformStyle, const Config *cfg, QWidget *parent = 0);
~ReceiveCoinsDialog();
void setModel(WalletModel *model);
@@ -59,7 +60,9 @@ private:
WalletModel *model;
QMenu *contextMenu;
const PlatformStyle *platformStyle;
const Config *cfg;
QModelIndex selectedRow();
void copyColumnToClipboard(int column);
virtual void resizeEvent(QResizeEvent *event);
@@ -71,6 +74,7 @@ private Q_SLOTS:
void recentRequestsView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void updateDisplayUnit();
void showMenu(const QPoint &point);
void copyURI();
void copyLabel();
void copyMessage();
void copyAmount();

View File

@@ -1,4 +1,6 @@
// Copyright (c) 2011-2013 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited 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.
@@ -6,6 +8,8 @@
#include "ui_receiverequestdialog.h"
#include "bitcoinunits.h"
#include "config.h"
#include "dstencode.h"
#include "guiconstants.h"
#include "guiutil.h"
#include "optionsmodel.h"
@@ -45,7 +49,7 @@ QImage QRImageWidget::exportImage()
{
if(!pixmap())
return QImage();
return pixmap()->toImage();
return pixmap()->toImage().scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE);
}
void QRImageWidget::mousePressEvent(QMouseEvent *event)
@@ -89,10 +93,11 @@ void QRImageWidget::contextMenuEvent(QContextMenuEvent *event)
contextMenu->exec(event->globalPos());
}
ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
ReceiveRequestDialog::ReceiveRequestDialog(const Config *cfg, QWidget *parent) :
QDialog(parent),
ui(new Ui::ReceiveRequestDialog),
model(0)
model(0),
cfg(cfg)
{
ui->setupUi(this);
@@ -120,9 +125,26 @@ void ReceiveRequestDialog::setModel(OptionsModel *model)
update();
}
void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &info)
// Addresses are stored in the database with the encoding that the client was
// configured with at the time of creation.
//
// This converts to clients current configuration.
QString ToCurrentEncoding(const QString &addr, const Config &cfg)
{
this->info = info;
if (!IsValidDestinationString(addr.toStdString(), cfg.GetChainParams()))
{
// We have something sketchy as input. Do not try to convert.
return addr;
}
CTxDestination dst = DecodeDestination(addr.toStdString(), cfg.GetChainParams());
return QString::fromStdString(EncodeDestination(dst, cfg.GetChainParams(), cfg));
}
void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info)
{
this->info = _info;
// Display addresses with currently configured encoding.
this->info.address = ToCurrentEncoding(this->info.address, *cfg);
update();
}
@@ -135,7 +157,7 @@ void ReceiveRequestDialog::update()
target = info.address;
setWindowTitle(tr("Request payment to %1").arg(target));
QString uri = GUIUtil::formatBitcoinURI(info);
QString uri = GUIUtil::formatBitcoinURI(*cfg, info);
ui->btnSaveAs->setEnabled(false);
QString html;
html += "<html><font face='verdana, arial, helvetica, sans-serif'>";
@@ -152,6 +174,8 @@ void ReceiveRequestDialog::update()
ui->outUri->setText(html);
#ifdef USE_QRCODE
int fontSize = cfg->UseCashAddrEncoding() ? 10 : 12;
ui->lblQRCode->setText("");
if(!uri.isEmpty())
{
@@ -179,16 +203,16 @@ void ReceiveRequestDialog::update()
}
QRcode_free(code);
QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE+20, QImage::Format_RGB32);
QImage qrAddrImage = QImage(QR_IMAGE_SIZE, QR_IMAGE_SIZE + 20, QImage::Format_RGB32);
qrAddrImage.fill(0xffffff);
QPainter painter(&qrAddrImage);
painter.drawImage(0, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
QFont font = GUIUtil::fixedPitchFont();
font.setPixelSize(12);
font.setPixelSize(fontSize);
painter.setFont(font);
QRect paddedRect = qrAddrImage.rect();
paddedRect.setHeight(QR_IMAGE_SIZE+12);
painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, info.address);
paddedRect.setHeight(QR_IMAGE_SIZE + 12);
painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, info.address);
painter.end();
ui->lblQRCode->setPixmap(QPixmap::fromImage(qrAddrImage));
@@ -200,7 +224,7 @@ void ReceiveRequestDialog::update()
void ReceiveRequestDialog::on_btnCopyURI_clicked()
{
GUIUtil::setClipboard(GUIUtil::formatBitcoinURI(info));
GUIUtil::setClipboard(GUIUtil::formatBitcoinURI(*cfg, info));
}
void ReceiveRequestDialog::on_btnCopyAddress_clicked()

View File

@@ -11,8 +11,10 @@
#include <QImage>
#include <QLabel>
#include <QPainter>
#include <QString>
class OptionsModel;
class Config;
namespace Ui {
class ReceiveRequestDialog;
@@ -50,7 +52,7 @@ class ReceiveRequestDialog : public QDialog
Q_OBJECT
public:
explicit ReceiveRequestDialog(QWidget *parent = 0);
explicit ReceiveRequestDialog(const Config *cfg, QWidget *parent = 0);
~ReceiveRequestDialog();
void setModel(OptionsModel *model);
@@ -66,6 +68,10 @@ private:
Ui::ReceiveRequestDialog *ui;
OptionsModel *model;
SendCoinsRecipient info;
const Config *cfg;
};
// exported for unittesting
QString ToCurrentEncoding(const QString &addr, const Config &);
#endif // BITCOIN_QT_RECEIVEREQUESTDIALOG_H

View File

@@ -53,7 +53,8 @@ private:
Qt::SortOrder order;
};
/** Model for list of recently generated payment requests / bitcoin: URIs.
/**
* Model for list of recently generated payment requests / blackcoin: URIs.
* Part of wallet model.
*/
class RecentRequestsTableModel: public QAbstractTableModel

View File

@@ -15,8 +15,8 @@
#include "sendcoinsentry.h"
#include "walletmodel.h"
#include "base58.h"
#include "coincontrol.h"
#include "dstencode.h"
#include "main.h" // mempool and minRelayTxFee
#include "ui_interface.h"
#include "txmempool.h"
@@ -746,26 +746,43 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
CoinControlDialog::coinControl->destChange = CNoDestination();
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
const CTxDestination dest = DecodeDestination(text.toStdString());
if (text.isEmpty()) // Nothing entered
{
ui->labelCoinControlChangeLabel->setText("");
}
else if (!addr.IsValid()) // Invalid address
else if (!IsValidDestination(dest))
{
// Invalid address
ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address"));
}
else // Valid address
else
{
CKeyID keyid;
addr.GetKeyID(keyid);
if (!model->havePrivKey(keyid)) // Unknown change address
// Valid address
if (!model->IsSpendable(dest))
{
ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
// confirmation dialog
QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm custom change address"),
tr("The address you selected for change is not part of "
"this wallet. Any or all funds in your wallet may be "
"sent to this address. Are you sure?"),
QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
if (btnRetVal == QMessageBox::Yes)
CoinControlDialog::coinControl->destChange = dest;
else
{
ui->lineEditCoinControlChange->setText("");
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
ui->labelCoinControlChangeLabel->setText("");
}
}
else // Known change address
else
{
// Known change address
ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
// Query label
@@ -775,7 +792,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text)
else
ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
CoinControlDialog::coinControl->destChange = addr.Get();
CoinControlDialog::coinControl->destChange = dest;
}
}
}

View File

@@ -7,6 +7,7 @@
#include "addressbookpage.h"
#include "addresstablemodel.h"
#include "config.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "platformstyle.h"
@@ -29,6 +30,11 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *platformStyle, QWidget *pare
ui->deleteButton_is->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
ui->deleteButton_s->setIcon(platformStyle->SingleColorIcon(":/icons/remove"));
ui->messageTextLabel->setToolTip(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::bitcoinURIScheme(GetConfig())));
setCurrentWidget(ui->SendCoins);
if (platformStyle->getUseExtraSpacing())

View File

@@ -10,7 +10,7 @@
#include "platformstyle.h"
#include "walletmodel.h"
#include "base58.h"
#include "dstencode.h"
#include "init.h"
#include "main.h" // For strMessageMagic
#include "wallet/wallet.h"
@@ -117,15 +117,15 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
/* Clear old signature to ensure users don't get confused on error with an old signature displayed */
ui->signatureOut_SM->clear();
CBitcoinAddress addr(ui->addressIn_SM->text().toStdString());
if (!addr.IsValid())
CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString());
if (!IsValidDestination(destination))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
CKeyID keyID;
if (!addr.GetKeyID(keyID))
const CKeyID *keyID = boost::get<CKeyID>(&destination);
if (!keyID)
{
ui->addressIn_SM->setValid(false);
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
@@ -142,7 +142,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
if (!pwalletMain->GetKey(keyID, key))
if (!pwalletMain->GetKey(*keyID, key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
@@ -197,15 +197,14 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked()
void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
{
CBitcoinAddress addr(ui->addressIn_VM->text().toStdString());
if (!addr.IsValid())
CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString());
if (!IsValidDestination(destination))
{
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again."));
return;
}
CKeyID keyID;
if (!addr.GetKeyID(keyID))
if (!boost::get<CKeyID>(&destination))
{
ui->addressIn_VM->setValid(false);
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
@@ -237,7 +236,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked()
return;
}
if (!(CBitcoinAddress(pubkey.GetID()) == addr))
if (!(CTxDestination(pubkey.GetID()) == destination))
{
ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>"));

View File

@@ -0,0 +1,36 @@
// 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 "qt/test/bitcoinaddressvalidatortests.h"
#include "chainparams.h"
#include "qt/bitcoinaddressvalidator.h"
#include <QValidator>
void BitcoinAddressValidatorTests::inputTests() {
const std::string prefix = Params(CBaseChainParams::MAIN).CashAddrPrefix();
BitcoinAddressEntryValidator v(prefix, nullptr);
int unused = 0;
QString in;
// invalid base58 because of I, invalid cashaddr
in = "BIIC";
QVERIFY(QValidator::Invalid == v.validate(in, unused));
// invalid base58, invalid cashaddr
in = "BITCOINCASHH";
QVERIFY(QValidator::Invalid == v.validate(in, unused));
// invalid base58 because of I, but could be a cashaddr prefix
in = "BITC";
QVERIFY(QValidator::Acceptable == v.validate(in, unused));
// invalid base58, valid cashaddr
in = "BITCOINCASH:QP";
QVERIFY(QValidator::Acceptable == v.validate(in, unused));
// valid base58, invalid cash
in = "BBBBBBBBBBBBBB";
QVERIFY(QValidator::Acceptable == v.validate(in, unused));
}

View File

@@ -0,0 +1,18 @@
// 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.
#ifndef BITCOIN_QT_TEST_BITCOINADDRESSVALIDATORTESTS_H
#define BITCOIN_QT_TEST_BITCOINADDRESSVALIDATORTESTS_H
#include <QObject>
#include <QTest>
class BitcoinAddressValidatorTests : public QObject {
Q_OBJECT
private Q_SLOTS:
void inputTests();
};
#endif

View File

@@ -0,0 +1,62 @@
// 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 "guiutiltests.h"
#include "chainparams.h"
#include "config.h"
#include "dstencode.h"
#include "guiutil.h"
#include "receiverequestdialog.h"
namespace {
class UtilCfgDummy : public DummyConfig {
public:
UtilCfgDummy() : useCashAddr(false) {}
void SetCashAddrEncoding(bool b) override { useCashAddr = b; }
bool UseCashAddrEncoding() const override { return useCashAddr; }
const CChainParams &GetChainParams() const override {
return Params(CBaseChainParams::MAIN);
}
private:
bool useCashAddr;
};
} // anon ns
void GUIUtilTests::dummyAddressTest() {
CChainParams &params = Params(CBaseChainParams::MAIN);
UtilCfgDummy cfg;
std::string dummyaddr;
cfg.SetCashAddrEncoding(false);
dummyaddr = GUIUtil::DummyAddress(params, cfg);
QVERIFY(!IsValidDestinationString(dummyaddr, params));
QVERIFY(!dummyaddr.empty());
cfg.SetCashAddrEncoding(true);
dummyaddr = GUIUtil::DummyAddress(params, cfg);
QVERIFY(!IsValidDestinationString(dummyaddr, params));
QVERIFY(!dummyaddr.empty());
}
void GUIUtilTests::toCurrentEncodingTest() {
UtilCfgDummy config;
// garbage in, garbage out
QVERIFY(ToCurrentEncoding("garbage", config) == "garbage");
QString cashaddr_pubkey =
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a";
QString base58_pubkey = "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu";
config.SetCashAddrEncoding(true);
QVERIFY(ToCurrentEncoding(cashaddr_pubkey, config) == cashaddr_pubkey);
QVERIFY(ToCurrentEncoding(base58_pubkey, config) == cashaddr_pubkey);
config.SetCashAddrEncoding(false);
QVERIFY(ToCurrentEncoding(cashaddr_pubkey, config) == base58_pubkey);
QVERIFY(ToCurrentEncoding(base58_pubkey, config) == base58_pubkey);
}

View File

@@ -0,0 +1,19 @@
// 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.
#ifndef BITCOIN_QT_TEST_GUIUTILTESTS_H
#define BITCOIN_QT_TEST_GUIUTILTESTS_H
#include <QObject>
#include <QTest>
class GUIUtilTests : public QObject {
Q_OBJECT
private Q_SLOTS:
void dummyAddressTest();
void toCurrentEncodingTest();
};
#endif // BITCOIN_QT_TEST_GUIUTILTESTS_H

View File

@@ -6,6 +6,8 @@
#include "config/bitcoin-config.h"
#endif
#include "bitcoinaddressvalidatortests.h"
#include "guiutiltests.h"
#include "util.h"
#include "uritests.h"
@@ -48,6 +50,10 @@ int main(int argc, char *argv[])
if (QTest::qExec(&test2) != 0)
fInvalid = true;
#endif
GUIUtilTests test5;
if (QTest::qExec(&test5) != 0) fInvalid = true;
BitcoinAddressValidatorTests test6;
if (QTest::qExec(&test6) != 0) fInvalid = true;
return fInvalid;
}

View File

@@ -1,66 +1,219 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Copyright (c) 2015-2017 The Bitcoin Unlimited 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"
#include <QUrl>
void URITests::uriTests()
void URITests::uriTestsBase58()
{
SendCoinsRecipient rv;
QString scheme =
QString::fromStdString(Params(CBaseChainParams::MAIN).CashAddrPrefix());
QUrl uri;
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-dontexist="));
QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-dontexist="));
QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv));
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?dontexist="));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?dontexist="));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"));
QVERIFY(rv.label == QString());
QVERIFY(rv.amount == 0);
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Wikipedia Example Address"));
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("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=0.001"));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=0.001"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"));
QVERIFY(rv.label == QString());
QVERIFY(rv.amount == 100000);
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1.001"));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1.001"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"));
QVERIFY(rv.label == QString());
QVERIFY(rv.amount == 100100000);
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&label=Wikipedia Example"));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=100&label=Wikipedia Example"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"));
QVERIFY(rv.amount == 10000000000LL);
QVERIFY(rv.label == QString("Wikipedia Example"));
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"));
QVERIFY(rv.label == QString());
QVERIFY(GUIUtil::parseBitcoinURI("bitcoin://175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=Wikipedia Example Address", &rv));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, "blackcoin://175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?"
"message=Wikipedia Example Address",
&rv));
QVERIFY(rv.address == QString("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"));
QVERIFY(rv.label == QString());
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-message=Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example"));
QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000&label=Wikipedia Example"));
QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv));
uri.setUrl(QString("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000.0&label=Wikipedia Example"));
QVERIFY(!GUIUtil::parseBitcoinURI(uri, &rv));
uri.setUrl(QString("blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=1,000.0&label=Wikipedia Example"));
QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv));
}
void URITests::uriTestsCashAddr() {
SendCoinsRecipient rv;
QUrl uri;
QString scheme =
QString::fromStdString(Params(CBaseChainParams::MAIN).CashAddrPrefix());
uri.setUrl(QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?"
"req-dontexist="));
QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv));
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?dontexist="));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.label == QString());
QVERIFY(rv.amount == 0);
uri.setUrl(
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?label="
"Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.label == QString("Wikipedia Example Address"));
QVERIFY(rv.amount == 0);
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=0.001"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.label == QString());
QVERIFY(rv.amount == 100000);
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1.001"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.label == QString());
QVERIFY(rv.amount == 100100000);
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=100&"
"label=Wikipedia Example"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.amount == 10000000000LL);
QVERIFY(rv.label == QString("Wikipedia Example"));
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?message="
"Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.label == QString());
QVERIFY(GUIUtil::parseBitcoinURI(
scheme, "blackcoin://"
"qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?"
"message=Wikipedia Example Address",
&rv));
QVERIFY(rv.address ==
QString("blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"));
QVERIFY(rv.label == QString());
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?req-message="
"Wikipedia Example Address"));
QVERIFY(GUIUtil::parseBitcoinURI(scheme, uri, &rv));
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1,"
"000&label=Wikipedia Example"));
QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv));
uri.setUrl(QString(
"blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1,"
"000.0&label=Wikipedia Example"));
QVERIFY(!GUIUtil::parseBitcoinURI(scheme, uri, &rv));
}
namespace {
class UriTestConfig : public DummyConfig {
public:
UriTestConfig(bool useCashAddr)
: useCashAddr(useCashAddr), net(CBaseChainParams::MAIN) {}
bool UseCashAddrEncoding() const override { return useCashAddr; }
const CChainParams &GetChainParams() const override { return Params(net); }
void SetChainParams(const std::string &n) { net = n; }
private:
bool useCashAddr;
std::string net;
};
} // anon ns
void URITests::uriTestFormatURI() {
{
UriTestConfig cfg(true);
SendCoinsRecipient r;
r.address = "blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a";
r.message = "test";
QString uri = GUIUtil::formatBitcoinURI(cfg, r);
QVERIFY(uri == "blackcoin:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?"
"message=test");
}
{
UriTestConfig cfg(false);
SendCoinsRecipient r;
r.address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W";
r.message = "test";
QString uri = GUIUtil::formatBitcoinURI(cfg, r);
QVERIFY(uri ==
"blackcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?message=test");
}
}
void URITests::uriTestScheme() {
{
// cashaddr - scheme depends on selected chain params
UriTestConfig config(true);
config.SetChainParams(CBaseChainParams::MAIN);
QVERIFY("blackcoin" == GUIUtil::bitcoinURIScheme(config));
config.SetChainParams(CBaseChainParams::TESTNET);
QVERIFY("blktest" == GUIUtil::bitcoinURIScheme(config));
config.SetChainParams(CBaseChainParams::REGTEST);
QVERIFY("blkreg" == GUIUtil::bitcoinURIScheme(config));
}
{
// legacy - scheme is "blackcoin" regardless of chain params
UriTestConfig config(false);
config.SetChainParams(CBaseChainParams::MAIN);
QVERIFY("blackcoin" == GUIUtil::bitcoinURIScheme(config));
config.SetChainParams(CBaseChainParams::TESTNET);
QVERIFY("blackcoin" == GUIUtil::bitcoinURIScheme(config));
config.SetChainParams(CBaseChainParams::REGTEST);
QVERIFY("blackcoin" == GUIUtil::bitcoinURIScheme(config));
}
}

View File

@@ -13,7 +13,10 @@ class URITests : public QObject
Q_OBJECT
private Q_SLOTS:
void uriTests();
void uriTestsBase58();
void uriTestsCashAddr();
void uriTestFormatURI();
void uriTestScheme();
};
#endif // BITCOIN_QT_TEST_URITESTS_H

View File

@@ -8,9 +8,8 @@
#include "guiutil.h"
#include "paymentserver.h"
#include "transactionrecord.h"
#include "base58.h"
#include "consensus/consensus.h"
#include "dstencode.h"
#include "main.h"
#include "script/script.h"
#include "timedata.h"
@@ -91,9 +90,9 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
if (nNet > 0)
{
// Credit
if (CBitcoinAddress(rec->address).IsValid())
CTxDestination address = DecodeDestination(rec->address);
if (IsValidDestination(address))
{
CTxDestination address = CBitcoinAddress(rec->address).Get();
if (wallet->mapAddressBook.count(address))
{
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
@@ -118,7 +117,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Online transaction
std::string strAddress = wtx.mapValue["to"];
strHTML += "<b>" + tr("To") + ":</b> ";
CTxDestination dest = CBitcoinAddress(strAddress).Get();
CTxDestination dest = DecodeDestination(strAddress);
if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " ";
strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>";
@@ -189,7 +188,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("To") + ":</b> ";
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString());
strHTML += GUIUtil::HtmlEscape(EncodeDestination(address));
if(toSelf == ISMINE_SPENDABLE)
strHTML += " (own address)";
else if(toSelf & ISMINE_WATCH_ONLY)
@@ -243,10 +242,12 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("Transaction ID") + ":</b> " + rec->getTxID() + "<br>";
strHTML += "<b>" + tr("Output index") + ":</b> " + QString::number(rec->getOutputIndex()) + "<br>";
// Message from normal bitcoin:URI (bitcoin:123...?message=example)
Q_FOREACH (const PAIRTYPE(std::string, std::string)& r, wtx.vOrderForm)
// Message from normal blackcoin:URI (blackcoin:123...?message=example)
for (const std::pair<std::string, std::string> &r : wtx.vOrderForm)
{
if (r.first == "Message")
strHTML += "<br><b>" + tr("Message") + ":</b><br>" + GUIUtil::HtmlEscape(r.second, true) + "<br>";
}
//
// PaymentRequest info:
@@ -304,7 +305,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
{
if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty())
strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " ";
strHTML += QString::fromStdString(CBitcoinAddress(address).ToString());
strHTML += QString::fromStdString(EncodeDestination(address));
}
strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue);
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";

View File

@@ -5,6 +5,7 @@
#include "transactionrecord.h"
#include "base58.h"
#include "dstencode.h"
#include "consensus/consensus.h"
#include "main.h"
#include "timedata.h"
@@ -61,7 +62,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
// Received by Bitcoin Address
sub.type = TransactionRecord::RecvWithAddress;
sub.address = CBitcoinAddress(address).ToString();
sub.address = EncodeDestination(address);
}
else
{
@@ -140,7 +141,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
{
// Sent to Bitcoin Address
sub.type = TransactionRecord::SendToAddress;
sub.address = CBitcoinAddress(address).ToString();
sub.address = EncodeDestination(address);
}
else
{

View File

@@ -12,10 +12,11 @@
#include <QHBoxLayout>
#include <QLabel>
WalletFrame::WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui) :
WalletFrame::WalletFrame(const PlatformStyle *platformStyle, const Config *cfg, BitcoinGUI *_gui) :
QFrame(_gui),
gui(_gui),
platformStyle(platformStyle)
platformStyle(platformStyle),
cfg(cfg)
{
// Leave HBox hook for adding a list view later
QHBoxLayout *walletFrameLayout = new QHBoxLayout(this);
@@ -43,7 +44,7 @@ bool WalletFrame::addWallet(const QString& name, WalletModel *walletModel)
if (!gui || !clientModel || !walletModel || mapWalletViews.count(name) > 0)
return false;
WalletView *walletView = new WalletView(platformStyle, this);
WalletView *walletView = new WalletView(platformStyle, cfg, this);
walletView->setBitcoinGUI(gui);
walletView->setClientModel(clientModel);
walletView->setWalletModel(walletModel);

View File

@@ -14,6 +14,7 @@ class PlatformStyle;
class SendCoinsRecipient;
class WalletModel;
class WalletView;
class Config;
QT_BEGIN_NAMESPACE
class QStackedWidget;
@@ -24,7 +25,7 @@ class WalletFrame : public QFrame
Q_OBJECT
public:
explicit WalletFrame(const PlatformStyle *platformStyle, BitcoinGUI *_gui = 0);
explicit WalletFrame(const PlatformStyle *platformStyle, const Config *cfg, BitcoinGUI *_gui = 0);
~WalletFrame();
void setClientModel(ClientModel *clientModel);
@@ -47,6 +48,7 @@ private:
bool bOutOfSync;
const PlatformStyle *platformStyle;
const Config *cfg;
WalletView *currentWalletView();

View File

@@ -10,8 +10,7 @@
#include "paymentserver.h"
#include "recentrequeststablemodel.h"
#include "transactiontablemodel.h"
#include "base58.h"
#include "dstencode.h"
#include "keystore.h"
#include "main.h"
#include "sync.h"
@@ -183,11 +182,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly)
Q_EMIT notifyWatchonlyChanged(fHaveWatchonly);
}
bool WalletModel::validateAddress(const QString &address)
{
CBitcoinAddress addressParsed(address.toStdString());
return addressParsed.IsValid();
}
bool WalletModel::validateAddress(const QString &address) { return IsValidDestinationString(address.toStdString()); }
WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl *coinControl)
{
@@ -244,14 +239,14 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
setAddress.insert(rcp.address);
++nAddresses;
CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get());
CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString()));
CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount};
vecSend.push_back(recipient);
total += rcp.amount;
}
}
if(setAddress.size() != nAddresses)
if (setAddress.size() != nAddresses)
{
return DuplicateAddress;
}
@@ -323,7 +318,13 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
rcp.paymentRequest.SerializeToString(&value);
newTx->vOrderForm.push_back(make_pair(key, value));
}
else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example)
else if (!rcp.message.isEmpty())
{
// Message from normal blackcoin:URI
// (blackcoin:123...?message=example)
newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString()));
}
else if (!rcp.message.isEmpty()) // Message from normal blackcoin:URI (blackcoin:123...?message=example)
newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString()));
}
@@ -345,7 +346,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran
if (!rcp.paymentRequest.IsInitialized())
{
std::string strAddress = rcp.address.toStdString();
CTxDestination dest = CBitcoinAddress(strAddress).Get();
CTxDestination dest = DecodeDestination(strAddress);
std::string strLabel = rcp.label.toStdString();
{
LOCK(wallet->cs_wallet);
@@ -461,7 +462,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet,
const CTxDestination &address, const std::string &label, bool isMine,
const std::string &purpose, ChangeType status)
{
QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString());
QString strAddress = QString::fromStdString(EncodeDestination(address));
QString strLabel = QString::fromStdString(label);
QString strPurpose = QString::fromStdString(purpose);
@@ -566,11 +567,7 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
return wallet->GetPubKey(address, vchPubKeyOut);
}
bool WalletModel::havePrivKey(const CKeyID &address) const
{
return wallet->HaveKey(address);
}
bool WalletModel::IsSpendable(const CTxDestination &dest) const { return wallet->IsMine(dest) & ISMINE_SPENDABLE; }
// returns a list of COutputs from COutPoints
void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
{
@@ -625,7 +622,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
CTxDestination address;
if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
continue;
mapCoins[QString::fromStdString(CBitcoinAddress(address).ToString())].push_back(out);
mapCoins[QString::fromStdString(EncodeDestination(address))].push_back(out);
}
}
@@ -664,7 +661,7 @@ void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests
bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest)
{
CTxDestination dest = CBitcoinAddress(sAddress).Get();
CTxDestination dest = DecodeDestination(sAddress);
std::stringstream ss;
ss << nId;

View File

@@ -187,7 +187,7 @@ public:
UnlockContext requestUnlock();
bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const;
bool havePrivKey(const CKeyID &address) const;
bool IsSpendable(const CTxDestination &dest) const;
void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs);
bool isSpent(const COutPoint& outpoint) const;
void listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const;

View File

@@ -29,11 +29,11 @@
#include <QPushButton>
#include <QVBoxLayout>
WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
WalletView::WalletView(const PlatformStyle *_platformStyle, const Config *cfg, QWidget *parent):
QStackedWidget(parent),
clientModel(0),
walletModel(0),
platformStyle(platformStyle)
platformStyle(_platformStyle)
{
// Create tabs
overviewPage = new OverviewPage(platformStyle);
@@ -53,7 +53,7 @@ WalletView::WalletView(const PlatformStyle *platformStyle, QWidget *parent):
vbox->addLayout(hbox_buttons);
transactionsPage->setLayout(vbox);
receiveCoinsPage = new ReceiveCoinsDialog(platformStyle);
receiveCoinsPage = new ReceiveCoinsDialog(platformStyle, cfg);
sendCoinsPage = new SendCoinsDialog(platformStyle);
usedSendingAddressesPage = new AddressBookPage(platformStyle, AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);

View File

@@ -19,6 +19,7 @@ class SendCoinsRecipient;
class TransactionView;
class WalletModel;
class AddressBookPage;
class Config;
QT_BEGIN_NAMESPACE
class QModelIndex;
@@ -36,7 +37,7 @@ class WalletView : public QStackedWidget
Q_OBJECT
public:
explicit WalletView(const PlatformStyle *platformStyle, QWidget *parent);
explicit WalletView(const PlatformStyle *platformStyle, const Config *cfg, QWidget *parent);
~WalletView();
void setBitcoinGUI(BitcoinGUI *gui);