Merge pull request #4570 from kittywhiskers/miscports_again_again

merge bitcoin#15928...#16984: backports
This commit is contained in:
PastaPastaPasta 2021-12-12 16:00:56 -05:00 committed by GitHub
commit a7f5379e19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 457 additions and 209 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -127,7 +127,7 @@
<customwidget>
<class>QRImageWidget</class>
<extends>QLabel</extends>
<header>qt/receiverequestdialog.h</header>
<header>qt/qrimagewidget.h</header>
</customwidget>
</customwidgets>
<resources/>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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

View File

@ -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));
if (ui->lblQRCode->setQR(uri, info.address)) {
ui->btnSaveAs->setEnabled(true);
}
}
#endif
}
void ReceiveRequestDialog::on_btnCopyURI_clicked()

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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()

View File

@ -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()) {
std::string ret = desc;
while (true) {
auto it = ret.find("'");
if (it != std::string::npos) {
auto it = ret.find('\'');
if (it == std::string::npos) break;
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;
}
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)

View File

@ -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()

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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;

View File

@ -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())

View File

@ -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)

View File

@ -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__)

View File

@ -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()