美文网首页区块链研习社
比特币源码研读7-程序入口函数解析(6)

比特币源码研读7-程序入口函数解析(6)

作者: Jacky_2c9f | 来源:发表于2017-11-26 18:18 被阅读0次

    上一节提到了一个问题:比特币默认的日志输出文件是哪个? 不知道大家找到了没,现在答案公布如下:

    如果有看过我的第一篇文章“比特币源码研读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

    相关文章

      网友评论

        本文标题:比特币源码研读7-程序入口函数解析(6)

        本文链接:https://www.haomeiwen.com/subject/diwyvxtx.html