/Users/eugenesiegel/btc/bitcoin/src/common/settings.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2019-2022 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 |  | #include <common/settings.h> | 
| 6 |  |  | 
| 7 |  | #include <bitcoin-build-config.h> // IWYU pragma: keep | 
| 8 |  |  | 
| 9 |  | #include <tinyformat.h> | 
| 10 |  | #include <univalue.h> | 
| 11 |  | #include <util/fs.h> | 
| 12 |  |  | 
| 13 |  | #include <algorithm> | 
| 14 |  | #include <fstream> | 
| 15 |  | #include <iterator> | 
| 16 |  | #include <map> | 
| 17 |  | #include <string> | 
| 18 |  | #include <utility> | 
| 19 |  | #include <vector> | 
| 20 |  |  | 
| 21 |  | namespace common { | 
| 22 |  | namespace { | 
| 23 |  |  | 
| 24 |  | enum class Source { | 
| 25 |  |    FORCED, | 
| 26 |  |    COMMAND_LINE, | 
| 27 |  |    RW_SETTINGS, | 
| 28 |  |    CONFIG_FILE_NETWORK_SECTION, | 
| 29 |  |    CONFIG_FILE_DEFAULT_SECTION | 
| 30 |  | }; | 
| 31 |  |  | 
| 32 |  | // Json object key for the auto-generated warning comment | 
| 33 |  | const std::string SETTINGS_WARN_MSG_KEY{"_warning_"}; | 
| 34 |  |  | 
| 35 |  | //! Merge settings from multiple sources in precedence order: | 
| 36 |  | //! Forced config > command line > read-write settings file > config file network-specific section > config file default section | 
| 37 |  | //! | 
| 38 |  | //! This function is provided with a callback function fn that contains | 
| 39 |  | //! specific logic for how to merge the sources. | 
| 40 |  | template <typename Fn> | 
| 41 |  | static void MergeSettings(const Settings& settings, const std::string& section, const std::string& name, Fn&& fn) | 
| 42 | 8.33M | { | 
| 43 |  |     // Merge in the forced settings | 
| 44 | 8.33M |     if (auto* value = FindKey(settings.forced_settings, name)) { | 
| 45 | 410k |         fn(SettingsSpan(*value), Source::FORCED); | 
| 46 | 410k |     } | 
| 47 |  |     // Merge in the command-line options | 
| 48 | 8.33M |     if (auto* values = FindKey(settings.command_line_options, name)) { | 
| 49 | 666k |         fn(SettingsSpan(*values), Source::COMMAND_LINE); | 
| 50 | 666k |     } | 
| 51 |  |     // Merge in the read-write settings | 
| 52 | 8.33M |     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { | 
| 53 | 0 |         fn(SettingsSpan(*value), Source::RW_SETTINGS); | 
| 54 | 0 |     } | 
| 55 |  |     // Merge in the network-specific section of the config file | 
| 56 | 8.33M |     if (!section.empty()) { | 
| 57 | 7.41M |         if (auto* map = FindKey(settings.ro_config, section)) { | 
| 58 | 0 |             if (auto* values = FindKey(*map, name)) { | 
| 59 | 0 |                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); | 
| 60 | 0 |             } | 
| 61 | 0 |         } | 
| 62 | 7.41M |     } | 
| 63 |  |     // Merge in the default section of the config file | 
| 64 | 8.33M |     if (auto* map = FindKey(settings.ro_config, "")) { | 
| 65 | 0 |         if (auto* values = FindKey(*map, name)) { | 
| 66 | 0 |             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); | 
| 67 | 0 |         } | 
| 68 | 0 |     } | 
| 69 | 8.33M | } settings.cpp:_ZN6common12_GLOBAL__N_113MergeSettingsIZNS_10GetSettingERKNS_8SettingsERKNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESD_bbbE3$_0EEvS4_SD_SD_OT_| Line | Count | Source |  | 42 | 7.20M | { |  | 43 |  |     // Merge in the forced settings |  | 44 | 7.20M |     if (auto* value = FindKey(settings.forced_settings, name)) { |  | 45 | 410k |         fn(SettingsSpan(*value), Source::FORCED); |  | 46 | 410k |     } |  | 47 |  |     // Merge in the command-line options |  | 48 | 7.20M |     if (auto* values = FindKey(settings.command_line_options, name)) { |  | 49 | 512k |         fn(SettingsSpan(*values), Source::COMMAND_LINE); |  | 50 | 512k |     } |  | 51 |  |     // Merge in the read-write settings |  | 52 | 7.20M |     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { |  | 53 | 0 |         fn(SettingsSpan(*value), Source::RW_SETTINGS); |  | 54 | 0 |     } |  | 55 |  |     // Merge in the network-specific section of the config file |  | 56 | 7.20M |     if (!section.empty()) { |  | 57 | 6.27M |         if (auto* map = FindKey(settings.ro_config, section)) { |  | 58 | 0 |             if (auto* values = FindKey(*map, name)) { |  | 59 | 0 |                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); |  | 60 | 0 |             } |  | 61 | 0 |         } |  | 62 | 6.27M |     } |  | 63 |  |     // Merge in the default section of the config file |  | 64 | 7.20M |     if (auto* map = FindKey(settings.ro_config, "")) { |  | 65 | 0 |         if (auto* values = FindKey(*map, name)) { |  | 66 | 0 |             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); |  | 67 | 0 |         } |  | 68 | 0 |     } |  | 69 | 7.20M | } | 
settings.cpp:_ZN6common12_GLOBAL__N_113MergeSettingsIZNS_15GetSettingsListERKNS_8SettingsERKNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESD_bE3$_0EEvS4_SD_SD_OT_| Line | Count | Source |  | 42 | 721k | { |  | 43 |  |     // Merge in the forced settings |  | 44 | 721k |     if (auto* value = FindKey(settings.forced_settings, name)) { |  | 45 | 0 |         fn(SettingsSpan(*value), Source::FORCED); |  | 46 | 0 |     } |  | 47 |  |     // Merge in the command-line options |  | 48 | 721k |     if (auto* values = FindKey(settings.command_line_options, name)) { |  | 49 | 153k |         fn(SettingsSpan(*values), Source::COMMAND_LINE); |  | 50 | 153k |     } |  | 51 |  |     // Merge in the read-write settings |  | 52 | 721k |     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { |  | 53 | 0 |         fn(SettingsSpan(*value), Source::RW_SETTINGS); |  | 54 | 0 |     } |  | 55 |  |     // Merge in the network-specific section of the config file |  | 56 | 721k |     if (!section.empty()) { |  | 57 | 721k |         if (auto* map = FindKey(settings.ro_config, section)) { |  | 58 | 0 |             if (auto* values = FindKey(*map, name)) { |  | 59 | 0 |                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); |  | 60 | 0 |             } |  | 61 | 0 |         } |  | 62 | 721k |     } |  | 63 |  |     // Merge in the default section of the config file |  | 64 | 721k |     if (auto* map = FindKey(settings.ro_config, "")) { |  | 65 | 0 |         if (auto* values = FindKey(*map, name)) { |  | 66 | 0 |             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); |  | 67 | 0 |         } |  | 68 | 0 |     } |  | 69 | 721k | } | 
settings.cpp:_ZN6common12_GLOBAL__N_113MergeSettingsIZNS_28OnlyHasDefaultSectionSettingERKNS_8SettingsERKNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESD_E3$_0EEvS4_SD_SD_OT_| Line | Count | Source |  | 42 | 410k | { |  | 43 |  |     // Merge in the forced settings |  | 44 | 410k |     if (auto* value = FindKey(settings.forced_settings, name)) { |  | 45 | 0 |         fn(SettingsSpan(*value), Source::FORCED); |  | 46 | 0 |     } |  | 47 |  |     // Merge in the command-line options |  | 48 | 410k |     if (auto* values = FindKey(settings.command_line_options, name)) { |  | 49 | 0 |         fn(SettingsSpan(*values), Source::COMMAND_LINE); |  | 50 | 0 |     } |  | 51 |  |     // Merge in the read-write settings |  | 52 | 410k |     if (const SettingsValue* value = FindKey(settings.rw_settings, name)) { |  | 53 | 0 |         fn(SettingsSpan(*value), Source::RW_SETTINGS); |  | 54 | 0 |     } |  | 55 |  |     // Merge in the network-specific section of the config file |  | 56 | 410k |     if (!section.empty()) { |  | 57 | 410k |         if (auto* map = FindKey(settings.ro_config, section)) { |  | 58 | 0 |             if (auto* values = FindKey(*map, name)) { |  | 59 | 0 |                 fn(SettingsSpan(*values), Source::CONFIG_FILE_NETWORK_SECTION); |  | 60 | 0 |             } |  | 61 | 0 |         } |  | 62 | 410k |     } |  | 63 |  |     // Merge in the default section of the config file |  | 64 | 410k |     if (auto* map = FindKey(settings.ro_config, "")) { |  | 65 | 0 |         if (auto* values = FindKey(*map, name)) { |  | 66 | 0 |             fn(SettingsSpan(*values), Source::CONFIG_FILE_DEFAULT_SECTION); |  | 67 | 0 |         } |  | 68 | 0 |     } |  | 69 | 410k | } | 
 | 
| 70 |  | } // namespace | 
| 71 |  |  | 
| 72 |  | bool ReadSettings(const fs::path& path, std::map<std::string, SettingsValue>& values, std::vector<std::string>& errors) | 
| 73 | 51.2k | { | 
| 74 | 51.2k |     values.clear(); | 
| 75 | 51.2k |     errors.clear(); | 
| 76 |  |  | 
| 77 |  |     // Ok for file to not exist | 
| 78 | 51.2k |     if (!fs::exists(path)) return true0; | 
| 79 |  |  | 
| 80 | 51.2k |     std::ifstream file; | 
| 81 | 51.2k |     file.open(path); | 
| 82 | 51.2k |     if (!file.is_open()) { | 
| 83 | 0 |       errors.emplace_back(strprintf("%s. Please check permissions.", fs::PathToString(path)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 84 | 0 |       return false; | 
| 85 | 0 |     } | 
| 86 |  |  | 
| 87 | 51.2k |     SettingsValue in; | 
| 88 | 51.2k |     if (!in.read(std::string{std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>()})) { | 
| 89 | 0 |         errors.emplace_back(strprintf("Settings file %s does not contain valid JSON. This is probably caused by disk corruption or a crash, "| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 90 | 0 |                                       "and can be fixed by removing the file, which will reset settings to default values.", | 
| 91 | 0 |                                       fs::PathToString(path))); | 
| 92 | 0 |         return false; | 
| 93 | 0 |     } | 
| 94 |  |  | 
| 95 | 51.2k |     if (file.fail()) { | 
| 96 | 0 |         errors.emplace_back(strprintf("Failed reading settings file %s", fs::PathToString(path)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 97 | 0 |         return false; | 
| 98 | 0 |     } | 
| 99 | 51.2k |     file.close(); // Done with file descriptor. Release while copying data. | 
| 100 |  |  | 
| 101 | 51.2k |     if (!in.isObject()) { | 
| 102 | 0 |         errors.emplace_back(strprintf("Found non-object value %s in settings file %s", in.write(), fs::PathToString(path)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 103 | 0 |         return false; | 
| 104 | 0 |     } | 
| 105 |  |  | 
| 106 | 51.2k |     const std::vector<std::string>& in_keys = in.getKeys(); | 
| 107 | 51.2k |     const std::vector<SettingsValue>& in_values = in.getValues(); | 
| 108 | 153k |     for (size_t i = 0; i < in_keys.size(); ++i102k) { | 
| 109 | 102k |         auto inserted = values.emplace(in_keys[i], in_values[i]); | 
| 110 | 102k |         if (!inserted.second) { | 
| 111 | 0 |             errors.emplace_back(strprintf("Found duplicate key %s in settings file %s", in_keys[i], fs::PathToString(path)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 112 | 0 |             values.clear(); | 
| 113 | 0 |             break; | 
| 114 | 0 |         } | 
| 115 | 102k |     } | 
| 116 |  |  | 
| 117 |  |     // Remove auto-generated warning comment from the accessible settings. | 
| 118 | 51.2k |     values.erase(SETTINGS_WARN_MSG_KEY); | 
| 119 |  |  | 
| 120 | 51.2k |     return errors.empty(); | 
| 121 | 51.2k | } | 
| 122 |  |  | 
| 123 |  | bool WriteSettings(const fs::path& path, | 
| 124 |  |     const std::map<std::string, SettingsValue>& values, | 
| 125 |  |     std::vector<std::string>& errors) | 
| 126 | 0 | { | 
| 127 | 0 |     SettingsValue out(SettingsValue::VOBJ); | 
| 128 |  |     // Add auto-generated warning comment | 
| 129 | 0 |     out.pushKV(SETTINGS_WARN_MSG_KEY, strprintf("This file is automatically generated and updated by %s. Please do not edit this file while the node "| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 130 | 0 |                                                 "is running, as any changes might be ignored or overwritten.", CLIENT_NAME)); | Line | Count | Source |  | 98 | 0 | #define CLIENT_NAME "Bitcoin Core" | 
 | 
| 131 |  |     // Push settings values | 
| 132 | 0 |     for (const auto& value : values) { | 
| 133 | 0 |         out.pushKVEnd(value.first, value.second); | 
| 134 | 0 |     } | 
| 135 | 0 |     std::ofstream file; | 
| 136 | 0 |     file.open(path); | 
| 137 | 0 |     if (file.fail()) { | 
| 138 | 0 |         errors.emplace_back(strprintf("Error: Unable to open settings file %s for writing", fs::PathToString(path)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 139 | 0 |         return false; | 
| 140 | 0 |     } | 
| 141 | 0 |     file << out.write(/* prettyIndent= */ 4, /* indentLevel= */ 1) << std::endl; | 
| 142 | 0 |     file.close(); | 
| 143 | 0 |     return true; | 
| 144 | 0 | } | 
| 145 |  |  | 
| 146 |  | SettingsValue GetSetting(const Settings& settings, | 
| 147 |  |     const std::string& section, | 
| 148 |  |     const std::string& name, | 
| 149 |  |     bool ignore_default_section_config, | 
| 150 |  |     bool ignore_nonpersistent, | 
| 151 |  |     bool get_chain_type) | 
| 152 | 7.20M | { | 
| 153 | 7.20M |     SettingsValue result; | 
| 154 | 7.20M |     bool done = false; // Done merging any more settings sources. | 
| 155 | 7.20M |     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { | 
| 156 |  |         // Weird behavior preserved for backwards compatibility: Apply negated | 
| 157 |  |         // setting even if non-negated setting would be ignored. A negated | 
| 158 |  |         // value in the default section is applied to network specific options, | 
| 159 |  |         // even though normal non-negated values there would be ignored. | 
| 160 | 922k |         const bool never_ignore_negated_setting = span.last_negated(); | 
| 161 |  |  | 
| 162 |  |         // Weird behavior preserved for backwards compatibility: Take first | 
| 163 |  |         // assigned value instead of last. In general, later settings take | 
| 164 |  |         // precedence over early settings, but for backwards compatibility in | 
| 165 |  |         // the config file the precedence is reversed for all settings except | 
| 166 |  |         // chain type settings. | 
| 167 | 922k |         const bool reverse_precedence = | 
| 168 | 922k |             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && | 
| 169 | 922k |             !get_chain_type0; | 
| 170 |  |  | 
| 171 |  |         // Weird behavior preserved for backwards compatibility: Negated | 
| 172 |  |         // -regtest and -testnet arguments which you would expect to override | 
| 173 |  |         // values set in the configuration file are currently accepted but | 
| 174 |  |         // silently ignored. It would be better to apply these just like other | 
| 175 |  |         // negated values, or at least warn they are ignored. | 
| 176 | 922k |         const bool skip_negated_command_line = get_chain_type; | 
| 177 |  |  | 
| 178 | 922k |         if (done) return0; | 
| 179 |  |  | 
| 180 |  |         // Ignore settings in default config section if requested. | 
| 181 | 922k |         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION0&& | 
| 182 | 922k |             !never_ignore_negated_setting0) { | 
| 183 | 0 |             return; | 
| 184 | 0 |         } | 
| 185 |  |  | 
| 186 |  |         // Ignore nonpersistent settings if requested. | 
| 187 | 922k |         if (ignore_nonpersistent && (0 source == Source::COMMAND_LINE0|| source == Source::FORCED0)) return0; | 
| 188 |  |  | 
| 189 |  |         // Skip negated command line settings. | 
| 190 | 922k |         if (skip_negated_command_line && span.last_negated()0) return0; | 
| 191 |  |  | 
| 192 | 922k |         if (!span.empty()) { | 
| 193 | 820k |             result = reverse_precedence ? span.begin()[0]0: span.end()[-1]; | 
| 194 | 820k |             done = true; | 
| 195 | 820k |         } else if (102k span.last_negated()102k) { | 
| 196 | 102k |             result = false; | 
| 197 | 102k |             done = true; | 
| 198 | 102k |         } | 
| 199 | 922k |     }); | 
| 200 | 7.20M |     return result; | 
| 201 | 7.20M | } | 
| 202 |  |  | 
| 203 |  | std::vector<SettingsValue> GetSettingsList(const Settings& settings, | 
| 204 |  |     const std::string& section, | 
| 205 |  |     const std::string& name, | 
| 206 |  |     bool ignore_default_section_config) | 
| 207 | 721k | { | 
| 208 | 721k |     std::vector<SettingsValue> result; | 
| 209 | 721k |     bool done = false; // Done merging any more settings sources. | 
| 210 | 721k |     bool prev_negated_empty = false; | 
| 211 | 721k |     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { | 
| 212 |  |         // Weird behavior preserved for backwards compatibility: Apply config | 
| 213 |  |         // file settings even if negated on command line. Negating a setting on | 
| 214 |  |         // command line will ignore earlier settings on the command line and | 
| 215 |  |         // ignore settings in the config file, unless the negated command line | 
| 216 |  |         // value is followed by non-negated value, in which case config file | 
| 217 |  |         // settings will be brought back from the dead (but earlier command | 
| 218 |  |         // line settings will still be ignored). | 
| 219 | 153k |         const bool add_zombie_config_values = | 
| 220 | 153k |             (source == Source::CONFIG_FILE_NETWORK_SECTION || source == Source::CONFIG_FILE_DEFAULT_SECTION) && | 
| 221 | 153k |             !prev_negated_empty0; | 
| 222 |  |  | 
| 223 |  |         // Ignore settings in default config section if requested. | 
| 224 | 153k |         if (ignore_default_section_config && source == Source::CONFIG_FILE_DEFAULT_SECTION0) return0; | 
| 225 |  |  | 
| 226 |  |         // Add new settings to the result if isn't already complete, or if the | 
| 227 |  |         // values are zombies. | 
| 228 | 153k |         if (!done || add_zombie_config_values0) { | 
| 229 | 153k |             for (const auto& value : span) { | 
| 230 | 153k |                 if (value.isArray()) { | 
| 231 | 0 |                     result.insert(result.end(), value.getValues().begin(), value.getValues().end()); | 
| 232 | 153k |                 } else { | 
| 233 | 153k |                     result.push_back(value); | 
| 234 | 153k |                 } | 
| 235 | 153k |             } | 
| 236 | 153k |         } | 
| 237 |  |  | 
| 238 |  |         // If a setting was negated, or if a setting was forced, set | 
| 239 |  |         // done to true to ignore any later lower priority settings. | 
| 240 | 153k |         done |= span.negated() > 0 || source == Source::FORCED102k; | 
| 241 |  |  | 
| 242 |  |         // Update the negated and empty state used for the zombie values check. | 
| 243 | 153k |         prev_negated_empty |= span.last_negated() && result.empty()51.2k; | 
| 244 | 153k |     }); | 
| 245 | 721k |     return result; | 
| 246 | 721k | } | 
| 247 |  |  | 
| 248 |  | bool OnlyHasDefaultSectionSetting(const Settings& settings, const std::string& section, const std::string& name) | 
| 249 | 410k | { | 
| 250 | 410k |     bool has_default_section_setting = false; | 
| 251 | 410k |     bool has_other_setting = false; | 
| 252 | 410k |     MergeSettings(settings, section, name, [&](SettingsSpan span, Source source) { | 
| 253 | 0 |         if (span.empty()) return; | 
| 254 | 0 |         else if (source == Source::CONFIG_FILE_DEFAULT_SECTION) has_default_section_setting = true; | 
| 255 | 0 |         else has_other_setting = true; | 
| 256 | 0 |     }); | 
| 257 |  |     // If a value is set in the default section and not explicitly overwritten by the | 
| 258 |  |     // user on the command line or in a different section, then we want to enable | 
| 259 |  |     // warnings about the value being ignored. | 
| 260 | 410k |     return has_default_section_setting && !has_other_setting0; | 
| 261 | 410k | } | 
| 262 |  |  | 
| 263 | 666k | SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {}Unexecuted instantiation: _ZN6common12SettingsSpanC2ERKNSt3__16vectorI8UniValueNS1_9allocatorIS3_EEEE_ZN6common12SettingsSpanC1ERKNSt3__16vectorI8UniValueNS1_9allocatorIS3_EEEE| Line | Count | Source |  | 263 | 666k | SettingsSpan::SettingsSpan(const std::vector<SettingsValue>& vec) noexcept : SettingsSpan(vec.data(), vec.size()) {} | 
 | 
| 264 | 153k | const SettingsValue* SettingsSpan::begin() const { return data + negated(); } | 
| 265 | 974k | const SettingsValue* SettingsSpan::end() const { return data + size; } | 
| 266 | 922k | bool SettingsSpan::empty() const { return size == 0 || last_negated(); } | 
| 267 | 2.10M | bool SettingsSpan::last_negated() const { return size > 0 && data[size - 1].isFalse(); } | 
| 268 |  | size_t SettingsSpan::negated() const | 
| 269 | 307k | { | 
| 270 | 615k |     for (size_t i = size; i > 0; --i307k) { | 
| 271 | 410k |         if (data[i - 1].isFalse()) return i102k; // Return number of negated values (position of last false value) | 
| 272 | 410k |     } | 
| 273 | 205k |     return 0; | 
| 274 | 307k | } | 
| 275 |  |  | 
| 276 |  | } // namespace common |