Skip to content
Bitcoin Roots

v30.3-roots.1 · Commit 2: [PATCH 02/68] feat(runtime): preserve Roots operator controls on Core 30.3

src/common/args.cpp

Before: src/common/args.cpp · After: src/common/args.cpp

File metadata
index d44cd4319b6aa17e5e0281545c887c5b4437379d..eaf15d8c3e0b98dca09b088f380bf575d6f43d6b 100644

Download original patch · Release notes

Diff rows 1–388 of 388. Line numbers refer to the original before/after files.

+ Added− RemovedUnmarked lines are context.

Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.

src/common/args.cpp CHANGED
@@ -29,15 +29,18 @@
29
  #include <cstdint>
30
  #include <cstdlib>
31
  #include <cstring>
 
32
  #include <map>
33
  #include <optional>
34
  #include <stdexcept>
35
  #include <string>
 
36
  #include <utility>
37
  #include <variant>
38
 
39
  const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
40
  const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
 
41
 
42
  ArgsManager gArgs;
43
 
@@ -115,7 +118,7 @@ std::optional<common::SettingsValue> InterpretValue(const KeyInfo& key, const st
115
  }
116
  // Double negatives like -nofoo=0 are supported (but discouraged)
117
  if (value && !InterpretBool(*value)) {
118
- LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key.name, *value);
119
  return true;
120
  }
121
  return false;
@@ -400,7 +403,7 @@ static void SaveErrors(const std::vector<std::string> errors, std::vector<std::s
400
  if (error_out) {
401
  error_out->emplace_back(error);
402
  } else {
403
- LogPrintf("%s\n", error);
404
  }
405
  }
406
  }
@@ -422,7 +425,7 @@ bool ArgsManager::ReadSettingsFile(std::vector<std::string>* errors)
422
  for (const auto& setting : m_settings.rw_settings) {
423
  KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
424
  if (!GetArgFlags('-' + key.name)) {
425
- LogPrintf("Ignoring unknown rw_settings value %s\n", setting.first);
426
  }
427
  }
428
  return true;
@@ -510,6 +513,25 @@ int64_t SettingToInt(const common::SettingsValue& value, int64_t nDefault)
510
  return SettingToInt(value).value_or(nDefault);
511
  }
512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
514
  {
515
  return GetBoolArg(strArg).value_or(fDefault);
@@ -523,9 +545,21 @@ std::optional<bool> ArgsManager::GetBoolArg(const std::string& strArg) const
523
 
524
  std::optional<bool> SettingToBool(const common::SettingsValue& value)
525
  {
526
- if (value.isNull()) return std::nullopt;
527
- if (value.isBool()) return value.get_bool();
528
- return InterpretBool(value.get_str());
 
 
 
 
 
 
 
 
 
 
 
 
529
  }
530
 
531
  bool SettingToBool(const common::SettingsValue& value, bool fDefault)
@@ -550,9 +584,19 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
550
  }
551
 
552
  void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
 
 
 
 
 
 
 
 
 
 
553
  {
554
  LOCK(cs_args);
555
- m_settings.forced_settings[SettingName(strArg)] = strValue;
556
  }
557
 
558
  void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
@@ -588,10 +632,10 @@ void ArgsManager::AddArg(const std::string& name, const std::string& help, unsig
588
  }
589
  }
590
 
591
- void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
592
  {
593
  for (const std::string& name : names) {
594
- AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
595
  }
596
  }
597
 
@@ -673,6 +717,9 @@ std::string ArgsManager::GetHelpMessage() const
673
  case OptionsCategory::CLI_COMMANDS:
674
  usage += HelpMessageGroup("CLI Commands:");
675
  break;
 
 
 
676
  default:
677
  break;
678
  }
@@ -702,8 +749,8 @@ bool HelpRequested(const ArgsManager& args)
702
 
703
  void SetupHelpOptions(ArgsManager& args)
704
  {
705
- args.AddArg("-help", "Print this help message and exit (also -h or -?)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
706
- args.AddHiddenArgs({"-h", "-?"});
707
  }
708
 
709
  static const int screenWidth = 79;
@@ -786,6 +833,12 @@ void ArgsManager::SetConfigFilePath(fs::path path)
786
  m_config_path = path;
787
  }
788
 
 
 
 
 
 
 
789
  ChainType ArgsManager::GetChainType() const
790
  {
791
  std::variant<ChainType, std::string> arg = GetChainArg();
@@ -877,9 +930,234 @@ void ArgsManager::LogArgs() const
877
  for (const auto& setting : m_settings.rw_settings) {
878
  LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
879
  }
 
880
  logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
881
  }
882
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
883
  namespace common {
884
  #ifdef WIN32
885
  WinCmdLineArgs::WinCmdLineArgs()
 
29
  #include <cstdint>
30
  #include <cstdlib>
31
  #include <cstring>
32
+ #include <fstream>
33
  #include <map>
34
  #include <optional>
35
  #include <stdexcept>
36
  #include <string>
37
+ #include <unordered_set>
38
  #include <utility>
39
  #include <variant>
40
 
41
  const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf";
42
  const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
43
+ const char * const BITCOIN_RW_CONF_FILENAME = "bitcoin_rw.conf";
44
 
45
  ArgsManager gArgs;
46
 
 
118
  }
119
  // Double negatives like -nofoo=0 are supported (but discouraged)
120
  if (value && !InterpretBool(*value)) {
121
+ LogWarning("Parsed potentially confusing double-negative -%s=%s", key.name, *value);
122
  return true;
123
  }
124
  return false;
 
403
  if (error_out) {
404
  error_out->emplace_back(error);
405
  } else {
406
+ LogWarning("%s", error);
407
  }
408
  }
409
  }
 
425
  for (const auto& setting : m_settings.rw_settings) {
426
  KeyInfo key = InterpretKey(setting.first); // Split setting key into section and argname
427
  if (!GetArgFlags('-' + key.name)) {
428
+ LogWarning("Ignoring unknown rw_settings value %s", setting.first);
429
  }
430
  }
431
  return true;
 
513
  return SettingToInt(value).value_or(nDefault);
514
  }
515
 
516
+ std::optional<int64_t> ArgsManager::GetFixedPointArg(const std::string& arg, int decimals) const
517
+ {
518
+ const common::SettingsValue value = GetSetting(arg);
519
+ return SettingToFixedPoint(value, decimals);
520
+ }
521
+
522
+ std::optional<int64_t> SettingToFixedPoint(const common::SettingsValue& value, int decimals)
523
+ {
524
+ if (value.isNull()) return std::nullopt;
525
+ if (value.isFalse()) return 0;
526
+ if (value.isTrue()) return 1;
527
+ if (!value.isNum()) value.get_str(); // throws an exception if type is wrong
528
+ int64_t v;
529
+ if (!ParseFixedPoint(value.getValStr(), decimals, &v)) {
530
+ throw std::runtime_error(strprintf("Parse error ('%s')", value.getValStr()));
531
+ }
532
+ return v;
533
+ }
534
+
535
  bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
536
  {
537
  return GetBoolArg(strArg).value_or(fDefault);
 
545
 
546
  std::optional<bool> SettingToBool(const common::SettingsValue& value)
547
  {
548
+ switch (value.getType()) {
549
+ case UniValue::VNULL:
550
+ return std::nullopt;
551
+ case UniValue::VBOOL:
552
+ return value.get_bool();
553
+ case UniValue::VOBJ:
554
+ case UniValue::VARR:
555
+ // Throws an exception
556
+ value.get_str();
557
+ assert(false);
558
+ case UniValue::VSTR:
559
+ case UniValue::VNUM:
560
+ return InterpretBool(value.getValStr());
561
+ }
562
+ assert(false);
563
  }
564
 
565
  bool SettingToBool(const common::SettingsValue& value, bool fDefault)
 
584
  }
585
 
586
  void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
587
+ {
588
+ ForceSetArgV(strArg, common::SettingsValue{strValue});
589
+ }
590
+
591
+ void ArgsManager::ForceSetArg(const std::string& arg, const int64_t value)
592
+ {
593
+ ForceSetArg(arg, util::ToString(value));
594
+ }
595
+
596
+ void ArgsManager::ForceSetArgV(const std::string& arg, const common::SettingsValue& value)
597
  {
598
  LOCK(cs_args);
599
+ m_settings.forced_settings[SettingName(arg)] = value;
600
  }
601
 
602
  void ArgsManager::AddCommand(const std::string& cmd, const std::string& help)
 
632
  }
633
  }
634
 
635
+ void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names, unsigned int flags)
636
  {
637
  for (const std::string& name : names) {
638
+ AddArg(name, "", flags, OptionsCategory::HIDDEN);
639
  }
640
  }
641
 
 
717
  case OptionsCategory::CLI_COMMANDS:
718
  usage += HelpMessageGroup("CLI Commands:");
719
  break;
720
+ case OptionsCategory::STATS:
721
+ usage += HelpMessageGroup("Statistic options:");
722
+ break;
723
  default:
724
  break;
725
  }
 
749
 
750
  void SetupHelpOptions(ArgsManager& args)
751
  {
752
+ args.AddArg("-help", "Print this help message and exit (also -h or -?)", ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
753
+ args.AddHiddenArgs({"-h", "-?"}, ArgsManager::DISALLOW_NEGATION);
754
  }
755
 
756
  static const int screenWidth = 79;
 
833
  m_config_path = path;
834
  }
835
 
836
+ fs::path ArgsManager::GetRWConfigFilePath() const
837
+ {
838
+ LOCK(cs_args);
839
+ return *Assert(m_rwconf_path);
840
+ }
841
+
842
  ChainType ArgsManager::GetChainType() const
843
  {
844
  std::variant<ChainType, std::string> arg = GetChainArg();
 
930
  for (const auto& setting : m_settings.rw_settings) {
931
  LogPrintf("Setting file arg: %s = %s\n", setting.first, setting.second.write());
932
  }
933
+ logArgsPrefix("R/W config file arg:", "", m_settings.rw_config);
934
  logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
935
  }
936
 
937
+ namespace {
938
+
939
+ // Like std::getline, but includes the EOL character in the result
940
+ bool getline_with_eol(std::istream& stream, std::string& result)
941
+ {
942
+ int current_char;
943
+ current_char = stream.get();
944
+ if (current_char == std::char_traits<char>::eof()) {
945
+ return false;
946
+ }
947
+ result.clear();
948
+ result.push_back(char(current_char));
949
+ while (current_char != '\n') {
950
+ current_char = stream.get();
951
+ if (current_char == std::char_traits<char>::eof()) {
952
+ break;
953
+ }
954
+ result.push_back(char(current_char));
955
+ }
956
+ return true;
957
+ }
958
+
959
+ const char * const ModifyRWConfigFile_ws_chars = " \t\r\n";
960
+
961
+ void ModifyRWConfigFile_SanityCheck(const std::string& s)
962
+ {
963
+ if (s.empty()) {
964
+ // Dereferencing .begin or .rbegin below is invalid unless the string has at least one character.
965
+ return;
966
+ }
967
+
968
+ static const char * const newline_chars = "\r\n";
969
+ static std::string ws_chars(ModifyRWConfigFile_ws_chars);
970
+ if (s.find_first_of(newline_chars) != std::string::npos) {
971
+ throw std::invalid_argument("New-line in config name/value");
972
+ }
973
+ if (ws_chars.find(*s.begin()) != std::string::npos || ws_chars.find(*s.rbegin()) != std::string::npos) {
974
+ throw std::invalid_argument("Config name/value has leading/trailing whitespace");
975
+ }
976
+ }
977
+
978
+ void ModifyRWConfigFile_WriteRemaining(std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change, std::set<std::string>& setFound)
979
+ {
980
+ for (const auto& setting_pair : settings_to_change) {
981
+ const std::string& key = setting_pair.first;
982
+ const std::string& val = setting_pair.second;
983
+ if (setFound.find(key) != setFound.end()) {
984
+ continue;
985
+ }
986
+ setFound.insert(key);
987
+ ModifyRWConfigFile_SanityCheck(key);
988
+ ModifyRWConfigFile_SanityCheck(val);
989
+ stream_out << key << "=" << val << "\n";
990
+ }
991
+ }
992
+ } // namespace
993
+
994
+ void ModifyRWConfigStream(std::istream& stream_in, std::ostream& stream_out, const std::map<std::string, std::string>& settings_to_change)
995
+ {
996
+ static const char * const ws_chars = ModifyRWConfigFile_ws_chars;
997
+ std::set<std::string> setFound;
998
+ std::string s, lineend, linebegin, key;
999
+ std::string::size_type n, n2;
1000
+ bool inside_group = false, have_eof_nl = true;
1001
+ std::map<std::string, std::string>::const_iterator iterCS;
1002
+ size_t lineno = 0;
1003
+ while (getline_with_eol(stream_in, s)) {
1004
+ ++lineno;
1005
+
1006
+ have_eof_nl = (!s.empty()) && (*s.rbegin() == '\n');
1007
+ n = s.find('#');
1008
+ const bool has_comment = (n != std::string::npos);
1009
+ if (!has_comment) {
1010
+ n = s.size();
1011
+ }
1012
+ if (n > 0) {
1013
+ n2 = s.find_last_not_of(ws_chars, n - 1);
1014
+ if (n2 != std::string::npos) {
1015
+ n = n2 + 1;
1016
+ }
1017
+ }
1018
+ n2 = s.find_first_not_of(ws_chars);
1019
+ if (n2 == std::string::npos || n2 >= n) {
1020
+ // Blank or comment-only line
1021
+ stream_out << s;
1022
+ continue;
1023
+ }
1024
+ lineend = s.substr(n);
1025
+ linebegin = s.substr(0, n2);
1026
+ s = s.substr(n2, n - n2);
1027
+
1028
+ // It is impossible for s to be empty here, due to the blank line check above
1029
+ if (*s.begin() == '[' && *s.rbegin() == ']') {
1030
+ // 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
1031
+ ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
1032
+ inside_group = true;
1033
+ key.clear();
1034
+
1035
+ stream_out << linebegin << s << lineend;
1036
+ continue;
1037
+ }
1038
+
1039
+ n = s.find('=');
1040
+ if (n == std::string::npos) {
1041
+ // Bad line; this causes boost to throw an exception when parsing, so we comment out the entire file
1042
+ stream_in.seekg(0, std::ios_base::beg);
1043
+ stream_out.seekp(0, std::ios_base::beg);
1044
+ if (!(stream_in.good() && stream_out.good())) {
1045
+ throw std::ios_base::failure("Failed to rewind (to comment out existing file)");
1046
+ }
1047
+ // First, write out all the settings we intend to set
1048
+ setFound.clear();
1049
+ ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
1050
+ // We then define a category to ensure new settings get added before the invalid stuff
1051
+ stream_out << "[INVALID]\n";
1052
+ // Then, describe the problem in a comment
1053
+ stream_out << "# Error parsing line " << lineno << ": " << s << "\n";
1054
+ // Finally, dump the rest of the file commented out
1055
+ while (getline_with_eol(stream_in, s)) {
1056
+ stream_out << "#" << s;
1057
+ }
1058
+ return;
1059
+ }
1060
+
1061
+ if (!inside_group) {
1062
+ // We don't support/use groups, so once we're inside key is always null to avoid setting anything
1063
+ n2 = s.find_last_not_of(ws_chars, n - 1);
1064
+ if (n2 == std::string::npos) {
1065
+ n2 = n - 1;
1066
+ } else {
1067
+ ++n2;
1068
+ }
1069
+ key = s.substr(0, n2);
1070
+ }
1071
+ if ((!key.empty()) && (iterCS = settings_to_change.find(key)) != settings_to_change.end() && setFound.find(key) == setFound.end()) {
1072
+ // This is the key we want to change
1073
+ const std::string& val = iterCS->second;
1074
+ setFound.insert(key);
1075
+ ModifyRWConfigFile_SanityCheck(val);
1076
+ if (has_comment) {
1077
+ // Rather than change a commented line, comment it out entirely (the existing comment may relate to the value) and replace it
1078
+ stream_out << key << "=" << val << "\n";
1079
+ linebegin.insert(linebegin.begin(), '#');
1080
+ } else {
1081
+ // Just modify the value in-line otherwise
1082
+ n2 = s.find_first_not_of(ws_chars, n + 1);
1083
+ if (n2 == std::string::npos) {
1084
+ n2 = n + 1;
1085
+ }
1086
+ s = s.substr(0, n2) + val;
1087
+ }
1088
+ }
1089
+ stream_out << linebegin << s << lineend;
1090
+ }
1091
+ if (setFound.size() < settings_to_change.size()) {
1092
+ if (!have_eof_nl) {
1093
+ stream_out << "\n";
1094
+ }
1095
+ ModifyRWConfigFile_WriteRemaining(stream_out, settings_to_change, setFound);
1096
+ }
1097
+ }
1098
+
1099
+ void ArgsManager::ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change, const bool also_settings_json)
1100
+ {
1101
+ LOCK(cs_args);
1102
+ fs::path rwconf_path{GetRWConfigFilePath()};
1103
+ fs::path rwconf_new_path{rwconf_path};
1104
+ rwconf_new_path += ".new";
1105
+ try {
1106
+ fs::remove(rwconf_new_path);
1107
+ std::ofstream streamRWConfigOut(rwconf_new_path, std::ios_base::out | std::ios_base::trunc);
1108
+ if (fs::exists(rwconf_path)) {
1109
+ std::ifstream streamRWConfig(rwconf_path);
1110
+ ::ModifyRWConfigStream(streamRWConfig, streamRWConfigOut, settings_to_change);
1111
+ } else {
1112
+ std::istringstream streamIn;
1113
+ ::ModifyRWConfigStream(streamIn, streamRWConfigOut, settings_to_change);
1114
+ }
1115
+ } catch (...) {
1116
+ fs::remove(rwconf_new_path);
1117
+ throw;
1118
+ }
1119
+ if (!RenameOver(rwconf_new_path, rwconf_path)) {
1120
+ fs::remove(rwconf_new_path);
1121
+ throw std::ios_base::failure(strprintf("Failed to replace %s", fs::PathToString(rwconf_new_path)));
1122
+ }
1123
+ for (const auto& setting_change : settings_to_change) {
1124
+ m_settings.rw_config[setting_change.first] = {setting_change.second};
1125
+ }
1126
+ if (also_settings_json && !IsArgNegated("-settings")) {
1127
+ // Also save to settings.json for Core (0.21+) compatibility
1128
+ for (const auto& setting_change : settings_to_change) {
1129
+ m_settings.rw_settings[setting_change.first] = setting_change.second;
1130
+ }
1131
+ WriteSettingsFile();
1132
+ }
1133
+ if (settings_to_change.count("prune")) {
1134
+ m_rwconf_had_prune_option = true;
1135
+ }
1136
+ }
1137
+
1138
+ void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value, const bool also_settings_json)
1139
+ {
1140
+ std::map<std::string, std::string> settings_to_change;
1141
+ settings_to_change[setting_to_change] = new_value;
1142
+ ModifyRWConfigFile(settings_to_change, also_settings_json);
1143
+ }
1144
+
1145
+ void ArgsManager::EraseRWConfigFile()
1146
+ {
1147
+ LOCK(cs_args);
1148
+ fs::path rwconf_path{GetRWConfigFilePath()};
1149
+ if (!fs::exists(rwconf_path)) {
1150
+ return;
1151
+ }
1152
+ fs::path rwconf_reset_path = rwconf_path;
1153
+ rwconf_reset_path += ".reset";
1154
+ if (!RenameOver(rwconf_path, rwconf_reset_path)) {
1155
+ if (fs::remove(rwconf_path)) {
1156
+ throw std::ios_base::failure(strprintf("Failed to remove %s", fs::PathToString(rwconf_path)));
1157
+ }
1158
+ }
1159
+ }
1160
+
1161
  namespace common {
1162
  #ifdef WIN32
1163
  WinCmdLineArgs::WinCmdLineArgs()