From ea9ad3d1ffff56b28fac8d0f772c9acf93207984 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 7 Oct 2021 16:06:42 +0000 Subject: [PATCH 01/23] Revert "net: remove SetMaxOutboundTarget" This reverts commit 2f3f1aec1f8aadd4a6fb08ca5da7eeda31eb388f. --- src/net.cpp | 7 +++++++ src/net.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/net.cpp b/src/net.cpp index ca186a45fa..6a5453cef1 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -3659,6 +3659,13 @@ void CConnman::RecordBytesSent(uint64_t bytes) nMaxOutboundTotalBytesSentInCycle += bytes; } +void CConnman::SetMaxOutboundTarget(uint64_t limit) +{ + AssertLockNotHeld(m_total_bytes_sent_mutex); + LOCK(m_total_bytes_sent_mutex); + nMaxOutboundLimit = limit; +} + uint64_t CConnman::GetMaxOutboundTarget() const { AssertLockNotHeld(m_total_bytes_sent_mutex); diff --git a/src/net.h b/src/net.h index b04b73e696..0f15eeb54b 100644 --- a/src/net.h +++ b/src/net.h @@ -1226,6 +1226,8 @@ public: void AddLocalServices(ServiceFlags services) { nLocalServices = ServiceFlags(nLocalServices | services); }; void RemoveLocalServices(ServiceFlags services) { nLocalServices = ServiceFlags(nLocalServices & ~services); } + //! set the max outbound target in bytes + void SetMaxOutboundTarget(uint64_t limit) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); uint64_t GetMaxOutboundTarget() const EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex); std::chrono::seconds GetMaxOutboundTimeframe() const; From 8518a3e56ebdb2147acccaf42416f04caff598cc Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 11 Feb 2016 05:58:01 +0000 Subject: [PATCH 02/23] Qt/Options: Expose maxuploadtarget in GUI using rwconf --- src/qt/forms/optionsdialog.ui | 40 +++++++++++++++++++++++++++++++++++ src/qt/optionsdialog.cpp | 24 +++++++++++++++++++++ src/qt/optionsmodel.cpp | 11 ++++++++++ src/qt/optionsmodel.h | 1 + 4 files changed, 76 insertions(+) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index b14df782f2..20e01cb5a5 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -672,6 +672,46 @@ + + + + + + Try to keep upload traffic under + + + + + + + + + + MB per day + + + Qt::PlainText + + + maxuploadtargetCheckbox + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 4767337d6d..d4ed792c92 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -132,6 +132,10 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled); connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState); + ui->maxuploadtarget->setMinimum(144 /* MB/day */); + ui->maxuploadtarget->setMaximum(std::numeric_limits::max()); + connect(ui->maxuploadtargetCheckbox, SIGNAL(toggled(bool)), ui->maxuploadtarget, SLOT(setEnabled(bool))); + /* Window elements init */ #ifdef Q_OS_MACOS /* remove Window tab on Mac */ @@ -324,6 +328,20 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->proxyIpTor, OptionsModel::ProxyIPTor); mapper->addMapping(ui->proxyPortTor, OptionsModel::ProxyPortTor); + int current_maxuploadtarget = model->data(model->index(OptionsModel::maxuploadtarget, 0), Qt::EditRole).toInt(); + if (current_maxuploadtarget == 0) { + ui->maxuploadtargetCheckbox->setChecked(false); + ui->maxuploadtarget->setEnabled(false); + ui->maxuploadtarget->setValue(ui->maxuploadtarget->minimum()); + } else { + if (current_maxuploadtarget < ui->maxuploadtarget->minimum()) { + ui->maxuploadtarget->setMinimum(current_maxuploadtarget); + } + ui->maxuploadtargetCheckbox->setChecked(true); + ui->maxuploadtarget->setEnabled(true); + ui->maxuploadtarget->setValue(current_maxuploadtarget); + } + /* Window */ #ifndef Q_OS_MACOS if (QSystemTrayIcon::isSystemTrayAvailable()) { @@ -431,6 +449,12 @@ void OptionsDialog::on_okButton_clicked() model->setData(model->index(OptionsModel::FontForMoney, 0), ui->moneyFont->itemData(ui->moneyFont->currentIndex())); model->setData(model->index(OptionsModel::FontForQRCodes, 0), ui->qrFont->itemData(ui->qrFont->currentIndex())); + if (ui->maxuploadtargetCheckbox->isChecked()) { + model->setData(model->index(OptionsModel::maxuploadtarget, 0), ui->maxuploadtarget->value()); + } else { + model->setData(model->index(OptionsModel::maxuploadtarget, 0), 0); + } + mapper->submit(); accept(); updateDefaultProxyNets(); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 2beecc4aa9..c1d34481bf 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include // for -dbcache defaults #include #include // For DEFAULT_SCRIPTCHECK_THREADS @@ -512,6 +513,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return SettingToBool(setting(), false); case MaskValues: return m_mask_values; + case maxuploadtarget: + return qlonglong(node().context()->connman->GetMaxOutboundTarget() / 1024 / 1024); default: return QVariant(); } @@ -752,6 +755,14 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: m_mask_values = value.toBool(); settings.setValue("mask_values", m_mask_values); break; + case maxuploadtarget: + { + if (changed()) { + gArgs.ModifyRWConfigFile("maxuploadtarget", value.toString().toStdString()); + node().context()->connman->SetMaxOutboundTarget(value.toLongLong() * 1024 * 1024); + } + break; + } default: break; } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 027dd42fe1..85342558d5 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -78,6 +78,7 @@ public: Server, // bool EnablePSBTControls, // bool MaskValues, // bool + maxuploadtarget, OptionIDRowCount, }; From fa7fcf57250f2019853f460354d90eb16d23427d Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 11 Feb 2016 06:46:42 +0000 Subject: [PATCH 03/23] Qt/Options: Expose peerbloomfilters in GUI using rwconf --- src/qt/forms/optionsdialog.ui | 10 ++++++++++ src/qt/optionsdialog.cpp | 3 +++ src/qt/optionsmodel.cpp | 13 +++++++++++++ src/qt/optionsmodel.h | 4 ++++ 4 files changed, 30 insertions(+) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 20e01cb5a5..1624afce8d 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -712,6 +712,16 @@ + + + + Support filtering of blocks and transaction with bloom filters + + + Provide search services for light clients + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index d4ed792c92..5019f7e6e8 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -282,6 +282,7 @@ void OptionsDialog::setModel(OptionsModel *_model) connect(ui->enableServer, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); + connect(ui->peerbloomfilters, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); /* Display */ connect(ui->lang, qOverload<>(&QValueComboBox::valueChanged), [this]{ showRestartWarning(); }); connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged, [this]{ showRestartWarning(); }); @@ -342,6 +343,8 @@ void OptionsDialog::setMapper() ui->maxuploadtarget->setValue(current_maxuploadtarget); } + mapper->addMapping(ui->peerbloomfilters, OptionsModel::peerbloomfilters); + /* Window */ #ifndef Q_OS_MACOS if (QSystemTrayIcon::isSystemTrayAvailable()) { diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index c1d34481bf..cbd5184e21 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -252,6 +253,9 @@ bool OptionsModel::Init(bilingual_str& error) if (!gArgs.SoftSetArg("-port", settings.value("nNetworkPort").toString().toStdString())) addOverriddenOption("-port"); + // rwconf settings that require a restart + f_peerbloomfilters = gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS); + // Display if (settings.contains("FontForMoney")) { m_font_money = FontChoiceFromString(settings.value("FontForMoney").toString()); @@ -515,6 +519,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return m_mask_values; case maxuploadtarget: return qlonglong(node().context()->connman->GetMaxOutboundTarget() / 1024 / 1024); + case peerbloomfilters: + return f_peerbloomfilters; default: return QVariant(); } @@ -763,6 +769,13 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: } break; } + case peerbloomfilters: + if (changed()) { + gArgs.ModifyRWConfigFile("peerbloomfilters", strprintf("%d", value.toBool())); + f_peerbloomfilters = value.toBool(); + setRestartRequired(true); + } + break; default: break; } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 85342558d5..f600da8c84 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -79,6 +79,7 @@ public: EnablePSBTControls, // bool MaskValues, // bool maxuploadtarget, + peerbloomfilters, // bool OptionIDRowCount, }; @@ -150,6 +151,9 @@ private: static QString FontChoiceToString(const OptionsModel::FontChoice&); static FontChoice FontChoiceFromString(const QString&); + /* rwconf settings that require a restart */ + bool f_peerbloomfilters; + // Add option to list of GUI options overridden through command line/config file void addOverriddenOption(const std::string &option); From 3a2e18081acfc61ca7bb0c9b937ea33895db71c1 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 13 Mar 2018 20:46:55 +0000 Subject: [PATCH 04/23] Qt/Options: Expose addresstype in GUI using rwconf NOTE: This doesn't update the receive dialog's "Generate Bech32 address" checkbox at all. That's okay for Bitcoin Knots since it just affects the default checked state, but Core hides the checkbox entirely when addresstype=legacy, so you would want to show/hide it as necessary. --- src/qt/forms/optionsdialog.ui | 51 +++++++++++++++++++++++++++++++++++ src/qt/optionsdialog.cpp | 26 ++++++++++++++++++ src/qt/optionsmodel.cpp | 30 +++++++++++++++++++++ src/qt/optionsmodel.h | 1 + 4 files changed, 108 insertions(+) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 1624afce8d..fd9766f9c9 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -295,6 +295,57 @@ + + + + Address type + + + false + + + + + + + 0 + 0 + + + + Legacy + + + + + + + + 0 + 0 + + + + P2SH Segwit + + + + + + + + 0 + 0 + + + + Native Segwit (bech32) + + + + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 5019f7e6e8..98b2489f19 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -314,6 +314,18 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath); mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls); + { + QString radio_name_lower = "addresstype" + model->data(model->index(OptionsModel::addresstype, 0), Qt::EditRole).toString().toLower(); + radio_name_lower.replace("-", "_"); + for (int i = ui->layoutAddressType->count(); i--; ) { + QRadioButton * const radio = qobject_cast(ui->layoutAddressType->itemAt(i)->widget()); + if (!radio) { + continue; + } + radio->setChecked(radio->objectName().toLower() == radio_name_lower); + } + } + /* Network */ mapper->addMapping(ui->networkPort, OptionsModel::NetworkPort); mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); @@ -452,6 +464,20 @@ void OptionsDialog::on_okButton_clicked() model->setData(model->index(OptionsModel::FontForMoney, 0), ui->moneyFont->itemData(ui->moneyFont->currentIndex())); model->setData(model->index(OptionsModel::FontForQRCodes, 0), ui->qrFont->itemData(ui->qrFont->currentIndex())); + { + QString new_addresstype; + for (int i = ui->layoutAddressType->count(); i--; ) { + QRadioButton * const radio = qobject_cast(ui->layoutAddressType->itemAt(i)->widget()); + if (!(radio && radio->objectName().startsWith("addressType") && radio->isChecked())) { + continue; + } + new_addresstype = radio->objectName().mid(11).toLower(); + new_addresstype.replace("_", "-"); + break; + } + model->setData(model->index(OptionsModel::addresstype, 0), new_addresstype); + } + if (ui->maxuploadtargetCheckbox->isChecked()) { model->setData(model->index(OptionsModel::maxuploadtarget, 0), ui->maxuploadtarget->value()); } else { diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index cbd5184e21..1ca95dd414 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -19,11 +19,16 @@ #include #include #include +#include #include // for -dbcache defaults #include #include // For DEFAULT_SCRIPTCHECK_THREADS #include // For DEFAULT_SPEND_ZEROCONF_CHANGE +#ifdef ENABLE_WALLET +#include +#endif + #include #include #include @@ -484,6 +489,11 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return QString::fromStdString(SettingToString(setting(), "")); case SubFeeFromAmount: return m_sub_fee_from_amount; + case addresstype: + { + const OutputType default_address_type = ParseOutputType(gArgs.GetArg("-addresstype", "")).value_or(wallet::DEFAULT_ADDRESS_TYPE); + return QString::fromStdString(FormatOutputType(default_address_type)); + } #endif case DisplayUnit: return QVariant::fromValue(m_display_bitcoin_unit); @@ -671,6 +681,26 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: m_sub_fee_from_amount = value.toBool(); settings.setValue("SubFeeFromAmount", m_sub_fee_from_amount); break; + case addresstype: + { + const std::string newvalue_str = value.toString().toStdString(); + const OutputType oldvalue = ParseOutputType(gArgs.GetArg("-addresstype", "")).value_or(wallet::DEFAULT_ADDRESS_TYPE); + const OutputType newvalue = ParseOutputType(newvalue_str).value_or(oldvalue); + if (newvalue != oldvalue) { + gArgs.ModifyRWConfigFile("addresstype", newvalue_str); + gArgs.ForceSetArg("-addresstype", newvalue_str); + for (auto& wallet_interface : m_node.walletLoader().getWallets()) { + wallet::CWallet *wallet; + if (wallet_interface && (wallet = wallet_interface->wallet())) { + wallet->m_default_address_type = newvalue; + } else { + setRestartRequired(true); + continue; + } + } + } + break; + } #endif case DisplayUnit: setDisplayUnit(value); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f600da8c84..0853c0de40 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -74,6 +74,7 @@ public: DatabaseCache, // int ExternalSignerPath, // QString SpendZeroConfChange, // bool + addresstype, // QString Listen, // bool Server, // bool EnablePSBTControls, // bool From a28c48bc4cfcb7fa0535901f84e3c16a07928435 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 3 Aug 2023 19:22:39 +0000 Subject: [PATCH 05/23] Qt/Options: Switch prune setting from GB to MiB --- src/qt/bitcoin.cpp | 2 +- src/qt/forms/intro.ui | 6 ++--- src/qt/forms/optionsdialog.ui | 4 +-- src/qt/guiconstants.h | 5 +++- src/qt/intro.cpp | 29 ++++++++++----------- src/qt/intro.h | 2 +- src/qt/optionsdialog.cpp | 13 +++++----- src/qt/optionsmodel.cpp | 47 ++++++++++++++++------------------- src/qt/optionsmodel.h | 14 ++--------- 9 files changed, 56 insertions(+), 66 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 690c2b6f5a..375a2d9fcf 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -340,7 +340,7 @@ void BitcoinApplication::parameterSetup() void BitcoinApplication::InitPruneSetting(int64_t prune_MiB) { - optionsModel->SetPruneTargetGB(PruneMiBtoGB(prune_MiB)); + optionsModel->SetPruneTargetMiB(prune_MiB); } void BitcoinApplication::requestInitialize() diff --git a/src/qt/forms/intro.ui b/src/qt/forms/intro.ui index 074ed42c92..d417f8fa38 100644 --- a/src/qt/forms/intro.ui +++ b/src/qt/forms/intro.ui @@ -243,16 +243,16 @@ - + - GB + MiB - pruneGB + pruneMiB diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index fd9766f9c9..5576f02f90 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -63,12 +63,12 @@ - + - GB + MiB Qt::PlainText diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 30ffa302a4..a0467369b3 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -54,10 +54,13 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80; #define QAPP_APP_NAME_SIGNET "Bitcoin-Qt-signet" #define QAPP_APP_NAME_REGTEST "Bitcoin-Qt-regtest" +/* One mebibyte (MiB) in bytes */ +static constexpr uint64_t MiB_BYTES{1024 * 1024}; + /* One gigabyte (GB) in bytes */ static constexpr uint64_t GB_BYTES{1000000000}; // Default prune target displayed in GUI. -static constexpr int DEFAULT_PRUNE_TARGET_GB{2}; +static constexpr int DEFAULT_PRUNE_TARGET_MiB{1907}; #endif // BITCOIN_QT_GUICONSTANTS_H diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index b5cabe06e8..c3d2075901 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -111,11 +111,11 @@ void FreespaceChecker::check() namespace { //! Return pruning size that will be used if automatic pruning is enabled. -int GetPruneTargetGB() +int GetPruneTargetMiB() { int64_t prune_target_mib = gArgs.GetIntArg("-prune", 0); // >1 means automatic pruning is enabled by config, 1 means manual pruning, 0 means no pruning. - return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib) : DEFAULT_PRUNE_TARGET_GB; + return prune_target_mib > 1 ? prune_target_mib : DEFAULT_PRUNE_TARGET_MiB; } } // namespace @@ -124,7 +124,7 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si ui(new Ui::Intro), m_blockchain_size_gb(blockchain_size_gb), m_chain_state_size_gb(chain_state_size_gb), - m_prune_target_gb{GetPruneTargetGB()} + m_prune_target_mib{GetPruneTargetMiB()} { ui->setupUi(this); ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME)); @@ -138,15 +138,15 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si ); ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME)); - const int min_prune_target_GB = std::ceil(MIN_DISK_SPACE_FOR_BLOCK_FILES / 1e9); - ui->pruneGB->setRange(min_prune_target_GB, std::numeric_limits::max()); + const int min_prune_target_MiB = (MIN_DISK_SPACE_FOR_BLOCK_FILES + MiB_BYTES - 1) / MiB_BYTES; + ui->pruneMiB->setRange(min_prune_target_MiB, std::numeric_limits::max()); if (gArgs.IsArgSet("-prune")) { m_prune_checkbox_is_default = false; ui->prune->setChecked(gArgs.GetIntArg("-prune", 0) >= 1); ui->prune->setEnabled(false); } - ui->pruneGB->setValue(m_prune_target_gb); - ui->pruneGB->setToolTip(ui->prune->toolTip()); + ui->pruneMiB->setValue(m_prune_target_mib); + ui->pruneMiB->setToolTip(ui->prune->toolTip()); ui->lblPruneSuffix->setToolTip(ui->prune->toolTip()); UpdatePruneLabels(ui->prune->isChecked()); @@ -155,8 +155,8 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si UpdatePruneLabels(prune_checked); UpdateFreeSpaceLabel(); }); - connect(ui->pruneGB, qOverload(&QSpinBox::valueChanged), [this](int prune_GB) { - m_prune_target_gb = prune_GB; + connect(ui->pruneMiB, qOverload(&QSpinBox::valueChanged), [this](int prune_MiB) { + m_prune_target_mib = prune_MiB; UpdatePruneLabels(ui->prune->isChecked()); UpdateFreeSpaceLabel(); }); @@ -196,7 +196,7 @@ int64_t Intro::getPruneMiB() const { switch (ui->prune->checkState()) { case Qt::Checked: - return PruneGBtoMiB(m_prune_target_gb); + return m_prune_target_mib; case Qt::Unchecked: default: return 0; } @@ -376,14 +376,15 @@ void Intro::UpdatePruneLabels(bool prune_checked) { m_required_space_gb = m_blockchain_size_gb + m_chain_state_size_gb; QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time."); - if (prune_checked && m_prune_target_gb <= m_blockchain_size_gb) { - m_required_space_gb = m_prune_target_gb + m_chain_state_size_gb; + const int64_t prune_target_gb = (m_prune_target_mib * MiB_BYTES + GB_BYTES - 1) / GB_BYTES; + if (prune_checked && prune_target_gb <= m_blockchain_size_gb) { + m_required_space_gb = prune_target_gb + m_chain_state_size_gb; storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory."); } - ui->pruneGB->setEnabled(prune_checked); + ui->pruneMiB->setEnabled(prune_checked); static constexpr uint64_t nPowTargetSpacing = 10 * 60; // from chainparams, which we don't have at this stage static constexpr uint32_t expected_block_data_size = 2250000; // includes undo data - const uint64_t expected_backup_days = m_prune_target_gb * 1e9 / (uint64_t(expected_block_data_size) * 86400 / nPowTargetSpacing); + const uint64_t expected_backup_days = m_prune_target_mib * MiB_BYTES / (uint64_t(expected_block_data_size) * 86400 / nPowTargetSpacing); ui->lblPruneSuffix->setText( //: Explanatory text on the capability of the current prune target. tr("(sufficient to restore backups %n day(s) old)", "", expected_backup_days)); diff --git a/src/qt/intro.h b/src/qt/intro.h index 7b34c73b02..c3f80fd007 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -74,7 +74,7 @@ private: //! Total required space (in GB) depending on user choice (prune or not prune). int64_t m_required_space_gb{0}; uint64_t m_bytes_available{0}; - int64_t m_prune_target_gb; + int64_t m_prune_target_mib; void startThread(); void checkPath(const QString &dataDir); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 98b2489f19..64f398bd0b 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -102,8 +102,8 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) ui->pruneWarning->setVisible(false); ui->pruneWarning->setStyleSheet("QLabel { color: red; }"); - ui->pruneSize->setEnabled(false); - connect(ui->prune, &QPushButton::toggled, ui->pruneSize, &QWidget::setEnabled); + ui->pruneSizeMiB->setEnabled(false); + connect(ui->prune, &QPushButton::toggled, ui->pruneSizeMiB, &QWidget::setEnabled); ui->networkPort->setValidator(new QIntValidator(1024, 65535, this)); connect(ui->networkPort, SIGNAL(textChanged(const QString&)), this, SLOT(checkLineEdit())); @@ -243,9 +243,8 @@ void OptionsDialog::setModel(OptionsModel *_model) if (_model->isRestartRequired()) showRestartWarning(true); - // Prune values are in GB to be consistent with intro.cpp - static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES / GB_BYTES) + (MIN_DISK_SPACE_FOR_BLOCK_FILES % GB_BYTES) ? 1 : 0; - ui->pruneSize->setRange(nMinDiskSpace, std::numeric_limits::max()); + static constexpr uint64_t nMinDiskSpace = (MIN_DISK_SPACE_FOR_BLOCK_FILES + MiB_BYTES - 1) / MiB_BYTES; + ui->pruneSizeMiB->setRange(nMinDiskSpace, std::numeric_limits::max()); QString strLabel = _model->getOverriddenByCommandLine(); if (strLabel.isEmpty()) @@ -270,7 +269,7 @@ void OptionsDialog::setModel(OptionsModel *_model) /* Main */ connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->prune, &QCheckBox::clicked, this, &OptionsDialog::togglePruneWarning); - connect(ui->pruneSize, qOverload(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); + connect(ui->pruneSizeMiB, qOverload(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); connect(ui->databaseCache, qOverload(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); connect(ui->externalSignerPath, &QLineEdit::textChanged, [this]{ showRestartWarning(); }); connect(ui->threadsScriptVerif, qOverload(&QSpinBox::valueChanged), this, &OptionsDialog::showRestartWarning); @@ -305,7 +304,7 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->threadsScriptVerif, OptionsModel::ThreadsScriptVerif); mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache); mapper->addMapping(ui->prune, OptionsModel::Prune); - mapper->addMapping(ui->pruneSize, OptionsModel::PruneSize); + mapper->addMapping(ui->pruneSizeMiB, OptionsModel::PruneSizeMiB); /* Wallet */ mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 1ca95dd414..9829092217 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -53,7 +53,7 @@ static const char* SettingName(OptionsModel::OptionID option) case OptionsModel::MapPortNatpmp: return "natpmp"; case OptionsModel::Listen: return "listen"; case OptionsModel::Server: return "server"; - case OptionsModel::PruneSize: return "prune"; + case OptionsModel::PruneSizeMiB: return "prune"; case OptionsModel::Prune: return "prune"; case OptionsModel::ProxyIP: return "proxy"; case OptionsModel::ProxyPort: return "proxy"; @@ -73,7 +73,7 @@ static void UpdateRwSetting(interfaces::Node& node, OptionsModel::OptionID optio (option == OptionsModel::DatabaseCache || option == OptionsModel::ThreadsScriptVerif || option == OptionsModel::Prune || - option == OptionsModel::PruneSize)) { + option == OptionsModel::PruneSizeMiB)) { // Write certain old settings as strings, even though they are numbers, // because Bitcoin 22.x releases try to read these specific settings as // strings in addOverriddenOption() calls at startup, triggering @@ -88,10 +88,10 @@ static void UpdateRwSetting(interfaces::Node& node, OptionsModel::OptionID optio } //! Convert enabled/size values to bitcoin -prune setting. -static common::SettingsValue PruneSetting(bool prune_enabled, int prune_size_gb) +static common::SettingsValue PruneSettingFromMiB(bool prune_enabled, int prune_size_mib) { - assert(!prune_enabled || prune_size_gb >= 1); // PruneSizeGB and ParsePruneSizeGB never return less - return prune_enabled ? PruneGBtoMiB(prune_size_gb) : 0; + assert(!prune_enabled || prune_size_mib >= 1); // PruneSizeMiB and ParsePruneSizeMiB never return less + return prune_enabled ? prune_size_mib : 0; } //! Get pruning enabled value to show in GUI from bitcoin -prune setting. @@ -103,18 +103,10 @@ static bool PruneEnabled(const common::SettingsValue& prune_setting) //! Get pruning size value to show in GUI from bitcoin -prune setting. If //! pruning is not enabled, just show default recommended pruning size (2GB). -static int PruneSizeGB(const common::SettingsValue& prune_setting) +static int PruneSizeAsMiB(const common::SettingsValue& prune_setting) { int value = SettingToInt(prune_setting, 0); - return value > 1 ? PruneMiBtoGB(value) : DEFAULT_PRUNE_TARGET_GB; -} - -//! Parse pruning size value provided by user in GUI or loaded from QSettings -//! (windows registry key or qt .conf file). Smallest value that the GUI can -//! display is 1 GB, so round up if anything less is parsed. -static int ParsePruneSizeGB(const QVariant& prune_size) -{ - return std::max(1, prune_size.toInt()); + return value > 1 ? value : DEFAULT_PRUNE_TARGET_MiB; } struct ProxySetting { @@ -375,10 +367,10 @@ static QString GetDefaultProxyAddress() return QString("%1:%2").arg(DEFAULT_GUI_PROXY_HOST).arg(DEFAULT_GUI_PROXY_PORT); } -void OptionsModel::SetPruneTargetGB(int prune_target_gb) +void OptionsModel::SetPruneTargetMiB(int prune_target_mib) { const common::SettingsValue cur_value = node().getPersistentSetting("prune"); - const common::SettingsValue new_value = PruneSetting(prune_target_gb > 0, prune_target_gb); + const common::SettingsValue new_value = PruneSettingFromMiB(prune_target_mib > 0, prune_target_mib); // Force setting to take effect. It is still safe to change the value at // this point because this function is only called after the intro screen is @@ -389,7 +381,7 @@ void OptionsModel::SetPruneTargetGB(int prune_target_gb) // from saved value. Avoid writing settings.json if bitcoin.conf value // doesn't need to be overridden. if (PruneEnabled(cur_value) != PruneEnabled(new_value) || - PruneSizeGB(cur_value) != PruneSizeGB(new_value)) { + PruneSizeAsMiB(cur_value) != PruneSizeAsMiB(new_value)) { // Call UpdateRwSetting() instead of setOption() to avoid setting // RestartRequired flag UpdateRwSetting(node(), Prune, "", new_value); @@ -513,10 +505,10 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return settings.value("enable_psbt_controls"); case Prune: return PruneEnabled(setting()); - case PruneSize: - return PruneEnabled(setting()) ? PruneSizeGB(setting()) : + case PruneSizeMiB: + return PruneEnabled(setting()) ? PruneSizeAsMiB(setting()) : suffix.empty() ? getOption(option, "-prev") : - DEFAULT_PRUNE_TARGET_GB; + DEFAULT_PRUNE_TARGET_MiB; case DatabaseCache: return qlonglong(SettingToInt(setting(), nDefaultDbCache)); case ThreadsScriptVerif: @@ -753,17 +745,17 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: case Prune: if (changed()) { if (suffix.empty() && !value.toBool()) setOption(option, true, "-prev"); - update(PruneSetting(value.toBool(), getOption(PruneSize).toInt())); + update(PruneSettingFromMiB(value.toBool(), getOption(PruneSizeMiB).toInt())); if (suffix.empty() && value.toBool()) UpdateRwSetting(node(), option, "-prev", {}); if (suffix.empty()) setRestartRequired(true); } break; - case PruneSize: + case PruneSizeMiB: if (changed()) { if (suffix.empty() && !getOption(Prune).toBool()) { setOption(option, value, "-prev"); } else { - update(PruneSetting(true, ParsePruneSizeGB(value))); + update(PruneSettingFromMiB(true, value.toInt())); } if (suffix.empty() && getOption(Prune).toBool()) setRestartRequired(true); } @@ -882,6 +874,11 @@ void OptionsModel::checkAndMigrate() ProxySetting parsed = ParseProxyString(value.toString()); setOption(ProxyIPTor, parsed.ip); setOption(ProxyPortTor, parsed.port); + } else if (option == PruneSizeMiB) { + // Stored as GB + const int64_t prune_size_gb = value.toInt(); + const int prune_size_mib = std::max(prune_size_gb * GB_BYTES / MiB_BYTES, MIN_DISK_SPACE_FOR_BLOCK_FILES / MiB_BYTES); + setOption(option, prune_size_mib); } else { setOption(option, value); } @@ -899,7 +896,7 @@ void OptionsModel::checkAndMigrate() migrate_setting(MapPortNatpmp, "fUseNatpmp"); migrate_setting(Listen, "fListen"); migrate_setting(Server, "server"); - migrate_setting(PruneSize, "nPruneSize"); + migrate_setting(PruneSizeMiB, "nPruneSize"); migrate_setting(Prune, "bPrune"); migrate_setting(ProxyIP, "addrProxy"); migrate_setting(ProxyUse, "fUseProxy"); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 0853c0de40..7d3c1eaa41 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -23,16 +23,6 @@ class Node; extern const char *DEFAULT_GUI_PROXY_HOST; static constexpr uint16_t DEFAULT_GUI_PROXY_PORT = 9050; -/** - * Convert configured prune target MiB to displayed GB. Round up to avoid underestimating max disk usage. - */ -static inline int PruneMiBtoGB(int64_t mib) { return (mib * 1024 * 1024 + GB_BYTES - 1) / GB_BYTES; } - -/** - * Convert displayed prune target GB to configured MiB. Round down so roundtrip GB -> MiB -> GB conversion is stable. - */ -static inline int64_t PruneGBtoMiB(int gb) { return gb * GB_BYTES / 1024 / 1024; } - /** Interface from Qt to configuration data structure for Bitcoin client. To Qt, the options are presented as a list with the different options laid out vertically. @@ -70,7 +60,7 @@ public: SubFeeFromAmount, // bool ThreadsScriptVerif, // int Prune, // bool - PruneSize, // int + PruneSizeMiB, // int DatabaseCache, // int ExternalSignerPath, // QString SpendZeroConfChange, // bool @@ -121,7 +111,7 @@ public: bool hasSigner(); /* Explicit setters */ - void SetPruneTargetGB(int prune_target_gb); + void SetPruneTargetMiB(int prune_target_mib); /* Restart flag helper */ void setRestartRequired(bool fRequired); From 32c3f975b4908ce1430fa0c2b5e099a3502b7f11 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 3 Aug 2023 20:34:08 +0000 Subject: [PATCH 06/23] Qt/Options: Handle manual pruning cleanly --- src/qt/intro.cpp | 22 +++++++++++--- src/qt/optionsdialog.cpp | 6 +++- src/qt/optionsmodel.cpp | 66 +++++++++++++++++++++++++++------------- src/qt/optionsmodel.h | 2 +- 4 files changed, 68 insertions(+), 28 deletions(-) diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index c3d2075901..26d1fcf9bb 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -142,22 +142,32 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si ui->pruneMiB->setRange(min_prune_target_MiB, std::numeric_limits::max()); if (gArgs.IsArgSet("-prune")) { m_prune_checkbox_is_default = false; - ui->prune->setChecked(gArgs.GetIntArg("-prune", 0) >= 1); + switch (gArgs.GetIntArg("-prune", 0)) { + case 0: + ui->prune->setChecked(false); + break; + case 1: + ui->prune->setTristate(); + ui->prune->setCheckState(Qt::PartiallyChecked); + break; + default: + ui->prune->setChecked(true); + } ui->prune->setEnabled(false); } ui->pruneMiB->setValue(m_prune_target_mib); ui->pruneMiB->setToolTip(ui->prune->toolTip()); ui->lblPruneSuffix->setToolTip(ui->prune->toolTip()); - UpdatePruneLabels(ui->prune->isChecked()); + UpdatePruneLabels(ui->prune->checkState() == Qt::Checked); - connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) { + connect(ui->prune, &QCheckBox::stateChanged, [this](int prune_state) { m_prune_checkbox_is_default = false; - UpdatePruneLabels(prune_checked); + UpdatePruneLabels(prune_state == Qt::Checked); UpdateFreeSpaceLabel(); }); connect(ui->pruneMiB, qOverload(&QSpinBox::valueChanged), [this](int prune_MiB) { m_prune_target_mib = prune_MiB; - UpdatePruneLabels(ui->prune->isChecked()); + UpdatePruneLabels(ui->prune->checkState() == Qt::Checked); UpdateFreeSpaceLabel(); }); @@ -197,6 +207,8 @@ int64_t Intro::getPruneMiB() const switch (ui->prune->checkState()) { case Qt::Checked: return m_prune_target_mib; + case Qt::PartiallyChecked: + return 1; case Qt::Unchecked: default: return 0; } diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 64f398bd0b..b61e1f1c03 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -303,7 +303,11 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->bitcoinAtStartup, OptionsModel::StartAtStartup); mapper->addMapping(ui->threadsScriptVerif, OptionsModel::ThreadsScriptVerif); mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache); - mapper->addMapping(ui->prune, OptionsModel::Prune); + + if (model->data(model->index(OptionsModel::PruneTristate, 0), Qt::EditRole).value() == Qt::PartiallyChecked) { + ui->prune->setTristate(); + } + mapper->addMapping(ui->prune, OptionsModel::PruneTristate); mapper->addMapping(ui->pruneSizeMiB, OptionsModel::PruneSizeMiB); /* Wallet */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 9829092217..967e86aac3 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -54,7 +54,7 @@ static const char* SettingName(OptionsModel::OptionID option) case OptionsModel::Listen: return "listen"; case OptionsModel::Server: return "server"; case OptionsModel::PruneSizeMiB: return "prune"; - case OptionsModel::Prune: return "prune"; + case OptionsModel::PruneTristate: return "prune"; case OptionsModel::ProxyIP: return "proxy"; case OptionsModel::ProxyPort: return "proxy"; case OptionsModel::ProxyUse: return "proxy"; @@ -72,7 +72,7 @@ static void UpdateRwSetting(interfaces::Node& node, OptionsModel::OptionID optio if (value.isNum() && (option == OptionsModel::DatabaseCache || option == OptionsModel::ThreadsScriptVerif || - option == OptionsModel::Prune || + option == OptionsModel::PruneTristate || option == OptionsModel::PruneSizeMiB)) { // Write certain old settings as strings, even though they are numbers, // because Bitcoin 22.x releases try to read these specific settings as @@ -88,10 +88,17 @@ static void UpdateRwSetting(interfaces::Node& node, OptionsModel::OptionID optio } //! Convert enabled/size values to bitcoin -prune setting. -static common::SettingsValue PruneSettingFromMiB(bool prune_enabled, int prune_size_mib) +static common::SettingsValue PruneSettingFromMiB(Qt::CheckState prune_enabled, int prune_size_mib) { - assert(!prune_enabled || prune_size_mib >= 1); // PruneSizeMiB and ParsePruneSizeMiB never return less - return prune_enabled ? prune_size_mib : 0; + assert(prune_enabled != Qt::Checked || prune_size_mib >= 1); // PruneSizeMiB and ParsePruneSizeMiB never return less + switch (prune_enabled) { + case Qt::Unchecked: + return 0; + case Qt::PartiallyChecked: + return 1; + default: + return prune_size_mib; + } } //! Get pruning enabled value to show in GUI from bitcoin -prune setting. @@ -101,6 +108,19 @@ static bool PruneEnabled(const common::SettingsValue& prune_setting) return SettingToInt(prune_setting, 0) > 1; } +//! Get pruning enabled value to show in GUI from bitcoin -prune setting. +static Qt::CheckState PruneSettingAsTristate(const common::SettingsValue& prune_setting) +{ + switch (SettingToInt(prune_setting, 0)) { + case 0: + return Qt::Unchecked; + case 1: + return Qt::PartiallyChecked; + default: + return Qt::Checked; + } +} + //! Get pruning size value to show in GUI from bitcoin -prune setting. If //! pruning is not enabled, just show default recommended pruning size (2GB). static int PruneSizeAsMiB(const common::SettingsValue& prune_setting) @@ -216,7 +236,7 @@ bool OptionsModel::Init(bilingual_str& error) // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. for (OptionID option : {DatabaseCache, ThreadsScriptVerif, SpendZeroConfChange, ExternalSignerPath, MapPortUPnP, - MapPortNatpmp, Listen, Server, Prune, ProxyUse, ProxyUseTor, Language}) { + MapPortNatpmp, Listen, Server, PruneTristate, ProxyUse, ProxyUseTor, Language}) { std::string setting = SettingName(option); if (node().isSettingIgnored(setting)) addOverriddenOption("-" + setting); try { @@ -370,7 +390,7 @@ static QString GetDefaultProxyAddress() void OptionsModel::SetPruneTargetMiB(int prune_target_mib) { const common::SettingsValue cur_value = node().getPersistentSetting("prune"); - const common::SettingsValue new_value = PruneSettingFromMiB(prune_target_mib > 0, prune_target_mib); + const common::SettingsValue new_value = prune_target_mib; // Force setting to take effect. It is still safe to change the value at // this point because this function is only called after the intro screen is @@ -380,16 +400,15 @@ void OptionsModel::SetPruneTargetMiB(int prune_target_mib) // Update settings.json if value configured in intro screen is different // from saved value. Avoid writing settings.json if bitcoin.conf value // doesn't need to be overridden. - if (PruneEnabled(cur_value) != PruneEnabled(new_value) || - PruneSizeAsMiB(cur_value) != PruneSizeAsMiB(new_value)) { + if (cur_value.write() != new_value.write()) { // Call UpdateRwSetting() instead of setOption() to avoid setting // RestartRequired flag - UpdateRwSetting(node(), Prune, "", new_value); + UpdateRwSetting(node(), PruneTristate, "", new_value); } // Keep previous pruning size, if pruning was disabled. if (PruneEnabled(cur_value)) { - UpdateRwSetting(node(), Prune, "-prev", PruneEnabled(new_value) ? common::SettingsValue{} : cur_value); + UpdateRwSetting(node(), PruneTristate, "-prev", PruneEnabled(new_value) ? common::SettingsValue{} : cur_value); } } @@ -503,8 +522,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return fCoinControlFeatures; case EnablePSBTControls: return settings.value("enable_psbt_controls"); - case Prune: - return PruneEnabled(setting()); + case PruneTristate: + return PruneSettingAsTristate(setting()); case PruneSizeMiB: return PruneEnabled(setting()) ? PruneSizeAsMiB(setting()) : suffix.empty() ? getOption(option, "-prev") : @@ -742,22 +761,24 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: m_enable_psbt_controls = value.toBool(); settings.setValue("enable_psbt_controls", m_enable_psbt_controls); break; - case Prune: + case PruneTristate: if (changed()) { - if (suffix.empty() && !value.toBool()) setOption(option, true, "-prev"); - update(PruneSettingFromMiB(value.toBool(), getOption(PruneSizeMiB).toInt())); - if (suffix.empty() && value.toBool()) UpdateRwSetting(node(), option, "-prev", {}); + const bool is_autoprune = (value.value() == Qt::Checked); + if (suffix.empty() && !is_autoprune) setOption(option, true, "-prev"); + update(PruneSettingFromMiB(value.value(), getOption(PruneSizeMiB).toInt())); + if (suffix.empty() && is_autoprune) UpdateRwSetting(node(), option, "-prev", {}); if (suffix.empty()) setRestartRequired(true); } break; case PruneSizeMiB: if (changed()) { - if (suffix.empty() && !getOption(Prune).toBool()) { + const bool is_autoprune = (Qt::Checked == getOption(PruneTristate).value()); + if (suffix.empty() && !is_autoprune) { setOption(option, value, "-prev"); } else { - update(PruneSettingFromMiB(true, value.toInt())); + update(PruneSettingFromMiB(Qt::Checked, value.toInt())); } - if (suffix.empty() && getOption(Prune).toBool()) setRestartRequired(true); + if (suffix.empty() && is_autoprune) setRestartRequired(true); } break; case DatabaseCache: @@ -879,6 +900,9 @@ void OptionsModel::checkAndMigrate() const int64_t prune_size_gb = value.toInt(); const int prune_size_mib = std::max(prune_size_gb * GB_BYTES / MiB_BYTES, MIN_DISK_SPACE_FOR_BLOCK_FILES / MiB_BYTES); setOption(option, prune_size_mib); + } else if (option == PruneTristate) { + // Stored as bool + setOption(option, value.toBool() ? Qt::Checked : Qt::Unchecked); } else { setOption(option, value); } @@ -897,7 +921,7 @@ void OptionsModel::checkAndMigrate() migrate_setting(Listen, "fListen"); migrate_setting(Server, "server"); migrate_setting(PruneSizeMiB, "nPruneSize"); - migrate_setting(Prune, "bPrune"); + migrate_setting(PruneTristate, "bPrune"); migrate_setting(ProxyIP, "addrProxy"); migrate_setting(ProxyUse, "fUseProxy"); migrate_setting(ProxyIPTor, "addrSeparateProxyTor"); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 7d3c1eaa41..482dfcbe08 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -59,7 +59,7 @@ public: CoinControlFeatures, // bool SubFeeFromAmount, // bool ThreadsScriptVerif, // int - Prune, // bool + PruneTristate, // Qt::CheckState PruneSizeMiB, // int DatabaseCache, // int ExternalSignerPath, // QString From d6afef43f8152a8506f868ed2f8591f81213e79d Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 3 Aug 2023 20:40:48 +0000 Subject: [PATCH 07/23] Bugfix: Qt/Intro: Don't claim "for full chain" when pruning --- src/qt/intro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 26d1fcf9bb..8046b8e567 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -315,7 +315,7 @@ void Intro::UpdateFreeSpaceLabel() freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb); ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) { - freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb); + freeString += " " + tr("(%n GB needed)", "", m_required_space_gb); ui->freeSpace->setStyleSheet("QLabel { color: #999900 }"); } else { ui->freeSpace->setStyleSheet(""); From 6be499d8ee10e4154d821418de2dc3e888756e99 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 3 Aug 2023 23:58:32 +0000 Subject: [PATCH 08/23] Qt/Options: Update rwconf for prune changes --- src/qt/optionsmodel.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 967e86aac3..17d89e30a4 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -404,6 +404,7 @@ void OptionsModel::SetPruneTargetMiB(int prune_target_mib) // Call UpdateRwSetting() instead of setOption() to avoid setting // RestartRequired flag UpdateRwSetting(node(), PruneTristate, "", new_value); + gArgs.ModifyRWConfigFile("prune", new_value.getValStr()); } // Keep previous pruning size, if pruning was disabled. @@ -765,7 +766,9 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: if (changed()) { const bool is_autoprune = (value.value() == Qt::Checked); if (suffix.empty() && !is_autoprune) setOption(option, true, "-prev"); - update(PruneSettingFromMiB(value.value(), getOption(PruneSizeMiB).toInt())); + const auto prune_setting = PruneSettingFromMiB(value.value(), getOption(PruneSizeMiB).toInt()); + update(prune_setting); + if (suffix.empty()) gArgs.ModifyRWConfigFile("prune", prune_setting.getValStr()); if (suffix.empty() && is_autoprune) UpdateRwSetting(node(), option, "-prev", {}); if (suffix.empty()) setRestartRequired(true); } @@ -776,7 +779,9 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: if (suffix.empty() && !is_autoprune) { setOption(option, value, "-prev"); } else { - update(PruneSettingFromMiB(Qt::Checked, value.toInt())); + const auto prune_setting = PruneSettingFromMiB(Qt::Checked, value.toInt()); + update(prune_setting); + if (suffix.empty()) gArgs.ModifyRWConfigFile("prune", prune_setting.getValStr()); } if (suffix.empty() && is_autoprune) setRestartRequired(true); } From 3b16582612c9f981e57f8b0b9f11a480146ab890 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 4 Aug 2023 00:10:48 +0000 Subject: [PATCH 09/23] Qt/Intro: Allow changing prune setting even when specified on commandline --- src/qt/intro.cpp | 1 - src/qt/optionsmodel.cpp | 4 ++++ src/qt/optionsmodel.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 8046b8e567..715321eb51 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -153,7 +153,6 @@ Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_si default: ui->prune->setChecked(true); } - ui->prune->setEnabled(false); } ui->pruneMiB->setValue(m_prune_target_mib); ui->pruneMiB->setToolTip(ui->prune->toolTip()); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 17d89e30a4..4ba71a84e9 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -237,6 +237,9 @@ bool OptionsModel::Init(bilingual_str& error) // and we want command-line parameters to overwrite the GUI settings. for (OptionID option : {DatabaseCache, ThreadsScriptVerif, SpendZeroConfChange, ExternalSignerPath, MapPortUPnP, MapPortNatpmp, Listen, Server, PruneTristate, ProxyUse, ProxyUseTor, Language}) { + // isSettingIgnored will have a false positive here during first-run prune changes + if (option == PruneTristate && m_prune_forced_by_gui) continue; + std::string setting = SettingName(option); if (node().isSettingIgnored(setting)) addOverriddenOption("-" + setting); try { @@ -396,6 +399,7 @@ void OptionsModel::SetPruneTargetMiB(int prune_target_mib) // this point because this function is only called after the intro screen is // shown, before the node starts. node().forceSetting("prune", new_value); + m_prune_forced_by_gui = true; // Update settings.json if value configured in intro screen is different // from saved value. Avoid writing settings.json if bitcoin.conf value diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 482dfcbe08..26506ff62c 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -138,6 +138,7 @@ private: /* settings that were overridden by command-line */ QString strOverriddenByCommandLine; + bool m_prune_forced_by_gui{false}; static QString FontChoiceToString(const OptionsModel::FontChoice&); static FontChoice FontChoiceFromString(const QString&); From 6c0b49cbc2bfff349622c797f65b4c0e9caa12c3 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 9 Feb 2017 06:26:32 +0000 Subject: [PATCH 10/23] Qt/Options: When resetting options, re-assign prune if it was configured via rwconf --- src/common/args.cpp | 3 +++ src/common/args.h | 2 ++ src/common/config.cpp | 2 ++ src/qt/optionsmodel.cpp | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/src/common/args.cpp b/src/common/args.cpp index a7dcb96ebb..87c67a49cd 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -1057,6 +1057,9 @@ void ArgsManager::ModifyRWConfigFile(const std::map& s } WriteSettingsFile(); } + if (settings_to_change.count("prune")) { + m_rwconf_had_prune_option = true; + } } void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value) diff --git a/src/common/args.h b/src/common/args.h index 4d380fac5e..b08ab50a7a 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -142,6 +142,7 @@ protected: std::list m_config_sections GUARDED_BY(cs_args); std::optional m_config_path GUARDED_BY(cs_args); std::optional m_rwconf_path GUARDED_BY(cs_args); + bool m_rwconf_had_prune_option{false}; mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args); mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args); mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args); @@ -188,6 +189,7 @@ protected: fs::path GetRWConfigFilePath() const; [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); + bool RWConfigHasPruneOption() const { return m_rwconf_had_prune_option; } void ModifyRWConfigFile(const std::map& settings_to_change); void ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value); void EraseRWConfigFile(); diff --git a/src/common/config.cpp b/src/common/config.cpp index a2c0cc19f5..0684f746aa 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -128,6 +128,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) LOCK(cs_args); m_settings.ro_config.clear(); m_settings.rw_config.clear(); + m_rwconf_had_prune_option = false; m_config_sections.clear(); m_config_path = AbsPathForConfigVal(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME), /*net_specific=*/false); } @@ -230,6 +231,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) if (!ReadConfigStream(rwconf_stream, fs::PathToString(rwconf_path), error, ignore_invalid_keys, &m_settings.rw_config)) { return false; } + m_rwconf_had_prune_option = m_settings.rw_config.count("prune"); } return true; diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4ba71a84e9..dfa9ff7bde 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -346,6 +346,11 @@ void OptionsModel::Reset() // Set strDataDir settings.setValue("strDataDir", dataDir); + // Set prune option iff it was configured in rwconf + if (gArgs.RWConfigHasPruneOption()) { + SetPruneTargetMiB(gArgs.GetIntArg("-prune", 0)); + } + // Set that this was reset settings.setValue("fReset", true); From 7dce75e5f8f56a977c8798d9fe84196bbd8588d5 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 13 Jan 2020 17:29:27 +0000 Subject: [PATCH 11/23] Qt/Options: Expose peerblockfilters in GUI using rwconf For now, enables basic block filters permanently (can't be disabled) --- src/qt/forms/optionsdialog.ui | 10 ++++++++++ src/qt/optionsdialog.cpp | 2 ++ src/qt/optionsmodel.cpp | 19 +++++++++++++++++++ src/qt/optionsmodel.h | 1 + 4 files changed, 32 insertions(+) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 5576f02f90..f9207c6611 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -773,6 +773,16 @@ + + + + Generate compact block filters and allow peers to download them + + + Provide compact block filters for light clients + + + diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b61e1f1c03..1df92fb0ef 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -282,6 +282,7 @@ void OptionsDialog::setModel(OptionsModel *_model) connect(ui->connectSocks, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->connectSocksTor, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); connect(ui->peerbloomfilters, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); + connect(ui->peerblockfilters, &QCheckBox::clicked, this, &OptionsDialog::showRestartWarning); /* Display */ connect(ui->lang, qOverload<>(&QValueComboBox::valueChanged), [this]{ showRestartWarning(); }); connect(ui->thirdPartyTxUrls, &QLineEdit::textChanged, [this]{ showRestartWarning(); }); @@ -359,6 +360,7 @@ void OptionsDialog::setMapper() } mapper->addMapping(ui->peerbloomfilters, OptionsModel::peerbloomfilters); + mapper->addMapping(ui->peerblockfilters, OptionsModel::peerblockfilters); /* Window */ #ifndef Q_OS_MACOS diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index dfa9ff7bde..99e5bb73d6 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -552,6 +553,8 @@ QVariant OptionsModel::getOption(OptionID option, const std::string& suffix) con return qlonglong(node().context()->connman->GetMaxOutboundTarget() / 1024 / 1024); case peerbloomfilters: return f_peerbloomfilters; + case peerblockfilters: + return gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS); default: return QVariant(); } @@ -833,6 +836,22 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: setRestartRequired(true); } break; + case peerblockfilters: + { + bool nv = value.toBool(); + if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS) != nv) { + gArgs.ModifyRWConfigFile("peerblockfilters", strprintf("%d", nv)); + gArgs.ForceSetArg("peerblockfilters", nv); + if (nv && !GetBlockFilterIndex(BlockFilterType::BASIC)) { + // TODO: When other options are possible, we need to append a list! + // TODO: Some way to unset/delete this... + gArgs.ModifyRWConfigFile("blockfilterindex", "basic"); + gArgs.ForceSetArg("blockfilterindex", "basic"); + } + setRestartRequired(true); + } + break; + } default: break; } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 26506ff62c..b3adf156bd 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -71,6 +71,7 @@ public: MaskValues, // bool maxuploadtarget, peerbloomfilters, // bool + peerblockfilters, // bool OptionIDRowCount, }; From 857523fc33dfbbfffde9171b38a921fb186c900f Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 9 Jun 2020 20:51:57 +0000 Subject: [PATCH 12/23] Qt/Options: When changing peerblockfilters, also set peercfilters for better downgrade compatibility Only in bitcoin_rw.conf --- src/common/args.cpp | 8 ++++---- src/common/args.h | 4 ++-- src/qt/optionsmodel.cpp | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/common/args.cpp b/src/common/args.cpp index 87c67a49cd..7edafc3ad9 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -1023,7 +1023,7 @@ void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, con } } -void ArgsManager::ModifyRWConfigFile(const std::map& settings_to_change) +void ArgsManager::ModifyRWConfigFile(const std::map& settings_to_change, const bool also_settings_json) { LOCK(cs_args); fs::path rwconf_path{GetRWConfigFilePath()}; @@ -1050,7 +1050,7 @@ void ArgsManager::ModifyRWConfigFile(const std::map& s for (const auto& setting_change : settings_to_change) { m_settings.rw_config[setting_change.first] = {setting_change.second}; } - if (!IsArgNegated("-settings")) { + if (also_settings_json && !IsArgNegated("-settings")) { // Also save to settings.json for Core (0.21+) compatibility for (const auto& setting_change : settings_to_change) { m_settings.rw_settings[setting_change.first] = setting_change.second; @@ -1062,11 +1062,11 @@ void ArgsManager::ModifyRWConfigFile(const std::map& s } } -void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value) +void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value, const bool also_settings_json) { std::map settings_to_change; settings_to_change[setting_to_change] = new_value; - ModifyRWConfigFile(settings_to_change); + ModifyRWConfigFile(settings_to_change, also_settings_json); } void ArgsManager::EraseRWConfigFile() diff --git a/src/common/args.h b/src/common/args.h index b08ab50a7a..58c7f7197b 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -190,8 +190,8 @@ protected: [[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); bool RWConfigHasPruneOption() const { return m_rwconf_had_prune_option; } - void ModifyRWConfigFile(const std::map& settings_to_change); - void ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value); + void ModifyRWConfigFile(const std::map& settings_to_change, bool also_settings_json = true); + void ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value, bool also_settings_json = true); void EraseRWConfigFile(); /** diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 99e5bb73d6..6356528fdc 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -841,6 +841,7 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: bool nv = value.toBool(); if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS) != nv) { gArgs.ModifyRWConfigFile("peerblockfilters", strprintf("%d", nv)); + gArgs.ModifyRWConfigFile("peercfilters", strprintf("%d", nv), /*also_settings_json=*/ false); // for downgrade compatibility with Knots 0.19 gArgs.ForceSetArg("peerblockfilters", nv); if (nv && !GetBlockFilterIndex(BlockFilterType::BASIC)) { // TODO: When other options are possible, we need to append a list! From 7dd21f4e9d1a5f43cd6735f72f3465d077c9cf3f Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 02:51:24 +0000 Subject: [PATCH 13/23] GUI: When addresstype is changed, apply to all open receive dialogs immediately --- src/qt/optionsmodel.cpp | 1 + src/qt/optionsmodel.h | 3 +++ src/qt/receivecoinsdialog.cpp | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 6356528fdc..8b08506b84 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -722,6 +722,7 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: continue; } } + Q_EMIT addresstypeChanged(newvalue); } break; } diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index b3adf156bd..f56b194b0c 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -16,6 +16,8 @@ #include struct bilingual_str; +enum class OutputType; + namespace interfaces { class Node; } @@ -156,6 +158,7 @@ private: Q_SIGNALS: void displayUnitChanged(BitcoinUnit unit); void coinControlFeaturesChanged(bool); + void addresstypeChanged(OutputType); void showTrayIconChanged(bool); void fontForMoneyChanged(const QFont&); void fontForQRCodesChanged(const FontChoice&); diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index be7741e8a8..1b5f5228e6 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -100,6 +100,11 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) add_address_type(OutputType::BECH32M, tr("Bech32m (Taproot)"), tr("Bech32m (BIP-350) is an upgrade to Bech32, wallet support is still limited.")); } + connect(_model->getOptionsModel(), &OptionsModel::addresstypeChanged, [this](const OutputType type) { + const int index = ui->addressType->findData((int) type); + if (index != -1) ui->addressType->setCurrentIndex(index); + }); + // Set the button to be enabled or disabled based on whether the wallet can give out new addresses. ui->receiveButton->setEnabled(model->wallet().canGetAddresses()); From db5c972ef3bb6ed0bd4162269650492f1aec4f2e Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 03:26:55 +0000 Subject: [PATCH 14/23] GUI: Move OutputType descriptions to map in optionsmodel --- src/qt/optionsmodel.cpp | 27 +++++++++++++++++++++++++++ src/qt/optionsmodel.h | 5 +++++ src/qt/receivecoinsdialog.cpp | 11 ++++++----- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 8b08506b84..fc457c28a1 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -142,6 +142,33 @@ static const QLatin1String fontchoice_str_embedded{"embedded"}; static const QLatin1String fontchoice_str_best_system{"best_system"}; static const QString fontchoice_str_custom_prefix{QStringLiteral("custom, ")}; +static const std::map> UntranslatedOutputTypeDescriptions{ + {OutputType::LEGACY, { + QT_TRANSLATE_NOOP("Output type name", "Base58 (Legacy)"), + QT_TRANSLATE_NOOP("Output type description", "Not recommended due to higher fees and less protection against typos."), + }}, + {OutputType::P2SH_SEGWIT, { + QT_TRANSLATE_NOOP("Output type name", "Base58 (P2SH-SegWit)"), + QT_TRANSLATE_NOOP("Output type description", "Generates an address compatible with older wallets."), + }}, + {OutputType::BECH32, { + QT_TRANSLATE_NOOP("Output type name", "Bech32 (SegWit)"), + QT_TRANSLATE_NOOP("Output type description", "Generates a native segwit address (BIP-173). Some old wallets don't support it."), + }}, + {OutputType::BECH32M, { + QT_TRANSLATE_NOOP("Output type name", "Bech32m (Taproot)"), + QT_TRANSLATE_NOOP("Output type description", "Bech32m (BIP-350) is an upgrade to Bech32, wallet support is still limited."), + }}, +}; + +std::pair GetOutputTypeDescription(const OutputType type) +{ + auto& untr = UntranslatedOutputTypeDescriptions.at(type); + QString text = QCoreApplication::translate("Output type name", untr.first); + QString tooltip = QCoreApplication::translate("Output type description", untr.second); + return std::make_pair(text, tooltip); +} + QString OptionsModel::FontChoiceToString(const OptionsModel::FontChoice& f) { if (std::holds_alternative(f)) { diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index f56b194b0c..d9c1ddbc5f 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -11,8 +11,11 @@ #include #include +#include #include +#include +#include #include struct bilingual_str; @@ -25,6 +28,8 @@ class Node; extern const char *DEFAULT_GUI_PROXY_HOST; static constexpr uint16_t DEFAULT_GUI_PROXY_PORT = 9050; +std::pair GetOutputTypeDescription(const OutputType type); + /** Interface from Qt to configuration data structure for Bitcoin client. To Qt, the options are presented as a list with the different options laid out vertically. diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 1b5f5228e6..9d152277a0 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -87,17 +87,18 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) &ReceiveCoinsDialog::recentRequestsView_selectionChanged); // Populate address type dropdown and select default - auto add_address_type = [&](OutputType type, const QString& text, const QString& tooltip) { + auto add_address_type = [&](OutputType type) { + const auto [text, tooltip] = GetOutputTypeDescription(type); const auto index = ui->addressType->count(); ui->addressType->addItem(text, (int) type); ui->addressType->setItemData(index, tooltip, Qt::ToolTipRole); if (model->wallet().getDefaultAddressType() == type) ui->addressType->setCurrentIndex(index); }; - add_address_type(OutputType::LEGACY, tr("Base58 (Legacy)"), tr("Not recommended due to higher fees and less protection against typos.")); - add_address_type(OutputType::P2SH_SEGWIT, tr("Base58 (P2SH-SegWit)"), tr("Generates an address compatible with older wallets.")); - add_address_type(OutputType::BECH32, tr("Bech32 (SegWit)"), tr("Generates a native segwit address (BIP-173). Some old wallets don't support it.")); + add_address_type(OutputType::LEGACY); + add_address_type(OutputType::P2SH_SEGWIT); + add_address_type(OutputType::BECH32); if (model->wallet().taprootEnabled()) { - add_address_type(OutputType::BECH32M, tr("Bech32m (Taproot)"), tr("Bech32m (BIP-350) is an upgrade to Bech32, wallet support is still limited.")); + add_address_type(OutputType::BECH32M); } connect(_model->getOptionsModel(), &OptionsModel::addresstypeChanged, [this](const OutputType type) { From f9226184b0331f0ecbee517a9fcf3db479237924 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 07:44:32 +0000 Subject: [PATCH 15/23] GUI: Options: Replace addresstype radioboxes with a combobox to match Receive dialog --- src/qt/forms/optionsdialog.ui | 72 ++++++++++------------------------- src/qt/optionsdialog.cpp | 37 ++++++------------ 2 files changed, 32 insertions(+), 77 deletions(-) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index f9207c6611..08e65e135c 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -226,6 +226,27 @@ + + + + + + Address type: + + + addressType + + + + + + + Choose the default address type to select when receiving coins. + + + + + @@ -295,57 +316,6 @@ - - - - Address type - - - false - - - - - - - 0 - 0 - - - - Legacy - - - - - - - - 0 - 0 - - - - P2SH Segwit - - - - - - - - 0 - 0 - - - - Native Segwit (bech32) - - - - - - diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 1df92fb0ef..522a2e9982 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -151,6 +152,15 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet)); ui->thirdPartyTxUrlsLabel->setVisible(false); ui->thirdPartyTxUrls->setVisible(false); + } else { + for (OutputType type : OUTPUT_TYPES) { + const QString& val = QString::fromStdString(FormatOutputType(type)); + const auto [text, tooltip] = GetOutputTypeDescription(type); + + const auto index = ui->addressType->count(); + ui->addressType->addItem(text, val); + ui->addressType->setItemData(index, tooltip, Qt::ToolTipRole); + } } #ifdef ENABLE_EXTERNAL_SIGNER @@ -312,24 +322,13 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->pruneSizeMiB, OptionsModel::PruneSizeMiB); /* Wallet */ + mapper->addMapping(ui->addressType, OptionsModel::addresstype); mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); mapper->addMapping(ui->subFeeFromAmount, OptionsModel::SubFeeFromAmount); mapper->addMapping(ui->externalSignerPath, OptionsModel::ExternalSignerPath); mapper->addMapping(ui->m_enable_psbt_controls, OptionsModel::EnablePSBTControls); - { - QString radio_name_lower = "addresstype" + model->data(model->index(OptionsModel::addresstype, 0), Qt::EditRole).toString().toLower(); - radio_name_lower.replace("-", "_"); - for (int i = ui->layoutAddressType->count(); i--; ) { - QRadioButton * const radio = qobject_cast(ui->layoutAddressType->itemAt(i)->widget()); - if (!radio) { - continue; - } - radio->setChecked(radio->objectName().toLower() == radio_name_lower); - } - } - /* Network */ mapper->addMapping(ui->networkPort, OptionsModel::NetworkPort); mapper->addMapping(ui->mapPortUpnp, OptionsModel::MapPortUPnP); @@ -469,20 +468,6 @@ void OptionsDialog::on_okButton_clicked() model->setData(model->index(OptionsModel::FontForMoney, 0), ui->moneyFont->itemData(ui->moneyFont->currentIndex())); model->setData(model->index(OptionsModel::FontForQRCodes, 0), ui->qrFont->itemData(ui->qrFont->currentIndex())); - { - QString new_addresstype; - for (int i = ui->layoutAddressType->count(); i--; ) { - QRadioButton * const radio = qobject_cast(ui->layoutAddressType->itemAt(i)->widget()); - if (!(radio && radio->objectName().startsWith("addressType") && radio->isChecked())) { - continue; - } - new_addresstype = radio->objectName().mid(11).toLower(); - new_addresstype.replace("_", "-"); - break; - } - model->setData(model->index(OptionsModel::addresstype, 0), new_addresstype); - } - if (ui->maxuploadtargetCheckbox->isChecked()) { model->setData(model->index(OptionsModel::maxuploadtarget, 0), ui->maxuploadtarget->value()); } else { From d046ac37d97d1744e6a9c51a8ed3eb22cc6ace39 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 16 May 2022 07:46:36 +0000 Subject: [PATCH 16/23] GUI: Re-word OutputType descriptions to give better advice --- src/qt/optionsmodel.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index fc457c28a1..7bfb7718dd 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -145,19 +145,19 @@ static const QString fontchoice_str_custom_prefix{QStringLiteral("custom, ")}; static const std::map> UntranslatedOutputTypeDescriptions{ {OutputType::LEGACY, { QT_TRANSLATE_NOOP("Output type name", "Base58 (Legacy)"), - QT_TRANSLATE_NOOP("Output type description", "Not recommended due to higher fees and less protection against typos."), + QT_TRANSLATE_NOOP("Output type description", "Widest compatibility and best for health of the Bitcoin network, but may result in higher fees later. Recommended."), }}, {OutputType::P2SH_SEGWIT, { QT_TRANSLATE_NOOP("Output type name", "Base58 (P2SH-SegWit)"), - QT_TRANSLATE_NOOP("Output type description", "Generates an address compatible with older wallets."), + QT_TRANSLATE_NOOP("Output type description", "Compatible with most older wallets, and may result in lower fees than Legacy."), }}, {OutputType::BECH32, { - QT_TRANSLATE_NOOP("Output type name", "Bech32 (SegWit)"), - QT_TRANSLATE_NOOP("Output type description", "Generates a native segwit address (BIP-173). Some old wallets don't support it."), + QT_TRANSLATE_NOOP("Output type name", "Native Segwit (Bech32)"), + QT_TRANSLATE_NOOP("Output type description", "Lower fees than Base58, but some old wallets don't support it."), }}, {OutputType::BECH32M, { - QT_TRANSLATE_NOOP("Output type name", "Bech32m (Taproot)"), - QT_TRANSLATE_NOOP("Output type description", "Bech32m (BIP-350) is an upgrade to Bech32, wallet support is still limited."), + QT_TRANSLATE_NOOP("Output type name", "Taproot (Bech32m)"), + QT_TRANSLATE_NOOP("Output type description", "Lowest fees, but wallet support is still limited."), }}, }; From 55dc9e1847901322e05fdfb6c750b2d68f5c7ff0 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Fri, 4 Aug 2023 23:56:44 +0000 Subject: [PATCH 17/23] Qt: Reformat name for OutputType::P2SH_SEGWIT --- src/qt/optionsmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 7bfb7718dd..7c2d813660 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -148,7 +148,7 @@ static const std::map> Untransla QT_TRANSLATE_NOOP("Output type description", "Widest compatibility and best for health of the Bitcoin network, but may result in higher fees later. Recommended."), }}, {OutputType::P2SH_SEGWIT, { - QT_TRANSLATE_NOOP("Output type name", "Base58 (P2SH-SegWit)"), + QT_TRANSLATE_NOOP("Output type name", "Base58 (P2SH Segwit)"), QT_TRANSLATE_NOOP("Output type description", "Compatible with most older wallets, and may result in lower fees than Legacy."), }}, {OutputType::BECH32, { From 03317ace8447ae6cea85f74c303f559d0f9af15c Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 19 Mar 2024 15:22:48 +0000 Subject: [PATCH 18/23] Bugfix: GUI/Options: Correct maxuploadtarget label to use MiB --- src/qt/forms/optionsdialog.ui | 2 +- src/qt/optionsdialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 08e65e135c..d5b830636b 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -708,7 +708,7 @@ - MB per day + MiB per day Qt::PlainText diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 522a2e9982..8306f2b046 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -133,7 +133,7 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled); connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState); - ui->maxuploadtarget->setMinimum(144 /* MB/day */); + ui->maxuploadtarget->setMinimum(144 /* MiB/day */); ui->maxuploadtarget->setMaximum(std::numeric_limits::max()); connect(ui->maxuploadtargetCheckbox, SIGNAL(toggled(bool)), ui->maxuploadtarget, SLOT(setEnabled(bool))); From 208270f6d1453c75fa8962750414d9102b2e3b87 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 19 Mar 2024 05:07:23 +0000 Subject: [PATCH 19/23] Bugfix: GUI/Options: Prune size field should be disabled for manual-prune checkbox state --- src/qt/optionsdialog.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 8306f2b046..05d62ac15b 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -104,7 +104,9 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) ui->pruneWarning->setStyleSheet("QLabel { color: red; }"); ui->pruneSizeMiB->setEnabled(false); - connect(ui->prune, &QPushButton::toggled, ui->pruneSizeMiB, &QWidget::setEnabled); + connect(ui->prune, &QCheckBox::stateChanged, [this](int state){ + ui->pruneSizeMiB->setEnabled(state == Qt::Checked); + }); ui->networkPort->setValidator(new QIntValidator(1024, 65535, this)); connect(ui->networkPort, SIGNAL(textChanged(const QString&)), this, SLOT(checkLineEdit())); From 8e0c670bb6476afc114ff06deafce7debcd6b16a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 19 Mar 2024 05:09:22 +0000 Subject: [PATCH 20/23] Bugfix: GUI/Options: Handle PruneTristate manually QDataWidgetMapper apparently doesn't handle tristate checkboxes correctly --- src/qt/optionsdialog.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 05d62ac15b..dd531946b9 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -317,10 +317,11 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->threadsScriptVerif, OptionsModel::ThreadsScriptVerif); mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache); - if (model->data(model->index(OptionsModel::PruneTristate, 0), Qt::EditRole).value() == Qt::PartiallyChecked) { + const auto prune_checkstate = model->data(model->index(OptionsModel::PruneTristate, 0), Qt::EditRole).value(); + if (prune_checkstate == Qt::PartiallyChecked) { ui->prune->setTristate(); } - mapper->addMapping(ui->prune, OptionsModel::PruneTristate); + ui->prune->setCheckState(prune_checkstate); mapper->addMapping(ui->pruneSizeMiB, OptionsModel::PruneSizeMiB); /* Wallet */ @@ -467,6 +468,8 @@ void OptionsDialog::on_okButton_clicked() } } + model->setData(model->index(OptionsModel::PruneTristate, 0), ui->prune->checkState()); + model->setData(model->index(OptionsModel::FontForMoney, 0), ui->moneyFont->itemData(ui->moneyFont->currentIndex())); model->setData(model->index(OptionsModel::FontForQRCodes, 0), ui->qrFont->itemData(ui->qrFont->currentIndex())); From 8cfe0ad8fbf2ac3c7a90a23592731daa53b71217 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 19 Mar 2024 05:10:22 +0000 Subject: [PATCH 21/23] Bugfix: GUI/Options: Correctly set prune-prev (true becomes Qt::PartiallyChecked or Qt::Unchecked) --- src/qt/optionsmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 7c2d813660..838aa22c79 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -805,8 +805,8 @@ bool OptionsModel::setOption(OptionID option, const QVariant& value, const std:: case PruneTristate: if (changed()) { const bool is_autoprune = (value.value() == Qt::Checked); - if (suffix.empty() && !is_autoprune) setOption(option, true, "-prev"); const auto prune_setting = PruneSettingFromMiB(value.value(), getOption(PruneSizeMiB).toInt()); + if (suffix.empty() && !is_autoprune) setOption(option, Qt::Checked, "-prev"); update(prune_setting); if (suffix.empty()) gArgs.ModifyRWConfigFile("prune", prune_setting.getValStr()); if (suffix.empty() && is_autoprune) UpdateRwSetting(node(), option, "-prev", {}); From c089a0d22f4a451ffcd3e80bcd419d2b53db7d71 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 11 Jun 2024 15:46:19 +0000 Subject: [PATCH 22/23] Bugfix: GUI/optionsmodel: Mention if addresstype, maxuploadtarget, peer{bloom,block}filters are overridden options --- src/qt/optionsmodel.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 838aa22c79..199c913bf3 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -30,6 +30,9 @@ #include #endif +#include +#include + #include #include #include @@ -54,6 +57,7 @@ static const char* SettingName(OptionsModel::OptionID option) case OptionsModel::MapPortNatpmp: return "natpmp"; case OptionsModel::Listen: return "listen"; case OptionsModel::Server: return "server"; + case OptionsModel::addresstype: return "addresstype"; case OptionsModel::PruneSizeMiB: return "prune"; case OptionsModel::PruneTristate: return "prune"; case OptionsModel::ProxyIP: return "proxy"; @@ -63,6 +67,9 @@ static const char* SettingName(OptionsModel::OptionID option) case OptionsModel::ProxyPortTor: return "onion"; case OptionsModel::ProxyUseTor: return "onion"; case OptionsModel::Language: return "lang"; + case OptionsModel::maxuploadtarget: return "maxuploadtarget"; + case OptionsModel::peerbloomfilters: return "peerbloomfilters"; + case OptionsModel::peerblockfilters: return "peerblockfilters"; default: throw std::logic_error(strprintf("GUI option %i has no corresponding node setting.", option)); } } @@ -263,12 +270,14 @@ bool OptionsModel::Init(bilingual_str& error) // These are shared with the core or have a command-line parameter // and we want command-line parameters to overwrite the GUI settings. + std::unordered_set checked_settings; for (OptionID option : {DatabaseCache, ThreadsScriptVerif, SpendZeroConfChange, ExternalSignerPath, MapPortUPnP, MapPortNatpmp, Listen, Server, PruneTristate, ProxyUse, ProxyUseTor, Language}) { // isSettingIgnored will have a false positive here during first-run prune changes if (option == PruneTristate && m_prune_forced_by_gui) continue; std::string setting = SettingName(option); + checked_settings.insert(setting); if (node().isSettingIgnored(setting)) addOverriddenOption("-" + setting); try { getOption(option); @@ -281,6 +290,18 @@ bool OptionsModel::Init(bilingual_str& error) } } + if (m_prune_forced_by_gui) checked_settings.insert("prune"); + for (OptionID option = OptionID(0); option < OptionIDRowCount; option = OptionID(option + 1)) { + std::string setting; + try { + setting = SettingName(option); + } catch (const std::logic_error&) { + continue; // Ignore GUI-only settings + } + if (!checked_settings.insert(setting).second) continue; + if (node().isSettingIgnored(setting)) addOverriddenOption("-" + setting); + } + // If setting doesn't exist create it with defaults. // Main From db8eba71d53e81215e6971e9cde5caef631bc1df Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 27 Jan 2025 16:10:18 +0000 Subject: [PATCH 23/23] GUI/Options: Disable peerblockfilters option if pruning is already enabled but basic blockfilterindex is not Otherwise, enabling it will prompt the user to restart, and restart will fail --- src/qt/optionsdialog.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index dd531946b9..f60c8b3fa3 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -363,6 +364,13 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->peerbloomfilters, OptionsModel::peerbloomfilters); mapper->addMapping(ui->peerblockfilters, OptionsModel::peerblockfilters); + if (prune_checkstate != Qt::Unchecked && !GetBlockFilterIndex(BlockFilterType::BASIC)) { + // Once pruning begins, it's too late to enable block filters, and doing so will prevent starting the client + // Rather than try to monitor sync state, just disable the option once pruning is enabled + // Advanced users can override this manually anyway + ui->peerblockfilters->setEnabled(false); + ui->peerblockfilters->setToolTip(ui->peerblockfilters->toolTip() + " " + tr("(only available if enabled at least once before turning on pruning)")); + } /* Window */ #ifndef Q_OS_MACOS