mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Merge pull request #4570 from kittywhiskers/miscports_again_again
merge bitcoin#15928...#16984: backports
This commit is contained in:
commit
a7f5379e19
@ -84,6 +84,7 @@ QT_MOC_CPP = \
|
|||||||
qt/moc_peertablemodel.cpp \
|
qt/moc_peertablemodel.cpp \
|
||||||
qt/moc_paymentserver.cpp \
|
qt/moc_paymentserver.cpp \
|
||||||
qt/moc_qrdialog.cpp \
|
qt/moc_qrdialog.cpp \
|
||||||
|
qt/moc_qrimagewidget.cpp \
|
||||||
qt/moc_qvalidatedlineedit.cpp \
|
qt/moc_qvalidatedlineedit.cpp \
|
||||||
qt/moc_qvaluecombobox.cpp \
|
qt/moc_qvaluecombobox.cpp \
|
||||||
qt/moc_receivecoinsdialog.cpp \
|
qt/moc_receivecoinsdialog.cpp \
|
||||||
@ -167,6 +168,7 @@ BITCOIN_QT_H = \
|
|||||||
qt/paymentserver.h \
|
qt/paymentserver.h \
|
||||||
qt/peertablemodel.h \
|
qt/peertablemodel.h \
|
||||||
qt/qrdialog.h \
|
qt/qrdialog.h \
|
||||||
|
qt/qrimagewidget.h \
|
||||||
qt/qvalidatedlineedit.h \
|
qt/qvalidatedlineedit.h \
|
||||||
qt/qvaluecombobox.h \
|
qt/qvaluecombobox.h \
|
||||||
qt/receivecoinsdialog.h \
|
qt/receivecoinsdialog.h \
|
||||||
@ -266,6 +268,7 @@ BITCOIN_QT_WALLET_CPP = \
|
|||||||
qt/overviewpage.cpp \
|
qt/overviewpage.cpp \
|
||||||
qt/paymentserver.cpp \
|
qt/paymentserver.cpp \
|
||||||
qt/qrdialog.cpp \
|
qt/qrdialog.cpp \
|
||||||
|
qt/qrimagewidget.cpp \
|
||||||
qt/receivecoinsdialog.cpp \
|
qt/receivecoinsdialog.cpp \
|
||||||
qt/receiverequestdialog.cpp \
|
qt/receiverequestdialog.cpp \
|
||||||
qt/recentrequeststablemodel.cpp \
|
qt/recentrequeststablemodel.cpp \
|
||||||
|
@ -152,6 +152,7 @@ public:
|
|||||||
consensus.DIP0003EnforcementHeight = 1047200;
|
consensus.DIP0003EnforcementHeight = 1047200;
|
||||||
consensus.DIP0003EnforcementHash = uint256S("000000000000002d1734087b4c5afc3133e4e1c3e1a89218f62bcd9bb3d17f81");
|
consensus.DIP0003EnforcementHash = uint256S("000000000000002d1734087b4c5afc3133e4e1c3e1a89218f62bcd9bb3d17f81");
|
||||||
consensus.DIP0008Height = 1088640; // 00000000000000112e41e4b3afda8b233b8cc07c532d2eac5de097b68358c43e
|
consensus.DIP0008Height = 1088640; // 00000000000000112e41e4b3afda8b233b8cc07c532d2eac5de097b68358c43e
|
||||||
|
consensus.MinBIP9WarningHeight = 1090656; // dip8 activation height + miner confirmation window
|
||||||
consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20
|
consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20
|
||||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||||
@ -371,6 +372,7 @@ public:
|
|||||||
consensus.DIP0003EnforcementHeight = 7300;
|
consensus.DIP0003EnforcementHeight = 7300;
|
||||||
consensus.DIP0003EnforcementHash = uint256S("00000055ebc0e974ba3a3fb785c5ad4365a39637d4df168169ee80d313612f8f");
|
consensus.DIP0003EnforcementHash = uint256S("00000055ebc0e974ba3a3fb785c5ad4365a39637d4df168169ee80d313612f8f");
|
||||||
consensus.DIP0008Height = 78800; // 000000000e9329d964d80e7dab2e704b43b6bd2b91fea1e9315d38932e55fb55
|
consensus.DIP0008Height = 78800; // 000000000e9329d964d80e7dab2e704b43b6bd2b91fea1e9315d38932e55fb55
|
||||||
|
consensus.MinBIP9WarningHeight = 80816; // dip8 activation height + miner confirmation window
|
||||||
consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20
|
consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20
|
||||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||||
@ -563,6 +565,7 @@ public:
|
|||||||
consensus.DIP0003EnforcementHeight = 2; // DIP0003 activated immediately on devnet
|
consensus.DIP0003EnforcementHeight = 2; // DIP0003 activated immediately on devnet
|
||||||
consensus.DIP0003EnforcementHash = uint256();
|
consensus.DIP0003EnforcementHash = uint256();
|
||||||
consensus.DIP0008Height = 2; // DIP0008 activated immediately on devnet
|
consensus.DIP0008Height = 2; // DIP0008 activated immediately on devnet
|
||||||
|
consensus.MinBIP9WarningHeight = 2018; // dip8 activation height + miner confirmation window
|
||||||
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1
|
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1
|
||||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||||
@ -798,6 +801,7 @@ public:
|
|||||||
consensus.DIP0003EnforcementHeight = 500;
|
consensus.DIP0003EnforcementHeight = 500;
|
||||||
consensus.DIP0003EnforcementHash = uint256();
|
consensus.DIP0003EnforcementHash = uint256();
|
||||||
consensus.DIP0008Height = 432;
|
consensus.DIP0008Height = 432;
|
||||||
|
consensus.MinBIP9WarningHeight = 0;
|
||||||
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1
|
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1
|
||||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||||
|
@ -85,6 +85,9 @@ struct Params {
|
|||||||
uint256 DIP0003EnforcementHash;
|
uint256 DIP0003EnforcementHash;
|
||||||
/** Block height at which DIP0008 becomes active */
|
/** Block height at which DIP0008 becomes active */
|
||||||
int DIP0008Height;
|
int DIP0008Height;
|
||||||
|
/** Don't warn about unknown BIP 9 activations below this height.
|
||||||
|
* This prevents us from warning about the CSV and DIP activations. */
|
||||||
|
int MinBIP9WarningHeight;
|
||||||
/**
|
/**
|
||||||
* Minimum blocks including miner confirmation of the total of nMinerConfirmationWindow blocks in a retargeting period,
|
* Minimum blocks including miner confirmation of the total of nMinerConfirmationWindow blocks in a retargeting period,
|
||||||
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
|
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
|
||||||
|
@ -1469,7 +1469,7 @@ bool AppInitParameterInteraction()
|
|||||||
if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
|
if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
|
||||||
return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
|
return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")));
|
||||||
}
|
}
|
||||||
// High fee check is done afterward in WalletParameterInteraction()
|
// High fee check is done afterward in CWallet::CreateWalletFromFile()
|
||||||
::minRelayTxFee = CFeeRate(n);
|
::minRelayTxFee = CFeeRate(n);
|
||||||
} else if (incrementalRelayFee > ::minRelayTxFee) {
|
} else if (incrementalRelayFee > ::minRelayTxFee) {
|
||||||
// Allow only setting incrementalRelayFee to control both
|
// Allow only setting incrementalRelayFee to control both
|
||||||
|
@ -117,7 +117,7 @@ public:
|
|||||||
ensurePolished();
|
ensurePolished();
|
||||||
const QFontMetrics fm(fontMetrics());
|
const QFontMetrics fm(fontMetrics());
|
||||||
int h = 0;
|
int h = 0;
|
||||||
int w = fm.width(BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
|
int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
|
||||||
w += 2; // cursor blinking space
|
w += 2; // cursor blinking space
|
||||||
w += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css
|
w += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css
|
||||||
return QSize(w, h);
|
return QSize(w, h);
|
||||||
|
@ -46,7 +46,6 @@
|
|||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDesktopWidget>
|
|
||||||
#include <QDragEnterEvent>
|
#include <QDragEnterEvent>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
@ -54,6 +53,7 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <QScreen>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
@ -89,7 +89,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
|
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
|
||||||
// Restore failed (perhaps missing setting), center the window
|
// Restore failed (perhaps missing setting), center the window
|
||||||
move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
|
move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
@ -489,6 +489,8 @@ void BitcoinGUI::createActions()
|
|||||||
for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
|
for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
|
||||||
const std::string& path = i.first;
|
const std::string& path = i.first;
|
||||||
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
|
QString name = path.empty() ? QString("["+tr("default wallet")+"]") : QString::fromStdString(path);
|
||||||
|
// Menu items remove single &. Single & are shown when && is in the string, but only the first occurrence. So replace only the first & with &&
|
||||||
|
name.replace(name.indexOf(QChar('&')), 1, QString("&&"));
|
||||||
QAction* action = m_open_wallet_menu->addAction(name);
|
QAction* action = m_open_wallet_menu->addAction(name);
|
||||||
|
|
||||||
if (i.second) {
|
if (i.second) {
|
||||||
@ -1324,7 +1326,7 @@ void BitcoinGUI::updateWidth()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QFontMetrics fm(button->font());
|
QFontMetrics fm(button->font());
|
||||||
nWidthWidestButton = std::max<int>(nWidthWidestButton, fm.width(button->text()));
|
nWidthWidestButton = std::max<int>(nWidthWidestButton, GUIUtil::TextWidth(fm, button->text()));
|
||||||
++nButtonsVisible;
|
++nButtonsVisible;
|
||||||
}
|
}
|
||||||
// Add 30 per button as padding and use minimum 980 which is the minimum required to show all tab's contents
|
// Add 30 per button as padding and use minimum 980 which is the minimum required to show all tab's contents
|
||||||
@ -1990,7 +1992,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl() :
|
|||||||
const QFontMetrics fm(GUIUtil::getFontNormal());
|
const QFontMetrics fm(GUIUtil::getFontNormal());
|
||||||
for (const BitcoinUnits::Unit unit : units)
|
for (const BitcoinUnits::Unit unit : units)
|
||||||
{
|
{
|
||||||
max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit)));
|
max_width = qMax(max_width, GUIUtil::TextWidth(fm, BitcoinUnits::name(unit)));
|
||||||
}
|
}
|
||||||
setMinimumSize(max_width, 0);
|
setMinimumSize(max_width, 0);
|
||||||
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<customwidget>
|
<customwidget>
|
||||||
<class>QRImageWidget</class>
|
<class>QRImageWidget</class>
|
||||||
<extends>QLabel</extends>
|
<extends>QLabel</extends>
|
||||||
<header>qt/receiverequestdialog.h</header>
|
<header>qt/qrimagewidget.h</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
|
@ -32,12 +32,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
|
|||||||
*/
|
*/
|
||||||
static const int TOOLTIP_WRAP_THRESHOLD = 80;
|
static const int TOOLTIP_WRAP_THRESHOLD = 80;
|
||||||
|
|
||||||
/* Maximum allowed URI length */
|
|
||||||
static const int MAX_URI_LENGTH = 255;
|
|
||||||
|
|
||||||
/* QRCodeDialog -- size of exported QR Code image */
|
|
||||||
#define QR_IMAGE_SIZE 300
|
|
||||||
|
|
||||||
/* Number of frames in spinner animation */
|
/* Number of frames in spinner animation */
|
||||||
#define SPINNER_FRAMES 90
|
#define SPINNER_FRAMES 90
|
||||||
|
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QDesktopWidget>
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QDoubleValidator>
|
#include <QDoubleValidator>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@ -1832,7 +1831,7 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m
|
|||||||
while(font_size >= minPointSize) {
|
while(font_size >= minPointSize) {
|
||||||
font.setPointSizeF(font_size);
|
font.setPointSizeF(font_size);
|
||||||
QFontMetrics fm(font);
|
QFontMetrics fm(font);
|
||||||
if (fm.width(text) < width) {
|
if (TextWidth(fm, text) < width) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
font_size -= 0.5;
|
font_size -= 0.5;
|
||||||
@ -1864,7 +1863,7 @@ void PolishProgressDialog(QProgressDialog* dialog)
|
|||||||
{
|
{
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
|
// Workaround for macOS-only Qt bug; see: QTBUG-65750, QTBUG-70357.
|
||||||
const int margin = dialog->fontMetrics().width("X");
|
const int margin = TextWidth(dialog->fontMetrics(), ("X"));
|
||||||
dialog->resize(dialog->width() + 2 * margin, dialog->height());
|
dialog->resize(dialog->width() + 2 * margin, dialog->height());
|
||||||
dialog->show();
|
dialog->show();
|
||||||
#else
|
#else
|
||||||
@ -1872,4 +1871,13 @@ void PolishProgressDialog(QProgressDialog* dialog)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TextWidth(const QFontMetrics& fm, const QString& text)
|
||||||
|
{
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
|
||||||
|
return fm.horizontalAdvance(text);
|
||||||
|
#else
|
||||||
|
return fm.width(text);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace GUIUtil
|
} // namespace GUIUtil
|
||||||
|
@ -467,6 +467,14 @@ namespace GUIUtil
|
|||||||
|
|
||||||
// Fix known bugs in QProgressDialog class.
|
// Fix known bugs in QProgressDialog class.
|
||||||
void PolishProgressDialog(QProgressDialog* dialog);
|
void PolishProgressDialog(QProgressDialog* dialog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the distance in pixels appropriate for drawing a subsequent character after text.
|
||||||
|
*
|
||||||
|
* In Qt 5.12 and before the QFontMetrics::width() is used and it is deprecated since Qt 13.0.
|
||||||
|
* In Qt 5.11 the QFontMetrics::horizontalAdvance() was introduced.
|
||||||
|
*/
|
||||||
|
int TextWidth(const QFontMetrics& fm, const QString& text);
|
||||||
} // namespace GUIUtil
|
} // namespace GUIUtil
|
||||||
|
|
||||||
#endif // BITCOIN_QT_GUIUTIL_H
|
#endif // BITCOIN_QT_GUIUTIL_H
|
||||||
|
@ -497,7 +497,7 @@ void OptionsDialog::updateWidth()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QFontMetrics fm(button->font());
|
QFontMetrics fm(button->font());
|
||||||
nWidthWidestButton = std::max<int>(nWidthWidestButton, fm.width(button->text()));
|
nWidthWidestButton = std::max<int>(nWidthWidestButton, GUIUtil::TextWidth(fm, button->text()));
|
||||||
++nButtonsVisible;
|
++nButtonsVisible;
|
||||||
}
|
}
|
||||||
// Add 10 per button as padding and use minimum 585 which is what we used in css before
|
// Add 10 per button as padding and use minimum 585 which is what we used in css before
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <qt/bitcoinunits.h>
|
#include <qt/bitcoinunits.h>
|
||||||
#include <qt/guiconstants.h>
|
#include <qt/guiconstants.h>
|
||||||
#include <qt/guiutil.h>
|
#include <qt/guiutil.h>
|
||||||
|
#include <qt/qrimagewidget.h>
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QDrag>
|
#include <QDrag>
|
||||||
|
152
src/qt/qrimagewidget.cpp
Normal file
152
src/qt/qrimagewidget.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Copyright (c) 2011-2018 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <qt/qrimagewidget.h>
|
||||||
|
|
||||||
|
#include <qt/guiutil.h>
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QDrag>
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#if defined(HAVE_CONFIG_H)
|
||||||
|
#include <config/dash-config.h> /* for USE_QRCODE */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_QRCODE
|
||||||
|
#include <qrencode.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QRImageWidget::QRImageWidget(QWidget *parent):
|
||||||
|
QLabel(parent), contextMenu(nullptr)
|
||||||
|
{
|
||||||
|
contextMenu = new QMenu(this);
|
||||||
|
QAction *saveImageAction = new QAction(tr("&Save Image..."), this);
|
||||||
|
connect(saveImageAction, &QAction::triggered, this, &QRImageWidget::saveImage);
|
||||||
|
contextMenu->addAction(saveImageAction);
|
||||||
|
QAction *copyImageAction = new QAction(tr("&Copy Image"), this);
|
||||||
|
connect(copyImageAction, &QAction::triggered, this, &QRImageWidget::copyImage);
|
||||||
|
contextMenu->addAction(copyImageAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QRImageWidget::setQR(const QString& data, const QString& text)
|
||||||
|
{
|
||||||
|
#ifdef USE_QRCODE
|
||||||
|
setText("");
|
||||||
|
if (data.isEmpty()) return false;
|
||||||
|
|
||||||
|
// limit length
|
||||||
|
if (data.length() > MAX_URI_LENGTH) {
|
||||||
|
setText(tr("Resulting URI too long, try to reduce the text for label / message."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRcode *code = QRcode_encodeString(data.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
setText(tr("Error encoding URI into QR Code."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage qrImage = QImage(code->width + 6, code->width + 6, QImage::Format_RGB32);
|
||||||
|
qrImage.fill(GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BACKGROUND_WIDGET));
|
||||||
|
unsigned char *p = code->data;
|
||||||
|
for (int y = 0; y < code->width; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < code->width; x++)
|
||||||
|
{
|
||||||
|
qrImage.setPixel(x + 3, y + 3, ((*p & 1) ? GUIUtil::getThemedQColor(GUIUtil::ThemedColor::QR_PIXEL).rgb() : GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BACKGROUND_WIDGET).rgb()));
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QRcode_free(code);
|
||||||
|
|
||||||
|
// Create the image with respect to the device pixel ratio
|
||||||
|
int qrAddrImageWidth = QR_IMAGE_SIZE;
|
||||||
|
int qrAddrImageHeight = QR_IMAGE_SIZE + 20;
|
||||||
|
qreal scale = qApp->devicePixelRatio();
|
||||||
|
QImage qrAddrImage = QImage(qrAddrImageWidth * scale, qrAddrImageHeight * scale, QImage::Format_RGB32);
|
||||||
|
qrAddrImage.setDevicePixelRatio(scale);
|
||||||
|
QPainter painter(&qrAddrImage);
|
||||||
|
|
||||||
|
// Fill the whole image with border color
|
||||||
|
qrAddrImage.fill(GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BORDER_WIDGET));
|
||||||
|
|
||||||
|
// Create a 2px/2px smaller rect and fill it with background color to keep the 1px border with the border color
|
||||||
|
QRect paddedRect = QRect(1, 1, qrAddrImageWidth - 2, qrAddrImageHeight - 2);
|
||||||
|
painter.fillRect(paddedRect, GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BACKGROUND_WIDGET));
|
||||||
|
painter.drawImage(2, 2, qrImage.scaled(QR_IMAGE_SIZE - 4, QR_IMAGE_SIZE - 4));
|
||||||
|
|
||||||
|
// calculate ideal font size
|
||||||
|
QFont font = GUIUtil::getFontNormal();
|
||||||
|
qreal font_size = GUIUtil::calculateIdealFontSize((paddedRect.width() - 20), text, font);
|
||||||
|
font.setPointSizeF(font_size);
|
||||||
|
|
||||||
|
// paint the address
|
||||||
|
painter.setFont(font);
|
||||||
|
painter.setPen(GUIUtil::getThemedQColor(GUIUtil::ThemedColor::QR_PIXEL));
|
||||||
|
paddedRect.setHeight(QR_IMAGE_SIZE + 3);
|
||||||
|
painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, text);
|
||||||
|
painter.end();
|
||||||
|
|
||||||
|
setPixmap(QPixmap::fromImage(qrAddrImage));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
setText(tr("QR code support not available."));
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage QRImageWidget::exportImage()
|
||||||
|
{
|
||||||
|
if(!pixmap())
|
||||||
|
return QImage();
|
||||||
|
return pixmap()->toImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRImageWidget::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
if(event->button() == Qt::LeftButton && pixmap())
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
QMimeData *mimeData = new QMimeData;
|
||||||
|
mimeData->setImageData(exportImage());
|
||||||
|
|
||||||
|
QDrag *drag = new QDrag(this);
|
||||||
|
drag->setMimeData(mimeData);
|
||||||
|
drag->exec();
|
||||||
|
} else {
|
||||||
|
QLabel::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRImageWidget::saveImage()
|
||||||
|
{
|
||||||
|
if(!pixmap())
|
||||||
|
return;
|
||||||
|
QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr);
|
||||||
|
if (!fn.isEmpty())
|
||||||
|
{
|
||||||
|
exportImage().save(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRImageWidget::copyImage()
|
||||||
|
{
|
||||||
|
if(!pixmap())
|
||||||
|
return;
|
||||||
|
QApplication::clipboard()->setImage(exportImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QRImageWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||||
|
{
|
||||||
|
if(!pixmap())
|
||||||
|
return;
|
||||||
|
contextMenu->exec(event->globalPos());
|
||||||
|
}
|
45
src/qt/qrimagewidget.h
Normal file
45
src/qt/qrimagewidget.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2011-2018 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_QT_QRIMAGEWIDGET_H
|
||||||
|
#define BITCOIN_QT_QRIMAGEWIDGET_H
|
||||||
|
|
||||||
|
#include <QImage>
|
||||||
|
#include <QLabel>
|
||||||
|
|
||||||
|
/* Maximum allowed URI length */
|
||||||
|
static const int MAX_URI_LENGTH = 255;
|
||||||
|
|
||||||
|
/* Size of exported QR Code image */
|
||||||
|
static const int QR_IMAGE_SIZE = 300;
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QMenu;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
/* Label widget for QR code. This image can be dragged, dropped, copied and saved
|
||||||
|
* to disk.
|
||||||
|
*/
|
||||||
|
class QRImageWidget : public QLabel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit QRImageWidget(QWidget *parent = nullptr);
|
||||||
|
bool setQR(const QString& data, const QString& text = "");
|
||||||
|
QImage exportImage();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
void saveImage();
|
||||||
|
void copyImage();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMenu *contextMenu;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_QT_QRIMAGEWIDGET_H
|
@ -6,85 +6,17 @@
|
|||||||
#include <qt/forms/ui_receiverequestdialog.h>
|
#include <qt/forms/ui_receiverequestdialog.h>
|
||||||
|
|
||||||
#include <qt/bitcoinunits.h>
|
#include <qt/bitcoinunits.h>
|
||||||
#include <qt/guiconstants.h>
|
|
||||||
#include <qt/guiutil.h>
|
#include <qt/guiutil.h>
|
||||||
#include <qt/optionsmodel.h>
|
#include <qt/optionsmodel.h>
|
||||||
|
#include <qt/qrimagewidget.h>
|
||||||
|
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QDrag>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QMimeData>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
|
||||||
#if defined(HAVE_CONFIG_H)
|
#if defined(HAVE_CONFIG_H)
|
||||||
#include <config/dash-config.h> /* for USE_QRCODE */
|
#include <config/dash-config.h> /* for USE_QRCODE */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_QRCODE
|
|
||||||
#include <qrencode.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QRImageWidget::QRImageWidget(QWidget *parent):
|
|
||||||
QLabel(parent), contextMenu(nullptr)
|
|
||||||
{
|
|
||||||
contextMenu = new QMenu(this);
|
|
||||||
QAction *saveImageAction = new QAction(tr("&Save Image..."), this);
|
|
||||||
connect(saveImageAction, &QAction::triggered, this, &QRImageWidget::saveImage);
|
|
||||||
contextMenu->addAction(saveImageAction);
|
|
||||||
QAction *copyImageAction = new QAction(tr("&Copy Image"), this);
|
|
||||||
connect(copyImageAction, &QAction::triggered, this, &QRImageWidget::copyImage);
|
|
||||||
contextMenu->addAction(copyImageAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
QImage QRImageWidget::exportImage()
|
|
||||||
{
|
|
||||||
if(!pixmap())
|
|
||||||
return QImage();
|
|
||||||
return pixmap()->toImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QRImageWidget::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if(event->button() == Qt::LeftButton && pixmap())
|
|
||||||
{
|
|
||||||
event->accept();
|
|
||||||
QMimeData *mimeData = new QMimeData;
|
|
||||||
mimeData->setImageData(exportImage());
|
|
||||||
|
|
||||||
QDrag *drag = new QDrag(this);
|
|
||||||
drag->setMimeData(mimeData);
|
|
||||||
drag->exec();
|
|
||||||
} else {
|
|
||||||
QLabel::mousePressEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QRImageWidget::saveImage()
|
|
||||||
{
|
|
||||||
if(!pixmap())
|
|
||||||
return;
|
|
||||||
QString fn = GUIUtil::getSaveFileName(this, tr("Save QR Code"), QString(), tr("PNG Image (*.png)"), nullptr);
|
|
||||||
if (!fn.isEmpty())
|
|
||||||
{
|
|
||||||
exportImage().save(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QRImageWidget::copyImage()
|
|
||||||
{
|
|
||||||
if(!pixmap())
|
|
||||||
return;
|
|
||||||
QApplication::clipboard()->setImage(exportImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QRImageWidget::contextMenuEvent(QContextMenuEvent *event)
|
|
||||||
{
|
|
||||||
if(!pixmap())
|
|
||||||
return;
|
|
||||||
contextMenu->exec(event->globalPos());
|
|
||||||
}
|
|
||||||
|
|
||||||
ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
|
ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) :
|
||||||
QDialog(parent),
|
QDialog(parent),
|
||||||
ui(new Ui::ReceiveRequestDialog),
|
ui(new Ui::ReceiveRequestDialog),
|
||||||
@ -153,62 +85,9 @@ void ReceiveRequestDialog::update()
|
|||||||
}
|
}
|
||||||
ui->outUri->setText(html);
|
ui->outUri->setText(html);
|
||||||
|
|
||||||
#ifdef USE_QRCODE
|
if (ui->lblQRCode->setQR(uri, info.address)) {
|
||||||
ui->lblQRCode->setText("");
|
ui->btnSaveAs->setEnabled(true);
|
||||||
if(!uri.isEmpty())
|
|
||||||
{
|
|
||||||
// limit URI length
|
|
||||||
if (uri.length() > MAX_URI_LENGTH)
|
|
||||||
{
|
|
||||||
ui->lblQRCode->setText(tr("Resulting URI too long, try to reduce the text for label / message."));
|
|
||||||
} else {
|
|
||||||
QRcode *code = QRcode_encodeString(uri.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);
|
|
||||||
if (!code)
|
|
||||||
{
|
|
||||||
ui->lblQRCode->setText(tr("Error encoding URI into QR Code."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QImage qrImage = QImage(code->width + 6, code->width + 6, QImage::Format_RGB32);
|
|
||||||
qrImage.fill(GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BACKGROUND_WIDGET));
|
|
||||||
unsigned char *p = code->data;
|
|
||||||
for (int y = 0; y < code->width; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < code->width; x++)
|
|
||||||
{
|
|
||||||
qrImage.setPixel(x + 3, y + 3, ((*p & 1) ? GUIUtil::getThemedQColor(GUIUtil::ThemedColor::QR_PIXEL).rgb() : GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BACKGROUND_WIDGET).rgb()));
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QRcode_free(code);
|
|
||||||
// Create the image with respect to the device pixel ratio
|
|
||||||
int qrAddrImageWidth = QR_IMAGE_SIZE;
|
|
||||||
int qrAddrImageHeight = QR_IMAGE_SIZE + 20;
|
|
||||||
qreal scale = qApp->devicePixelRatio();
|
|
||||||
QImage qrAddrImage = QImage(qrAddrImageWidth * scale, qrAddrImageHeight * scale, QImage::Format_RGB32);
|
|
||||||
qrAddrImage.setDevicePixelRatio(scale);
|
|
||||||
QPainter painter(&qrAddrImage);
|
|
||||||
// Fill the whole image with border color
|
|
||||||
qrAddrImage.fill(GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BORDER_WIDGET));
|
|
||||||
// Create a 2px/2px smaller rect and fill it with background color to keep the 1px border with the border color
|
|
||||||
QRect paddedRect = QRect(1, 1, qrAddrImageWidth - 2, qrAddrImageHeight - 2);
|
|
||||||
painter.fillRect(paddedRect, GUIUtil::getThemedQColor(GUIUtil::ThemedColor::BACKGROUND_WIDGET));
|
|
||||||
painter.drawImage(2, 2, qrImage.scaled(QR_IMAGE_SIZE - 4, QR_IMAGE_SIZE - 4));
|
|
||||||
// calculate ideal font size
|
|
||||||
QFont font = GUIUtil::getFontNormal();
|
|
||||||
qreal font_size = GUIUtil::calculateIdealFontSize((paddedRect.width() - 20), info.address, font);
|
|
||||||
font.setPointSizeF(font_size);
|
|
||||||
// paint the address
|
|
||||||
painter.setFont(font);
|
|
||||||
painter.setPen(GUIUtil::getThemedQColor(GUIUtil::ThemedColor::QR_PIXEL));
|
|
||||||
paddedRect.setHeight(QR_IMAGE_SIZE + 3);
|
|
||||||
painter.drawText(paddedRect, Qt::AlignBottom|Qt::AlignCenter, info.address);
|
|
||||||
painter.end();
|
|
||||||
|
|
||||||
ui->lblQRCode->setPixmap(QPixmap::fromImage(qrAddrImage));
|
|
||||||
ui->btnSaveAs->setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReceiveRequestDialog::on_btnCopyURI_clicked()
|
void ReceiveRequestDialog::on_btnCopyURI_clicked()
|
||||||
|
@ -8,41 +8,11 @@
|
|||||||
#include <qt/walletmodel.h>
|
#include <qt/walletmodel.h>
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QImage>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class ReceiveRequestDialog;
|
class ReceiveRequestDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
|
||||||
class QMenu;
|
|
||||||
QT_END_NAMESPACE
|
|
||||||
|
|
||||||
/* Label widget for QR code. This image can be dragged, dropped, copied and saved
|
|
||||||
* to disk.
|
|
||||||
*/
|
|
||||||
class QRImageWidget : public QLabel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit QRImageWidget(QWidget *parent = nullptr);
|
|
||||||
QImage exportImage();
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void saveImage();
|
|
||||||
void copyImage();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
virtual void contextMenuEvent(QContextMenuEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QMenu *contextMenu;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReceiveRequestDialog : public QDialog
|
class ReceiveRequestDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -31,12 +31,12 @@
|
|||||||
|
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDesktopWidget>
|
|
||||||
#include <QFontDatabase>
|
#include <QFontDatabase>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
|
#include <QScreen>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@ -466,7 +466,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
|
|||||||
QSettings settings;
|
QSettings settings;
|
||||||
if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
|
if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
|
||||||
// Restore failed (perhaps missing setting), center the window
|
// Restore failed (perhaps missing setting), center the window
|
||||||
move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center());
|
move(QGuiApplication::primaryScreen()->availableGeometry().center() - frameGeometry().center());
|
||||||
}
|
}
|
||||||
|
|
||||||
QChar nonbreaking_hyphen(8209);
|
QChar nonbreaking_hyphen(8209);
|
||||||
|
@ -9,22 +9,22 @@
|
|||||||
|
|
||||||
#include <qt/splashscreen.h>
|
#include <qt/splashscreen.h>
|
||||||
|
|
||||||
#include <qt/guiutil.h>
|
|
||||||
#include <qt/networkstyle.h>
|
|
||||||
|
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
#include <interfaces/handler.h>
|
#include <interfaces/handler.h>
|
||||||
#include <interfaces/node.h>
|
#include <interfaces/node.h>
|
||||||
#include <interfaces/wallet.h>
|
#include <interfaces/wallet.h>
|
||||||
|
#include <qt/guiutil.h>
|
||||||
|
#include <qt/networkstyle.h>
|
||||||
#include <ui_interface.h>
|
#include <ui_interface.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCloseEvent>
|
#include <QCloseEvent>
|
||||||
#include <QDesktopWidget>
|
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
|
|
||||||
SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
|
SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) :
|
||||||
@ -89,7 +89,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
|||||||
fontBold.setPointSize(50 * fontFactor);
|
fontBold.setPointSize(50 * fontFactor);
|
||||||
pixPaint.setFont(fontBold);
|
pixPaint.setFont(fontBold);
|
||||||
QFontMetrics fm = pixPaint.fontMetrics();
|
QFontMetrics fm = pixPaint.fontMetrics();
|
||||||
int titleTextWidth = fm.width(titleText);
|
int titleTextWidth = GUIUtil::TextWidth(fm, titleText);
|
||||||
if (titleTextWidth > width * 0.8) {
|
if (titleTextWidth > width * 0.8) {
|
||||||
fontFactor = 0.75;
|
fontFactor = 0.75;
|
||||||
}
|
}
|
||||||
@ -97,14 +97,14 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
|||||||
fontBold.setPointSize(50 * fontFactor);
|
fontBold.setPointSize(50 * fontFactor);
|
||||||
pixPaint.setFont(fontBold);
|
pixPaint.setFont(fontBold);
|
||||||
fm = pixPaint.fontMetrics();
|
fm = pixPaint.fontMetrics();
|
||||||
titleTextWidth = fm.width(titleText);
|
titleTextWidth = GUIUtil::TextWidth(fm, titleText);
|
||||||
int titleTextHeight = fm.height();
|
int titleTextHeight = fm.height();
|
||||||
pixPaint.drawText((width / 2) - (titleTextWidth / 2), titleTextHeight + paddingTop, titleText);
|
pixPaint.drawText((width / 2) - (titleTextWidth / 2), titleTextHeight + paddingTop, titleText);
|
||||||
|
|
||||||
fontNormal.setPointSize(16 * fontFactor);
|
fontNormal.setPointSize(16 * fontFactor);
|
||||||
pixPaint.setFont(fontNormal);
|
pixPaint.setFont(fontNormal);
|
||||||
fm = pixPaint.fontMetrics();
|
fm = pixPaint.fontMetrics();
|
||||||
int versionTextWidth = fm.width(versionText);
|
int versionTextWidth = GUIUtil::TextWidth(fm, versionText);
|
||||||
pixPaint.drawText((width / 2) - (versionTextWidth / 2), titleTextHeight + paddingTop + titleVersionVSpace, versionText);
|
pixPaint.drawText((width / 2) - (versionTextWidth / 2), titleTextHeight + paddingTop + titleVersionVSpace, versionText);
|
||||||
|
|
||||||
// draw additional text if special network
|
// draw additional text if special network
|
||||||
@ -112,7 +112,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
|||||||
fontBold.setPointSize(10 * fontFactor);
|
fontBold.setPointSize(10 * fontFactor);
|
||||||
pixPaint.setFont(fontBold);
|
pixPaint.setFont(fontBold);
|
||||||
fm = pixPaint.fontMetrics();
|
fm = pixPaint.fontMetrics();
|
||||||
int titleAddTextWidth = fm.width(titleAddText);
|
int titleAddTextWidth = GUIUtil::TextWidth(fm, titleAddText);
|
||||||
// Draw the badge background with the network-specific color
|
// Draw the badge background with the network-specific color
|
||||||
QRect badgeRect = QRect(width - titleAddTextWidth - 20, 5, width, fm.height() + 10);
|
QRect badgeRect = QRect(width - titleAddTextWidth - 20, 5, width, fm.height() + 10);
|
||||||
QColor badgeColor = networkStyle->getBadgeColor();
|
QColor badgeColor = networkStyle->getBadgeColor();
|
||||||
@ -128,7 +128,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
|||||||
QRect r(QPoint(), QSize(width, height));
|
QRect r(QPoint(), QSize(width, height));
|
||||||
resize(r.size());
|
resize(r.size());
|
||||||
setFixedSize(r.size());
|
setFixedSize(r.size());
|
||||||
move(QApplication::desktop()->screenGeometry().center() - r.center());
|
move(QGuiApplication::primaryScreen()->geometry().center() - r.center());
|
||||||
|
|
||||||
subscribeToCoreSignals();
|
subscribeToCoreSignals();
|
||||||
installEventFilter(this);
|
installEventFilter(this);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <qt/guiutil.h>
|
||||||
#include <qt/walletcontroller.h>
|
#include <qt/walletcontroller.h>
|
||||||
|
|
||||||
#include <interfaces/handler.h>
|
#include <interfaces/handler.h>
|
||||||
@ -70,7 +71,7 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
|
|||||||
{
|
{
|
||||||
QMessageBox box(parent);
|
QMessageBox box(parent);
|
||||||
box.setWindowTitle(tr("Close wallet"));
|
box.setWindowTitle(tr("Close wallet"));
|
||||||
box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(wallet_model->getDisplayName()));
|
box.setText(tr("Are you sure you wish to close wallet <i>%1</i>?").arg(GUIUtil::HtmlEscape(wallet_model->getDisplayName())));
|
||||||
box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
|
box.setInformativeText(tr("Closing the wallet for too long can result in having to resync the entire chain if pruning is enabled."));
|
||||||
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
|
box.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
|
||||||
box.setDefaultButton(QMessageBox::Yes);
|
box.setDefaultButton(QMessageBox::Yes);
|
||||||
|
@ -229,9 +229,9 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int
|
|||||||
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
|
qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong();
|
||||||
QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
|
QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString();
|
||||||
QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
|
QString address = ttm->data(index, TransactionTableModel::AddressRole).toString();
|
||||||
QString label = ttm->data(index, TransactionTableModel::LabelRole).toString();
|
QString label = GUIUtil::HtmlEscape(ttm->data(index, TransactionTableModel::LabelRole).toString());
|
||||||
|
|
||||||
Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, walletModel->getWalletName());
|
Q_EMIT incomingTransaction(date, walletModel->getOptionsModel()->getDisplayUnit(), amount, type, address, label, GUIUtil::HtmlEscape(walletModel->getWalletName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletView::gotoGovernancePage()
|
void WalletView::gotoGovernancePage()
|
||||||
|
@ -40,32 +40,46 @@ bool EqualDescriptor(std::string a, std::string b)
|
|||||||
return a == b;
|
return a == b;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string MaybeUseHInsteadOfApostrophy(std::string ret)
|
std::string UseHInsteadOfApostrophe(const std::string& desc)
|
||||||
{
|
{
|
||||||
if (InsecureRandBool()) {
|
std::string ret = desc;
|
||||||
while (true) {
|
while (true) {
|
||||||
auto it = ret.find("'");
|
auto it = ret.find('\'');
|
||||||
if (it != std::string::npos) {
|
if (it == std::string::npos) break;
|
||||||
ret[it] = 'h';
|
ret[it] = 'h';
|
||||||
if (ret.size() > 9 && ret[ret.size() - 9] == '#') ret = ret.substr(0, ret.size() - 9); // Changing apostrophe to h breaks the checksum
|
}
|
||||||
} else {
|
|
||||||
break;
|
// GetDescriptorChecksum returns "" if the checksum exists but is bad.
|
||||||
}
|
// Switching apostrophes with 'h' breaks the checksum if it exists - recalculate it and replace the broken one.
|
||||||
}
|
if (GetDescriptorChecksum(ret) == "") {
|
||||||
|
ret = ret.substr(0, desc.size() - 9);
|
||||||
|
ret += std::string("#") + GetDescriptorChecksum(ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
|
const std::set<std::vector<uint32_t>> ONLY_EMPTY{{}};
|
||||||
|
|
||||||
void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
|
void DoCheck(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY,
|
||||||
|
bool replace_apostrophe_with_h_in_prv=false, bool replace_apostrophe_with_h_in_pub=false)
|
||||||
{
|
{
|
||||||
FlatSigningProvider keys_priv, keys_pub;
|
FlatSigningProvider keys_priv, keys_pub;
|
||||||
std::set<std::vector<uint32_t>> left_paths = paths;
|
std::set<std::vector<uint32_t>> left_paths = paths;
|
||||||
|
|
||||||
|
std::unique_ptr<Descriptor> parse_priv;
|
||||||
|
std::unique_ptr<Descriptor> parse_pub;
|
||||||
// Check that parsing succeeds.
|
// Check that parsing succeeds.
|
||||||
std::unique_ptr<Descriptor> parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv);
|
if (replace_apostrophe_with_h_in_prv) {
|
||||||
std::unique_ptr<Descriptor> parse_pub = Parse(MaybeUseHInsteadOfApostrophy(pub), keys_pub);
|
parse_priv = Parse(UseHInsteadOfApostrophe(prv), keys_priv);
|
||||||
|
} else {
|
||||||
|
parse_priv = Parse(prv, keys_priv);
|
||||||
|
}
|
||||||
|
if (replace_apostrophe_with_h_in_pub) {
|
||||||
|
parse_pub = Parse(UseHInsteadOfApostrophe(pub), keys_pub);
|
||||||
|
} else {
|
||||||
|
parse_pub = Parse(pub, keys_pub);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_CHECK(parse_priv);
|
BOOST_CHECK(parse_priv);
|
||||||
BOOST_CHECK(parse_pub);
|
BOOST_CHECK(parse_pub);
|
||||||
|
|
||||||
@ -164,6 +178,32 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std:
|
|||||||
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
|
BOOST_CHECK_MESSAGE(left_paths.empty(), "Not all expected key paths found: " + prv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Check(const std::string& prv, const std::string& pub, int flags, const std::vector<std::vector<std::string>>& scripts, const std::set<std::vector<uint32_t>>& paths = ONLY_EMPTY)
|
||||||
|
{
|
||||||
|
bool found_apostrophes_in_prv = false;
|
||||||
|
bool found_apostrophes_in_pub = false;
|
||||||
|
|
||||||
|
// Do not replace apostrophes with 'h' in prv and pub
|
||||||
|
DoCheck(prv, pub, flags, scripts, paths);
|
||||||
|
|
||||||
|
// Replace apostrophes with 'h' in prv but not in pub, if apostrophes are found in prv
|
||||||
|
if (prv.find('\'') != std::string::npos) {
|
||||||
|
found_apostrophes_in_prv = true;
|
||||||
|
DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace apostrophes with 'h' in pub but not in prv, if apostrophes are found in pub
|
||||||
|
if (pub.find('\'') != std::string::npos) {
|
||||||
|
found_apostrophes_in_pub = true;
|
||||||
|
DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */false, /*replace_apostrophe_with_h_in_pub = */true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace apostrophes with 'h' both in prv and in pub, if apostrophes are found in both
|
||||||
|
if (found_apostrophes_in_prv && found_apostrophes_in_pub) {
|
||||||
|
DoCheck(prv, pub, flags, scripts, paths, /* replace_apostrophe_with_h_in_prv = */true, /*replace_apostrophe_with_h_in_pub = */true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
|
BOOST_FIXTURE_TEST_SUITE(descriptor_tests, BasicTestingSetup)
|
||||||
|
@ -249,4 +249,82 @@ BOOST_AUTO_TEST_CASE(merkle_test)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(merkle_test_empty_block)
|
||||||
|
{
|
||||||
|
bool mutated = false;
|
||||||
|
CBlock block;
|
||||||
|
uint256 root = BlockMerkleRoot(block, &mutated);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(root.IsNull(), true);
|
||||||
|
BOOST_CHECK_EQUAL(mutated, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(merkle_test_oneTx_block)
|
||||||
|
{
|
||||||
|
bool mutated = false;
|
||||||
|
CBlock block;
|
||||||
|
|
||||||
|
block.vtx.resize(1);
|
||||||
|
CMutableTransaction mtx;
|
||||||
|
mtx.nLockTime = 0;
|
||||||
|
block.vtx[0] = MakeTransactionRef(std::move(mtx));
|
||||||
|
uint256 root = BlockMerkleRoot(block, &mutated);
|
||||||
|
BOOST_CHECK_EQUAL(root, block.vtx[0]->GetHash());
|
||||||
|
BOOST_CHECK_EQUAL(mutated, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(merkle_test_OddTxWithRepeatedLastTx_block)
|
||||||
|
{
|
||||||
|
bool mutated;
|
||||||
|
CBlock block, blockWithRepeatedLastTx;
|
||||||
|
|
||||||
|
block.vtx.resize(3);
|
||||||
|
|
||||||
|
for (std::size_t pos = 0; pos < block.vtx.size(); pos++) {
|
||||||
|
CMutableTransaction mtx;
|
||||||
|
mtx.nLockTime = pos;
|
||||||
|
block.vtx[pos] = MakeTransactionRef(std::move(mtx));
|
||||||
|
}
|
||||||
|
|
||||||
|
blockWithRepeatedLastTx = block;
|
||||||
|
blockWithRepeatedLastTx.vtx.push_back(blockWithRepeatedLastTx.vtx.back());
|
||||||
|
|
||||||
|
uint256 rootofBlock = BlockMerkleRoot(block, &mutated);
|
||||||
|
BOOST_CHECK_EQUAL(mutated, false);
|
||||||
|
|
||||||
|
uint256 rootofBlockWithRepeatedLastTx = BlockMerkleRoot(blockWithRepeatedLastTx, &mutated);
|
||||||
|
BOOST_CHECK_EQUAL(rootofBlock, rootofBlockWithRepeatedLastTx);
|
||||||
|
BOOST_CHECK_EQUAL(mutated, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(merkle_test_LeftSubtreeRightSubtree)
|
||||||
|
{
|
||||||
|
CBlock block, leftSubtreeBlock, rightSubtreeBlock;
|
||||||
|
|
||||||
|
block.vtx.resize(4);
|
||||||
|
std::size_t pos;
|
||||||
|
for (pos = 0; pos < block.vtx.size(); pos++) {
|
||||||
|
CMutableTransaction mtx;
|
||||||
|
mtx.nLockTime = pos;
|
||||||
|
block.vtx[pos] = MakeTransactionRef(std::move(mtx));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pos = 0; pos < block.vtx.size() / 2; pos++)
|
||||||
|
leftSubtreeBlock.vtx.push_back(block.vtx[pos]);
|
||||||
|
|
||||||
|
for (pos = block.vtx.size() / 2; pos < block.vtx.size(); pos++)
|
||||||
|
rightSubtreeBlock.vtx.push_back(block.vtx[pos]);
|
||||||
|
|
||||||
|
uint256 root = BlockMerkleRoot(block);
|
||||||
|
uint256 rootOfLeftSubtree = BlockMerkleRoot(leftSubtreeBlock);
|
||||||
|
uint256 rootOfRightSubtree = BlockMerkleRoot(rightSubtreeBlock);
|
||||||
|
std::vector<uint256> leftRight;
|
||||||
|
leftRight.push_back(rootOfLeftSubtree);
|
||||||
|
leftRight.push_back(rootOfRightSubtree);
|
||||||
|
uint256 rootOfLR = ComputeMerkleRoot(leftRight);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(root, rootOfLR);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -57,7 +57,7 @@ static void SetInternalName(std::string name) { }
|
|||||||
|
|
||||||
void util::ThreadRename(std::string&& name)
|
void util::ThreadRename(std::string&& name)
|
||||||
{
|
{
|
||||||
SetThreadName(("dash-" + name).c_str());
|
SetThreadName(("d-" + name).c_str());
|
||||||
SetInternalName(std::move(name));
|
SetInternalName(std::move(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1895,7 +1895,8 @@ public:
|
|||||||
|
|
||||||
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
|
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
|
||||||
{
|
{
|
||||||
return ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
|
return pindex->nHeight >= params.MinBIP9WarningHeight &&
|
||||||
|
((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
|
||||||
((pindex->nVersion >> bit) & 1) != 0 &&
|
((pindex->nVersion >> bit) & 1) != 0 &&
|
||||||
((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
|
((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,8 @@ class Chain;
|
|||||||
} // namespace interfaces
|
} // namespace interfaces
|
||||||
|
|
||||||
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
|
//! Responsible for reading and validating the -wallet arguments and verifying the wallet database.
|
||||||
//! This function will perform salvage on the wallet if requested, as long as only one wallet is
|
// This function will perform salvage on the wallet if requested, as long as only one wallet is
|
||||||
//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
// being loaded (WalletInit::ParameterInteraction() forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
||||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
||||||
|
|
||||||
//! Load wallet databases.
|
//! Load wallet databases.
|
||||||
|
@ -4885,7 +4885,7 @@ bool CWallet::Verify(interfaces::Chain& chain, const WalletLocation& location, b
|
|||||||
|
|
||||||
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
|
std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const WalletLocation& location, uint64_t wallet_creation_flags)
|
||||||
{
|
{
|
||||||
const std::string& walletFile = WalletDataFilePath(location.GetPath()).string();
|
const std::string walletFile = WalletDataFilePath(location.GetPath()).string();
|
||||||
|
|
||||||
// needed to restore wallet transaction meta data after -zapwallettxes
|
// needed to restore wallet transaction meta data after -zapwallettxes
|
||||||
std::vector<CWalletTx> vWtx;
|
std::vector<CWalletTx> vWtx;
|
||||||
|
0
test/functional/data/wallets/high_minversion/db.log
Normal file
0
test/functional/data/wallets/high_minversion/db.log
Normal file
BIN
test/functional/data/wallets/high_minversion/wallet.dat
Normal file
BIN
test/functional/data/wallets/high_minversion/wallet.dat
Normal file
Binary file not shown.
@ -18,7 +18,29 @@ from test_framework.util import (
|
|||||||
wait_until,
|
wait_until,
|
||||||
)
|
)
|
||||||
from test_framework.mininode import P2PInterface
|
from test_framework.mininode import P2PInterface
|
||||||
from test_framework.messages import CAddress, msg_addr, NODE_NETWORK
|
from test_framework.messages import (
|
||||||
|
CAddress,
|
||||||
|
msg_addr,
|
||||||
|
NODE_NETWORK,
|
||||||
|
NODE_GETUTXO,NODE_BLOOM,
|
||||||
|
NODE_NETWORK_LIMITED,
|
||||||
|
)
|
||||||
|
|
||||||
|
def assert_net_servicesnames(servicesflag, servicenames):
|
||||||
|
"""Utility that checks if all flags are correctly decoded in
|
||||||
|
`getpeerinfo` and `getnetworkinfo`.
|
||||||
|
|
||||||
|
:param servicesflag: The services as an integer.
|
||||||
|
:param servicesnames: The list of decoded services names, as strings.
|
||||||
|
"""
|
||||||
|
if servicesflag & NODE_NETWORK:
|
||||||
|
assert "NETWORK" in servicenames
|
||||||
|
if servicesflag & NODE_GETUTXO:
|
||||||
|
assert "GETUTXO" in servicenames
|
||||||
|
if servicesflag & NODE_BLOOM:
|
||||||
|
assert "BLOOM" in servicenames
|
||||||
|
if servicesflag & NODE_NETWORK_LIMITED:
|
||||||
|
assert "NETWORK_LIMITED" in servicenames
|
||||||
|
|
||||||
class NetTest(BitcoinTestFramework):
|
class NetTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
@ -38,7 +60,7 @@ class NetTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
self._test_connection_count()
|
self._test_connection_count()
|
||||||
self._test_getnettotals()
|
self._test_getnettotals()
|
||||||
self._test_getnetworkinginfo()
|
self._test_getnetworkinfo()
|
||||||
self._test_getaddednodeinfo()
|
self._test_getaddednodeinfo()
|
||||||
self._test_getpeerinfo()
|
self._test_getpeerinfo()
|
||||||
self._test_getnodeaddresses()
|
self._test_getnodeaddresses()
|
||||||
@ -77,7 +99,7 @@ class NetTest(BitcoinTestFramework):
|
|||||||
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
|
assert_greater_than_or_equal(after['bytesrecv_per_msg'].get('pong', 0), before['bytesrecv_per_msg'].get('pong', 0) + 32)
|
||||||
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
|
assert_greater_than_or_equal(after['bytessent_per_msg'].get('ping', 0), before['bytessent_per_msg'].get('ping', 0) + 32)
|
||||||
|
|
||||||
def _test_getnetworkinginfo(self):
|
def _test_getnetworkinfo(self):
|
||||||
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
|
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
|
||||||
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
|
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
|
||||||
|
|
||||||
@ -92,6 +114,11 @@ class NetTest(BitcoinTestFramework):
|
|||||||
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
|
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
|
||||||
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
|
assert_equal(self.nodes[0].getnetworkinfo()['connections'], 2)
|
||||||
|
|
||||||
|
# check the `servicesnames` field
|
||||||
|
network_info = [node.getnetworkinfo() for node in self.nodes]
|
||||||
|
for info in network_info:
|
||||||
|
assert_net_servicesnames(int(info["localservices"]), info["localservicesnames"])
|
||||||
|
|
||||||
def _test_getaddednodeinfo(self):
|
def _test_getaddednodeinfo(self):
|
||||||
assert_equal(self.nodes[0].getaddednodeinfo(), [])
|
assert_equal(self.nodes[0].getaddednodeinfo(), [])
|
||||||
# add a node (node2) to node0
|
# add a node (node2) to node0
|
||||||
@ -110,6 +137,9 @@ class NetTest(BitcoinTestFramework):
|
|||||||
# the address bound to on one side will be the source address for the other node
|
# the address bound to on one side will be the source address for the other node
|
||||||
assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr'])
|
assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr'])
|
||||||
assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr'])
|
assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr'])
|
||||||
|
# check the `servicesnames` field
|
||||||
|
for info in peer_info:
|
||||||
|
assert_net_servicesnames(int(info[0]["services"]), info[0]["servicesnames"])
|
||||||
|
|
||||||
def _test_getnodeaddresses(self):
|
def _test_getnodeaddresses(self):
|
||||||
self.nodes[0].add_p2p_connection(P2PInterface())
|
self.nodes[0].add_p2p_connection(P2PInterface())
|
||||||
|
@ -44,7 +44,7 @@ COIN = 100000000 # 1 btc in satoshis
|
|||||||
BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
|
BIP125_SEQUENCE_NUMBER = 0xfffffffd # Sequence number that is BIP 125 opt-in and BIP 68-opt-out
|
||||||
|
|
||||||
NODE_NETWORK = (1 << 0)
|
NODE_NETWORK = (1 << 0)
|
||||||
# NODE_GETUTXO = (1 << 1)
|
NODE_GETUTXO = (1 << 1)
|
||||||
NODE_BLOOM = (1 << 2)
|
NODE_BLOOM = (1 << 2)
|
||||||
NODE_COMPACT_FILTERS = (1 << 6)
|
NODE_COMPACT_FILTERS = (1 << 6)
|
||||||
NODE_NETWORK_LIMITED = (1 << 10)
|
NODE_NETWORK_LIMITED = (1 << 10)
|
||||||
|
@ -68,7 +68,9 @@ def assert_raises_message(exc, message, fun, *args, **kwds):
|
|||||||
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
|
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
|
||||||
except exc as e:
|
except exc as e:
|
||||||
if message is not None and message not in e.error['message']:
|
if message is not None and message not in e.error['message']:
|
||||||
raise AssertionError("Expected substring not found:" + e.error['message'])
|
raise AssertionError(
|
||||||
|
"Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
|
||||||
|
message, e.error['message']))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||||
else:
|
else:
|
||||||
@ -128,7 +130,9 @@ def try_rpc(code, message, fun, *args, **kwds):
|
|||||||
if (code is not None) and (code != e.error["code"]):
|
if (code is not None) and (code != e.error["code"]):
|
||||||
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
|
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
|
||||||
if (message is not None) and (message not in e.error['message']):
|
if (message is not None) and (message not in e.error['message']):
|
||||||
raise AssertionError("Expected substring not found:" + e.error['message'])
|
raise AssertionError(
|
||||||
|
"Expected substring not found in error message:\nsubstring: '{}'\nerror message: '{}'.".format(
|
||||||
|
message, e.error['message']))
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||||
|
@ -17,6 +17,8 @@ from test_framework.util import (
|
|||||||
assert_raises_rpc_error,
|
assert_raises_rpc_error,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
FEATURE_LATEST = 120200
|
||||||
|
|
||||||
|
|
||||||
class MultiWalletTest(BitcoinTestFramework):
|
class MultiWalletTest(BitcoinTestFramework):
|
||||||
def set_test_params(self):
|
def set_test_params(self):
|
||||||
@ -27,6 +29,13 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
|
|
||||||
|
def add_options(self, parser):
|
||||||
|
parser.add_argument(
|
||||||
|
'--data_wallets_dir',
|
||||||
|
default=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/wallets/'),
|
||||||
|
help='Test data with wallet directories (default: %(default)s)',
|
||||||
|
)
|
||||||
|
|
||||||
def run_test(self):
|
def run_test(self):
|
||||||
node = self.nodes[0]
|
node = self.nodes[0]
|
||||||
|
|
||||||
@ -328,6 +337,22 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||||||
self.nodes[0].unloadwallet(wallet)
|
self.nodes[0].unloadwallet(wallet)
|
||||||
self.nodes[1].loadwallet(wallet)
|
self.nodes[1].loadwallet(wallet)
|
||||||
|
|
||||||
|
# Fail to load if wallet is downgraded
|
||||||
|
shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
|
||||||
|
self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
|
||||||
|
assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
|
||||||
|
self.log.info("Fail -upgradewallet that results in downgrade")
|
||||||
|
assert_raises_rpc_error(
|
||||||
|
-4,
|
||||||
|
"Wallet loading failed.",
|
||||||
|
lambda: self.nodes[0].loadwallet(filename='high_minversion'),
|
||||||
|
)
|
||||||
|
self.stop_node(
|
||||||
|
i=0,
|
||||||
|
expected_stderr='Error: Error loading {}: Wallet requires newer version of Dash Core'.format(
|
||||||
|
wallet_dir('high_minversion', 'wallet.dat')),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
MultiWalletTest().main()
|
MultiWalletTest().main()
|
||||||
|
Loading…
Reference in New Issue
Block a user