Merge bitcoin/bitcoin#33229: multiprocess: Don't require bitcoin -m argument when IPC options are used
453b0fa286bitcoin: Make wrapper not require -m (Ryan Ofsky)29e836fae6test: add tool_bitcoin to test bitcoin wrapper behavior (Ryan Ofsky)0972f55040init: add exe name to bitcoind, bitcoin-node -version output to be able to distinguish these in tests (Ryan Ofsky) Pull request description: This change makes the `bitcoin` command respect IPC command line options and _bitcoin.conf_ settings, so IPC listening can be enabled by just running `bitcoin node -ipcbind=unix` or `bitcoin node` with `ipcbind=unix` in the configuration file, and there is no longer a need to specify a multiprocess `-m` option like `bitcoin -m node [...]` sipa and theuni in #31802 pointed out that users shouldn't be exposed to multiprocess implementation details just to use IPC features, so current need to specify the `bitcoin -m` option in conjunction with `-ipcbind` could be seen as a design mistake and not just a usage inconvenience. This PR also adds a dedicated functional test for the `bitcoin` wrapper command and to make sure it calls the right binaries and test the new functionality. --- This PR is part of the [process separation project](https://github.com/bitcoin/bitcoin/issues/28722). ACKs for top commit: Sjors: re-ACK453b0fa286achow101: ACK453b0fa286TheCharlatan: Re-ACK453b0fa286Tree-SHA512: 9e49cb7e183fd220fa7a4e8ac68cef55f3cb2ccec40ad2a9d3e3f31db64c4953db8337f8caf7fce877bc97002ae97568dcf47ee269a06ca1f503f119bfe392c1
This commit is contained in:
13 files changed
+174
-10
No files matched your search
+1
-1
@@ -292,7 +292,7 @@ if(BUILD_BITCOIN_BIN)
|
|||||||
add_executable(bitcoin bitcoin.cpp)
|
add_executable(bitcoin bitcoin.cpp)
|
||||||
add_windows_resources(bitcoin bitcoin-res.rc)
|
add_windows_resources(bitcoin bitcoin-res.rc)
|
||||||
add_windows_application_manifest(bitcoin)
|
add_windows_application_manifest(bitcoin)
|
||||||
target_link_libraries(bitcoin core_interface bitcoin_util)
|
target_link_libraries(bitcoin core_interface bitcoin_common bitcoin_util)
|
||||||
install_binary_component(bitcoin HAS_MANPAGE)
|
install_binary_component(bitcoin HAS_MANPAGE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
+29
-3
@@ -5,6 +5,7 @@
|
|||||||
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
#include <bitcoin-build-config.h> // IWYU pragma: keep
|
||||||
|
|
||||||
#include <clientversion.h>
|
#include <clientversion.h>
|
||||||
|
#include <common/args.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
#include <util/exec.h>
|
#include <util/exec.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
@@ -47,7 +48,7 @@ Run '%s help' to see additional commands (e.g. for testing and debugging).
|
|||||||
)";
|
)";
|
||||||
|
|
||||||
struct CommandLine {
|
struct CommandLine {
|
||||||
bool use_multiprocess{false};
|
std::optional<bool> use_multiprocess;
|
||||||
bool show_version{false};
|
bool show_version{false};
|
||||||
bool show_help{false};
|
bool show_help{false};
|
||||||
std::string_view command;
|
std::string_view command;
|
||||||
@@ -55,6 +56,7 @@ struct CommandLine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
CommandLine ParseCommandLine(int argc, char* argv[]);
|
CommandLine ParseCommandLine(int argc, char* argv[]);
|
||||||
|
bool UseMultiprocess(const CommandLine& cmd);
|
||||||
static void ExecCommand(const std::vector<const char*>& args, std::string_view argv0);
|
static void ExecCommand(const std::vector<const char*>& args, std::string_view argv0);
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
@@ -78,9 +80,9 @@ int main(int argc, char* argv[])
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
} else if (cmd.command == "gui") {
|
} else if (cmd.command == "gui") {
|
||||||
args.emplace_back(cmd.use_multiprocess ? "bitcoin-gui" : "bitcoin-qt");
|
args.emplace_back(UseMultiprocess(cmd) ? "bitcoin-gui" : "bitcoin-qt");
|
||||||
} else if (cmd.command == "node") {
|
} else if (cmd.command == "node") {
|
||||||
args.emplace_back(cmd.use_multiprocess ? "bitcoin-node" : "bitcoind");
|
args.emplace_back(UseMultiprocess(cmd) ? "bitcoin-node" : "bitcoind");
|
||||||
} else if (cmd.command == "rpc") {
|
} else if (cmd.command == "rpc") {
|
||||||
args.emplace_back("bitcoin-cli");
|
args.emplace_back("bitcoin-cli");
|
||||||
// Since "bitcoin rpc" is a new interface that doesn't need to be
|
// Since "bitcoin rpc" is a new interface that doesn't need to be
|
||||||
@@ -143,6 +145,30 @@ CommandLine ParseCommandLine(int argc, char* argv[])
|
|||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UseMultiprocess(const CommandLine& cmd)
|
||||||
|
{
|
||||||
|
// If -m or -M options were explicitly specified, there is no need to
|
||||||
|
// further parse arguments to determine which to use.
|
||||||
|
if (cmd.use_multiprocess) return *cmd.use_multiprocess;
|
||||||
|
|
||||||
|
ArgsManager args;
|
||||||
|
args.SetDefaultFlags(ArgsManager::ALLOW_ANY);
|
||||||
|
std::string error_message;
|
||||||
|
auto argv{cmd.args};
|
||||||
|
argv.insert(argv.begin(), nullptr);
|
||||||
|
if (!args.ParseParameters(argv.size(), argv.data(), error_message)) {
|
||||||
|
tfm::format(std::cerr, "Warning: failed to parse subcommand command line options: %s\n", error_message);
|
||||||
|
}
|
||||||
|
if (!args.ReadConfigFiles(error_message, true)) {
|
||||||
|
tfm::format(std::cerr, "Warning: failed to parse subcommand config: %s\n", error_message);
|
||||||
|
}
|
||||||
|
args.SelectConfigNetwork(args.GetChainTypeString());
|
||||||
|
|
||||||
|
// If any -ipc* options are set these need to be processed by a
|
||||||
|
// multiprocess-capable binary.
|
||||||
|
return args.IsArgSet("-ipcbind") || args.IsArgSet("-ipcconnect") || args.IsArgSet("-ipcfd");
|
||||||
|
}
|
||||||
|
|
||||||
//! Execute the specified bitcoind, bitcoin-qt or other command line in `args`
|
//! Execute the specified bitcoind, bitcoin-qt or other command line in `args`
|
||||||
//! using src, bin and libexec directory paths relative to this executable, where
|
//! using src, bin and libexec directory paths relative to this executable, where
|
||||||
//! the path to this executable is specified in `wrapper_argv0`.
|
//! the path to this executable is specified in `wrapper_argv0`.
|
||||||
|
|||||||
+8
-3
@@ -132,11 +132,16 @@ static bool ParseArgs(NodeContext& node, int argc, char* argv[])
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ProcessInitCommands(ArgsManager& args)
|
static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args)
|
||||||
{
|
{
|
||||||
// Process help and version before taking care about datadir
|
// Process help and version before taking care about datadir
|
||||||
if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
|
if (HelpRequested(args) || args.GetBoolArg("-version", false)) {
|
||||||
std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion() + "\n";
|
std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion();
|
||||||
|
if (const char* exe_name{init.exeName()}) {
|
||||||
|
strUsage += " ";
|
||||||
|
strUsage += exe_name;
|
||||||
|
}
|
||||||
|
strUsage += "\n";
|
||||||
|
|
||||||
if (args.GetBoolArg("-version", false)) {
|
if (args.GetBoolArg("-version", false)) {
|
||||||
strUsage += FormatParagraph(LicenseInfo());
|
strUsage += FormatParagraph(LicenseInfo());
|
||||||
@@ -277,7 +282,7 @@ MAIN_FUNCTION
|
|||||||
ArgsManager& args = *Assert(node.args);
|
ArgsManager& args = *Assert(node.args);
|
||||||
if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
|
if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE;
|
||||||
// Process early info return commands such as -help or -version
|
// Process early info return commands such as -help or -version
|
||||||
if (ProcessInitCommands(args)) return EXIT_SUCCESS;
|
if (ProcessInitCommands(*init, args)) return EXIT_SUCCESS;
|
||||||
|
|
||||||
// Start application
|
// Start application
|
||||||
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
|
if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) {
|
||||||
|
|||||||
+7
-1
@@ -266,7 +266,13 @@ std::optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) co
|
|||||||
return search->second.m_flags;
|
return search->second.m_flags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return m_default_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgsManager::SetDefaultFlags(std::optional<unsigned int> flags)
|
||||||
|
{
|
||||||
|
LOCK(cs_args);
|
||||||
|
m_default_flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
|
fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const
|
||||||
|
|||||||
+7
-1
@@ -137,6 +137,7 @@ protected:
|
|||||||
std::string m_network GUARDED_BY(cs_args);
|
std::string m_network GUARDED_BY(cs_args);
|
||||||
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
|
std::set<std::string> m_network_only_args GUARDED_BY(cs_args);
|
||||||
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
|
std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
|
||||||
|
std::optional<unsigned int> m_default_flags GUARDED_BY(cs_args){};
|
||||||
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
bool m_accept_any_command GUARDED_BY(cs_args){true};
|
||||||
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
|
||||||
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
|
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
|
||||||
@@ -375,10 +376,15 @@ protected:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return Flags for known arg.
|
* Return Flags for known arg.
|
||||||
* Return nullopt for unknown arg.
|
* Return default flags for unknown arg.
|
||||||
*/
|
*/
|
||||||
std::optional<unsigned int> GetArgFlags(const std::string& name) const;
|
std::optional<unsigned int> GetArgFlags(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default flags to return for an unknown arg.
|
||||||
|
*/
|
||||||
|
void SetDefaultFlags(std::optional<unsigned int>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get settings file path, or return false if read-write settings were
|
* Get settings file path, or return false if read-write settings were
|
||||||
* disabled with -nosettings.
|
* disabled with -nosettings.
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public:
|
|||||||
// bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node
|
// bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node
|
||||||
// options and will start the node with those options.
|
// options and will start the node with those options.
|
||||||
bool canListenIpc() override { return true; }
|
bool canListenIpc() override { return true; }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
node::NodeContext m_node;
|
node::NodeContext m_node;
|
||||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public:
|
|||||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
interfaces::Ipc* ipc() override { return m_ipc.get(); }
|
||||||
bool canListenIpc() override { return true; }
|
bool canListenIpc() override { return true; }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
node::NodeContext& m_node;
|
node::NodeContext& m_node;
|
||||||
std::unique_ptr<interfaces::Ipc> m_ipc;
|
std::unique_ptr<interfaces::Ipc> m_ipc;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
namespace {
|
namespace {
|
||||||
|
const char* EXE_NAME = "bitcoin-qt";
|
||||||
|
|
||||||
class BitcoinQtInit : public interfaces::Init
|
class BitcoinQtInit : public interfaces::Init
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -32,6 +34,7 @@ public:
|
|||||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||||
}
|
}
|
||||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
node::NodeContext m_node;
|
node::NodeContext m_node;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ using node::NodeContext;
|
|||||||
|
|
||||||
namespace init {
|
namespace init {
|
||||||
namespace {
|
namespace {
|
||||||
|
const char* EXE_NAME = "bitcoind";
|
||||||
|
|
||||||
class BitcoindInit : public interfaces::Init
|
class BitcoindInit : public interfaces::Init
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -34,6 +36,7 @@ public:
|
|||||||
return MakeWalletLoader(chain, *Assert(m_node.args));
|
return MakeWalletLoader(chain, *Assert(m_node.args));
|
||||||
}
|
}
|
||||||
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
|
||||||
|
const char* exeName() override { return EXE_NAME; }
|
||||||
NodeContext& m_node;
|
NodeContext& m_node;
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public:
|
|||||||
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
|
virtual std::unique_ptr<Echo> makeEcho() { return nullptr; }
|
||||||
virtual Ipc* ipc() { return nullptr; }
|
virtual Ipc* ipc() { return nullptr; }
|
||||||
virtual bool canListenIpc() { return false; }
|
virtual bool canListenIpc() { return false; }
|
||||||
|
virtual const char* exeName() { return nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
//! Return implementation of Init interface for the node process. If the argv
|
//! Return implementation of Init interface for the node process. If the argv
|
||||||
|
|||||||
@@ -337,6 +337,7 @@ BASE_SCRIPTS = [
|
|||||||
'p2p_tx_privacy.py',
|
'p2p_tx_privacy.py',
|
||||||
'rpc_getdescriptoractivity.py',
|
'rpc_getdescriptoractivity.py',
|
||||||
'rpc_scanblocks.py',
|
'rpc_scanblocks.py',
|
||||||
|
'tool_bitcoin.py',
|
||||||
'p2p_sendtxrcncl.py',
|
'p2p_sendtxrcncl.py',
|
||||||
'rpc_scantxoutset.py',
|
'rpc_scantxoutset.py',
|
||||||
'feature_unsupported_utxo_db.py',
|
'feature_unsupported_utxo_db.py',
|
||||||
|
|||||||
Executable
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
"""Test the bitcoin wrapper tool."""
|
||||||
|
from test_framework.test_framework import (
|
||||||
|
BitcoinTestFramework,
|
||||||
|
SkipTest,
|
||||||
|
)
|
||||||
|
from test_framework.util import (
|
||||||
|
append_config,
|
||||||
|
assert_equal,
|
||||||
|
)
|
||||||
|
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class ToolBitcoinTest(BitcoinTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.num_nodes = 1
|
||||||
|
|
||||||
|
def skip_test_if_missing_module(self):
|
||||||
|
# Skip test on windows because currently when `bitcoin node -version` is
|
||||||
|
# run on windows, python doesn't capture output from the child
|
||||||
|
# `bitcoind` and `bitcoin-node` process started with _wexecvp, and
|
||||||
|
# stdout/stderr are always empty. See
|
||||||
|
# https://github.com/bitcoin/bitcoin/pull/33229#issuecomment-3265524908
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
raise SkipTest("Test does not currently work on windows")
|
||||||
|
|
||||||
|
def setup_network(self):
|
||||||
|
"""Set up nodes normally, but save a copy of their arguments before starting them."""
|
||||||
|
self.add_nodes(self.num_nodes, self.extra_args)
|
||||||
|
node_argv = self.get_binaries().node_argv()
|
||||||
|
self.node_options = [node.args[len(node_argv):] for node in self.nodes]
|
||||||
|
assert all(node.args[:len(node_argv)] == node_argv for node in self.nodes)
|
||||||
|
|
||||||
|
def set_cmd_args(self, node, args):
|
||||||
|
"""Set up node so it will be started through bitcoin wrapper command with specified arguments."""
|
||||||
|
node.args = [self.binary_paths.bitcoin_bin] + args + ["node"] + self.node_options[node.index]
|
||||||
|
|
||||||
|
def test_args(self, cmd_args, node_args, expect_exe=None, expect_error=None):
|
||||||
|
node = self.nodes[0]
|
||||||
|
self.set_cmd_args(node, cmd_args)
|
||||||
|
extra_args = node_args + ["-version"]
|
||||||
|
if expect_error is not None:
|
||||||
|
node.assert_start_raises_init_error(expected_msg=expect_error, extra_args=extra_args)
|
||||||
|
else:
|
||||||
|
assert expect_exe
|
||||||
|
node.start(extra_args=extra_args)
|
||||||
|
ret, out, err = get_node_output(node)
|
||||||
|
try:
|
||||||
|
assert_equal(get_exe_name(out), expect_exe.encode())
|
||||||
|
assert_equal(err, b"")
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError(f"Unexpected output from {node.args + extra_args}: {out=!r} {err=!r} {ret=!r}") from e
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
node = self.nodes[0]
|
||||||
|
|
||||||
|
self.log.info("Ensure bitcoin node command invokes bitcoind by default")
|
||||||
|
self.test_args([], [], expect_exe="bitcoind")
|
||||||
|
|
||||||
|
self.log.info("Ensure bitcoin -M invokes bitcoind")
|
||||||
|
self.test_args(["-M"], [], expect_exe="bitcoind")
|
||||||
|
|
||||||
|
self.log.info("Ensure bitcoin -M does not accept -ipcbind")
|
||||||
|
self.test_args(["-M"], ["-ipcbind=unix"], expect_error='Error: Error parsing command line arguments: Invalid parameter -ipcbind=unix')
|
||||||
|
|
||||||
|
if self.is_ipc_compiled():
|
||||||
|
self.log.info("Ensure bitcoin -m invokes bitcoin-node")
|
||||||
|
self.test_args(["-m"], [], expect_exe="bitcoin-node")
|
||||||
|
|
||||||
|
self.log.info("Ensure bitcoin -m does accept -ipcbind")
|
||||||
|
self.test_args(["-m"], ["-ipcbind=unix"], expect_exe="bitcoin-node")
|
||||||
|
|
||||||
|
self.log.info("Ensure bitcoin accepts -ipcbind by default")
|
||||||
|
self.test_args([], ["-ipcbind=unix"], expect_exe="bitcoin-node")
|
||||||
|
|
||||||
|
self.log.info("Ensure bitcoin recognizes -ipcbind in config file")
|
||||||
|
append_config(node.datadir_path, ["ipcbind=unix"])
|
||||||
|
self.test_args([], [], expect_exe="bitcoin-node")
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_output(node):
|
||||||
|
ret = node.process.wait(timeout=60)
|
||||||
|
node.stdout.seek(0)
|
||||||
|
node.stderr.seek(0)
|
||||||
|
out = node.stdout.read()
|
||||||
|
err = node.stderr.read()
|
||||||
|
node.stdout.close()
|
||||||
|
node.stderr.close()
|
||||||
|
|
||||||
|
# Clean up TestNode state
|
||||||
|
node.running = False
|
||||||
|
node.process = None
|
||||||
|
node.rpc_connected = False
|
||||||
|
node.rpc = None
|
||||||
|
|
||||||
|
return ret, out, err
|
||||||
|
|
||||||
|
|
||||||
|
def get_exe_name(version_str):
|
||||||
|
"""Get exe name from last word of first line of version string."""
|
||||||
|
return re.match(rb".*?(\S+)\s*?(?:\n|$)", version_str.strip()).group(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ToolBitcoinTest(__file__).main()
|
||||||
@@ -23,7 +23,7 @@ CMD_GREP_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWallet
|
|||||||
CMD_GREP_WALLET_HIDDEN_ARGS = r"git grep --function-context 'void DummyWalletInit::AddWalletOptions' -- {}".format(CMD_ROOT_DIR)
|
CMD_GREP_WALLET_HIDDEN_ARGS = r"git grep --function-context 'void DummyWalletInit::AddWalletOptions' -- {}".format(CMD_ROOT_DIR)
|
||||||
CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR)
|
CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR)
|
||||||
# list unsupported, deprecated and duplicate args as they need no documentation
|
# list unsupported, deprecated and duplicate args as they need no documentation
|
||||||
SET_DOC_OPTIONAL = set(['-h', '-?', '-dbcrashratio', '-forcecompactdb'])
|
SET_DOC_OPTIONAL = set(['-h', '-?', '-dbcrashratio', '-forcecompactdb', '-ipcconnect', '-ipcfd'])
|
||||||
|
|
||||||
|
|
||||||
def lint_missing_argument_documentation():
|
def lint_missing_argument_documentation():
|
||||||
|
|||||||
Reference in new issue
Block a user