diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -9,4 +9,15 @@ transactions - Remove miner policy estimator in favor of minimum fees, also remove `fee_estimates.dat`. Old copies will be left in place. - - The log timestamp format is now ISO 8601 (e.g. "2019-01-28T15:41:17Z") \ No newline at end of file + - The log timestamp format is now ISO 8601 (e.g. "2019-01-28T15:41:17Z") + - The configuration files now support assigning options to a specific network. + To do so, sections or prefix can be used: + main.uacomment=bch-mainnet + test.uacomment=bch-testnet + regtest.uacomment=bch-regtest + [main] + mempoolsize=300 + [test] + mempoolsize=200 + [regtest] + mempoolsize=50 \ No newline at end of file diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -78,4 +78,5 @@ void SelectBaseParams(const std::string &chain) { globalChainBaseParams = CreateBaseChainParams(chain); + gArgs.SelectConfigNetwork(chain); } diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -312,15 +312,24 @@ "h=1\n" "noh=1\n" "noi=1\n" - "i=1\n"; + "i=1\n" + "sec1.ccc=extend1\n" + "\n" + "[sec1]\n" + "ccc=extend2\n" + "h=1\n" + "[sec2]\n" + "ccc=extend3\n" + "iii=2\n"; TestArgsManager test_args; test_args.ReadConfigString(str_config); // expectation: a, b, ccc, d, fff, ggg, h, i end up in map + // so do sec1.ccc, sec1.h, sec2.ccc, sec2.iii BOOST_CHECK(test_args.GetOverrideArgs().empty()); - BOOST_CHECK(test_args.GetConfigArgs().size() == 8); + BOOST_CHECK(test_args.GetConfigArgs().size() == 12); BOOST_CHECK(test_args.GetConfigArgs().count("-a") && test_args.GetConfigArgs().count("-b") && @@ -330,12 +339,16 @@ test_args.GetConfigArgs().count("-ggg") && test_args.GetConfigArgs().count("-h") && test_args.GetConfigArgs().count("-i")); + BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc") && + test_args.GetConfigArgs().count("-sec1.h") && + test_args.GetConfigArgs().count("-sec2.ccc") && + test_args.GetConfigArgs().count("-sec2.iii")); BOOST_CHECK(test_args.IsArgSet("-a") && test_args.IsArgSet("-b") && test_args.IsArgSet("-ccc") && test_args.IsArgSet("-d") && test_args.IsArgSet("-fff") && test_args.IsArgSet("-ggg") && test_args.IsArgSet("-h") && test_args.IsArgSet("-i") && - !test_args.IsArgSet("-zzz")); + !test_args.IsArgSet("-zzz") && !test_args.IsArgSet("-iii")); BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" && test_args.GetArg("-b", "xxx") == "1" && @@ -345,7 +358,8 @@ test_args.GetArg("-ggg", "xxx") == "1" && test_args.GetArg("-h", "xxx") == "0" && test_args.GetArg("-i", "xxx") == "1" && - test_args.GetArg("-zzz", "xxx") == "xxx"); + test_args.GetArg("-zzz", "xxx") == "xxx" && + test_args.GetArg("-iii", "xxx") == "xxx"); for (bool def : {false, true}) { BOOST_CHECK(test_args.GetBoolArg("-a", def) && @@ -356,7 +370,8 @@ test_args.GetBoolArg("-ggg", def) && !test_args.GetBoolArg("-h", def) && test_args.GetBoolArg("-i", def) && - test_args.GetBoolArg("-zzz", def) == def); + test_args.GetBoolArg("-zzz", def) == def && + test_args.GetBoolArg("-iii", def) == def); } BOOST_CHECK(test_args.GetArgs("-a").size() == 1 && @@ -389,6 +404,51 @@ // last setting takes precedence BOOST_CHECK(!test_args.IsArgNegated("-i")); BOOST_CHECK(!test_args.IsArgNegated("-zzz")); + + // Test sections work + test_args.SelectConfigNetwork("sec1"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" && + test_args.GetArg("-b", "xxx") == "1" && + test_args.GetArg("-d", "xxx") == "e" && + test_args.GetArg("-fff", "xxx") == "0" && + test_args.GetArg("-ggg", "xxx") == "1" && + test_args.GetArg("-zzz", "xxx") == "xxx" && + test_args.GetArg("-iii", "xxx") == "xxx"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1"); + // check multiple values works + const std::vector sec1_ccc_expected = {"extend1", "extend2", + "argument", "multiple"}; + const auto &sec1_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), + sec1_ccc_expected.begin(), + sec1_ccc_expected.end()); + + test_args.SelectConfigNetwork("sec2"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" && + test_args.GetArg("-b", "xxx") == "1" && + test_args.GetArg("-d", "xxx") == "e" && + test_args.GetArg("-fff", "xxx") == "0" && + test_args.GetArg("-ggg", "xxx") == "1" && + test_args.GetArg("-zzz", "xxx") == "xxx" && + test_args.GetArg("-h", "xxx") == "0"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3"); + // check multiple values works + const std::vector sec2_ccc_expected = {"extend3", "argument", + "multiple"}; + const auto &sec2_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), + sec2_ccc_expected.begin(), + sec2_ccc_expected.end()); } BOOST_AUTO_TEST_CASE(util_GetArg) { diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -100,10 +100,15 @@ mutable CCriticalSection cs_args; std::map> m_override_args; std::map> m_config_args; - + std::string m_network; void ReadConfigStream(std::istream &stream); public: + /** + * Select the network in use + */ + void SelectConfigNetwork(const std::string &network); + void ParseParameters(int argc, const char *const argv[]); void ReadConfigFile(const std::string &confPath); diff --git a/src/util.cpp b/src/util.cpp --- a/src/util.cpp +++ b/src/util.cpp @@ -173,6 +173,13 @@ public: typedef std::map> MapArgs; + /** 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 &res, const MapArgs &map_args, @@ -222,6 +229,13 @@ // 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; + } + } + found_result = GetArgHelper(am.m_config_args, arg); if (found_result.first) { return found_result; @@ -252,9 +266,17 @@ * output is not sent to any file at all). */ static bool InterpretNegatedOption(std::string &key, std::string &val) { - if (key.substr(0, 3) == "-no") { + 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") { bool bool_val = InterpretBool(val); - key.erase(1, 2); + key.erase(option_index, 2); if (!bool_val) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf( @@ -268,6 +290,10 @@ return false; } +void ArgsManager::SelectConfigNetwork(const std::string &network) { + m_network = network; +} + void ArgsManager::ParseParameters(int argc, const char *const argv[]) { LOCK(cs_args); m_override_args.clear(); @@ -314,6 +340,11 @@ LOCK(cs_args); ArgsManagerHelper::AddArgs(result, m_override_args, strArg); + if (!m_network.empty()) { + ArgsManagerHelper::AddArgs( + result, m_config_args, + ArgsManagerHelper::NetworkArg(*this, strArg)); + } ArgsManagerHelper::AddArgs(result, m_config_args, strArg); return result; } @@ -334,6 +365,14 @@ 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(); diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -33,8 +33,13 @@ # Check that using non-existent datadir in conf file fails conf_file = os.path.join(default_data_dir, "bitcoin.conf") - with open(conf_file, 'a', encoding='utf8') as f: + + # datadir needs to be set before [regtest] section + conf_file_contents = open(conf_file, encoding='utf8').read() + with open(conf_file, 'w', encoding='utf8') as f: f.write("datadir=" + new_data_dir + "\n") + f.write(conf_file_contents) + self.assert_start_raises_init_error( 0, ['-conf='+conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -326,6 +326,7 @@ os.makedirs(datadir) with open(os.path.join(datadir, "bitcoin.conf"), 'w', encoding='utf8') as f: f.write("regtest=1\n") + f.write("[regtest]\n") f.write("port=" + str(p2p_port(n)) + "\n") f.write("rpcport=" + str(rpc_port(n)) + "\n") f.write("listenonion=0\n")