今天分析一下比特币代码:bitcoin/src/init.cpp 中的这个函数:
AppInitParameterInteraction,用于初始化客户端启动时配置文件或者启动命令接收的参数进行解析判断,用于设置客户端的运行环境参数。
bool AppInitParameterInteraction(const ArgsManager& args)
{
const CChainParams& chainparams = Params();
// ********************************************************* Step 2: parameter interactions
// also see: InitParameterInteraction()
// Error if network-specific options (-addnode, -connect, etc) are
// specified in default section of config file, but not overridden
// on the command line or in this chain's section of the config file.
ChainType chain = args.GetChainType();
if (chain == ChainType::SIGNET) {
LogPrintf("Signet derived magic (message start): %s\n", HexStr(chainparams.MessageStart()));
}
bilingual_str errors;
for (const auto& arg : args.GetUnsuitableSectionOnlyArgs()) {
errors += strprintf(_("Config setting for %s only applied on %s network when in [%s] section.") + Untranslated("\n"), arg, ChainTypeToString(chain), ChainTypeToString(chain));
}
if (!errors.empty()) {
return InitError(errors);
}
// Warn if unrecognized section name are present in the config file.
bilingual_str warnings;
for (const auto& section : args.GetUnrecognizedSections()) {
warnings += strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized.") + Untranslated("\n"), section.m_file, section.m_line, section.m_name);
}
if (!warnings.empty()) {
InitWarning(warnings);
}
if (!fs::is_directory(args.GetBlocksDirPath())) {
return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", "")));
}
// parse and validate enabled filter types
std::string blockfilterindex_value = args.GetArg("-blockfilterindex", DEFAULT_BLOCKFILTERINDEX);
if (blockfilterindex_value == "" || blockfilterindex_value == "1") {
g_enabled_filter_types = AllBlockFilterTypes();
} else if (blockfilterindex_value != "0") {
const std::vector<std::string> names = args.GetArgs("-blockfilterindex");
for (const auto& name : names) {
BlockFilterType filter_type;
if (!BlockFilterTypeByName(name, filter_type)) {
return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name));
}
g_enabled_filter_types.insert(filter_type);
}
}
// Signal NODE_P2P_V2 if BIP324 v2 transport is enabled.
if (args.GetBoolArg("-v2transport", DEFAULT_V2_TRANSPORT)) {
nLocalServices = ServiceFlags(nLocalServices | NODE_P2P_V2);
}
// Signal NODE_COMPACT_FILTERS if peerblockfilters and basic filters index are both enabled.
if (args.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)) {
if (g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) {
return InitError(_("Cannot set -peerblockfilters without -blockfilterindex."));
}
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
}
if (args.GetIntArg("-prune", 0)) {
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
if (args.GetBoolArg("-reindex-chainstate", false)) {
return InitError(_("Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead."));
}
}
// If -forcednsseed is set to true, ensure -dnsseed has not been set to false
if (args.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED) && !args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED)){
return InitError(_("Cannot set -forcednsseed to true when setting -dnsseed to false."));
}
// -bind and -whitebind can't be set when not listening
size_t nUserBind = args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
return InitError(Untranslated("Cannot set -bind or -whitebind together with -listen=0"));
}
// if listen=0, then disallow listenonion=1
if (!args.GetBoolArg("-listen", DEFAULT_LISTEN) && args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
return InitError(Untranslated("Cannot set -listen=0 together with -listenonion=1"));
}
// Make sure enough file descriptors are available
int nBind = std::max(nUserBind, size_t(1));
nUserMaxConnections = args.GetIntArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS);
nMaxConnections = std::max(nUserMaxConnections, 0);
nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + MAX_ADDNODE_CONNECTIONS + nBind + NUM_FDS_MESSAGE_CAPTURE);
#ifdef USE_POLL
int fd_max = nFD;
#else
int fd_max = FD_SETSIZE;
#endif
// Trim requested connection counts, to fit into system limitations
// <int> in std::min<int>(...) to work around FreeBSD compilation issue described in #2695
nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE), 0);
if (nFD < MIN_CORE_FILEDESCRIPTORS)
return InitError(_("Not enough file descriptors available."));
nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS - NUM_FDS_MESSAGE_CAPTURE, nMaxConnections);
if (nMaxConnections < nUserMaxConnections)
InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections));
// ********************************************************* Step 3: parameter-to-internal-flags
auto result = init::SetLoggingCategories(args);
if (!result) return InitError(util::ErrorString(result));
result = init::SetLoggingLevel(args);
if (!result) return InitError(util::ErrorString(result));
nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
if (nConnectTimeout <= 0) {
nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
peer_connect_timeout = args.GetIntArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT);
if (peer_connect_timeout <= 0) {
return InitError(Untranslated("peertimeout must be a positive integer."));
}
// Sanity check argument for min fee for including tx in block
// TODO: Harmonize which arguments need sanity checking and where that happens
if (args.IsArgSet("-blockmintxfee")) {
if (!ParseMoney(args.GetArg("-blockmintxfee", ""))) {
return InitError(AmountErrMsg("blockmintxfee", args.GetArg("-blockmintxfee", "")));
}
}
nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp);
if (!g_wallet_init_interface.ParameterInteraction()) return false;
// Option to startup with mocktime set (used for regression testing):
SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
if (args.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS))
nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM);
// Also report errors from parsing before daemonization
{
kernel::Notifications notifications{};
ChainstateManager::Options chainman_opts_dummy{
.chainparams = chainparams,
.datadir = args.GetDataDirNet(),
.notifications = notifications,
};
auto chainman_result{ApplyArgsManOptions(args, chainman_opts_dummy)};
if (!chainman_result) {
return InitError(util::ErrorString(chainman_result));
}
BlockManager::Options blockman_opts_dummy{
.chainparams = chainman_opts_dummy.chainparams,
.blocks_dir = args.GetBlocksDirPath(),
.notifications = chainman_opts_dummy.notifications,
};
auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)};
if (!blockman_result) {
return InitError(util::ErrorString(blockman_result));
}
}
return true;
}
这段代码是比特币中的初始化函数
AppInitParameterInteraction?的一部分,用于处理命令行参数之间的交互和设置一些内部标志。让我们逐步解读这段代码:
以下是代码的详细解读:
1. 获取链参数:
const CChainParams& chainparams = Params(); 获取当前链的参数,比特币支持多条链,这里通过 Params() 函数获取当前链的参数。
2. 网络配置设置:
ChainType chain = args.GetChainType(); 通过 args 参数管理器获取当前链的类型。
如果链的类型是 SIGNET,则记录关于 MessageStart 的信息到日志中。
3. 错误检查:
遍历命令行参数中仅适用于特定区块链网络的选项(
args.GetUnsuitableSectionOnlyArgs())。
如果这些网络特定的选项在配置文件的默认部分设置,但在命令行或链配置文件的相应部分未被覆盖,则生成错误信息。
4. 错误处理:
如果存在错误信息,通过 InitError 函数返回错误并终止初始化过程。
5. 警告检查:
遍历命令行参数中未被识别的配置文件部分(
args.GetUnrecognizedSections())。
如果存在未被识别的部分,生成相应的警告信息。
6. 目录存在性检查:
检查指定的区块数据目录是否存在,如果不存在则返回错误。
7. 启用的过滤器类型设置:
解析和验证启用的区块过滤器类型,根据 -blockfilterindex 参数设置。
如果参数为空或为 "1",则启用所有区块过滤器类型;如果为 "0",则禁用所有区块过滤器类型。
如果参数为其他值,将按照参数列表启用相应的区块过滤器类型。
8. 设置 P2P 协议版本:
如果启用了 BIP324 v2 传输协议(-v2transport 参数),则设置本地服务标志为 NODE_P2P_V2。
9. 设置 Compact Filters 标志:
如果启用了 -peerblockfilters 且基本过滤器索引已启用,设置本地服务标志为 NODE_COMPACT_FILTERS。
10. 配置文件参数检查:
这个函数主要负责处理命令行参数之间的交互,设置一些内部标志,检查配置文件的正确性,以及对一些系统和网络相关的参数进行合法性检查。如果在这个过程中遇到错误,函数将返回 false 并带有相应的错误信息。
总体而言,
AppInitParameterInteraction 函数是比特币初始化过程中的一个重要步骤,确保比特币节点在启动时正确配置和处理各种参数,以确保其正常运行和参与网络。
下面需要重点解释说明一下这个对象:const ArgsManager& args
ArgsManager:参数管理类的说明
class ArgsManager
{
public:
/**
* Flags controlling how config and command line arguments are validated and
* interpreted.
*/
enum Flags : uint32_t {
ALLOW_ANY = 0x01, //!< disable validation
// ALLOW_BOOL = 0x02, //!< unimplemented, draft implementation in #16545
// ALLOW_INT = 0x04, //!< unimplemented, draft implementation in #16545
// ALLOW_STRING = 0x08, //!< unimplemented, draft implementation in #16545
// ALLOW_LIST = 0x10, //!< unimplemented, draft implementation in #16545
DISALLOW_NEGATION = 0x20, //!< disallow -nofoo syntax
DISALLOW_ELISION = 0x40, //!< disallow -foo syntax that doesn't assign any value
DEBUG_ONLY = 0x100,
/* Some options would cause cross-contamination if values for
* mainnet were used while running on regtest/testnet (or vice-versa).
* Setting them as NETWORK_ONLY ensures that sharing a config file
* between mainnet and regtest/testnet won't cause problems due to these
* parameters by accident. */
NETWORK_ONLY = 0x200,
// This argument's value is sensitive (such as a password).
SENSITIVE = 0x400,
COMMAND = 0x800,
};
protected:
struct Arg
{
std::string m_help_param;
std::string m_help_text;
unsigned int m_flags;
};
mutable RecursiveMutex cs_args;
common::Settings m_settings GUARDED_BY(cs_args);
std::vector<std::string> m_command 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::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args);
bool m_accept_any_command GUARDED_BY(cs_args){true};
std::list<SectionInfo> m_config_sections GUARDED_BY(cs_args);
std::optional<fs::path> m_config_path GUARDED_BY(cs_args);
mutable fs::path m_cached_blocks_path GUARDED_BY(cs_args);
mutable fs::path m_cached_datadir_path GUARDED_BY(cs_args);
mutable fs::path m_cached_network_datadir_path GUARDED_BY(cs_args);
[[nodiscard]] bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false);
/**
* Returns true if settings values from the default section should be used,
* depending on the current network and whether the setting is
* network-specific.
*/
bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args);
public:
/**
* Get setting value.
*
* Result will be null if setting was unset, true if "-setting" argument was passed
* false if "-nosetting" argument was passed, and a string if a "-setting=value"
* argument was passed.
*/
common::SettingsValue GetSetting(const std::string& arg) const;
/**
* Get list of setting values.
*/
std::vector<common::SettingsValue> GetSettingsList(const std::string& arg) const;
ArgsManager();
~ArgsManager();
/**
* Select the network in use
*/
void SelectConfigNetwork(const std::string& network);
[[nodiscard]] bool ParseParameters(int argc, const char* const argv[], std::string& error);
/**
* Return config file path (read-only)
*/
fs::path GetConfigFilePath() const;
void SetConfigFilePath(fs::path);
[[nodiscard]] bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false);
/**
* Log warnings for options in m_section_only_args when
* they are specified in the default section but not overridden
* on the command line or in a network-specific section in the
* config file.
*/
std::set<std::string> GetUnsuitableSectionOnlyArgs() const;
/**
* Log warnings for unrecognized section names in the config file.
*/
std::list<SectionInfo> GetUnrecognizedSections() const;
struct Command {
/** The command (if one has been registered with AddCommand), or empty */
std::string command;
/**
* If command is non-empty: Any args that followed it
* If command is empty: The unregistered command and any args that followed it
*/
std::vector<std::string> args;
};
/**
* Get the command and command args (returns std::nullopt if no command provided)
*/
std::optional<const Command> GetCommand() const;
/**
* Get blocks directory path
*
* @return Blocks path which is network specific
*/
fs::path GetBlocksDirPath() const;
/**
* Get data directory path
*
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
*/
fs::path GetDataDirBase() const { return GetDataDir(false); }
/**
* Get data directory path with appended network identifier
*
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
*/
fs::path GetDataDirNet() const { return GetDataDir(true); }
/**
* Clear cached directory paths
*/
void ClearPathCache();
/**
* Return a vector of strings of the given argument
*
* @param strArg Argument to get (e.g. "-foo")
* @return command-line arguments
*/
std::vector<std::string> GetArgs(const std::string& strArg) const;
/**
* Return true if the given argument has been manually set
*
* @param strArg Argument to get (e.g. "-foo")
* @return true if the argument has been set
*/
bool IsArgSet(const std::string& strArg) const;
/**
* Return true if the argument was originally passed as a negated option,
* i.e. -nofoo.
*
* @param strArg Argument to get (e.g. "-foo")
* @return true if the argument was passed negated
*/
bool IsArgNegated(const std::string& strArg) const;
/**
* Return string argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param strDefault (e.g. "1")
* @return command-line argument or default value
*/
std::string GetArg(const std::string& strArg, const std::string& strDefault) const;
std::optional<std::string> GetArg(const std::string& strArg) const;
/**
* Return path argument or default value
*
* @param arg Argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir")
* @param default_value Optional default value to return instead of the empty path.
* @return normalized path if argument is set, with redundant "." and ".."
* path components and trailing separators removed (see patharg unit test
* for examples or implementation for details). If argument is empty or not
* set, default_value is returned unchanged.
*/
fs::path GetPathArg(std::string arg, const fs::path& default_value = {}) const;
/**
* Return integer argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param nDefault (e.g. 1)
* @return command-line argument (0 if invalid number) or default value
*/
int64_t GetIntArg(const std::string& strArg, int64_t nDefault) const;
std::optional<int64_t> GetIntArg(const std::string& strArg) const;
/**
* Return boolean argument or default value
*
* @param strArg Argument to get (e.g. "-foo")
* @param fDefault (true or false)
* @return command-line argument or default value
*/
bool GetBoolArg(const std::string& strArg, bool fDefault) const;
std::optional<bool> GetBoolArg(const std::string& strArg) const;
/**
* Set an argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param strValue Value (e.g. "1")
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetArg(const std::string& strArg, const std::string& strValue);
/**
* Set a boolean argument if it doesn't already have a value
*
* @param strArg Argument to set (e.g. "-foo")
* @param fValue Value (e.g. false)
* @return true if argument gets set, false if it already had a value
*/
bool SoftSetBoolArg(const std::string& strArg, bool fValue);
// Forces an arg setting. Called by SoftSetArg() if the arg hasn't already
// been set. Also called directly in testing.
void ForceSetArg(const std::string& strArg, const std::string& strValue);
/**
* Returns the appropriate chain type from the program arguments.
* @return ChainType::MAIN by default; raises runtime error if an invalid
* combination, or unknown chain is given.
*/
ChainType GetChainType() const;
/**
* Returns the appropriate chain type string from the program arguments.
* @return ChainType::MAIN string by default; raises runtime error if an
* invalid combination is given.
*/
std::string GetChainTypeString() const;
/**
* Add argument
*/
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat);
/**
* Add subcommand
*/
void AddCommand(const std::string& cmd, const std::string& help);
/**
* Add many hidden arguments
*/
void AddHiddenArgs(const std::vector<std::string>& args);
/**
* Clear available arguments
*/
void ClearArgs() {
LOCK(cs_args);
m_available_args.clear();
m_network_only_args.clear();
}
/**
* Get the help string
*/
std::string GetHelpMessage() const;
/**
* Return Flags for known arg.
* Return nullopt for unknown arg.
*/
std::optional<unsigned int> GetArgFlags(const std::string& name) const;
/**
* Get settings file path, or return false if read-write settings were
* disabled with -nosettings.
*/
bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const;
/**
* Read settings file. Push errors to vector, or log them if null.
*/
bool ReadSettingsFile(std::vector<std::string>* errors = nullptr);
/**
* Write settings file or backup settings file. Push errors to vector, or
* log them if null.
*/
bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const;
/**
* Get current setting from config file or read/write settings file,
* ignoring nonpersistent command line or forced settings values.
*/
common::SettingsValue GetPersistentSetting(const std::string& name) const;
/**
* Access settings with lock held.
*/
template <typename Fn>
void LockSettings(Fn&& fn)
{
LOCK(cs_args);
fn(m_settings);
}
/**
* Log the config file options and the command line arguments,
* useful for troubleshooting.
*/
void LogArgs() const;
private:
/**
* Get data directory path
*
* @param net_specific Append network identifier to the returned path
* @return Absolute path on success, otherwise an empty path when a non-directory path would be returned
*/
fs::path GetDataDir(bool net_specific) const;
/**
* Return -regtest/-signet/-testnet/-chain= setting as a ChainType enum if a
* recognized chain type was set, or as a string if an unrecognized chain
* name was set. Raise an exception if an invalid combination of flags was
* provided.
*/
std::variant<ChainType, std::string> GetChainArg() const;
// Helper function for LogArgs().
void logArgsPrefix(
const std::string& prefix,
const std::string& section,
const std::map<std::string, std::vector<common::SettingsValue>>& args) const;
};
这是一个 C++ 类的定义,名为 ArgsManager。下面逐个部分进行详细解读:
1. Flags 枚举:
enum Flags : uint32_t 定义了一系列用于配置和命令行参数验证及解释的标志。这些标志用于指示参数的类型、允许的值等。
2. Arg 结构体:
struct Arg 包含了参数的帮助信息 (m_help_param)、帮助文本 (m_help_text) 和标志 (m_flags)。
3. 成员变量:
mutable RecursiveMutex cs_args; 用于保护多线程操作的互斥锁。
common::Settings m_settings GUARDED_BY(cs_args); 存储参数的设置值。
std::vector<std::string> m_command GUARDED_BY(cs_args); 存储命令行参数。
其他成员变量用于存储配置文件路径、网络标识、可用参数、命令等信息。
4. 成员函数:
bool ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys = false); 从输入流中读取配置文件。
bool UseDefaultSection(const std::string& arg) const EXCLUSIVE_LOCKS_REQUIRED(cs_args); 判断是否使用默认配置。
common::SettingsValue GetSetting(const std::string& arg) const; 获取设置值。
其他函数用于获取不同类型的参数值,如路径、整数、布尔值等。
void SelectConfigNetwork(const std::string& network); 选择配置文件中的网络标识。
bool ParseParameters(int argc, const char* const argv[], std::string& error); 解析命令行参数。
fs::path GetConfigFilePath() const; 获取配置文件路径。
其他函数用于读取、写入、获取配置文件信息等。
5. 命令处理:
struct Command 用于表示命令及其参数。
std::optional<const Command> GetCommand() const; 获取命令及其参数。
6. 路径处理:
fs::path GetDataDir(bool net_specific) const; 获取数据目录路径。
7. 参数设置:
void AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat); 添加参数。
void AddCommand(const std::string& cmd, const std::string& help); 添加命令。
void ClearArgs(); 清除可用参数。
std::string GetHelpMessage() const; 获取帮助信息。
std::optional<unsigned int> GetArgFlags(const std::string& name) const; 获取参数标志。
8. 文件读写:
bool GetSettingsPath(fs::path* filepath = nullptr, bool temp = false, bool backup = false) const; 获取设置文件路径。
bool ReadSettingsFile(std::vector<std::string>* errors = nullptr); 读取设置文件。
bool WriteSettingsFile(std::vector<std::string>* errors = nullptr, bool backup = false) const; 写入设置文件。
9. 链类型处理:
ChainType GetChainType() const; 获取链类型。
std::string GetChainTypeString() const; 获取链类型的字符串表示。
std::variant<ChainType, std::string> GetChainArg() const; 获取链参数。
10. 参数日志:
void LogArgs() const; 记录配置文件和命令行参数,用于排查问题。
11. 锁定设置:
template <typename Fn> void LockSettings(Fn&& fn) 通过锁定互斥锁执行一段代码。
该类的目的是提供一个灵活的参数管理器,支持命令行参数的解析、配置文件的读写、参数值的获取等功能,同时支持多线程环境下的安全操作。类中包含了丰富的方法和成员变量,以满足比特币程序对参数管理的需求。
如果看不懂,下面是更加超详细的解读:
这个类设计的目标是提供灵活、易用的接口,方便地处理命令行参数和配置文件,支持多线程环境下的安全访问。通过对各种类型的参数和配置的支持,以及对网络特定设置的处理,使得该类能够适应不同的使用场景和需求。ArgsManager 类提供了丰富的功能,包括参数的添加、获取、设置,命令的处理,配置文件的读写等。通过使用互斥锁保证在多线程环境中的安全操作。该类是比特币程序中参数管理的一个关键组件,为比特币提供了配置的灵活性和可维护性。