Bitcoin Core  31.0.0
P2P Digital Currency
intro.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <bitcoin-build-config.h> // IWYU pragma: keep
6 
7 #include <chainparams.h>
8 #include <qt/intro.h>
9 #include <qt/forms/ui_intro.h>
10 #include <util/chaintype.h>
11 #include <util/fs.h>
12 
13 #include <qt/freespacechecker.h>
14 #include <qt/guiconstants.h>
15 #include <qt/guiutil.h>
16 #include <qt/optionsmodel.h>
17 
18 #include <common/args.h>
19 #include <interfaces/node.h>
20 #include <util/fs_helpers.h>
21 #include <validation.h>
22 
23 #include <QFileDialog>
24 #include <QSettings>
25 #include <QMessageBox>
26 
27 #include <cmath>
28 
29 namespace {
31 int GetPruneTargetGB()
32 {
33  int64_t prune_target_mib = gArgs.GetIntArg("-prune", 0);
34  // >1 means automatic pruning is enabled by config, 1 means manual pruning, 0 means no pruning.
35  return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib) : DEFAULT_PRUNE_TARGET_GB;
36 }
37 } // namespace
38 
39 Intro::Intro(QWidget *parent, int64_t blockchain_size_gb, int64_t chain_state_size_gb) :
40  QDialog(parent, GUIUtil::dialog_flags),
41  ui(new Ui::Intro),
42  m_blockchain_size_gb(blockchain_size_gb),
43  m_chain_state_size_gb(chain_state_size_gb),
44  m_prune_target_gb{GetPruneTargetGB()}
45 {
46  ui->setupUi(this);
47  ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(CLIENT_NAME));
48  ui->storageLabel->setText(ui->storageLabel->text().arg(CLIENT_NAME));
49 
50  ui->lblExplanation1->setText(ui->lblExplanation1->text()
51  .arg(CLIENT_NAME)
52  .arg(m_blockchain_size_gb)
53  .arg(2009)
54  .arg(tr("Bitcoin"))
55  );
56  ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(CLIENT_NAME));
57 
58  const int min_prune_target_GB = std::ceil(MIN_DISK_SPACE_FOR_BLOCK_FILES / 1e9);
59  ui->pruneGB->setRange(min_prune_target_GB, std::numeric_limits<int>::max());
60  if (const auto arg{gArgs.GetIntArg("-prune")}) {
61  m_prune_checkbox_is_default = false;
62  ui->prune->setChecked(*arg >= 1);
63  ui->prune->setEnabled(false);
64  }
65  ui->pruneGB->setValue(m_prune_target_gb);
66  ui->pruneGB->setToolTip(ui->prune->toolTip());
67  ui->lblPruneSuffix->setToolTip(ui->prune->toolTip());
68  UpdatePruneLabels(ui->prune->isChecked());
69 
70  connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) {
71  m_prune_checkbox_is_default = false;
72  UpdatePruneLabels(prune_checked);
73  UpdateFreeSpaceLabel();
74  });
75  connect(ui->pruneGB, qOverload<int>(&QSpinBox::valueChanged), [this](int prune_GB) {
76  m_prune_target_gb = prune_GB;
77  UpdatePruneLabels(ui->prune->isChecked());
78  UpdateFreeSpaceLabel();
79  });
80 
81  startThread();
82 }
83 
85 {
86  delete ui;
87  /* Ensure thread is finished before it is deleted */
88  thread->quit();
89  thread->wait();
90 }
91 
93 {
94  return ui->dataDirectory->text();
95 }
96 
97 void Intro::setDataDirectory(const QString &dataDir)
98 {
99  ui->dataDirectory->setText(dataDir);
100  if(dataDir == GUIUtil::getDefaultDataDirectory())
101  {
102  ui->dataDirDefault->setChecked(true);
103  ui->dataDirectory->setEnabled(false);
104  ui->ellipsisButton->setEnabled(false);
105  } else {
106  ui->dataDirCustom->setChecked(true);
107  ui->dataDirectory->setEnabled(true);
108  ui->ellipsisButton->setEnabled(true);
109  }
110 }
111 
112 int64_t Intro::getPruneMiB() const
113 {
114  switch (ui->prune->checkState()) {
115  case Qt::Checked:
117  case Qt::Unchecked: default:
118  return 0;
119  }
120 }
121 
122 bool Intro::showIfNeeded(bool& did_show_intro, int64_t& prune_MiB)
123 {
124  did_show_intro = false;
125 
126  QSettings settings;
127  /* If data directory provided on command line, no need to look at settings
128  or show a picking dialog */
129  if(!gArgs.GetArg("-datadir", "").empty())
130  return true;
131  /* 1) Default data directory for operating system */
132  QString dataDir = GUIUtil::getDefaultDataDirectory();
133  /* 2) Allow QSettings to override default dir */
134  dataDir = settings.value("strDataDir", dataDir).toString();
135 
136  if(!fs::exists(GUIUtil::QStringToPath(dataDir)) || gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) || settings.value("fReset", false).toBool() || gArgs.GetBoolArg("-resetguisettings", false))
137  {
138  /* Use selectParams here to guarantee Params() can be used by node interface */
139  try {
141  } catch (const std::exception&) {
142  return false;
143  }
144 
145  /* If current default data directory does not exist, let the user choose one */
146  Intro intro(nullptr, Params().AssumedBlockchainSize(), Params().AssumedChainStateSize());
147  intro.setDataDirectory(dataDir);
148  intro.setWindowIcon(QIcon(":icons/bitcoin"));
149  did_show_intro = true;
150 
151  while(true)
152  {
153  if(!intro.exec())
154  {
155  /* Cancel clicked */
156  return false;
157  }
158  dataDir = intro.getDataDirectory();
159  try {
161  // If a new data directory has been created, make wallets subdirectory too
162  TryCreateDirectories(GUIUtil::QStringToPath(dataDir) / "wallets");
163  }
164  break;
165  } catch (const fs::filesystem_error&) {
166  QMessageBox::critical(nullptr, CLIENT_NAME,
167  tr("Error: Specified data directory \"%1\" cannot be created.").arg(dataDir));
168  /* fall through, back to choosing screen */
169  }
170  }
171 
172  // Additional preferences:
173  prune_MiB = intro.getPruneMiB();
174 
175  settings.setValue("strDataDir", dataDir);
176  settings.setValue("fReset", false);
177  }
178  /* Only override -datadir if different from the default, to make it possible to
179  * override -datadir in the bitcoin.conf file in the default data directory
180  * (to be consistent with bitcoind behavior)
181  */
182  if(dataDir != GUIUtil::getDefaultDataDirectory()) {
183  gArgs.SoftSetArg("-datadir", fs::PathToString(GUIUtil::QStringToPath(dataDir))); // use OS locale for path setting
184  }
185  return true;
186 }
187 
188 void Intro::setStatus(int status, const QString &message, quint64 bytesAvailable)
189 {
190  switch(status)
191  {
193  ui->errorMessage->setText(message);
194  ui->errorMessage->setStyleSheet("");
195  break;
197  ui->errorMessage->setText(tr("Error") + ": " + message);
198  ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
199  break;
200  }
201  /* Indicate number of bytes available */
202  if(status == FreespaceChecker::ST_ERROR)
203  {
204  ui->freeSpace->setText("");
205  } else {
206  m_bytes_available = bytesAvailable;
207  if (ui->prune->isEnabled() && m_prune_checkbox_is_default) {
209  }
211  }
212  /* Don't allow confirm in ERROR state */
213  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(status != FreespaceChecker::ST_ERROR);
214 }
215 
217 {
218  QString freeString = tr("%n GB of space available", "", m_bytes_available / GB_BYTES);
220  freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb);
221  ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
222  } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) {
223  freeString += " " + tr("(%n GB needed for full chain)", "", m_required_space_gb);
224  ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
225  } else {
226  ui->freeSpace->setStyleSheet("");
227  }
228  ui->freeSpace->setText(freeString + ".");
229 }
230 
231 void Intro::on_dataDirectory_textChanged(const QString &dataDirStr)
232 {
233  /* Disable OK button until check result comes in */
234  ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
235  checkPath(dataDirStr);
236 }
237 
239 {
240  QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(nullptr, tr("Choose data directory"), ui->dataDirectory->text()));
241  if(!dir.isEmpty())
242  ui->dataDirectory->setText(dir);
243 }
244 
246 {
248 }
249 
251 {
252  ui->dataDirectory->setEnabled(true);
253  ui->ellipsisButton->setEnabled(true);
254 }
255 
257 {
258  thread = new QThread(this);
259  FreespaceChecker *executor = new FreespaceChecker(this);
260  executor->moveToThread(thread);
261 
262  connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
263  connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
264  /* make sure executor object is deleted in its own thread */
265  connect(thread, &QThread::finished, executor, &QObject::deleteLater);
266 
267  thread->start();
268 }
269 
270 void Intro::checkPath(const QString &dataDir)
271 {
272  mutex.lock();
273  pathToCheck = dataDir;
274  if(!signalled)
275  {
276  signalled = true;
277  Q_EMIT requestCheck();
278  }
279  mutex.unlock();
280 }
281 
283 {
284  QString retval;
285  mutex.lock();
286  retval = pathToCheck;
287  signalled = false; /* new request can be queued now */
288  mutex.unlock();
289  return retval;
290 }
291 
292 void Intro::UpdatePruneLabels(bool prune_checked)
293 {
295  QString storageRequiresMsg = tr("At least %1 GB of data will be stored in this directory, and it will grow over time.");
296  if (prune_checked && m_prune_target_gb <= m_blockchain_size_gb) {
298  storageRequiresMsg = tr("Approximately %1 GB of data will be stored in this directory.");
299  }
300  ui->lblExplanation3->setVisible(prune_checked);
301  ui->pruneGB->setEnabled(prune_checked);
302  static constexpr uint64_t nPowTargetSpacing = 10 * 60; // from chainparams, which we don't have at this stage
303  static constexpr uint32_t expected_block_data_size = 2250000; // includes undo data
304  const uint64_t expected_backup_days = m_prune_target_gb * 1e9 / (uint64_t(expected_block_data_size) * 86400 / nPowTargetSpacing);
305  ui->lblPruneSuffix->setText(
306  //: Explanatory text on the capability of the current prune target.
307  tr("(sufficient to restore backups %n day(s) old)", "", expected_backup_days));
308  ui->sizeWarningLabel->setText(
309  tr("%1 will download and store a copy of the Bitcoin block chain.").arg(CLIENT_NAME) + " " +
310  storageRequiresMsg.arg(m_required_space_gb) + " " +
311  tr("The wallet will also be stored in this directory.")
312  );
313  this->adjustSize();
314 }
static const bool DEFAULT_CHOOSE_DATADIR
Definition: intro.h:14
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
Definition: fs_helpers.cpp:255
static constexpr int DEFAULT_PRUNE_TARGET_GB
Definition: guiconstants.h:61
Utility functions used by the Bitcoin Qt UI.
Definition: bitcoingui.h:58
void reply(int status, const QString &message, quint64 available)
void requestCheck()
void on_dataDirCustom_clicked()
Definition: intro.cpp:250
int64_t m_required_space_gb
Total required space (in GB) depending on user choice (prune or not prune).
Definition: intro.h:75
void UpdatePruneLabels(bool prune_checked)
Definition: intro.cpp:292
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:515
QString getDataDirectory()
Definition: intro.cpp:92
static constexpr uint64_t GB_BYTES
Definition: guiconstants.h:58
ChainType GetChainType() const
Returns the appropriate chain type from the program arguments.
Definition: args.cpp:808
static int64_t PruneGBtoMiB(int gb)
Convert displayed prune target GB to configured MiB.
Definition: optionsmodel.h:34
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn&#39;t already have a value.
Definition: args.cpp:555
bool signalled
Definition: intro.h:70
constexpr auto dialog_flags
Definition: guiutil.h:60
static bool showIfNeeded(bool &did_show_intro, int64_t &prune_MiB)
Determine data directory.
Definition: intro.cpp:122
static bool exists(const path &p)
Definition: fs.h:95
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:231
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:188
void checkPath(const QString &dataDir)
Definition: intro.cpp:270
void on_ellipsisButton_clicked()
Definition: intro.cpp:238
fs::path QStringToPath(const QString &path)
Convert QString to OS specific boost path through UTF-8.
Definition: guiutil.cpp:670
void on_dataDirDefault_clicked()
Definition: intro.cpp:245
int64_t getPruneMiB() const
Definition: intro.cpp:112
int64_t m_prune_target_gb
Definition: intro.h:77
static int PruneMiBtoGB(int64_t mib)
Convert configured prune target MiB to displayed GB.
Definition: optionsmodel.h:29
void UpdateFreeSpaceLabel()
Definition: intro.cpp:216
ArgsManager gArgs
Definition: args.cpp:40
const int64_t m_blockchain_size_gb
Definition: intro.h:72
friend class FreespaceChecker
Definition: intro.h:85
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:97
Ui::Intro * ui
Definition: intro.h:66
const CChainParams & Params()
Return the currently selected parameters.
void startThread()
Definition: intro.cpp:256
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Definition: args.h:306
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:461
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:157
const int64_t m_chain_state_size_gb
Definition: intro.h:73
static const uint64_t MIN_DISK_SPACE_FOR_BLOCK_FILES
Definition: validation.h:87
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:297
~Intro()
Definition: intro.cpp:84
bool m_prune_checkbox_is_default
Definition: intro.h:67
QMutex mutex
Definition: intro.h:69
QString getPathToCheck() override
Definition: intro.cpp:282
QThread * thread
Definition: intro.h:68
QString pathToCheck
Definition: intro.h:71
void SelectParams(const ChainType chain)
Sets the params returned by Params() to those for the given chain type.
Introduction screen (pre-GUI startup).
Definition: intro.h:28
uint64_t m_bytes_available
Definition: intro.h:76
Intro(QWidget *parent=nullptr, int64_t blockchain_size_gb=0, int64_t chain_state_size_gb=0)
Definition: intro.cpp:39