Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2019-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <bitcoin-build-config.h> // IWYU pragma: keep7#include <common/settings.h>9#include <tinyformat.h>10#include <univalue.h>11#include <util/fs.h>13#include <fstream>14#include <iterator>15#include <map>16#include <string>17#include <utility>18#include <vector>20namespace common {21namespace {23enum class Source {24FORCED,25COMMAND_LINE,26RW_SETTINGS,27CONFIG_FILE_NETWORK_SECTION,28CONFIG_FILE_DEFAULT_SECTION29};31// Json object key for the auto-generated warning comment32const std::string SETTINGS_WARN_MSG_KEY{"_warning_"};34//! Merge settings from multiple sources in precedence order:35//! Forced config > command line > read-write settings file > config file network-specific section > config file default section36//!37//! This function is provided with a callback function fn that contains38//! specific logic for how to merge the sources.39template <typename Fn>40static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn)41{42// Merge in the forced settings43if (auto* value = FindKey(settings.forced_settings, name)) {44fn(SettingsSpan(*value), Source::FORCED);45}46// Merge in the command-line options47if (auto* values = FindKey(settings.command_line_options, name)) {48fn(SettingsSpan(*values), Source::COMMAND_LINE);49}50// Merge in the read-write settings51if (const SettingsValue* value = FindKey(settings.rw_settings, name)) {52fn(SettingsSpan(*value), Source::RW_SETTINGS);53}54// Merge in the network-specific section of the config file55if (!section.empty()) {56if (auto* map = FindKey(settings.ro_config, section)) {57if (auto* values = FindKey(*map, name)) {58fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION);59}60}61}62// Merge in the default section of the config file63if (auto* map = FindKey(settings.ro_config, "")) {64if (auto* values = FindKey(*map, name)) {65fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION);66}67}68}69} // namespace71bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors)72{73values.clear();74errors.clear();76// Ok for file to not exist77if (!fs::exists(path)) return true;79std::ifstream file;80file.open(path.std_path());81if (!file.is_open()) {82errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));83return false;84}86SettingsValue in;87if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) {88errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This may be caused by a crash, power loss, full disk, or storage error, "89"and can be fixed by removing the file, which will reset settings to default values.",90fs::PathToString(path)));91return false;92}94if (file.fail()) {95errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));96return false;97}98file.close(); // Done with file descriptor. Release while copying data.100if (!in.isObject()) {101errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));102return false;103}105const std::vector<std::string>& in_keys = in.getKeys();106const std::vector<SettingsValue>& in_values = in.getValues();107for (size_t i = 0; i < in_keys.size(); ++i) {108auto inserted = values.emplace(in_keys[i], in_values[i]);109if (!inserted.second) {110errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));111values.clear();112break;113}114}116// Remove auto-generated warning comment from the accessible settings.117values.erase(SETTINGS_WARN_MSG_KEY);119return errors.empty();120}122bool WriteSettings(const fs::path& path,123const std::map<std::string, SettingsValue>& values,124std::vector<std::string>& errors)125{126SettingsValue out(SettingsValue::VOBJ);127// Add auto-generated warning comment128out.pushKV(SETTINGS_WARN_MSG_KEY, strprintf("This file is automatically generated and updated by %s. Please do not edit this file while the node "129"is running, as any changes might be ignored or overwritten.", CLIENT_NAME));130// Push settings values131for (const auto& value : values) {132out.pushKVEnd(value.first, value.second);133}134std::ofstream file;135file.open(path.std_path());136if (file.fail()) {137errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));138return false;139}140file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl;141if (file.fail()) {142errors.emplace_back(strprintf("Error: Unable to write settings file %s", fs::PathToString(path)));143return false;144}145file.close();146if (file.fail()) {147errors.emplace_back(strprintf("Error: Unable to close settings file %s", fs::PathToString(path)));148return false;149}150return true;151}153SettingsValue GetSetting(const Settings& settings,154const std::string& section,155const std::string& name,156bool ignore_default_section_config,157bool ignore_nonpersistent,158bool get_chain_type)159{160SettingsValue result;161bool done = false; // Done merging any more settings sources.162MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {163// Weird behavior preserved for backwards compatibility: Apply negated164// setting even if non-negated setting would be ignored. A negated165// value in the default section is applied to network specific options,166// even though normal non-negated values there would be ignored.167const bool never_ignore_negated_setting = span.last_negated();169// Weird behavior preserved for backwards compatibility: Take first170// assigned value instead of last. In general, later settings take171// precedence over early settings, but for backwards compatibility in172// the config file the precedence is reversed for all settings except173// chain type settings.174const bool reverse_precedence =175(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&176!get_chain_type;178// Weird behavior preserved for backwards compatibility: Negated179// -regtest and -testnet arguments which you would expect to override180// values set in the configuration file are currently accepted but181// silently ignored. It would be better to apply these just like other182// negated values, or at least warn they are ignored.183const bool skip_negated_command_line = get_chain_type;185if (done) return;187// Ignore settings in default config section if requested.188if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION &&189!never_ignore_negated_setting) {190return;191}193// Ignore nonpersistent settings if requested.194if (ignore_nonpersistent && (source == Source::COMMAND_LINE || source == Source::FORCED)) return;196// Skip negated command line settings.197if (skip_negated_command_line && span.last_negated()) return;199if (!span.empty()) {200result = reverse_precedence ? span.begin()[0] : span.end()[-1];201done = true;202} else if (span.last_negated()) {203result = false;204done = true;205}206});207return result;208}210std::vector<SettingsValue> GetSettingsList(const Settings& settings,211const std::string& section,212const std::string& name,213bool ignore_default_section_config)214{215std::vector<SettingsValue> result;216bool done = false; // Done merging any more settings sources.217bool prev_negated_empty = false;218MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) {219// Weird behavior preserved for backwards compatibility: Apply config220// file settings even if negated on command line. Negating a setting on221// command line will ignore earlier settings on the command line and222// ignore settings in the config file, unless the negated command line223// value is followed by non-negated value, in which case config file224// settings will be brought back from the dead (but earlier command225// line settings will still be ignored).226const bool add_zombie_config_values =227(source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) &&228!prev_negated_empty;230// Ignore settings in default config section if requested.231if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION) return;233// Add new settings to the result if isn't already complete, or if the234// values are zombies.235if (!done || add_zombie_config_values) {236for (const auto& value : span) {237if (value.isArray()) {238result.insert(result.end(), value.getValues().begin(), value.getValues().end());239} else {240result.push_back(value);241}242}243}245// If a setting was negated, or if a setting was forced, set246// done to true to ignore any later lower priority settings.247done |= span.negated() > 0 || source == Source::FORCED;249// Update the negated and empty state used for the zombie values check.250prev_negated_empty |= span.last_negated() && result.empty();
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.