diff --git a/doc/bitcoin-conf.md b/doc/bitcoin-conf.md index 9b318797900..daa7fcba000 100644 --- a/doc/bitcoin-conf.md +++ b/doc/bitcoin-conf.md @@ -8,6 +8,16 @@ Changes to the configuration file while `bitcoind` or `bitcoin-qt` is running on Users should never make any configuration changes which they do not understand. Furthermore, users should always be wary of accepting any configuration changes provided to them by another source (even if they believe that they do understand them). +## Configuration File Precedence + +Options specified in the configuration file can be overridden by options in the [`settings.json` file](files.md) and by options specified on the command line. + +The `settings.json` file contains dynamic settings that are set by the Bitcoin Core GUI and RPCs at runtime, and augment or replace the static settings specified in the `bitcoin.conf` file. + +Command line options also augment or replace `bitcoin.conf` options, and can be useful for scripting and debugging. + +It is possible to see which setting values are in use by checking `debug.log` output. Any unrecognized options that are found in `bitcoin.conf` also show up as warnings in `debug.log` output. + ## Configuration File Format The configuration file is a plain text file and consists of `option=value` entries, one per line. Leading and trailing whitespaces are removed. @@ -49,6 +59,14 @@ regtest.rpcport=3000 rpcport=4000 ``` +### Negated options + +Almost all options can be negated by being specified with a `no` prefix. For example an option `-foo` could be negated by writing `nofoo=1` or `nofoo=` in the configuration file or `-nofoo=1` or `-nofoo` on the command line. + +In general, negating an option is like setting it to `0` if it is a boolean or integer option, and setting it to an empty string or path or list if it is a string or path or list option. + +However, there are exceptions to this general rule. For example, it is an error to negate some options (e.g. `-nodatadir` is disallowed), and some negated strings are treated like `"0"` instead of `""` (e.g. `-noproxy` is treated like `-proxy=0`), and some negating some lists can have side effects in addition to clearing the lists (e.g. `-noconnect` disables automatic connections in addition to dropping any manual connections specified previously with `-connect=`). When there are exceptions to the rule, they should either be obvious from context, or should be mentioned in usage documentation. Nonobvious, undocumented exceptions should be reported as bugs. + ## Configuration File Path The configuration file is not automatically created; you can create it using your favorite text editor. By default, the configuration file name is `bitcoin.conf` and it is located in the Bitcoin data directory, but both the Bitcoin data directory and the configuration file path may be changed using the `-datadir` and `-conf` command-line options. diff --git a/doc/release-notes-30529.md b/doc/release-notes-30529.md new file mode 100644 index 00000000000..71362b3abe2 --- /dev/null +++ b/doc/release-notes-30529.md @@ -0,0 +1,4 @@ +Configuration +------------- + +Handling of negated `-noseednode`, `-nobind`, `-nowhitebind`, `-norpcbind`, `-norpcallowip`, `-norpcwhitelist`, `-notest`, `-noasmap`, `-norpcwallet`, `-noonlynet`, and `-noexternalip` options has changed. Previously negating these options had various confusing and undocumented side effects. Now negating them just resets the settings and restores default behaviors, as if the options were not specified. diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 1b2d97faff5..75911d00874 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -110,6 +110,13 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-stdinwalletpassphrase", "Read wallet passphrase from standard input as a single line. When combined with -stdin, the first line from standard input is used for the wallet passphrase.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); } +std::optional RpcWalletName(const ArgsManager& args) +{ + // Check IsArgNegated to return nullopt instead of "0" if -norpcwallet is specified + if (args.IsArgNegated("-rpcwallet")) return std::nullopt; + return args.GetArg("-rpcwallet"); +} + /** libevent event log callback */ static void libevent_log_cb(int severity, const char *msg) { @@ -1183,10 +1190,8 @@ static void ParseGetInfoResult(UniValue& result) */ static UniValue GetNewAddress() { - std::optional wallet_name{}; - if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", ""); DefaultRequestHandler rh; - return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, wallet_name); + return ConnectAndCallRPC(&rh, "getnewaddress", /* args=*/{}, RpcWalletName(gArgs)); } /** @@ -1291,8 +1296,7 @@ static int CommandLineRPC(int argc, char *argv[]) } if (nRet == 0) { // Perform RPC call - std::optional wallet_name{}; - if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", ""); + const std::optional wallet_name{RpcWalletName(gArgs)}; const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name); // Parse reply @@ -1300,7 +1304,7 @@ static int CommandLineRPC(int argc, char *argv[]) const UniValue& error = reply.find_value("error"); if (error.isNull()) { if (gArgs.GetBoolArg("-getinfo", false)) { - if (!gArgs.IsArgSet("-rpcwallet")) { + if (!wallet_name) { GetWalletBalances(result); // fetch multiwallet balances and append to result } ParseGetInfoResult(result); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7966b80a273..7cf28d69b42 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -25,10 +25,10 @@ using util::SplitString; void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options) { - if (args.IsArgSet("-signetseednode")) { + if (!args.GetArgs("-signetseednode").empty()) { options.seeds.emplace(args.GetArgs("-signetseednode")); } - if (args.IsArgSet("-signetchallenge")) { + if (!args.GetArgs("-signetchallenge").empty()) { const auto signet_challenge = args.GetArgs("-signetchallenge"); if (signet_challenge.size() != 1) { throw std::runtime_error("-signetchallenge cannot be multiple values."); @@ -66,8 +66,6 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } } - if (!args.IsArgSet("-vbparams")) return; - for (const std::string& strDeployment : args.GetArgs("-vbparams")) { std::vector vDeploymentParams = SplitString(strDeployment, ':'); if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) { diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 5d906ffa0c2..57893702b8b 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -334,7 +334,7 @@ static bool InitRPCAuthentication() } } - g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist")); + g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", !gArgs.GetArgs("-rpcwhitelist").empty()); for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) { auto pos = strRPCWhitelist.find(':'); std::string strUser = strRPCWhitelist.substr(0, pos); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 88e640c377c..bd2dec19b97 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -362,16 +362,20 @@ static bool HTTPBindAddresses(struct evhttp* http) std::vector> endpoints; // Determine what addresses to bind to - if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs + // To prevent misconfiguration and accidental exposure of the RPC + // interface, require -rpcallowip and -rpcbind to both be specified + // together. If either is missing, ignore both values, bind to localhost + // instead, and log warnings. + if (gArgs.GetArgs("-rpcallowip").empty() || gArgs.GetArgs("-rpcbind").empty()) { // Default to loopback if not allowing external IPs endpoints.emplace_back("::1", http_port); endpoints.emplace_back("127.0.0.1", http_port); - if (gArgs.IsArgSet("-rpcallowip")) { + if (!gArgs.GetArgs("-rpcallowip").empty()) { LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n"); } - if (gArgs.IsArgSet("-rpcbind")) { + if (!gArgs.GetArgs("-rpcbind").empty()) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } - } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address + } else { // Specific bind addresses for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) { uint16_t port{http_port}; std::string host; diff --git a/src/init.cpp b/src/init.cpp index d46318fd45e..3cfd301fbab 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -716,17 +716,18 @@ void InitParameterInteraction(ArgsManager& args) { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified - if (args.IsArgSet("-bind")) { + if (!args.GetArgs("-bind").empty()) { if (args.SoftSetBoolArg("-listen", true)) LogInfo("parameter interaction: -bind set -> setting -listen=1\n"); } - if (args.IsArgSet("-whitebind")) { + if (!args.GetArgs("-whitebind").empty()) { if (args.SoftSetBoolArg("-listen", true)) LogInfo("parameter interaction: -whitebind set -> setting -listen=1\n"); } - if (args.IsArgSet("-connect") || args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS) <= 0) { + if (!args.GetArgs("-connect").empty() || args.IsArgNegated("-connect") || args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS) <= 0) { // when only connecting to trusted nodes, do not seed via DNS, or listen by default + // do the same when connections are disabled if (args.SoftSetBoolArg("-dnsseed", false)) LogInfo("parameter interaction: -connect or -maxconnections=0 set -> setting -dnsseed=0\n"); if (args.SoftSetBoolArg("-listen", false)) @@ -762,7 +763,7 @@ void InitParameterInteraction(ArgsManager& args) } } - if (args.IsArgSet("-externalip")) { + if (!args.GetArgs("-externalip").empty()) { // if an explicit public IP is specified, do not try to find others if (args.SoftSetBoolArg("-discover", false)) LogInfo("parameter interaction: -externalip set -> setting -discover=0\n"); @@ -782,8 +783,8 @@ void InitParameterInteraction(ArgsManager& args) if (args.SoftSetBoolArg("-whitelistrelay", true)) LogInfo("parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n"); } - if (args.IsArgSet("-onlynet")) { - const auto onlynets = args.GetArgs("-onlynet"); + const auto onlynets = args.GetArgs("-onlynet"); + if (!onlynets.empty()) { bool clearnet_reachable = std::any_of(onlynets.begin(), onlynets.end(), [](const auto& net) { const auto n = ParseNetwork(net); return n == NET_IPV4 || n == NET_IPV6; @@ -1044,12 +1045,12 @@ bool AppInitParameterInteraction(const ArgsManager& args) if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) g_local_services = ServiceFlags(g_local_services | NODE_BLOOM); - if (args.IsArgSet("-test")) { + const std::vector test_options = args.GetArgs("-test"); + if (!test_options.empty()) { if (chainparams.GetChainType() != ChainType::REGTEST) { return InitError(Untranslated("-test=