@ -63,6 +63,7 @@
# endif
# include <thread>
# include <univalue.h>
// Application startup time (used for uptime calculation)
const int64_t nStartupTime = GetTime ( ) ;
@ -161,11 +162,14 @@ static bool InterpretBool(const std::string& strValue)
return ( atoi ( strValue ) ! = 0 ) ;
}
static std : : string SettingName ( const std : : string & arg )
{
return arg . size ( ) > 0 & & arg [ 0 ] = = ' - ' ? arg . substr ( 1 ) : arg ;
}
/** Internal helper functions for ArgsManager */
class ArgsManagerHelper {
public :
typedef std : : map < std : : string , std : : vector < std : : string > > MapArgs ;
/** Determine whether to use config settings in the default section,
* See also comments around ArgsManager : : ArgsManager ( ) below . */
static inline bool UseDefaultSection ( const ArgsManager & am , const std : : string & arg ) EXCLUSIVE_LOCKS_REQUIRED ( am . cs_args )
@ -173,91 +177,10 @@ public:
return ( am . m_network = = CBaseChainParams : : MAIN | | am . m_network_only_args . count ( arg ) = = 0 ) ;
}
/** Convert regular argument into the network-specific setting */
static inline std : : string NetworkArg ( const ArgsManager & am , const std : : string & arg )
{
assert ( arg . length ( ) > 1 & & arg [ 0 ] = = ' - ' ) ;
return " - " + am . m_network + " . " + arg . substr ( 1 ) ;
}
/** Find arguments in a map and add them to a vector */
static inline void AddArgs ( std : : vector < std : : string > & res , const MapArgs & map_args , const std : : string & arg )
{
auto it = map_args . find ( arg ) ;
if ( it ! = map_args . end ( ) ) {
res . insert ( res . end ( ) , it - > second . begin ( ) , it - > second . end ( ) ) ;
}
}
/** Return true/false if an argument is set in a map, and also
* return the first ( or last ) of the possibly multiple values it has
*/
static inline std : : pair < bool , std : : string > GetArgHelper ( const MapArgs & map_args , const std : : string & arg , bool getLast = false )
{
auto it = map_args . find ( arg ) ;
if ( it = = map_args . end ( ) | | it - > second . empty ( ) ) {
return std : : make_pair ( false , std : : string ( ) ) ;
}
if ( getLast ) {
return std : : make_pair ( true , it - > second . back ( ) ) ;
} else {
return std : : make_pair ( true , it - > second . front ( ) ) ;
}
}
/* Get the string value of an argument, returning a pair of a boolean
* indicating the argument was found , and the value for the argument
* if it was found ( or the empty string if not found ) .
*/
static inline std : : pair < bool , std : : string > GetArg ( const ArgsManager & am , const std : : string & arg )
static util : : SettingsValue Get ( const ArgsManager & am , const std : : string & arg )
{
LOCK ( am . cs_args ) ;
std : : pair < bool , std : : string > found_result ( false , std : : string ( ) ) ;
// We pass "true" to GetArgHelper in order to return the last
// argument value seen from the command line (so "bitcoind -foo=bar
// -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
found_result = GetArgHelper ( am . m_override_args , arg , true ) ;
if ( found_result . first ) {
return found_result ;
}
// But in contrast we return the first argument seen in a config file,
// so "foo=bar \n foo=baz" in the config file gives
// GetArg(am,"foo")={true,"bar"}
if ( ! am . m_network . empty ( ) ) {
found_result = GetArgHelper ( am . m_config_args , NetworkArg ( am , arg ) ) ;
if ( found_result . first ) {
return found_result ;
}
}
if ( UseDefaultSection ( am , arg ) ) {
found_result = GetArgHelper ( am . m_config_args , arg ) ;
if ( found_result . first ) {
return found_result ;
}
}
return found_result ;
}
/* Special test for -testnet and -regtest args, because we
* don ' t want to be confused by craziness like " [regtest] testnet=1 "
*/
static inline bool GetNetBoolArg ( const ArgsManager & am , const std : : string & net_arg ) EXCLUSIVE_LOCKS_REQUIRED ( am . cs_args )
{
std : : pair < bool , std : : string > found_result ( false , std : : string ( ) ) ;
found_result = GetArgHelper ( am . m_override_args , net_arg , true ) ;
if ( ! found_result . first ) {
found_result = GetArgHelper ( am . m_config_args , net_arg , true ) ;
if ( ! found_result . first ) {
return false ; // not set
}
}
return InterpretBool ( found_result . second ) ; // is set, so evaluate
return GetSetting ( am . m_settings , am . m_network , SettingName ( arg ) , ! UseDefaultSection ( am , arg ) , /* get_chain_name= */ false ) ;
}
} ;
@ -268,13 +191,12 @@ public:
* checks whether there was a double - negative ( - nofoo = 0 - > - foo = 1 ) .
*
* If there was not a double negative , it removes the " no " from the key
* and clears the args vector to indicate a negated option .
* and returns false .
*
* If there was a double negative , it removes " no " from the key , sets the
* value to " 1 " and pushes the key and the updated value to the args vector .
* If there was a double negative , it removes " no " from the key , and
* returns true .
*
* If there was no " no " , it leaves key and value untouched and pushes them
* to the args vector .
* If there was no " no " , it returns the string value untouched .
*
* Where an option was negated can be later checked using the
* IsArgNegated ( ) method . One use case for this is to have a way to disable
@ -282,34 +204,39 @@ public:
* that debug log output is not sent to any file at all ) .
*/
NODISCARD static bool InterpretOption ( std : : string key , std : : string val , unsigned int flags ,
std : : map < std : : string , std : : vector < std : : string > > & args ,
std : : string & error )
static util : : SettingsValue InterpretOption ( std : : string & section , std : : string & key , const std : : string & value )
{
assert ( key [ 0 ] = = ' - ' ) ;
// Split section name from key name for keys like "testnet.foo" or "regtest.bar"
size_t option_index = key . find ( ' . ' ) ;
if ( option_index = = std : : string : : npos ) {
option_index = 1 ;
} else {
+ + option_index ;
if ( option_index ! = std : : string : : npos ) {
section = key . substr ( 0 , option_index ) ;
key . erase ( 0 , option_index + 1 ) ;
}
if ( key . substr ( option_index , 2 ) = = " no " ) {
key . erase ( option_index , 2 ) ;
if ( flags & ArgsManager : : ALLOW_BOOL ) {
if ( InterpretBool ( val ) ) {
args [ key ] . clear ( ) ;
return true ;
}
// Double negatives like -nofoo=0 are supported (but discouraged)
LogPrintf ( " Warning: parsed potentially confusing double-negative %s=%s \n " , key , val ) ;
val = " 1 " ;
} else {
error = strprintf ( " Negating of %s is meaningless and therefore forbidden " , key ) ;
return false ;
if ( key . substr ( 0 , 2 ) = = " no " ) {
key . erase ( 0 , 2 ) ;
// Double negatives like -nofoo=0 are supported (but discouraged)
if ( ! InterpretBool ( value ) ) {
LogPrintf ( " Warning: parsed potentially confusing double-negative -%s=%s \n " , key , value ) ;
return true ;
}
return false ;
}
return value ;
}
/**
* Check settings value validity according to flags .
*
* TODO : Add more meaningful error checks here in the future
* See " here's how the flags are meant to behave " in
* https : //github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
*/
static bool CheckValid ( const std : : string & key , const util : : SettingsValue & val , unsigned int flags , std : : string & error )
{
if ( val . isBool ( ) & & ! ( flags & ArgsManager : : ALLOW_BOOL ) ) {
error = strprintf ( " Negating of -%s is meaningless and therefore forbidden " , key ) ;
return false ;
}
args [ key ] . push_back ( val ) ;
return true ;
}
@ -331,22 +258,9 @@ const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
if ( m_network = = CBaseChainParams : : MAIN ) return std : : set < std : : string > { } ;
for ( const auto & arg : m_network_only_args ) {
std : : pair < bool , std : : string > found_result ;
// if this option is overridden it's fine
found_result = ArgsManagerHelper : : GetArgHelper ( m_override_args , arg ) ;
if ( found_result . first ) continue ;
// if there's a network-specific value for this option, it's fine
found_result = ArgsManagerHelper : : GetArgHelper ( m_config_args , ArgsManagerHelper : : NetworkArg ( * this , arg ) ) ;
if ( found_result . first ) continue ;
// if there isn't a default value for this option, it's fine
found_result = ArgsManagerHelper : : GetArgHelper ( m_config_args , arg ) ;
if ( ! found_result . first ) continue ;
// otherwise, issue a warning
unsuitables . insert ( arg ) ;
if ( OnlyHasDefaultSectionSetting ( m_settings , m_network , SettingName ( arg ) ) ) {
unsuitables . insert ( arg ) ;
}
}
return unsuitables ;
}
@ -375,7 +289,7 @@ void ArgsManager::SelectConfigNetwork(const std::string& network)
bool ArgsManager : : ParseParameters ( int argc , const char * const argv [ ] , std : : string & error )
{
LOCK ( cs_args ) ;
m_ override_arg s. clear ( ) ;
m_ settings. command_line_option s. clear ( ) ;
for ( int i = 1 ; i < argc ; i + + ) {
std : : string key ( argv [ i ] ) ;
@ -408,49 +322,44 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
if ( key . length ( ) > 1 & & key [ 1 ] = = ' - ' )
key . erase ( 0 , 1 ) ;
// Transform -foo to foo
key . erase ( 0 , 1 ) ;
std : : string section ;
util : : SettingsValue value = InterpretOption ( section , key , val ) ;
const unsigned int flags = FlagsOfKnownArg ( key ) ;
if ( flags ) {
if ( ! InterpretOption ( key , val , flags , m_override_args , error ) ) {
if ( ! CheckValid( key , value , fla gs, error ) ) {
return false ;
}
// Weird behavior preserved for backwards compatibility: command
// line options with section prefixes are allowed but ignored. It
// would be better if these options triggered the Invalid parameter
// error below.
if ( section . empty ( ) ) {
m_settings . command_line_options [ key ] . push_back ( value ) ;
}
} else {
error = strprintf ( " Invalid parameter %s " , key ) ;
error = strprintf ( " Invalid parameter - %s" , key ) ;
return false ;
}
}
// we do not allow -includeconf from command line, so we clear it here
auto it = m_override_args . find ( " -includeconf " ) ;
if ( it ! = m_override_args . end ( ) ) {
if ( it - > second . size ( ) > 0 ) {
for ( const auto & ic : it - > second ) {
error + = " -includeconf cannot be used from commandline; -includeconf= " + ic + " \n " ;
}
return false ;
bool success = true ;
if ( auto * includes = util : : FindKey ( m_settings . command_line_options , " includeconf " ) ) {
for ( const auto & include : util : : SettingsSpan ( * includes ) ) {
error + = " -includeconf cannot be used from commandline; -includeconf= " + include . get_str ( ) + " \n " ;
success = false ;
}
}
return true ;
return success ;
}
unsigned int ArgsManager : : FlagsOfKnownArg ( const std : : string & key ) const
{
assert ( key [ 0 ] = = ' - ' ) ;
size_t option_index = key . find ( ' . ' ) ;
if ( option_index = = std : : string : : npos ) {
option_index = 1 ;
} else {
+ + option_index ;
}
if ( key . substr ( option_index , 2 ) = = " no " ) {
option_index + = 2 ;
}
const std : : string base_arg_name = ' - ' + key . substr ( option_index ) ;
LOCK ( cs_args ) ;
for ( const auto & arg_map : m_available_args ) {
const auto search = arg_map . second . find ( base_arg_name ) ;
const auto search = arg_map . second . find ( ' - ' + key ) ;
if ( search ! = arg_map . second . end ( ) ) {
return search - > second . m_flags ;
}
@ -460,69 +369,42 @@ unsigned int ArgsManager::FlagsOfKnownArg(const std::string& key) const
std : : vector < std : : string > ArgsManager : : GetArgs ( const std : : string & strArg ) const
{
std : : vector < std : : string > result = { } ;
if ( IsArgNegated ( strArg ) ) return result ; // special case
LOCK ( cs_args ) ;
ArgsManagerHelper : : AddArgs ( result , m_override_args , strArg ) ;
if ( ! m_network . empty ( ) ) {
ArgsManagerHelper : : AddArgs ( result , m_config_args , ArgsManagerHelper : : NetworkArg ( * this , strArg ) ) ;
}
if ( ArgsManagerHelper : : UseDefaultSection ( * this , strArg ) ) {
ArgsManagerHelper : : AddArgs ( result , m_config_args , strArg ) ;
bool ignore_default_section_config = ! ArgsManagerHelper : : UseDefaultSection ( * this , strArg ) ;
std : : vector < std : : string > result ;
for ( const util : : SettingsValue & value :
util : : GetSettingsList ( m_settings , m_network , SettingName ( strArg ) , ignore_default_section_config ) ) {
result . push_back ( value . isFalse ( ) ? " 0 " : value . isTrue ( ) ? " 1 " : value . get_str ( ) ) ;
}
return result ;
}
bool ArgsManager : : IsArgSet ( const std : : string & strArg ) const
{
if ( IsArgNegated ( strArg ) ) return true ; // special case
return ArgsManagerHelper : : GetArg ( * this , strArg ) . first ;
return ! ArgsManagerHelper : : Get ( * this , strArg ) . isNull ( ) ;
}
bool ArgsManager : : IsArgNegated ( const std : : string & strArg ) const
{
LOCK ( cs_args ) ;
const auto & ov = m_override_args . find ( strArg ) ;
if ( ov ! = m_override_args . end ( ) ) return ov - > second . empty ( ) ;
if ( ! m_network . empty ( ) ) {
const auto & cfs = m_config_args . find ( ArgsManagerHelper : : NetworkArg ( * this , strArg ) ) ;
if ( cfs ! = m_config_args . end ( ) ) return cfs - > second . empty ( ) ;
}
const auto & cf = m_config_args . find ( strArg ) ;
if ( cf ! = m_config_args . end ( ) ) return cf - > second . empty ( ) ;
return false ;
return ArgsManagerHelper : : Get ( * this , strArg ) . isFalse ( ) ;
}
std : : string ArgsManager : : GetArg ( const std : : string & strArg , const std : : string & strDefault ) const
{
if ( IsArgNegated ( strArg ) ) return " 0 " ;
std : : pair < bool , std : : string > found_res = ArgsManagerHelper : : GetArg ( * this , strArg ) ;
if ( found_res . first ) return found_res . second ;
return strDefault ;
const util : : SettingsValue value = ArgsManagerHelper : : Get ( * this , strArg ) ;
return value . isNull ( ) ? strDefault : value . isFalse ( ) ? " 0 " : value . isTrue ( ) ? " 1 " : value . get_str ( ) ;
}
int64_t ArgsManager : : GetArg ( const std : : string & strArg , int64_t nDefault ) const
{
if ( IsArgNegated ( strArg ) ) return 0 ;
std : : pair < bool , std : : string > found_res = ArgsManagerHelper : : GetArg ( * this , strArg ) ;
if ( found_res . first ) return atoi64 ( found_res . second ) ;
return nDefault ;
const util : : SettingsValue value = ArgsManagerHelper : : Get ( * this , strArg ) ;
return value . isNull ( ) ? nDefault : value . isFalse ( ) ? 0 : value . isTrue ( ) ? 1 : value . isNum ( ) ? value . get_int64 ( ) : atoi64 ( value . get_str ( ) ) ;
}
bool ArgsManager : : GetBoolArg ( const std : : string & strArg , bool fDefault ) const
{
if ( IsArgNegated ( strArg ) ) return false ;
std : : pair < bool , std : : string > found_res = ArgsManagerHelper : : GetArg ( * this , strArg ) ;
if ( found_res . first ) return InterpretBool ( found_res . second ) ;
return fDefault ;
const util : : SettingsValue value = ArgsManagerHelper : : Get ( * this , strArg ) ;
return value . isNull ( ) ? fDefault : value . isBool ( ) ? value . get_bool ( ) : InterpretBool ( value . get_str ( ) ) ;
}
bool ArgsManager : : SoftSetArg ( const std : : string & strArg , const std : : string & strValue )
@ -544,7 +426,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
void ArgsManager : : ForceSetArg ( const std : : string & strArg , const std : : string & strValue )
{
LOCK ( cs_args ) ;
m_ override_args[ strArg ] = { strValue } ;
m_ settings. forced_settings [ SettingName ( strArg ) ] = strValue ;
}
void ArgsManager : : AddArg ( const std : : string & name , const std : : string & help , unsigned int flags , const OptionsCategory & cat )
@ -860,12 +742,15 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
return false ;
}
for ( const std : : pair < std : : string , std : : string > & option : options ) {
const std : : string strKey = std : : string ( " - " ) + option . first ;
const unsigned int flags = FlagsOfKnownArg ( strKey ) ;
std : : string section ;
std : : string key = option . first ;
util : : SettingsValue value = InterpretOption ( section , key , option . second ) ;
const unsigned int flags = FlagsOfKnownArg ( key ) ;
if ( flags ) {
if ( ! InterpretOption ( strKey , option . second , flags , m_config_args , error ) ) {
if ( ! CheckValid( key , value , fla gs, error ) ) {
return false ;
}
m_settings . ro_config [ section ] [ key ] . push_back ( value ) ;
} else {
if ( ignore_invalid_keys ) {
LogPrintf ( " Ignoring unknown configuration value %s \n " , option . first ) ;
@ -882,7 +767,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
{
{
LOCK ( cs_args ) ;
m_ config_args . clear ( ) ;
m_ settings. ro_ config. clear ( ) ;
m_config_sections . clear ( ) ;
}
@ -899,30 +784,34 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
bool use_conf_file { true } ;
{
LOCK ( cs_args ) ;
auto it = m_override_args . find ( " -includeconf " ) ;
if ( it ! = m_override_args . end ( ) ) {
if ( auto * includes = util : : FindKey ( m_settings . command_line_options , " includeconf " ) ) {
// ParseParameters() fails if a non-negated -includeconf is passed on the command-line
assert ( it- > second . empty ( ) ) ;
assert ( util: : SettingsSpan ( * includes ) . last_negated ( ) ) ;
use_conf_file = false ;
}
}
if ( use_conf_file ) {
std : : string chain_id = GetChainName ( ) ;
std : : vector < std : : string > conf_file_names ( GetArgs ( " -includeconf " ) ) ;
{
// We haven't set m_network yet (that happens in SelectParams()), so manually check
// for network.includeconf args.
std : : vector < std : : string > includeconf_net ( GetArgs ( std : : string ( " - " ) + chain_id + " .includeconf " ) ) ;
conf_file_names . insert ( conf_file_names . end ( ) , includeconf_net . begin ( ) , includeconf_net . end ( ) ) ;
}
std : : vector < std : : string > conf_file_names ;
// Remove -includeconf from configuration, so we can warn about recursion
// later
{
auto add_includes = [ & ] ( const std : : string & network , size_t skip = 0 ) {
size_t num_values = 0 ;
LOCK ( cs_args ) ;
m_config_args . erase ( " -includeconf " ) ;
m_config_args . erase ( std : : string ( " - " ) + chain_id + " .includeconf " ) ;
}
if ( auto * section = util : : FindKey ( m_settings . ro_config , network ) ) {
if ( auto * values = util : : FindKey ( * section , " includeconf " ) ) {
for ( size_t i = std : : max ( skip , util : : SettingsSpan ( * values ) . negated ( ) ) ; i < values - > size ( ) ; + + i ) {
conf_file_names . push_back ( ( * values ) [ i ] . get_str ( ) ) ;
}
num_values = values - > size ( ) ;
}
}
return num_values ;
} ;
// We haven't set m_network yet (that happens in SelectParams()), so manually check
// for network.includeconf args.
const size_t chain_includes = add_includes ( chain_id ) ;
const size_t default_includes = add_includes ( { } ) ;
for ( const std : : string & conf_file_name : conf_file_names ) {
fsbridge : : ifstream conf_file_stream ( GetConfigFile ( conf_file_name ) ) ;
@ -938,14 +827,13 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
}
// Warn about recursive -includeconf
conf_file_names = GetArgs ( " -includeconf " ) ;
std: : vector < std : : string > includeconf_net ( GetArgs ( std : : string ( " - " ) + chain_id + " .includeconf " ) ) ;
conf_file_names. insert ( conf_file_names . end ( ) , includeconf_net . begin ( ) , includeconf_net . end ( ) ) ;
conf_file_names . clear ( ) ;
add_includes( chain_id , /* skip= */ chain_includes ) ;
add_includes( { } , /* skip= */ default_includes ) ;
std : : string chain_id_final = GetChainName ( ) ;
if ( chain_id_final ! = chain_id ) {
// Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
includeconf_net = GetArgs ( std : : string ( " - " ) + chain_id_final + " .includeconf " ) ;
conf_file_names . insert ( conf_file_names . end ( ) , includeconf_net . begin ( ) , includeconf_net . end ( ) ) ;
add_includes ( chain_id_final ) ;
}
for ( const std : : string & conf_file_name : conf_file_names ) {
tfm : : format ( std : : cerr , " warning: -includeconf cannot be used from included files; ignoring -includeconf=%s \n " , conf_file_name ) ;
@ -964,9 +852,16 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
std : : string ArgsManager : : GetChainName ( ) const
{
LOCK ( cs_args ) ;
const bool fRegTest = ArgsManagerHelper : : GetNetBoolArg ( * this , " -regtest " ) ;
const bool fTestNet = ArgsManagerHelper : : GetNetBoolArg ( * this , " -testnet " ) ;
auto get_net = [ & ] ( const std : : string & arg ) {
LOCK ( cs_args ) ;
util : : SettingsValue value = GetSetting ( m_settings , /* section= */ " " , SettingName ( arg ) ,
/* ignore_default_section_config= */ false ,
/* get_chain_name= */ true ) ;
return value . isNull ( ) ? false : value . isBool ( ) ? value . get_bool ( ) : InterpretBool ( value . get_str ( ) ) ;
} ;
const bool fRegTest = get_net ( " -regtest " ) ;
const bool fTestNet = get_net ( " -testnet " ) ;
const bool is_chain_arg_set = IsArgSet ( " -chain " ) ;
if ( ( int ) is_chain_arg_set + ( int ) fRegTest + ( int ) fTestNet > 1 ) {