mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
Merge 11082 via rwconf-27+knots
This commit is contained in:
commit
c90495c624
@ -60,6 +60,7 @@ Subdirectory | File(s) | Description
|
||||
`./` | `anchors.dat` | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
|
||||
`./` | `banlist.json` | Stores the addresses/subnets of banned nodes.
|
||||
`./` | `bitcoin.conf` | User-defined [configuration settings](bitcoin-conf.md) for `bitcoind` or `bitcoin-qt`. File is not written to by the software and must be created manually. Path can be specified by `-conf` option
|
||||
`./` | `bitcoin_rw.conf` | Contains [configuration settings](bitcoin-conf.md) modified by `bitcoind` or `bitcoin-qt`; can be specified by `-confrw` option
|
||||
`./` | `bitcoind.pid` | Stores the process ID (PID) of `bitcoind` or `bitcoin-qt` while running; created at start and deleted on shutdown; can be specified by `-pid` option
|
||||
`./` | `debug.log` | Contains debug information and general logging generated by `bitcoind` or `bitcoin-qt`; can be specified by `-debuglogfile` option
|
||||
`./` | `fee_estimates.dat` | Stores statistics used to estimate minimum transaction fees required for confirmation
|
||||
|
@ -83,6 +83,7 @@ static void SetupCliArgs(ArgsManager& argsman)
|
||||
|
||||
argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-confrw=<file>", strprintf("Specify read/write configuration file. Relative paths will be prefixed by the network-specific datadir location. (default: %s)", BITCOIN_RW_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-generate",
|
||||
strprintf("Generate blocks, equivalent to RPC getnewaddress followed by RPC generatetoaddress. Optional positional integer "
|
||||
@ -181,13 +182,6 @@ static int AppInitRPC(int argc, char* argv[])
|
||||
tfm::format(std::cerr, "Error reading configuration file: %s\n", error);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
// Check for chain settings (BaseParams() calls are only valid after this clause)
|
||||
try {
|
||||
SelectBaseParams(gArgs.GetChainType());
|
||||
} catch (const std::exception& e) {
|
||||
tfm::format(std::cerr, "Error: %s\n", e.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
|
@ -29,15 +29,18 @@
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
|
||||
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
||||
const char * const BITCOIN_RW_CONF_FILENAME = "bitcoin_rw.conf";
|
||||
|
||||
ArgsManager gArgs;
|
||||
|
||||
@ -772,6 +775,12 @@ void ArgsManager::SetConfigFilePath(fs::path path)
|
||||
m_config_path = path;
|
||||
}
|
||||
|
||||
fs::path ArgsManager::GetRWConfigFilePath() const
|
||||
{
|
||||
LOCK(cs_args);
|
||||
return *Assert(m_rwconf_path);
|
||||
}
|
||||
|
||||
ChainType ArgsManager::GetChainType() const
|
||||
{
|
||||
std::variant<ChainType, std::string> arg = GetChainArg();
|
||||
@ -863,9 +872,231 @@ void ArgsManager::LogArgs() const
|
||||
for (const auto& setting : m_settings.rw_settings) {
|
||||
LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
|
||||
}
|
||||
logArgsPrefix("R/W config file arg:", "", m_settings.rw_config);
|
||||
logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Like std::getline, but includes the EOL character in the result
|
||||
bool getline_with_eol(std::istream& stream, std::string& result)
|
||||
{
|
||||
int current_char;
|
||||
current_char = stream.get();
|
||||
if (current_char == std::char_traits<char>::eof()) {
|
||||
return false;
|
||||
}
|
||||
result.clear();
|
||||
result.push_back(char(current_char));
|
||||
while (current_char != '\n') {
|
||||
current_char = stream.get();
|
||||
if (current_char == std::char_traits<char>::eof()) {
|
||||
break;
|
||||
}
|
||||
result.push_back(char(current_char));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * const ModifyRWConfigFile_ws_chars = " \t\r\n";
|
||||
|
||||
void ModifyRWConfigFile_SanityCheck(const std::string& s)
|
||||
{
|
||||
if (s.empty()) {
|
||||
// Dereferencing .begin or .rbegin below is invalid unless the string has at least one character.
|
||||
return;
|
||||
}
|
||||
|
||||
static const char * const newline_chars = "\r\n";
|
||||
static std::string ws_chars(ModifyRWConfigFile_ws_chars);
|
||||
if (s.find_first_of(newline_chars) != std::string::npos) {
|
||||
throw std::invalid_argument("New-line in config name/value");
|
||||
}
|
||||
if (ws_chars.find(*s.begin()) != std::string::npos || ws_chars.find(*s.rbegin()) != std::string::npos) {
|
||||
throw std::invalid_argument("Config name/value has leading/trailing whitespace");
|
||||
}
|
||||
}
|
||||
|
||||
void ModifyRWConfigFile_WriteRemaining(std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change, std::set<std::string>& setFound)
|
||||
{
|
||||
for (const auto& setting_pair : settings_to_change) {
|
||||
const std::string& key = setting_pair.first;
|
||||
const std::string& val = setting_pair.second;
|
||||
if (setFound.find(key) != setFound.end()) {
|
||||
continue;
|
||||
}
|
||||
setFound.insert(key);
|
||||
ModifyRWConfigFile_SanityCheck(key);
|
||||
ModifyRWConfigFile_SanityCheck(val);
|
||||
stream_out << key << "=" << val << "\n";
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change)
|
||||
{
|
||||
static const char * const ws_chars = ModifyRWConfigFile_ws_chars;
|
||||
std::set<std::string> setFound;
|
||||
std::string s, lineend, linebegin, key;
|
||||
std::string::size_type n, n2;
|
||||
bool inside_group = false, have_eof_nl = true;
|
||||
std::map<std::string, std::string>::const_iterator iterCS;
|
||||
size_t lineno = 0;
|
||||
while (getline_with_eol(stream_in, s)) {
|
||||
++lineno;
|
||||
|
||||
have_eof_nl = (!s.empty()) && (*s.rbegin() == '\n');
|
||||
n = s.find('#');
|
||||
const bool has_comment = (n != std::string::npos);
|
||||
if (!has_comment) {
|
||||
n = s.size();
|
||||
}
|
||||
if (n > 0) {
|
||||
n2 = s.find_last_not_of(ws_chars, n - 1);
|
||||
if (n2 != std::string::npos) {
|
||||
n = n2 + 1;
|
||||
}
|
||||
}
|
||||
n2 = s.find_first_not_of(ws_chars);
|
||||
if (n2 == std::string::npos || n2 >= n) {
|
||||
// Blank or comment-only line
|
||||
stream_out << s;
|
||||
continue;
|
||||
}
|
||||
lineend = s.substr(n);
|
||||
linebegin = s.substr(0, n2);
|
||||
s = s.substr(n2, n - n2);
|
||||
|
||||
// It is impossible for s to be empty here, due to the blank line check above
|
||||
if (*s.begin() == '[' && *s.rbegin() == ']') {
|
||||
// We don't use sections, so we could possibly just write out the rest of the file - but we need to check for unparsable lines, so we just set a flag to ignore settings from here on
|
||||
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
||||
inside_group = true;
|
||||
key.clear();
|
||||
|
||||
stream_out << linebegin << s << lineend;
|
||||
continue;
|
||||
}
|
||||
|
||||
n = s.find('=');
|
||||
if (n == std::string::npos) {
|
||||
// Bad line; this causes boost to throw an exception when parsing, so we comment out the entire file
|
||||
stream_in.seekg(0, std::ios_base::beg);
|
||||
stream_out.seekp(0, std::ios_base::beg);
|
||||
if (!(stream_in.good() && stream_out.good())) {
|
||||
throw std::ios_base::failure("Failed to rewind (to comment out existing file)");
|
||||
}
|
||||
// First, write out all the settings we intend to set
|
||||
setFound.clear();
|
||||
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
||||
// We then define a category to ensure new settings get added before the invalid stuff
|
||||
stream_out << "[INVALID]\n";
|
||||
// Then, describe the problem in a comment
|
||||
stream_out << "# Error parsing line " << lineno << ": " << s << "\n";
|
||||
// Finally, dump the rest of the file commented out
|
||||
while (getline_with_eol(stream_in, s)) {
|
||||
stream_out << "#" << s;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inside_group) {
|
||||
// We don't support/use groups, so once we're inside key is always null to avoid setting anything
|
||||
n2 = s.find_last_not_of(ws_chars, n - 1);
|
||||
if (n2 == std::string::npos) {
|
||||
n2 = n - 1;
|
||||
} else {
|
||||
++n2;
|
||||
}
|
||||
key = s.substr(0, n2);
|
||||
}
|
||||
if ((!key.empty()) && (iterCS = settings_to_change.find(key)) != settings_to_change.end() && setFound.find(key) == setFound.end()) {
|
||||
// This is the key we want to change
|
||||
const std::string& val = iterCS->second;
|
||||
setFound.insert(key);
|
||||
ModifyRWConfigFile_SanityCheck(val);
|
||||
if (has_comment) {
|
||||
// Rather than change a commented line, comment it out entirely (the existing comment may relate to the value) and replace it
|
||||
stream_out << key << "=" << val << "\n";
|
||||
linebegin.insert(linebegin.begin(), '#');
|
||||
} else {
|
||||
// Just modify the value in-line otherwise
|
||||
n2 = s.find_first_not_of(ws_chars, n + 1);
|
||||
if (n2 == std::string::npos) {
|
||||
n2 = n + 1;
|
||||
}
|
||||
s = s.substr(0, n2) + val;
|
||||
}
|
||||
}
|
||||
stream_out << linebegin << s << lineend;
|
||||
}
|
||||
if (setFound.size() < settings_to_change.size()) {
|
||||
if (!have_eof_nl) {
|
||||
stream_out << "\n";
|
||||
}
|
||||
ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
|
||||
}
|
||||
}
|
||||
|
||||
void ArgsManager::ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change)
|
||||
{
|
||||
LOCK(cs_args);
|
||||
fs::path rwconf_path{GetRWConfigFilePath()};
|
||||
fs::path rwconf_new_path{rwconf_path};
|
||||
rwconf_new_path += ".new";
|
||||
try {
|
||||
fs::remove(rwconf_new_path);
|
||||
std::ofstream streamRWConfigOut(rwconf_new_path, std::ios_base::out | std::ios_base::trunc);
|
||||
if (fs::exists(rwconf_path)) {
|
||||
std::ifstream streamRWConfig(rwconf_path);
|
||||
::ModifyRWConfigStream(streamRWConfig, streamRWConfigOut, settings_to_change);
|
||||
} else {
|
||||
std::istringstream streamIn;
|
||||
::ModifyRWConfigStream(streamIn, streamRWConfigOut, settings_to_change);
|
||||
}
|
||||
} catch (...) {
|
||||
fs::remove(rwconf_new_path);
|
||||
throw;
|
||||
}
|
||||
if (!RenameOver(rwconf_new_path, rwconf_path)) {
|
||||
fs::remove(rwconf_new_path);
|
||||
throw std::ios_base::failure(strprintf("Failed to replace %s", fs::PathToString(rwconf_new_path)));
|
||||
}
|
||||
for (const auto& setting_change : settings_to_change) {
|
||||
m_settings.rw_config[setting_change.first] = {setting_change.second};
|
||||
}
|
||||
if (!IsArgNegated("-settings")) {
|
||||
// Also save to settings.json for Core (0.21+) compatibility
|
||||
for (const auto& setting_change : settings_to_change) {
|
||||
m_settings.rw_settings[setting_change.first] = setting_change.second;
|
||||
}
|
||||
WriteSettingsFile();
|
||||
}
|
||||
}
|
||||
|
||||
void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value)
|
||||
{
|
||||
std::map<std::string, std::string> settings_to_change;
|
||||
settings_to_change[setting_to_change] = new_value;
|
||||
ModifyRWConfigFile(settings_to_change);
|
||||
}
|
||||
|
||||
void ArgsManager::EraseRWConfigFile()
|
||||
{
|
||||
LOCK(cs_args);
|
||||
fs::path rwconf_path{GetRWConfigFilePath()};
|
||||
if (!fs::exists(rwconf_path)) {
|
||||
return;
|
||||
}
|
||||
fs::path rwconf_reset_path = rwconf_path;
|
||||
rwconf_reset_path += ".reset";
|
||||
if (!RenameOver(rwconf_path, rwconf_reset_path)) {
|
||||
if (fs::remove(rwconf_path)) {
|
||||
throw std::ios_base::failure(strprintf("Failed to remove %s", fs::PathToString(rwconf_path)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace common {
|
||||
#ifdef WIN32
|
||||
WinCmdLineArgs::WinCmdLineArgs()
|
||||
|
@ -25,6 +25,7 @@ class ArgsManager;
|
||||
|
||||
extern const char * const BITCOIN_CONF_FILENAME;
|
||||
extern const char * const BITCOIN_SETTINGS_FILENAME;
|
||||
extern const char * const BITCOIN_RW_CONF_FILENAME;
|
||||
|
||||
// Return true if -datadir option points to a valid directory or is not specified.
|
||||
bool CheckDataDirOption(const ArgsManager& args);
|
||||
@ -94,6 +95,8 @@ std::optional<int64_t> SettingToInt(const common::SettingsValue&);
|
||||
bool SettingToBool(const common::SettingsValue&, bool);
|
||||
std::optional<bool> SettingToBool(const common::SettingsValue&);
|
||||
|
||||
void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change);
|
||||
|
||||
class ArgsManager
|
||||
{
|
||||
public:
|
||||
@ -139,11 +142,12 @@ protected:
|
||||
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
||||
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
||||
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
|
||||
std::optional<fs::path> m_rwconf_path GUARDED_BY(cs_args);
|
||||
mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
|
||||
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
|
||||
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
|
||||
|
||||
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
|
||||
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false, std::map<std::string, std::vector<common::SettingsValue>>* settings_target = nullptr);
|
||||
|
||||
/**
|
||||
* Returns true if settings values from the default section should be used,
|
||||
@ -182,8 +186,13 @@ protected:
|
||||
*/
|
||||
fs::path GetConfigFilePath() const;
|
||||
void SetConfigFilePath(fs::path);
|
||||
fs::path GetRWConfigFilePath() const;
|
||||
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
|
||||
|
||||
void ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change);
|
||||
void ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value);
|
||||
void EraseRWConfigFile();
|
||||
|
||||
/**
|
||||
* Log warnings for options in m_section_only_args when
|
||||
* they are specified in the default section but not overridden
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <common/args.h>
|
||||
|
||||
#include <chainparamsbase.h>
|
||||
#include <common/settings.h>
|
||||
#include <logging.h>
|
||||
#include <sync.h>
|
||||
@ -90,7 +91,7 @@ bool IsConfSupported(KeyInfo& key, std::string& error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
|
||||
bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys, std::map<std::string, std::vector<common::SettingsValue>>* settings_target)
|
||||
{
|
||||
LOCK(cs_args);
|
||||
std::vector<std::pair<std::string, std::string>> options;
|
||||
@ -106,6 +107,9 @@ bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& file
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
if (settings_target) {
|
||||
(*settings_target)[key.name].push_back(*value);
|
||||
} else
|
||||
m_settings.ro_config[key.section][key.name].push_back(*value);
|
||||
} else {
|
||||
if (ignore_invalid_keys) {
|
||||
@ -124,6 +128,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
|
||||
{
|
||||
LOCK(cs_args);
|
||||
m_settings.ro_config.clear();
|
||||
m_settings.rw_config.clear();
|
||||
m_config_sections.clear();
|
||||
m_config_path = AbsPathForConfigVal(*this, GetPathArg("-conf", BITCOIN_CONF_FILENAME), /*net_specific=*/false);
|
||||
}
|
||||
@ -214,12 +219,31 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for chain settings (BaseParams() calls are only valid after this clause)
|
||||
try {
|
||||
SelectBaseParams(gArgs.GetChainType());
|
||||
} catch (const std::exception& e) {
|
||||
error = e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If datadir is changed in .conf file:
|
||||
ClearPathCache();
|
||||
if (!CheckDataDirOption(*this)) {
|
||||
error = strprintf("specified data directory \"%s\" does not exist.", GetArg("-datadir", ""));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOCK(cs_args);
|
||||
m_rwconf_path = AbsPathForConfigVal(*this, GetPathArg("-confrw", BITCOIN_RW_CONF_FILENAME));
|
||||
const auto rwconf_path{GetRWConfigFilePath()};
|
||||
std::ifstream rwconf_stream(rwconf_path);
|
||||
if (rwconf_stream.good()) {
|
||||
if (!ReadConfigStream(rwconf_stream, fs::PathToString(rwconf_path), error, ignore_invalid_keys, &m_settings.rw_config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ namespace {
|
||||
enum class Source {
|
||||
FORCED,
|
||||
COMMAND_LINE,
|
||||
CONFIG_FILE_RW,
|
||||
RW_SETTINGS,
|
||||
CONFIG_FILE_NETWORK_SECTION,
|
||||
CONFIG_FILE_DEFAULT_SECTION
|
||||
@ -48,6 +49,10 @@ static void MergeSettings(const Settings& settings, const std::string& section,
|
||||
if (auto* values = FindKey(settings.command_line_options, name)) {
|
||||
fn(SettingsSpan(*values), Source::COMMAND_LINE);
|
||||
}
|
||||
// Merge in the rw config file
|
||||
if (auto* values = FindKey(settings.rw_config, name)) {
|
||||
fn(SettingsSpan(*values), Source::CONFIG_FILE_RW);
|
||||
}
|
||||
// Merge in the read-write settings
|
||||
if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {
|
||||
fn(SettingsSpan(*value), Source::RW_SETTINGS);
|
||||
@ -165,7 +170,7 @@ SettingsValue GetSetting(const Settings& settings,
|
||||
// the config file the precedence is reversed for all settings except
|
||||
// chain type settings.
|
||||
const bool reverse_precedence =
|
||||
(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
||||
(source == Source::CONFIG_FILE_RW || source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
||||
!get_chain_type;
|
||||
|
||||
// Weird behavior preserved for backwards compatibility: Negated
|
||||
@ -217,7 +222,7 @@ std::vector<SettingsValue> GetSettingsList(const Settings& settings,
|
||||
// settings will be brought back from the dead (but earlier command
|
||||
// line settings will still be ignored).
|
||||
const bool add_zombie_config_values =
|
||||
(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
||||
(source == Source::CONFIG_FILE_RW || source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&
|
||||
!prev_negated_empty;
|
||||
|
||||
// Ignore settings in default config section if requested.
|
||||
|
@ -34,6 +34,8 @@ struct Settings {
|
||||
std::map<std::string, SettingsValue> forced_settings;
|
||||
//! Map of setting name to list of command line values.
|
||||
std::map<std::string, std::vector<SettingsValue>> command_line_options;
|
||||
//! Map of setting name to list of r/w config file values.
|
||||
std::map<std::string, std::vector<SettingsValue>> rw_config;
|
||||
//! Map of setting name to read-write file setting value.
|
||||
std::map<std::string, SettingsValue> rw_settings;
|
||||
//! Map of config section name and setting name to list of config file values.
|
||||
|
@ -493,6 +493,7 @@ void SetupServerArgs(ArgsManager& argsman)
|
||||
argsman.AddArg("-blocksonly", strprintf("Whether to reject transactions from network peers. Disables automatic broadcast and rebroadcast of transactions, unless the source peer has the 'forcerelay' permission. RPC transactions are not affected. (default: %u)", DEFAULT_BLOCKSONLY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-conf=<file>", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-confrw=<file>", strprintf("Specify read/write configuration file. Relative paths will be prefixed by the network-specific datadir location (default: %s)", BITCOIN_RW_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
|
||||
argsman.AddArg("-dbcache=<n>", strprintf("Maximum database cache size <n> MiB (%d to %d, default: %d). In addition, unused mempool memory is shared for this cache (see -maxmempool).", nMinDbCache, nMaxDbCache, nDefaultDbCache), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
|
@ -136,6 +136,15 @@ bool StartLogging(const ArgsManager& args)
|
||||
LogPrintf("Config file: %s (not found, skipping)\n", fs::PathToString(config_file_path));
|
||||
}
|
||||
|
||||
fs::path rwconfig_file_path = args.GetRWConfigFilePath();
|
||||
if (fs::exists(rwconfig_file_path)) {
|
||||
LogPrintf("R/W Config file: %s\n", fs::PathToString(rwconfig_file_path));
|
||||
} else if (gArgs.IsArgSet("-confrw")) {
|
||||
InitWarning(strprintf(_("The specified R/W config file %s does not exist"), fs::PathToString(rwconfig_file_path)));
|
||||
} else {
|
||||
LogPrintf("R/W Config file: %s (not found, skipping)\n", fs::PathToString(rwconfig_file_path));
|
||||
}
|
||||
|
||||
// Log the config arguments to debug.log
|
||||
args.LogArgs();
|
||||
|
||||
|
@ -312,6 +312,9 @@ void OptionsModel::Reset()
|
||||
QString dataDir = GUIUtil::getDefaultDataDirectory();
|
||||
dataDir = settings.value("strDataDir", dataDir).toString();
|
||||
|
||||
// Remove rw config file
|
||||
gArgs.EraseRWConfigFile();
|
||||
|
||||
// Remove all entries from our QSettings object
|
||||
settings.clear();
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <qt/test/apptests.h>
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <common/args.h>
|
||||
#include <key.h>
|
||||
#include <logging.h>
|
||||
#include <qt/bitcoin.h>
|
||||
@ -66,6 +67,14 @@ void AppTests::appTests()
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
// Need to ensure datadir is setup so resetting settings can delete the non-existent bitcoin_rw.conf
|
||||
std::string error;
|
||||
if (!gArgs.ReadConfigFiles(error, true)) {
|
||||
QWARN("Error in readConfigFiles");
|
||||
}
|
||||
}
|
||||
|
||||
qRegisterMetaType<interfaces::BlockAndHeaderTipInfo>("interfaces::BlockAndHeaderTipInfo");
|
||||
m_app.parameterSetup();
|
||||
QVERIFY(m_app.createOptionsModel(/*resetSettings=*/true));
|
||||
|
@ -2037,4 +2037,157 @@ BOOST_AUTO_TEST_CASE(clearshrink_test)
|
||||
}
|
||||
}
|
||||
|
||||
static std::string CheckModifyRWConfigFile(std::map<std::string, std::string>& settings_to_change, const std::string& current_config_file)
|
||||
{
|
||||
std::istringstream stream_in(current_config_file);
|
||||
std::ostringstream stream_out;
|
||||
try {
|
||||
ModifyRWConfigStream(stream_in, stream_out, settings_to_change);
|
||||
} catch (...) {
|
||||
settings_to_change.clear();
|
||||
throw;
|
||||
}
|
||||
settings_to_change.clear();
|
||||
return stream_out.str();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_ModifyRWConfigFile)
|
||||
{
|
||||
std::map<std::string, std::string> cs;
|
||||
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b"), "a=b");
|
||||
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b"), "a=c");
|
||||
BOOST_CHECK(cs.empty());
|
||||
|
||||
// Multi-char name/value
|
||||
cs["ab"] = "cd";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "ab=bc"), "ab=cd");
|
||||
|
||||
// Preserved final newline
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n"), "a=b\n");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n"), "a=c\n");
|
||||
|
||||
// Preserved final tab
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t"), "a=b\t");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t"), "a=c\t");
|
||||
|
||||
// Preserved final space
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b "), "a=b ");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b "), "a=c ");
|
||||
|
||||
// Preserved final crnl
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\r\n"), "a=b\r\n");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\r\n"), "a=c\r\n");
|
||||
|
||||
// Empty file
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, ""), "a=c\n");
|
||||
|
||||
// Ignore k=v in comment
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "#a=b"), "#a=b\na=c\n");
|
||||
|
||||
// Preserved comment
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t# c"), "a=b\t# c");
|
||||
|
||||
// Commented out commented value
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\t# c"), "a=c\n#a=b\t# c");
|
||||
|
||||
// Preserved whitespace before name
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, " \t \ta=b"), " \t \ta=b");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, " \t \ta=b"), " \t \ta=c");
|
||||
|
||||
// Preserved whitespace after name
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a \t \t=b"), "a \t \t=b");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a \t \t=b"), "a \t \t=c");
|
||||
|
||||
// Preserved whitespace before value
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a= \t \tb"), "a= \t \tb");
|
||||
cs["a"] = "c";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a= \t \tb"), "a= \t \tc");
|
||||
|
||||
// Modifying value between others
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=bc\nd=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=x\nd=e");
|
||||
|
||||
// Blank key/value
|
||||
cs["ab"] = "";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=\nd=e");
|
||||
cs[""] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nd=e"), "a=b\nab=bc\nd=e\n=x\n");
|
||||
|
||||
// Blank line in source
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\nab=bc\n\nd=e"), "a=b\n\nab=bc\n\nd=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\nab=bc\n\nd=e"), "a=b\n\nab=x\n\nd=e");
|
||||
|
||||
// Duplicate keys in the source
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nf=x\nab=zx\nd=e"), "a=b\nab=bc\nf=x\nab=zx\nd=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nf=x\nab=zx\nd=e"), "a=b\nab=x\nf=x\nab=zx\nd=e");
|
||||
|
||||
// Comment out entire file if invalid input line
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nGARBAGE\nd=e"), "[INVALID]\n# Error parsing line 3: GARBAGE\n#a=b\n#ab=bc\n#GARBAGE\n#d=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nGARBAGE\nd=e"), "ab=x\n[INVALID]\n# Error parsing line 3: GARBAGE\n#a=b\n#ab=bc\n#GARBAGE\n#d=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=bc\nGARBAGE\nd=e\n"), "ab=x\n[INVALID]\n# Error parsing line 3: GARBAGE\n#a=b\n#ab=bc\n#GARBAGE\n#d=e\n");
|
||||
|
||||
// Whitespace inside values
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=b\t \t c\nd=e"), "a=b\nab=b\t \t c\nd=e");
|
||||
cs["ab"] = "x \t \tx";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\nab=b\t \t c\nd=e"), "a=b\nab=x \t \tx\nd=e");
|
||||
|
||||
// Newline inside name/value
|
||||
cs["a"] = "x\nx";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a"] = "x\rx";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a\nb"] = "x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a\rb"] = "x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
|
||||
// Whitespace leading/trailing name/value
|
||||
cs["a"] = " x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a"] = "\tx";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs[" a"] = "x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["\ta"] = "x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a"] = "x ";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a"] = "x\t";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a "] = "x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
cs["a\t"] = "x";
|
||||
BOOST_REQUIRE_THROW(CheckModifyRWConfigFile(cs, ""), std::invalid_argument);
|
||||
|
||||
// Ignore groups
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nd=e"), "a=b\n[group]\nab=bc\nd=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nd=e"), "a=b\nab=x\n[group]\nab=bc\nd=e");
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\t [group] \t#c\nab=bc\nd=e"), "a=b\n\t [group] \t#c\nab=bc\nd=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n\t [group] \t#c\nab=bc\nd=e"), "a=b\nab=x\n\t [group] \t#c\nab=bc\nd=e");
|
||||
|
||||
// Comment out entire file if invalid input line, even after a group
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nGARBAGE\nd=e"), "[INVALID]\n# Error parsing line 4: GARBAGE\n#a=b\n#[group]\n#ab=bc\n#GARBAGE\n#d=e");
|
||||
cs["ab"] = "x";
|
||||
BOOST_CHECK_EQUAL(CheckModifyRWConfigFile(cs, "a=b\n[group]\nab=bc\nGARBAGE\nd=e"), "ab=x\n[INVALID]\n# Error parsing line 4: GARBAGE\n#a=b\n#[group]\n#ab=bc\n#GARBAGE\n#d=e");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
Loading…
Reference in New Issue
Block a user