diff --git a/src/qt/intro.h b/src/qt/intro.h --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -29,8 +29,8 @@ Q_OBJECT public: - explicit Intro(QWidget *parent = nullptr, uint64_t blockchain_size = 0, - uint64_t chain_state_size = 0); + explicit Intro(QWidget *parent = nullptr, int64_t blockchain_size_gb = 0, + int64_t chain_state_size_gb = 0); ~Intro(); QString getDataDirectory(); @@ -68,12 +68,19 @@ QMutex mutex; bool signalled; QString pathToCheck; - uint64_t m_blockchain_size; - uint64_t m_chain_state_size; + const int64_t m_blockchain_size_gb; + const int64_t m_chain_state_size_gb; + //! 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}; + const int64_t m_prune_target_gb; void startThread(); void checkPath(const QString &dataDir); QString getPathToCheck(); + void UpdatePruneLabels(bool prune_checked); + void UpdateFreeSpaceLabel(); friend class FreespaceChecker; }; diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -21,11 +22,6 @@ #include -/** - * Total required space (in GB) depending on user choice (prune, not prune). - */ -static uint64_t requiredSpace; - /* Check free space asynchronously to prevent hanging the UI thread. Up to one request to check a path is in flight to this thread; when the @@ -109,53 +105,49 @@ Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable); } -Intro::Intro(QWidget *parent, uint64_t blockchain_size, - uint64_t chain_state_size) +namespace { +//! Return pruning size that will be used if automatic pruning is enabled. +int GetPruneTargetGB() { + int64_t prune_target_mib = gArgs.GetArg("-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; +} +} // namespace + +Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, + int64_t chain_state_size_gb) : QDialog(parent), ui(new Ui::Intro), thread(nullptr), signalled(false), - m_blockchain_size(blockchain_size), m_chain_state_size(chain_state_size) { + m_blockchain_size_gb(blockchain_size_gb), + m_chain_state_size_gb(chain_state_size_gb), + m_prune_target_gb(GetPruneTargetGB()) { ui->setupUi(this); ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME)); ui->storageLabel->setText(ui->storageLabel->text().arg(PACKAGE_NAME)); ui->lblExplanation1->setText(ui->lblExplanation1->text() .arg(PACKAGE_NAME) - .arg(m_blockchain_size) + .arg(m_blockchain_size_gb) .arg(2009) .arg(tr("Bitcoin"))); ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME)); - uint64_t pruneTarget = std::max(0, gArgs.GetArg("-prune", 0)); - // -prune=1 means enabled, above that it's a size in MB - if (pruneTarget > 1) { + // -prune=1 means enabled, above that it's a size in MiB + if (gArgs.GetArg("-prune", 0) > 1) { ui->prune->setChecked(true); ui->prune->setEnabled(false); } - ui->prune->setText( - tr("Discard blocks after verification, except most " - "recent %1 GB (prune)") - .arg(pruneTarget ? pruneTarget / 1000 : DEFAULT_PRUNE_TARGET_GB)); - requiredSpace = m_blockchain_size; - QString storageRequiresMsg = - tr("At least %1 GB of data will be stored in this directory, and it " - "will grow over time."); - if (pruneTarget) { - uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES); - if (prunedGBs <= requiredSpace) { - requiredSpace = prunedGBs; - storageRequiresMsg = tr("Approximately %1 GB of data will be " - "stored in this directory."); - } - ui->lblExplanation3->setVisible(true); - } else { - ui->lblExplanation3->setVisible(false); - } - requiredSpace += m_chain_state_size; - ui->sizeWarningLabel->setText( - tr("%1 will download and store a copy of the Bitcoin block chain.") - .arg(PACKAGE_NAME) + - " " + storageRequiresMsg.arg(requiredSpace) + " " + - tr("The wallet will also be stored in this directory.")); - this->adjustSize(); + ui->prune->setText(tr("Discard blocks after verification, except most " + "recent %1 GB (prune)") + .arg(m_prune_target_gb)); + UpdatePruneLabels(ui->prune->isChecked()); + + connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) { + UpdatePruneLabels(prune_checked); + UpdateFreeSpaceLabel(); + }); + startThread(); } @@ -281,25 +273,35 @@ if (status == FreespaceChecker::ST_ERROR) { ui->freeSpace->setText(""); } else { - QString freeString = - tr("%n GB of free space available", "", bytesAvailable / GB_BYTES); - if (bytesAvailable < requiredSpace * GB_BYTES) { - freeString += " " + tr("(of %n GB needed)", "", requiredSpace); - ui->freeSpace->setStyleSheet("QLabel { color: #800000 }"); - } else if (bytesAvailable / GB_BYTES - requiredSpace < 10) { - freeString += - " " + tr("(%n GB needed for full chain)", "", requiredSpace); - ui->freeSpace->setStyleSheet("QLabel { color: #999900 }"); - } else { - ui->freeSpace->setStyleSheet(""); + m_bytes_available = bytesAvailable; + if (ui->prune->isEnabled()) { + ui->prune->setChecked( + m_bytes_available < + (m_blockchain_size_gb + m_chain_state_size_gb + 10) * GB_BYTES); } - ui->freeSpace->setText(freeString + "."); + UpdateFreeSpaceLabel(); } /* Don't allow confirm in ERROR state */ ui->buttonBox->button(QDialogButtonBox::Ok) ->setEnabled(status != FreespaceChecker::ST_ERROR); } +void Intro::UpdateFreeSpaceLabel() { + QString freeString = + tr("%n GB of free space available", "", m_bytes_available / GB_BYTES); + if (m_bytes_available < m_required_space_gb * GB_BYTES) { + 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); + ui->freeSpace->setStyleSheet("QLabel { color: #999900 }"); + } else { + ui->freeSpace->setStyleSheet(""); + } + ui->freeSpace->setText(freeString + "."); +} + void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) { /* Disable OK button until check result comes in */ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); @@ -354,3 +356,22 @@ mutex.unlock(); return retval; } + +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; + storageRequiresMsg = + tr("Approximately %1 GB of data will be stored in this directory."); + } + ui->lblExplanation3->setVisible(prune_checked); + ui->sizeWarningLabel->setText( + tr("%1 will download and store a copy of the Bitcoin block chain.") + .arg(PACKAGE_NAME) + + " " + storageRequiresMsg.arg(m_required_space_gb) + " " + + tr("The wallet will also be stored in this directory.")); + this->adjustSize(); +} diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_OPTIONSMODEL_H #include +#include #include @@ -22,6 +23,22 @@ extern const char *DEFAULT_GUI_PROXY_HOST; static constexpr unsigned short 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. diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -285,10 +285,9 @@ void OptionsModel::SetPruneEnabled(bool prune, bool force) { QSettings settings; settings.setValue("bPrune", prune); - // Convert prune size from GB to MiB: - const uint64_t nPruneSizeMiB = - (settings.value("nPruneSize").toInt() * GB_BYTES) >> 20; - std::string prune_val = prune ? ToString(nPruneSizeMiB) : "0"; + const int64_t prune_target_mib = + PruneGBtoMiB(settings.value("nPruneSize").toInt()); + std::string prune_val = prune ? ToString(prune_target_mib) : "0"; if (force) { gArgs.ForceSetArg("-prune", prune_val); return;