上一节提到了一个问题:比特币默认的日志输出文件是哪个? 不知道大家找到了没,现在答案公布如下:
如果有看过我的第一篇文章“比特币源码研读1——下载与编译” 就知道,我们安装的路径为个人的目录下,那么比特币默认的日志文件就在:
~/.bitcoin/debug.log
tips: 这里说明下,有一种情况是看不到debug.log这个日志文件的——你从未运行过程序,解决方案就是跑以下的命令:
bitcoind
或是
bitcoind -daemon
如果不想下载一百多G的数据文件,对于第一个命令bitcoind,直接ctrl+c即可;第二个命令的话杀掉后台进程就可以了。
好了,我们接下来继续讲解参数的初始化设置。
InitParameterInteraction(): 该函数具体实现于init.cpp文件中,主要分为7部分,先看第一部分:
(1) 绑定并监听地址
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (IsArgSet("-bind")) {
if (SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -bind set -> setting -listen=1\n", __func__);
}
if (IsArgSet("-whitebind")) {
if (SoftSetBoolArg("-listen", true))
LogPrintf("%s: parameter interaction: -whitebind set -> setting -listen=1\n", __func__);
}
就是当你显式指定一个绑定地址,就监控该地址,即使你指定了“-connect”或者“-proxy”参数。
从代码可以看出,有两种方式来绑定地址:"-bind" 和 "-whitebind",这两种的处理方式也一样,都是通过函数SoftSetBoolArg设置 参数“-listen”为 true,表示对地址进行监听。
另外,关于记录日志的函数 LogPrintf,以后会经常碰面,这里介绍一下,它是以预编译方式定义于util.h文件中。从定义中看出,真正实现打印功能的函数是 LogPrintStr, 其定义于util.h文件中。
其中,“\” 表示换行, 实现代码在util.cpp文件中:
int LogPrintStr(const std::string &str)
{
int ret = 0; // Returns total number of characters written
static std::atomic_bool fStartedNewLine(true);
std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
if (fPrintToConsole)
{
// print to console
ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
fflush(stdout);
}
else if (fPrintToDebugLog)
{
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
// buffer if we haven't opened the log yet
if (fileout == NULL) {
assert(vMsgsBeforeOpenLog);
ret = strTimestamped.length();
vMsgsBeforeOpenLog->push_back(strTimestamped);
}
else
{
// reopen the log file, if requested
if (fReopenDebugLog) {
fReopenDebugLog = false;
fs::path pathDebug = GetDataDir() / "debug.log";
if (fsbridge::freopen(pathDebug,"a",fileout) != NULL)
setbuf(fileout, NULL); // unbuffered
}
ret = FileWriteStr(strTimestamped, fileout);
}
}
return ret;
}
大概逻辑如下:
1. 通过调用函数LogTimestampStr为日志加上时间戳;
2. 根据全局变量fPrintToConsole判断是否打印到控制台,默认值为false, 即不打印;
3. 根据全局变量fPrintToDebugLog判断是否打印到日志文件debug.log,默认是true,即打印;
好了,继续InitParameterInteraction函数讲解。
(2) 连接可信任节点
if (gArgs.IsArgSet("-connect")) {
// when only connecting to trusted nodes, do not seed via DNS, or listen by default
if (SoftSetBoolArg("-dnsseed", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -dnsseed=0\n", __func__);
if (SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -connect set -> setting -listen=0\n", __func__);
}
首先判断gArgs是否有"-connect"参数,如果有,将“dnsseed”和"-listen"参数设置为false,即只有当网络连接到可信任节点时,才取消通过DNS广播方式查找节点地址,不监听默认的地址。
这里需要注意的是,全局参数对象mapArgs中如果有“-listen” 参数,即在此之前已经设置了“-listen” 参数,则不会再重新设置了,即“-listen” 参数的值就不会变了。
这也就是当你在(1)中设置了”-bind”和”-whitebind”参数,那么即使你指定了“-connect”或者“-proxy”参数也无效。
(3)代理模式
if (IsArgSet("-proxy")) {
// to protect privacy, do not listen by default if a default proxy server is specified
if (SoftSetBoolArg("-listen", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -listen=0\n", __func__);
// to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1
// to listen locally, so don't rely on this happening through -listen below.
if (SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -upnp=0\n", __func__);
// to protect privacy, do not discover addresses by default
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -proxy set -> setting -discover=0\n", __func__);
}
如注释,设置代理是为了保护隐私,假如已经指定了代理服务器,将"-listen"、"-upnp"以及"-discover"均设为false,表示只使用指定的代理服务器,不使用UPNP监听端口,不去查找默认的地址。换言之,只使用代理参数提供的监听地址及端口。
以下是微软官方网站对UPnP的解释:
通用即插即用 (UPnP) 是一种用于 PC 机和智能设备(或仪器)的常见对等网络连接的体系结构,尤其是在家庭中。UPnP 以Internet标准和技术(例如 TCP/IP、HTTP 和 XML)为基础,使这样的设备彼此可自动连接和协同工作,从而使网络(尤其是家庭网络)对更多的人成为可能。
(4)监听设置
全局变量DEFAULT_LISTEN 默认值为true, 即不设置监听,将 "-upnp"、"-discover"以及"-listenonion"(匿名地址监听)均设为false
if (!GetBoolArg("-listen", DEFAULT_LISTEN)) {
// do not map ports or try to retrieve public IP when not listening (pointless)
if (SoftSetBoolArg("-upnp", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -upnp=0\n", __func__);
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -discover=0\n", __func__);
if (SoftSetBoolArg("-listenonion", false))
LogPrintf("%s: parameter interaction: -listen=0 -> setting -listenonion=0\n", __func__);
}
tips: listenonion,即匿名地址监听,这里涉及到通信机制的一个概念:第二代洋葱路由(onion routing)。
Tor(The Onion Router)是第二代洋葱路由(onion routing)的一种实现,用户通过Tor可以在因特网上进行匿名交流。Tor专门防范流量过滤、嗅探分析,让用户免受其害。最初该项目由美国海军研究实验室赞助。2004年后期,Tor成为电子前哨基金会的一个项目。2005年后期,EFF不再赞助Tor项目,但他们继续维持Tor的官方网站。
(5)外部IP地址设置
if (IsArgSet("-externalip")) {
// if an explicit public IP is specified, do not try to find others
if (SoftSetBoolArg("-discover", false))
LogPrintf("%s: parameter interaction: -externalip set -> setting -discover=0\n", __func__);
}
如果显式指定了公共IP地址,就不用查找其他的监听地址了。
(6)区块模式设置
// disable whitelistrelay in blocksonly mode
if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)) {
if (SoftSetBoolArg("-whitelistrelay", false))
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -whitelistrelay=0\n", __func__);
}
全局变量DEFAULT_BLOCKSONLY默认值为False,只有DEFAULT_BLOCKSONLY的值为true,GetBoolArg才为true,这时才会将"-whitelistrelay"设置为false,即区块模式下白名单列表失效。
tips: 这里的blocksonly参数是比特币客户端以调试状态启动时才会使用的,我们可以从src/init.cpp里面的帮助信息函数HelpMessage中得到相关信息:
if (showDebug)
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
关于DEFAULT_BLOCKSONLY, 我们已经知道其默认值为False,即默认情况下不会只以区块模式运行。假定只在该模式下运行,那么此时全网的交易都不会被打包,钱包的交易广播功能将失效,也就是我们看到的walletbroadcast参数此时需要设置为false,否则互斥。
(7)强制白名单节点连接参数设置
// Forcing relay from whitelisted hosts implies we will accept relays from them in the first place.
if (GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
if (SoftSetBoolArg("-whitelistrelay", true))
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}
DEFAULT_WHITELISTFORCERELAY默认为true,则参数"-whitelistrelay"设置为true,即比特币网络中的信息将优先在白名单节点间传递。
至此InitParameterInteraction函数已经解读完毕。下一节将讲解初始化基本环境搭建,敬请期待!
作者:区块链研习社比特币源码研读班 Jacky