Bitcoin ABC 0.30.5
P2P Digital Currency
intro.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2016 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#if defined(HAVE_CONFIG_H)
6#include <config/bitcoin-config.h>
7#endif
8
9#include <common/args.h>
10#include <config.h>
11#include <interfaces/node.h>
12#include <qt/forms/ui_intro.h>
13#include <qt/guiconstants.h>
14#include <qt/guiutil.h>
15#include <qt/intro.h>
16#include <qt/optionsmodel.h>
17#include <util/fs.h>
18#include <util/fs_helpers.h>
19
20#include <QFileDialog>
21#include <QMessageBox>
22#include <QSettings>
23
24#include <cmath>
25
26/* Check free space asynchronously to prevent hanging the UI thread.
27
28 Up to one request to check a path is in flight to this thread; when the
29 check()
30 function runs, the current path is requested from the associated Intro
31 object.
32 The reply is sent back through a signal.
33
34 This ensures that no queue of checking requests is built up while the user is
35 still entering the path, and that always the most recently entered path is
36 checked as
37 soon as the thread becomes available.
38*/
39class FreespaceChecker : public QObject {
40 Q_OBJECT
41
42public:
43 explicit FreespaceChecker(Intro *intro);
44
46
47public Q_SLOTS:
48 void check();
49
50Q_SIGNALS:
51 void reply(int status, const QString &message, quint64 available);
52
53private:
55};
56
57#include <qt/intro.moc>
58
60 this->intro = _intro;
61}
62
64 QString dataDirStr = intro->getPathToCheck();
65 fs::path dataDir = GUIUtil::qstringToBoostPath(dataDirStr);
66 uint64_t freeBytesAvailable = 0;
67 int replyStatus = ST_OK;
68 QString replyMessage = tr("A new data directory will be created.");
69
70 /* Find first parent that exists, so that fs::space does not fail */
71 fs::path parentDir = dataDir;
72 fs::path parentDirOld = fs::path();
73 while (parentDir.has_parent_path() && !fs::exists(parentDir)) {
74 parentDir = parentDir.parent_path();
75
76 /* Check if we make any progress, break if not to prevent an infinite
77 * loop here */
78 if (parentDirOld == parentDir) {
79 break;
80 }
81
82 parentDirOld = parentDir;
83 }
84
85 try {
86 freeBytesAvailable = fs::space(parentDir).available;
87 if (fs::exists(dataDir)) {
88 if (fs::is_directory(dataDir)) {
89 QString separator = "<code>" + QDir::toNativeSeparators("/") +
90 tr("name") + "</code>";
91 replyStatus = ST_OK;
92 replyMessage = tr("Directory already exists. Add %1 if you "
93 "intend to create a new directory here.")
94 .arg(separator);
95 } else {
96 replyStatus = ST_ERROR;
97 replyMessage =
98 tr("Path already exists, and is not a directory.");
99 }
100 }
101 } catch (const fs::filesystem_error &) {
102 /* Parent directory does not exist or is not accessible */
103 replyStatus = ST_ERROR;
104 replyMessage = tr("Cannot create data directory here.");
105 }
106 Q_EMIT reply(replyStatus, replyMessage, freeBytesAvailable);
107}
108
109namespace {
111int GetPruneTargetGB() {
112 int64_t prune_target_mib = gArgs.GetIntArg("-prune", 0);
113 // >1 means automatic pruning is enabled by config, 1 means manual pruning,
114 // 0 means no pruning.
115 return prune_target_mib > 1 ? PruneMiBtoGB(prune_target_mib)
117}
118} // namespace
119
120Intro::Intro(QWidget *parent, int64_t blockchain_size_gb,
121 int64_t chain_state_size_gb)
122 : QDialog(parent), ui(new Ui::Intro), thread(nullptr), signalled(false),
123 m_blockchain_size_gb(blockchain_size_gb),
124 m_chain_state_size_gb(chain_state_size_gb),
125 m_prune_target_gb(GetPruneTargetGB()) {
126 ui->setupUi(this);
127 ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(PACKAGE_NAME));
128 ui->storageLabel->setText(ui->storageLabel->text().arg(PACKAGE_NAME));
129
130 ui->lblExplanation1->setText(ui->lblExplanation1->text()
131 .arg(PACKAGE_NAME)
133 .arg(2009)
134 .arg(tr("Bitcoin")));
135 ui->lblExplanation2->setText(ui->lblExplanation2->text().arg(PACKAGE_NAME));
136
137 // -prune=1 means enabled, above that it's a size in MiB
138 if (gArgs.GetIntArg("-prune", 0) > 1) {
139 ui->prune->setChecked(true);
140 ui->prune->setEnabled(false);
141 }
142 ui->prune->setText(tr("Discard blocks after verification, except most "
143 "recent %1 GB (prune)")
144 .arg(m_prune_target_gb));
145 UpdatePruneLabels(ui->prune->isChecked());
146
147 connect(ui->prune, &QCheckBox::toggled, [this](bool prune_checked) {
148 UpdatePruneLabels(prune_checked);
149 UpdateFreeSpaceLabel();
150 });
151
152 startThread();
153}
154
156 delete ui;
157 /* Ensure thread is finished before it is deleted */
158 thread->quit();
159 thread->wait();
160}
161
163 return ui->dataDirectory->text();
164}
165
166void Intro::setDataDirectory(const QString &dataDir) {
167 ui->dataDirectory->setText(dataDir);
168 if (dataDir == GUIUtil::getDefaultDataDirectory()) {
169 ui->dataDirDefault->setChecked(true);
170 ui->dataDirectory->setEnabled(false);
171 ui->ellipsisButton->setEnabled(false);
172 } else {
173 ui->dataDirCustom->setChecked(true);
174 ui->dataDirectory->setEnabled(true);
175 ui->ellipsisButton->setEnabled(true);
176 }
177}
178
179bool Intro::showIfNeeded(bool &did_show_intro, bool &prune) {
180 did_show_intro = false;
181
182 QSettings settings;
183 /* If data directory provided on command line, no need to look at settings
184 or show a picking dialog */
185 if (!gArgs.GetArg("-datadir", "").empty()) {
186 return true;
187 }
188 /* 1) Default data directory for operating system */
189 QString dataDir = GUIUtil::getDefaultDataDirectory();
190 /* 2) Allow QSettings to override default dir */
191 dataDir = settings.value("strDataDir", dataDir).toString();
192
194 gArgs.GetBoolArg("-choosedatadir", DEFAULT_CHOOSE_DATADIR) ||
195 settings.value("fReset", false).toBool() ||
196 gArgs.GetBoolArg("-resetguisettings", false)) {
201 try {
203 } catch (const std::exception &) {
204 return false;
205 }
206
211 const CChainParams &params = GetConfig().GetChainParams();
212 Intro intro(nullptr, params.AssumedBlockchainSize(),
213 params.AssumedChainStateSize());
214 intro.setDataDirectory(dataDir);
215 intro.setWindowIcon(QIcon(":icons/bitcoin"));
216 did_show_intro = true;
217
218 while (true) {
219 if (!intro.exec()) {
220 /* Cancel clicked */
221 return false;
222 }
223 dataDir = intro.getDataDirectory();
224 try {
226 GUIUtil::qstringToBoostPath(dataDir))) {
227 // If a new data directory has been created, make wallets
228 // subdirectory too
230 "wallets");
231 }
232 break;
233 } catch (const fs::filesystem_error &) {
234 QMessageBox::critical(nullptr, PACKAGE_NAME,
235 tr("Error: Specified data directory "
236 "\"%1\" cannot be created.")
237 .arg(dataDir));
238 /* fall through, back to choosing screen */
239 }
240 }
241
242 // Additional preferences:
243 prune = intro.ui->prune->isChecked();
244
245 settings.setValue("strDataDir", dataDir);
246 settings.setValue("fReset", false);
247 }
248 /* Only override -datadir if different from the default, to make it possible
249 * to
250 * override -datadir in the bitcoin.conf file in the default data directory
251 * (to be consistent with bitcoind behavior)
252 */
253 if (dataDir != GUIUtil::getDefaultDataDirectory()) {
254 // use OS locale for path setting
256 "-datadir", fs::PathToString(GUIUtil::qstringToBoostPath(dataDir)));
257 }
258 return true;
259}
260
261void Intro::setStatus(int status, const QString &message,
262 quint64 bytesAvailable) {
263 switch (status) {
265 ui->errorMessage->setText(message);
266 ui->errorMessage->setStyleSheet("");
267 break;
269 ui->errorMessage->setText(tr("Error") + ": " + message);
270 ui->errorMessage->setStyleSheet("QLabel { color: #800000 }");
271 break;
272 }
273 /* Indicate number of bytes available */
274 if (status == FreespaceChecker::ST_ERROR) {
275 ui->freeSpace->setText("");
276 } else {
277 m_bytes_available = bytesAvailable;
278 if (ui->prune->isEnabled()) {
279 ui->prune->setChecked(
282 }
284 }
285 /* Don't allow confirm in ERROR state */
286 ui->buttonBox->button(QDialogButtonBox::Ok)
287 ->setEnabled(status != FreespaceChecker::ST_ERROR);
288}
289
291 QString freeString =
292 tr("%n GB of free space available", "", m_bytes_available / GB_BYTES);
294 freeString += " " + tr("(of %n GB needed)", "", m_required_space_gb);
295 ui->freeSpace->setStyleSheet("QLabel { color: #800000 }");
296 } else if (m_bytes_available / GB_BYTES - m_required_space_gb < 10) {
297 freeString +=
298 " " + tr("(%n GB needed for full chain)", "", m_required_space_gb);
299 ui->freeSpace->setStyleSheet("QLabel { color: #999900 }");
300 } else {
301 ui->freeSpace->setStyleSheet("");
302 }
303 ui->freeSpace->setText(freeString + ".");
304}
305
306void Intro::on_dataDirectory_textChanged(const QString &dataDirStr) {
307 /* Disable OK button until check result comes in */
308 ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
309 checkPath(dataDirStr);
310}
311
313 QString dir = QDir::toNativeSeparators(QFileDialog::getExistingDirectory(
314 nullptr, "Choose data directory", ui->dataDirectory->text()));
315 if (!dir.isEmpty()) {
316 ui->dataDirectory->setText(dir);
317 }
318}
319
322}
323
325 ui->dataDirectory->setEnabled(true);
326 ui->ellipsisButton->setEnabled(true);
327}
328
330 thread = new QThread(this);
331 FreespaceChecker *executor = new FreespaceChecker(this);
332 executor->moveToThread(thread);
333
334 connect(executor, &FreespaceChecker::reply, this, &Intro::setStatus);
335 connect(this, &Intro::requestCheck, executor, &FreespaceChecker::check);
336 /* make sure executor object is deleted in its own thread */
337 connect(thread, &QThread::finished, executor, &QObject::deleteLater);
338
339 thread->start();
340}
341
342void Intro::checkPath(const QString &dataDir) {
343 mutex.lock();
344 pathToCheck = dataDir;
345 if (!signalled) {
346 signalled = true;
347 Q_EMIT requestCheck();
348 }
349 mutex.unlock();
350}
351
353 QString retval;
354 mutex.lock();
355 retval = pathToCheck;
356 signalled = false; /* new request can be queued now */
357 mutex.unlock();
358 return retval;
359}
360
361void Intro::UpdatePruneLabels(bool prune_checked) {
363 QString storageRequiresMsg =
364 tr("At least %1 GB of data will be stored in this directory, and it "
365 "will grow over time.");
366 if (prune_checked && m_prune_target_gb <= m_blockchain_size_gb) {
368 storageRequiresMsg =
369 tr("Approximately %1 GB of data will be stored in this directory.");
370 }
371 ui->lblExplanation3->setVisible(prune_checked);
372 ui->sizeWarningLabel->setText(
373 tr("%1 will download and store a copy of the Bitcoin block chain.")
374 .arg(PACKAGE_NAME) +
375 " " + storageRequiresMsg.arg(m_required_space_gb) + " " +
376 tr("The wallet will also be stored in this directory."));
377 this->adjustSize();
378}
ArgsManager gArgs
Definition: args.cpp:38
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given BIP70 chain name.
Definition: chainparams.cpp:51
bool SoftSetArg(const std::string &strArg, const std::string &strValue)
Set an argument if it doesn't already have a value.
Definition: args.cpp:579
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:526
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:494
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:556
std::string GetChainName() const
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
Definition: args.cpp:793
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:80
uint64_t AssumedBlockchainSize() const
Minimum free space (in GB) needed for data directory.
Definition: chainparams.h:116
uint64_t AssumedChainStateSize() const
Minimum free space (in GB) needed for data directory when pruned; Does not include prune target.
Definition: chainparams.h:121
virtual const CChainParams & GetChainParams() const =0
FreespaceChecker(Intro *intro)
Definition: intro.cpp:59
Intro * intro
Definition: intro.cpp:54
void reply(int status, const QString &message, quint64 available)
void check()
Definition: intro.cpp:63
Introduction screen (pre-GUI startup).
Definition: intro.h:28
~Intro()
Definition: intro.cpp:155
void setStatus(int status, const QString &message, quint64 bytesAvailable)
Definition: intro.cpp:261
void on_ellipsisButton_clicked()
Definition: intro.cpp:312
QMutex mutex
Definition: intro.h:68
void UpdatePruneLabels(bool prune_checked)
Definition: intro.cpp:361
const int64_t m_blockchain_size_gb
Definition: intro.h:71
void setDataDirectory(const QString &dataDir)
Definition: intro.cpp:166
static bool showIfNeeded(bool &did_show_intro, bool &prune)
Determine data directory.
Definition: intro.cpp:179
uint64_t m_bytes_available
Definition: intro.h:76
QString pathToCheck
Definition: intro.h:70
friend class FreespaceChecker
Definition: intro.h:85
void on_dataDirectory_textChanged(const QString &arg1)
Definition: intro.cpp:306
int64_t m_required_space_gb
Total required space (in GB) depending on user choice (prune or not prune).
Definition: intro.h:75
void UpdateFreeSpaceLabel()
Definition: intro.cpp:290
bool signalled
Definition: intro.h:69
QString getPathToCheck()
Definition: intro.cpp:352
Ui::Intro * ui
Definition: intro.h:66
void requestCheck()
Intro(QWidget *parent=nullptr, int64_t blockchain_size_gb=0, int64_t chain_state_size_gb=0)
Definition: intro.cpp:120
const int64_t m_chain_state_size_gb
Definition: intro.h:72
QString getDataDirectory()
Definition: intro.cpp:162
void checkPath(const QString &dataDir)
Definition: intro.cpp:342
void startThread()
Definition: intro.cpp:329
void on_dataDirDefault_clicked()
Definition: intro.cpp:320
const int64_t m_prune_target_gb
Definition: intro.h:77
QThread * thread
Definition: intro.h:67
void on_dataDirCustom_clicked()
Definition: intro.cpp:324
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
const Config & GetConfig()
Definition: config.cpp:40
bool TryCreateDirectories(const fs::path &p)
Ignores exceptions thrown by create_directories if the requested directory exists.
Definition: fs_helpers.cpp:287
static constexpr int DEFAULT_PRUNE_TARGET_GB
Definition: guiconstants.h:53
static constexpr uint64_t GB_BYTES
Definition: guiconstants.h:50
static const bool DEFAULT_CHOOSE_DATADIR
Definition: intro.h:12
fs::path qstringToBoostPath(const QString &path)
Convert QString to OS specific boost path through UTF-8.
Definition: guiutil.cpp:781
QString getDefaultDataDirectory()
Determine default data directory for operating system.
Definition: guiutil.cpp:290
static bool exists(const path &p)
Definition: fs.h:102
static std::string PathToString(const path &path)
Convert path object to byte string.
Definition: fs.h:142
static int PruneMiBtoGB(int64_t mib)
Convert configured prune target MiB to displayed GB.
Definition: optionsmodel.h:30