2019-12-30 06:39:22 -03:00
// Copyright (c) 2011-2019 The Bitcoin Core developers
2014-12-13 01:09:33 -03:00
// Distributed under the MIT software license, see the accompanying
2013-11-04 12:20:43 -03:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2015-12-09 07:53:12 -03:00
# if defined(HAVE_CONFIG_H)
2017-11-09 21:57:53 -03:00
# include <config/bitcoin-config.h>
2015-12-09 07:53:12 -03:00
# endif
2017-11-09 21:57:53 -03:00
# include <fs.h>
# include <qt/intro.h>
2017-08-15 12:31:26 -03:00
# include <qt/forms/ui_intro.h>
2013-04-13 02:13:08 -03:00
2019-01-14 08:40:00 -03:00
# include <qt/guiconstants.h>
2017-11-09 21:57:53 -03:00
# include <qt/guiutil.h>
2019-11-12 08:23:09 -03:00
# include <qt/optionsmodel.h>
2014-03-22 06:22:42 -03:00
2018-04-07 04:42:02 -03:00
# include <interfaces/node.h>
2018-10-22 19:51:11 -03:00
# include <util/system.h>
2013-05-19 11:36:01 -04:00
# include <QFileDialog>
# include <QSettings>
# include <QMessageBox>
2015-12-29 11:38:38 -03:00
# include <cmath>
2013-05-19 11:36:01 -04:00
/* 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 check ( )
function runs , the current path is requested from the associated Intro object .
The reply is sent back through a signal .
This ensures that no queue of checking requests is built up while the user is
still entering the path , and that always the most recently entered path is checked as
soon as the thread becomes available .
*/
class FreespaceChecker : public QObject
{
Q_OBJECT
2013-07-23 06:00:41 -04:00
2013-05-19 11:36:01 -04:00
public :
2017-08-01 06:22:41 -04:00
explicit FreespaceChecker ( Intro * intro ) ;
2013-05-19 11:36:01 -04:00
enum Status {
ST_OK ,
ST_ERROR
} ;
2015-07-14 08:59:05 -03:00
public Q_SLOTS :
2013-05-19 11:36:01 -04:00
void check ( ) ;
2015-07-14 08:59:05 -03:00
Q_SIGNALS :
2013-05-19 11:36:01 -04:00
void reply ( int status , const QString & message , quint64 available ) ;
private :
Intro * intro ;
} ;
2017-08-15 12:31:26 -03:00
# include <qt/intro.moc>
2013-05-19 11:36:01 -04:00
2016-09-09 08:43:29 -03:00
FreespaceChecker : : FreespaceChecker ( Intro * _intro )
2013-05-19 11:36:01 -04:00
{
2016-09-09 08:43:29 -03:00
this - > intro = _intro ;
2013-05-19 11:36:01 -04:00
}
void FreespaceChecker : : check ( )
{
QString dataDirStr = intro - > getPathToCheck ( ) ;
2014-03-22 06:22:42 -03:00
fs : : path dataDir = GUIUtil : : qstringToBoostPath ( dataDirStr ) ;
2013-04-13 02:13:08 -03:00
uint64_t freeBytesAvailable = 0 ;
2013-05-19 11:36:01 -04:00
int replyStatus = ST_OK ;
QString replyMessage = tr ( " A new data directory will be created. " ) ;
/* Find first parent that exists, so that fs::space does not fail */
fs : : path parentDir = dataDir ;
2013-07-23 06:00:41 -04:00
fs : : path parentDirOld = fs : : path ( ) ;
2013-05-19 11:36:01 -04:00
while ( parentDir . has_parent_path ( ) & & ! fs : : exists ( parentDir ) )
{
parentDir = parentDir . parent_path ( ) ;
2013-07-23 06:00:41 -04:00
/* Check if we make any progress, break if not to prevent an infinite loop here */
if ( parentDirOld = = parentDir )
break ;
parentDirOld = parentDir ;
2013-05-19 11:36:01 -04:00
}
try {
freeBytesAvailable = fs : : space ( parentDir ) . available ;
if ( fs : : exists ( dataDir ) )
{
if ( fs : : is_directory ( dataDir ) )
{
2013-07-29 10:09:15 -04:00
QString separator = " <code> " + QDir : : toNativeSeparators ( " / " ) + tr ( " name " ) + " </code> " ;
2013-05-19 11:36:01 -04:00
replyStatus = ST_OK ;
2013-07-29 10:09:15 -04:00
replyMessage = tr ( " Directory already exists. Add %1 if you intend to create a new directory here. " ) . arg ( separator ) ;
2013-05-19 11:36:01 -04:00
} else {
replyStatus = ST_ERROR ;
replyMessage = tr ( " Path already exists, and is not a directory. " ) ;
}
}
2014-12-07 09:29:06 -03:00
} catch ( const fs : : filesystem_error & )
2013-05-19 11:36:01 -04:00
{
/* Parent directory does not exist or is not accessible */
replyStatus = ST_ERROR ;
replyMessage = tr ( " Cannot create data directory here. " ) ;
}
2015-07-14 08:59:05 -03:00
Q_EMIT reply ( replyStatus , replyMessage , freeBytesAvailable ) ;
2013-05-19 11:36:01 -04:00
}
2019-11-12 09:25:43 -03:00
Intro : : Intro ( QWidget * parent , int64_t blockchain_size_gb , int64_t chain_state_size_gb ) :
2013-05-19 11:36:01 -04:00
QDialog ( parent ) ,
ui ( new Ui : : Intro ) ,
2018-07-30 06:37:09 -04:00
thread ( nullptr ) ,
2018-10-07 09:11:36 -03:00
signalled ( false ) ,
2019-11-12 09:25:43 -03:00
m_blockchain_size_gb ( blockchain_size_gb ) ,
m_chain_state_size_gb ( chain_state_size_gb )
2013-05-19 11:36:01 -04:00
{
ui - > setupUi ( this ) ;
2019-06-26 10:28:13 -04:00
ui - > welcomeLabel - > setText ( ui - > welcomeLabel - > text ( ) . arg ( PACKAGE_NAME ) ) ;
ui - > storageLabel - > setText ( ui - > storageLabel - > text ( ) . arg ( PACKAGE_NAME ) ) ;
2017-02-08 01:21:04 -03:00
ui - > lblExplanation1 - > setText ( ui - > lblExplanation1 - > text ( )
2019-06-26 10:28:13 -04:00
. arg ( PACKAGE_NAME )
2019-11-12 09:25:43 -03:00
. arg ( m_blockchain_size_gb )
2017-02-08 01:21:04 -03:00
. arg ( 2009 )
. arg ( tr ( " Bitcoin " ) )
) ;
2019-06-26 10:28:13 -04:00
ui - > lblExplanation2 - > setText ( ui - > lblExplanation2 - > text ( ) . arg ( PACKAGE_NAME ) ) ;
2017-02-08 01:21:04 -03:00
2019-11-12 08:23:09 -03:00
int64_t prune_target_mib = std : : max < int64_t > ( 0 , gArgs . GetArg ( " -prune " , 0 ) ) ;
if ( prune_target_mib > 1 ) { // -prune=1 means enabled, above that it's a size in MiB
2019-08-24 13:13:04 -04:00
ui - > prune - > setChecked ( true ) ;
ui - > prune - > setEnabled ( false ) ;
}
2019-11-12 08:23:09 -03:00
const int prune_target_gb = PruneMiBtoGB ( prune_target_mib ) ;
ui - > prune - > setText ( tr ( " Discard blocks after verification, except most recent %1 GB (prune) " ) . arg ( prune_target_gb ? prune_target_gb : DEFAULT_PRUNE_TARGET_GB ) ) ;
2019-11-12 09:25:43 -03:00
m_required_space_gb = m_blockchain_size_gb + m_chain_state_size_gb ;
2017-02-08 01:05:55 -03:00
QString storageRequiresMsg = tr ( " At least %1 GB of data will be stored in this directory, and it will grow over time. " ) ;
2019-11-12 09:25:43 -03:00
if ( 0 < prune_target_gb & & 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. " ) ;
2017-02-08 01:00:16 -03:00
}
2019-11-12 09:25:43 -03:00
ui - > lblExplanation3 - > setVisible ( prune_target_gb > 0 ) ;
2017-02-08 00:56:52 -03:00
ui - > sizeWarningLabel - > setText (
2019-06-26 10:28:13 -04:00
tr ( " %1 will download and store a copy of the Bitcoin block chain. " ) . arg ( PACKAGE_NAME ) + " " +
2019-11-12 09:25:43 -03:00
storageRequiresMsg . arg ( m_required_space_gb ) + " " +
2017-02-08 00:56:52 -03:00
tr ( " The wallet will also be stored in this directory. " )
) ;
2019-09-26 16:50:12 -03:00
this - > adjustSize ( ) ;
2013-05-19 11:36:01 -04:00
startThread ( ) ;
}
Intro : : ~ Intro ( )
{
delete ui ;
/* Ensure thread is finished before it is deleted */
2018-09-17 19:17:22 -03:00
thread - > quit ( ) ;
2013-05-19 11:36:01 -04:00
thread - > wait ( ) ;
}
QString Intro : : getDataDirectory ( )
{
return ui - > dataDirectory - > text ( ) ;
}
void Intro : : setDataDirectory ( const QString & dataDir )
{
ui - > dataDirectory - > setText ( dataDir ) ;
2019-04-23 07:26:06 -04:00
if ( dataDir = = GUIUtil : : getDefaultDataDirectory ( ) )
2013-05-19 11:36:01 -04:00
{
ui - > dataDirDefault - > setChecked ( true ) ;
ui - > dataDirectory - > setEnabled ( false ) ;
ui - > ellipsisButton - > setEnabled ( false ) ;
} else {
ui - > dataDirCustom - > setChecked ( true ) ;
ui - > dataDirectory - > setEnabled ( true ) ;
ui - > ellipsisButton - > setEnabled ( true ) ;
}
}
2019-08-24 13:13:04 -04:00
bool Intro : : showIfNeeded ( interfaces : : Node & node , bool & did_show_intro , bool & prune )
2013-05-19 11:36:01 -04:00
{
2019-08-24 16:34:59 -04:00
did_show_intro = false ;
2013-05-19 11:36:01 -04:00
QSettings settings ;
/* If data directory provided on command line, no need to look at settings
or show a picking dialog */
2017-08-01 15:17:40 -04:00
if ( ! gArgs . GetArg ( " -datadir " , " " ) . empty ( ) )
2016-08-18 11:58:04 -03:00
return true ;
2013-05-19 11:36:01 -04:00
/* 1) Default data directory for operating system */
2019-04-23 07:26:06 -04:00
QString dataDir = GUIUtil : : getDefaultDataDirectory ( ) ;
2013-05-19 11:36:01 -04:00
/* 2) Allow QSettings to override default dir */
dataDir = settings . value ( " strDataDir " , dataDir ) . toString ( ) ;
2017-08-01 15:17:40 -04:00
if ( ! fs : : exists ( GUIUtil : : qstringToBoostPath ( dataDir ) ) | | gArgs . GetBoolArg ( " -choosedatadir " , DEFAULT_CHOOSE_DATADIR ) | | settings . value ( " fReset " , false ) . toBool ( ) | | gArgs . GetBoolArg ( " -resetguisettings " , false ) )
2013-05-19 11:36:01 -04:00
{
2018-10-07 09:11:36 -03:00
/* Use selectParams here to guarantee Params() can be used by node interface */
try {
node . selectParams ( gArgs . GetChainName ( ) ) ;
} catch ( const std : : exception & ) {
return false ;
}
2013-05-19 11:36:01 -04:00
/* If current default data directory does not exist, let the user choose one */
2018-10-07 09:11:36 -03:00
Intro intro ( 0 , node . getAssumedBlockchainSize ( ) , node . getAssumedChainStateSize ( ) ) ;
2013-05-19 11:36:01 -04:00
intro . setDataDirectory ( dataDir ) ;
2015-07-28 10:20:14 -03:00
intro . setWindowIcon ( QIcon ( " :icons/bitcoin " ) ) ;
2019-08-24 16:34:59 -04:00
did_show_intro = true ;
2013-08-24 09:20:37 -04:00
2013-05-19 11:36:01 -04:00
while ( true )
{
if ( ! intro . exec ( ) )
{
/* Cancel clicked */
2016-08-18 11:58:04 -03:00
return false ;
2013-05-19 11:36:01 -04:00
}
dataDir = intro . getDataDirectory ( ) ;
try {
2017-10-12 06:04:46 -03:00
if ( TryCreateDirectories ( GUIUtil : : qstringToBoostPath ( dataDir ) ) ) {
// If a new data directory has been created, make wallets subdirectory too
TryCreateDirectories ( GUIUtil : : qstringToBoostPath ( dataDir ) / " wallets " ) ;
}
2013-05-19 11:36:01 -04:00
break ;
2014-12-07 09:29:06 -03:00
} catch ( const fs : : filesystem_error & ) {
2019-06-26 10:28:13 -04:00
QMessageBox : : critical ( nullptr , PACKAGE_NAME ,
2014-07-31 10:56:14 -04:00
tr ( " Error: Specified data directory \" %1 \" cannot be created. " ) . arg ( dataDir ) ) ;
2013-05-19 11:36:01 -04:00
/* fall through, back to choosing screen */
}
}
2019-08-24 13:13:04 -04:00
// Additional preferences:
prune = intro . ui - > prune - > isChecked ( ) ;
2013-05-19 11:36:01 -04:00
settings . setValue ( " strDataDir " , dataDir ) ;
2016-08-10 15:35:22 -04:00
settings . setValue ( " fReset " , false ) ;
2013-05-19 11:36:01 -04:00
}
2014-03-25 05:26:11 -03:00
/* Only override -datadir if different from the default, to make it possible to
* override - datadir in the bitcoin . conf file in the default data directory
* ( to be consistent with bitcoind behavior )
*/
2019-04-23 07:26:06 -04:00
if ( dataDir ! = GUIUtil : : getDefaultDataDirectory ( ) ) {
2017-04-17 16:44:10 -03:00
node . softSetArg ( " -datadir " , GUIUtil : : qstringToBoostPath ( dataDir ) . string ( ) ) ; // use OS locale for path setting
}
2016-08-18 11:58:04 -03:00
return true ;
2013-05-19 11:36:01 -04:00
}
void Intro : : setStatus ( int status , const QString & message , quint64 bytesAvailable )
{
switch ( status )
{
case FreespaceChecker : : ST_OK :
ui - > errorMessage - > setText ( message ) ;
ui - > errorMessage - > setStyleSheet ( " " ) ;
break ;
case FreespaceChecker : : ST_ERROR :
ui - > errorMessage - > setText ( tr ( " Error " ) + " : " + message ) ;
ui - > errorMessage - > setStyleSheet ( " QLabel { color: #800000 } " ) ;
break ;
}
/* Indicate number of bytes available */
if ( status = = FreespaceChecker : : ST_ERROR )
{
ui - > freeSpace - > setText ( " " ) ;
} else {
2014-10-21 10:40:00 -03:00
QString freeString = tr ( " %n GB of free space available " , " " , bytesAvailable / GB_BYTES ) ;
2019-11-12 09:25:43 -03:00
if ( bytesAvailable < m_required_space_gb * GB_BYTES ) {
freeString + = " " + tr ( " (of %n GB needed) " , " " , m_required_space_gb ) ;
2013-05-19 11:36:01 -04:00
ui - > freeSpace - > setStyleSheet ( " QLabel { color: #800000 } " ) ;
2019-08-24 13:58:24 -04:00
ui - > prune - > setChecked ( true ) ;
2019-11-12 09:25:43 -03:00
} else if ( bytesAvailable / GB_BYTES - m_required_space_gb < 10 ) {
freeString + = " " + tr ( " (%n GB needed for full chain) " , " " , m_required_space_gb ) ;
2019-08-24 13:58:24 -04:00
ui - > freeSpace - > setStyleSheet ( " QLabel { color: #999900 } " ) ;
ui - > prune - > setChecked ( true ) ;
2013-05-19 11:36:01 -04:00
} else {
ui - > freeSpace - > setStyleSheet ( " " ) ;
}
2013-07-23 06:00:41 -04:00
ui - > freeSpace - > setText ( freeString + " . " ) ;
2013-05-19 11:36:01 -04:00
}
/* Don't allow confirm in ERROR state */
ui - > buttonBox - > button ( QDialogButtonBox : : Ok ) - > setEnabled ( status ! = FreespaceChecker : : ST_ERROR ) ;
}
void Intro : : on_dataDirectory_textChanged ( const QString & dataDirStr )
{
/* Disable OK button until check result comes in */
ui - > buttonBox - > button ( QDialogButtonBox : : Ok ) - > setEnabled ( false ) ;
checkPath ( dataDirStr ) ;
}
void Intro : : on_ellipsisButton_clicked ( )
{
2018-07-31 14:02:34 -04:00
QString dir = QDir : : toNativeSeparators ( QFileDialog : : getExistingDirectory ( nullptr , " Choose data directory " , ui - > dataDirectory - > text ( ) ) ) ;
2013-05-19 11:36:01 -04:00
if ( ! dir . isEmpty ( ) )
ui - > dataDirectory - > setText ( dir ) ;
}
void Intro : : on_dataDirDefault_clicked ( )
{
2019-04-23 07:26:06 -04:00
setDataDirectory ( GUIUtil : : getDefaultDataDirectory ( ) ) ;
2013-05-19 11:36:01 -04:00
}
void Intro : : on_dataDirCustom_clicked ( )
{
ui - > dataDirectory - > setEnabled ( true ) ;
ui - > ellipsisButton - > setEnabled ( true ) ;
}
void Intro : : startThread ( )
{
thread = new QThread ( this ) ;
FreespaceChecker * executor = new FreespaceChecker ( this ) ;
executor - > moveToThread ( thread ) ;
2018-06-24 11:18:22 -04:00
connect ( executor , & FreespaceChecker : : reply , this , & Intro : : setStatus ) ;
connect ( this , & Intro : : requestCheck , executor , & FreespaceChecker : : check ) ;
2013-05-19 11:36:01 -04:00
/* make sure executor object is deleted in its own thread */
2018-09-17 19:17:22 -03:00
connect ( thread , & QThread : : finished , executor , & QObject : : deleteLater ) ;
2013-05-19 11:36:01 -04:00
thread - > start ( ) ;
}
void Intro : : checkPath ( const QString & dataDir )
{
mutex . lock ( ) ;
pathToCheck = dataDir ;
if ( ! signalled )
{
signalled = true ;
2015-07-14 08:59:05 -03:00
Q_EMIT requestCheck ( ) ;
2013-05-19 11:36:01 -04:00
}
mutex . unlock ( ) ;
}
QString Intro : : getPathToCheck ( )
{
QString retval ;
mutex . lock ( ) ;
retval = pathToCheck ;
signalled = false ; /* new request can be queued now */
mutex . unlock ( ) ;
return retval ;
}