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_paymentserver.cpp \
|
||||
qt/moc_qrdialog.cpp \
|
||||
qt/moc_qrimagewidget.cpp \
|
||||
qt/moc_qvalidatedlineedit.cpp \
|
||||
qt/moc_qvaluecombobox.cpp \
|
||||
qt/moc_receivecoinsdialog.cpp \
|
||||
@ -167,6 +168,7 @@ BITCOIN_QT_H = \
|
||||
qt/paymentserver.h \
|
||||
qt/peertablemodel.h \
|
||||
qt/qrdialog.h \
|
||||
qt/qrimagewidget.h \
|
||||
qt/qvalidatedlineedit.h \
|
||||
qt/qvaluecombobox.h \
|
||||
qt/receivecoinsdialog.h \
|
||||
@ -266,6 +268,7 @@ BITCOIN_QT_WALLET_CPP = \
|
||||
qt/overviewpage.cpp \
|
||||
qt/paymentserver.cpp \
|
||||
qt/qrdialog.cpp \
|
||||
qt/qrimagewidget.cpp \
|
||||
qt/receivecoinsdialog.cpp \
|
||||
qt/receiverequestdialog.cpp \
|
||||
qt/recentrequeststablemodel.cpp \
|
||||
|
@ -152,6 +152,7 @@ public:
|
||||
consensus.DIP0003EnforcementHeight = 1047200;
|
||||
consensus.DIP0003EnforcementHash = uint256S("000000000000002d1734087b4c5afc3133e4e1c3e1a89218f62bcd9bb3d17f81");
|
||||
consensus.DIP0008Height = 1088640; // 00000000000000112e41e4b3afda8b233b8cc07c532d2eac5de097b68358c43e
|
||||
consensus.MinBIP9WarningHeight = 1090656; // dip8 activation height + miner confirmation window
|
||||
consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20
|
||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||
@ -371,6 +372,7 @@ public:
|
||||
consensus.DIP0003EnforcementHeight = 7300;
|
||||
consensus.DIP0003EnforcementHash = uint256S("00000055ebc0e974ba3a3fb785c5ad4365a39637d4df168169ee80d313612f8f");
|
||||
consensus.DIP0008Height = 78800; // 000000000e9329d964d80e7dab2e704b43b6bd2b91fea1e9315d38932e55fb55
|
||||
consensus.MinBIP9WarningHeight = 80816; // dip8 activation height + miner confirmation window
|
||||
consensus.powLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 20
|
||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||
@ -563,6 +565,7 @@ public:
|
||||
consensus.DIP0003EnforcementHeight = 2; // DIP0003 activated immediately on devnet
|
||||
consensus.DIP0003EnforcementHash = uint256();
|
||||
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.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||
@ -798,6 +801,7 @@ public:
|
||||
consensus.DIP0003EnforcementHeight = 500;
|
||||
consensus.DIP0003EnforcementHash = uint256();
|
||||
consensus.DIP0008Height = 432;
|
||||
consensus.MinBIP9WarningHeight = 0;
|
||||
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~uint256(0) >> 1
|
||||
consensus.nPowTargetTimespan = 24 * 60 * 60; // Dash: 1 day
|
||||
consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes
|
||||
|
@ -85,6 +85,9 @@ struct Params {
|
||||
uint256 DIP0003EnforcementHash;
|
||||
/** Block height at which DIP0008 becomes active */
|
||||
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,
|
||||
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
|
||||
|
@ -1469,7 +1469,7 @@ bool AppInitParameterInteraction()
|
||||
if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) {
|
||||
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);
|
||||
} else if (incrementalRelayFee > ::minRelayTxFee) {
|
||||
// Allow only setting incrementalRelayFee to control both
|
||||
|
@ -117,7 +117,7 @@ public:
|
||||
ensurePolished();
|
||||
const QFontMetrics fm(fontMetrics());
|
||||
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 += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css
|
||||
return QSize(w, h);
|
||||
|
@ -46,7 +46,6 @@
|
||||
#include <QButtonGroup>
|
||||
#include <QComboBox>
|
||||
#include <QDateTime>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDragEnterEvent>
|
||||
#include <QListWidget>
|
||||
#include <QMenu>
|
||||
@ -54,6 +53,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QMimeData>
|
||||
#include <QProgressDialog>
|
||||
#include <QScreen>
|
||||
#include <QSettings>
|
||||
#include <QShortcut>
|
||||
#include <QStackedWidget>
|
||||
@ -89,7 +89,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const NetworkStyle* networkStyle,
|
||||
QSettings settings;
|
||||
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
|
||||
// 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
|
||||
@ -489,6 +489,8 @@ void BitcoinGUI::createActions()
|
||||
for (const std::pair<const std::string, bool>& i : m_wallet_controller->listWalletDir()) {
|
||||
const std::string& path = i.first;
|
||||
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);
|
||||
|
||||
if (i.second) {
|
||||
@ -1324,7 +1326,7 @@ void BitcoinGUI::updateWidth()
|
||||
continue;
|
||||
}
|
||||
QFontMetrics fm(button->font());
|
||||
nWidthWidestButton = std::max<int>(nWidthWidestButton, fm.width(button->text()));
|
||||
nWidthWidestButton = std::max<int>(nWidthWidestButton, GUIUtil::TextWidth(fm, button->text()));
|
||||
++nButtonsVisible;
|
||||
}
|
||||
// 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());
|
||||
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);
|
||||
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
|
||||
|
@ -127,7 +127,7 @@
|
||||
<customwidget>
|
||||
<class>QRImageWidget</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>qt/receiverequestdialog.h</header>
|
||||
<header>qt/qrimagewidget.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
|
@ -32,12 +32,6 @@ static const bool DEFAULT_SPLASHSCREEN = true;
|
||||
*/
|
||||
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 */
|
||||
#define SPINNER_FRAMES 90
|
||||
|
||||
|
@ -51,7 +51,6 @@
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDoubleValidator>
|
||||
#include <QFileDialog>
|
||||
@ -1832,7 +1831,7 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m
|
||||
while(font_size >= minPointSize) {
|
||||
font.setPointSizeF(font_size);
|
||||
QFontMetrics fm(font);
|
||||
if (fm.width(text) < width) {
|
||||
if (TextWidth(fm, text) < width) {
|
||||
break;
|
||||
}
|
||||
font_size -= 0.5;
|
||||
@ -1864,7 +1863,7 @@ void PolishProgressDialog(QProgressDialog* dialog)
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
// 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->show();
|
||||
#else
|
||||
@ -1872,4 +1871,13 @@ void PolishProgressDialog(QProgressDialog* dialog)
|
||||
#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
|
||||
|
@ -467,6 +467,14 @@ namespace GUIUtil
|
||||
|
||||
// Fix known bugs in QProgressDialog class.
|
||||
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
|
||||
|
||||
#endif // BITCOIN_QT_GUIUTIL_H
|
||||
|
@ -497,7 +497,7 @@ void OptionsDialog::updateWidth()
|
||||
continue;
|
||||
}
|
||||
QFontMetrics fm(button->font());
|
||||
nWidthWidestButton = std::max<int>(nWidthWidestButton, fm.width(button->text()));
|
||||
nWidthWidestButton = std::max<int>(nWidthWidestButton, GUIUtil::TextWidth(fm, button->text()));
|
||||
++nButtonsVisible;
|
||||
}
|
||||
// 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/guiconstants.h>
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/qrimagewidget.h>
|
||||
|
||||
#include <QClipboard>
|
||||
#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/bitcoinunits.h>
|
||||
#include <qt/guiconstants.h>
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/optionsmodel.h>
|
||||
#include <qt/qrimagewidget.h>
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDrag>
|
||||
#include <QMenu>
|
||||
#include <QMimeData>
|
||||
#include <QMouseEvent>
|
||||
#include <QPixmap>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
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) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ReceiveRequestDialog),
|
||||
@ -153,62 +85,9 @@ void ReceiveRequestDialog::update()
|
||||
}
|
||||
ui->outUri->setText(html);
|
||||
|
||||
#ifdef USE_QRCODE
|
||||
ui->lblQRCode->setText("");
|
||||
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);
|
||||
}
|
||||
if (ui->lblQRCode->setQR(uri, info.address)) {
|
||||
ui->btnSaveAs->setEnabled(true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ReceiveRequestDialog::on_btnCopyURI_clicked()
|
||||
|
@ -8,41 +8,11 @@
|
||||
#include <qt/walletmodel.h>
|
||||
|
||||
#include <QDialog>
|
||||
#include <QImage>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
|
||||
namespace Ui {
|
||||
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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -31,12 +31,12 @@
|
||||
|
||||
#include <QButtonGroup>
|
||||
#include <QDir>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFontDatabase>
|
||||
#include <QKeyEvent>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollBar>
|
||||
#include <QScreen>
|
||||
#include <QSettings>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
@ -466,7 +466,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
|
||||
QSettings settings;
|
||||
if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) {
|
||||
// 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);
|
||||
|
@ -9,22 +9,22 @@
|
||||
|
||||
#include <qt/splashscreen.h>
|
||||
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/networkstyle.h>
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <clientversion.h>
|
||||
#include <interfaces/handler.h>
|
||||
#include <interfaces/node.h>
|
||||
#include <interfaces/wallet.h>
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/networkstyle.h>
|
||||
#include <ui_interface.h>
|
||||
#include <util/system.h>
|
||||
#include <version.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCloseEvent>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
|
||||
|
||||
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);
|
||||
pixPaint.setFont(fontBold);
|
||||
QFontMetrics fm = pixPaint.fontMetrics();
|
||||
int titleTextWidth = fm.width(titleText);
|
||||
int titleTextWidth = GUIUtil::TextWidth(fm, titleText);
|
||||
if (titleTextWidth > width * 0.8) {
|
||||
fontFactor = 0.75;
|
||||
}
|
||||
@ -97,14 +97,14 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
||||
fontBold.setPointSize(50 * fontFactor);
|
||||
pixPaint.setFont(fontBold);
|
||||
fm = pixPaint.fontMetrics();
|
||||
titleTextWidth = fm.width(titleText);
|
||||
titleTextWidth = GUIUtil::TextWidth(fm, titleText);
|
||||
int titleTextHeight = fm.height();
|
||||
pixPaint.drawText((width / 2) - (titleTextWidth / 2), titleTextHeight + paddingTop, titleText);
|
||||
|
||||
fontNormal.setPointSize(16 * fontFactor);
|
||||
pixPaint.setFont(fontNormal);
|
||||
fm = pixPaint.fontMetrics();
|
||||
int versionTextWidth = fm.width(versionText);
|
||||
int versionTextWidth = GUIUtil::TextWidth(fm, versionText);
|
||||
pixPaint.drawText((width / 2) - (versionTextWidth / 2), titleTextHeight + paddingTop + titleVersionVSpace, versionText);
|
||||
|
||||
// draw additional text if special network
|
||||
@ -112,7 +112,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
||||
fontBold.setPointSize(10 * fontFactor);
|
||||
pixPaint.setFont(fontBold);
|
||||
fm = pixPaint.fontMetrics();
|
||||
int titleAddTextWidth = fm.width(titleAddText);
|
||||
int titleAddTextWidth = GUIUtil::TextWidth(fm, titleAddText);
|
||||
// Draw the badge background with the network-specific color
|
||||
QRect badgeRect = QRect(width - titleAddTextWidth - 20, 5, width, fm.height() + 10);
|
||||
QColor badgeColor = networkStyle->getBadgeColor();
|
||||
@ -128,7 +128,7 @@ SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const Netw
|
||||
QRect r(QPoint(), QSize(width, height));
|
||||
resize(r.size());
|
||||
setFixedSize(r.size());
|
||||
move(QApplication::desktop()->screenGeometry().center() - r.center());
|
||||
move(QGuiApplication::primaryScreen()->geometry().center() - r.center());
|
||||
|
||||
subscribeToCoreSignals();
|
||||
installEventFilter(this);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/walletcontroller.h>
|
||||
|
||||
#include <interfaces/handler.h>
|
||||
@ -70,7 +71,7 @@ void WalletController::closeWallet(WalletModel* wallet_model, QWidget* parent)
|
||||
{
|
||||
QMessageBox box(parent);
|
||||
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.setStandardButtons(QMessageBox::Yes|QMessageBox::Cancel);
|
||||
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();
|
||||
QString type = ttm->index(start, TransactionTableModel::Type, parent).data().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()
|
||||
|
@ -40,32 +40,46 @@ bool EqualDescriptor(std::string a, std::string b)
|
||||
return a == b;
|
||||
}
|
||||
|
||||
std::string MaybeUseHInsteadOfApostrophy(std::string ret)
|
||||
std::string UseHInsteadOfApostrophe(const std::string& desc)
|
||||
{
|
||||
if (InsecureRandBool()) {
|
||||
while (true) {
|
||||
auto it = ret.find("'");
|
||||
if (it != std::string::npos) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
std::string ret = desc;
|
||||
while (true) {
|
||||
auto it = ret.find('\'');
|
||||
if (it == std::string::npos) break;
|
||||
ret[it] = 'h';
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
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.
|
||||
std::unique_ptr<Descriptor> parse_priv = Parse(MaybeUseHInsteadOfApostrophy(prv), keys_priv);
|
||||
std::unique_ptr<Descriptor> parse_pub = Parse(MaybeUseHInsteadOfApostrophy(pub), keys_pub);
|
||||
if (replace_apostrophe_with_h_in_prv) {
|
||||
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_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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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()
|
||||
|
@ -57,7 +57,7 @@ static void SetInternalName(std::string name) { }
|
||||
|
||||
void util::ThreadRename(std::string&& name)
|
||||
{
|
||||
SetThreadName(("dash-" + name).c_str());
|
||||
SetThreadName(("d-" + name).c_str());
|
||||
SetInternalName(std::move(name));
|
||||
}
|
||||
|
||||
|
@ -1895,7 +1895,8 @@ public:
|
||||
|
||||
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 &&
|
||||
((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ class Chain;
|
||||
} // namespace interfaces
|
||||
|
||||
//! 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
|
||||
//! being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
||||
// This function will perform salvage on the wallet if requested, as long as only one wallet is
|
||||
// being loaded (WalletInit::ParameterInteraction() forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet).
|
||||
bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wallet_files);
|
||||
|
||||
//! 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)
|
||||
{
|
||||
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
|
||||
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,
|
||||
)
|
||||
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):
|
||||
def set_test_params(self):
|
||||
@ -38,7 +60,7 @@ class NetTest(BitcoinTestFramework):
|
||||
|
||||
self._test_connection_count()
|
||||
self._test_getnettotals()
|
||||
self._test_getnetworkinginfo()
|
||||
self._test_getnetworkinfo()
|
||||
self._test_getaddednodeinfo()
|
||||
self._test_getpeerinfo()
|
||||
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['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()['connections'], 2)
|
||||
|
||||
@ -92,6 +114,11 @@ class NetTest(BitcoinTestFramework):
|
||||
assert_equal(self.nodes[0].getnetworkinfo()['networkactive'], True)
|
||||
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):
|
||||
assert_equal(self.nodes[0].getaddednodeinfo(), [])
|
||||
# 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
|
||||
assert_equal(peer_info[0][0]['addrbind'], peer_info[1][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):
|
||||
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
|
||||
|
||||
NODE_NETWORK = (1 << 0)
|
||||
# NODE_GETUTXO = (1 << 1)
|
||||
NODE_GETUTXO = (1 << 1)
|
||||
NODE_BLOOM = (1 << 2)
|
||||
NODE_COMPACT_FILTERS = (1 << 6)
|
||||
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")
|
||||
except exc as e:
|
||||
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:
|
||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||
else:
|
||||
@ -128,7 +130,9 @@ def try_rpc(code, message, fun, *args, **kwds):
|
||||
if (code is not None) and (code != 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']):
|
||||
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
|
||||
except Exception as e:
|
||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||
|
@ -17,6 +17,8 @@ from test_framework.util import (
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
FEATURE_LATEST = 120200
|
||||
|
||||
|
||||
class MultiWalletTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
@ -27,6 +29,13 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||
def skip_test_if_missing_module(self):
|
||||
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):
|
||||
node = self.nodes[0]
|
||||
|
||||
@ -328,6 +337,22 @@ class MultiWalletTest(BitcoinTestFramework):
|
||||
self.nodes[0].unloadwallet(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__':
|
||||
MultiWalletTest().main()
|
||||
|
Loading…
Reference in New Issue
Block a user