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

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

作者: Jacky_2c9f | 来源:发表于2018-03-05 00:10 被阅读0次

    最近加班到了新高度,晚上终于有点时间了,继续讲AppInitParameterInteraction 函数的 step3。

    首先一条华丽丽的分割线,写着 Step 3: parameter-to-internal-flags,即用于内部标志的参数。

    一. 内部标志参数

    1. DEBUG 标志参数

    首先判断对哪些目录进行调试日志的写入。

    特殊情况: 假如 -debug=0 或者 设置了 -nodebug参数,那么关闭调试日志。

    // ********************************************************* Step 3: parameter-to-internal-flags if (gArgs.IsArgSet("-debug")) { // Special-case: if -debug=0/-nodebug is set, turn off debugging messages const std::vector categories = gArgs.GetArgs("-debug");

            if (find(categories.begin(), categories.end(), std::string("0")) == categories.end()) {

                for (const auto& cat : categories) {

                    uint32_t flag = 0;

                    if (!GetLogCategory(&flag, &cat)) {

                        InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat));

                        continue;

                    }

                    logCategories |= flag;

                }

            }

        }

    从函数GetLogCategory可以得知,所有目录都记录在 util.cpp文件的LogCategories数组,如下图,每个目录对应一个编号。

    在init.cpp 中用到的 logCategories变量用来记录所有需要调试日志的集合,类型为uint32_t,

    而上面目录中的编号每一个都对应32位中一位,所以每做一次“|”操作,就相当于把当前的编号加到logCategories集合中。

    // Now remove the logging categories which were explicitly excluded

        for (const std::string& cat : gArgs.GetArgs("-debugexclude")) {

            uint32_t flag = 0;

            if (!GetLogCategory(&flag, &cat)) {

                InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat));

                continue;

            }

            logCategories &= ~flag;

        }

    上面这段代码则起到相反作用,即从集合中删除当前目录对应的编号,使用“&= ~”操作,“~"表示每一位取反,“&”表示与操作,与上反码就意味着去除当前目录编号。

    2. DEBUGNET 标志参数

    // Check for -debugnet

        if (GetBoolArg("-debugnet", false))

            InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net."));

    目前比特币网络暂不支持 -debugnet 参数,应该使用“-debug=net”代替。

    3. SOCKS 标志参数

    if (IsArgSet("-socks"))

            return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported."));

    目前程序也不支持 -socks 参数,假如设置了,那么程序将会提示初始化错误,并退出。

    只能设置 SOCKS5才行,那么什么是SOCKS5 呢? 根据搜狗百科,SOCKS5 就是一种代理协议。

    SOCKS5 是一个代理协议,它在使用 TCP/IP协议通讯的前端机器和服务器机器之间扮演一个中介角色,使得内部网中的前端机器变得能够访问Internet网中的服务器,或者使通讯更加安全。SOCKS5 服务器通过将前端发来的请求转发给真正的目标服务器, 模拟了一个前端的行为。在这里,前端和SOCKS5之间也是通过TCP/IP协议进行通讯,前端将原本要发送给真正服务器的请求发送给SOCKS5服务器,然后SOCKS5服务器将请求转发给真正的服务器。

    SOCKS5服务器在将通讯请求发送给真正服务器的过程中,对于请求数据包本身不加任何改变。SOCKS5服务器接收到真正服务器的响应后,也原样转发给前端。 因此,SOCKS5协议是一种代理协议,对于各种基于TCP/IP的应用层协议 都能够适应,几乎是万能的。它虽然不能理解自己转发的数据的内部结构,但 是它能够忠实地转发通讯包,完成协议本来要完成的功能。

    4. TOR 标志参数

    if (GetBoolArg("-tor", false))

            return InitError(_("Unsupported argument -tor found, use -onion."));

    目前程序也不支持 -tor参数,假如设置了,那么程序将会返回初始化错误。应该使用“-onion” 代替。另外关于tor(The Onion Router),之前已经有介绍,感兴趣的童鞋可以找前面的文章翻翻。这里不再赘述。

    5. BENCHMARK 标志参数

    if (GetBoolArg("-benchmark", false))

            InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench."));

    使用 "-benchmark"提示该参数将被忽略,请使用 “-debug=bench” 代替。

    6. whitelistalwaysrelay 标志参数

    if (GetBoolArg("-whitelistalwaysrelay", false))

            InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay."));

    使用 "-whitelistalwaysrelay"提示该参数将被忽略,请使用 “-whitelistrelay” 或者“whitelistforcerelay” 代替或者两者同时用之。

    7. blockminsize标志参数

    if (IsArgSet("-blockminsize"))

            InitWarning("Unsupported argument -blockminsize ignored.");

    使用 "-whitelistalwaysrelay"提示该参数将被忽略,sorry,此参数并没有替代品,就此消失与茫茫代码中。

    二. 内存池与区块索引检测参数

    注释说,检查内存池和区块索引这两个参数在开发模式下默认设置为true。

    // Checkmempool and checkblockindex default to true in regtest mode

    int ratio = std::min(std::max(GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);

        if (ratio != 0) {

            mempool.setSanityCheck(1.0 / ratio);

        }

        fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());

        fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);

    根据帮助信息中的解释,

    -checkmempool:表示每隔多少个交易进行一次sanity check。

    -checkblockindex:每隔一段时间检查mapBlockIndex、setBlockIndexCandidates、chainActive和mapBlockUnlinked变量的一致性。

    -checkpoints:该变量默认为true,表示需要验证校验点之前的区块信息是否正确;反之,如果为0,表示不需要检查,所有校验点的信息也都保存在chainparams中的checkpointdata中。

    sanity check: 表示检查 内存池(mempool)中所有交易的一致性,即没有双花,所有输入都是合法的。

    代码首先判断 chainparams中的 DefaultConsistencyChecks 是否为 true。如果为 false, ratio为 0,那么将不会做 sanity check.

    关于chainparams,之前发表的文章中也介绍过,主要是根据不同的网络(main, testnet, regtest)为参数选择不同的值。

    而对于DefaultConsistencyChecks,在main、testnet这两种模式下其返回值为false,详情可进入Chainparams.cpp文件的CMainParams与CTestNetParams类中便可以见之:fDefaultConsistencyChecks=false,regtest模式的返回值为true,同理可见于Chainparams.cpp的CRegTestParams类, 即只有在regtest网中才默认需要进行一致性检测。

    另外, 函数std::min是取而二者中的最小值, ratio 的值取决于 -checkmempool参数值与1000000的最小者,很明显,前者将小于1000000。

    tips: 关于checkpoints,即检查点,可以从以下链接了解到:

    https://bitcoin.stackexchange.com/questions/1797/what-are-checkpoints

    翻译过来大概是说,检查点是被硬编码进标准客户端的,而标准客户端会接受发生在该检查点之前的所有事务,而这些事务会被视为是有效地并且不可逆转的。假如有人尝试对在该检查点之前的区块进行硬分叉,那么该检查点不会接受该分叉。这将使得那些区块一成不变。由此可见,它的作用在于保护比特币网络免于51%攻击。

    三、哈希假设有效参数

    hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));

        if (!hashAssumeValid.IsNull())

            LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex());

        else

            LogPrintf("Validating signatures for all blocks.\n");

    hashAssumeValid 参数默认值的获取过程有以下三个步骤

    1. 获取链上共识参数

    根据chainparams.GetConsensus()可溯源到 chainparams.h文件,其定义如下:

    const Consensus::Params& GetConsensus() const { return consensus; }

    Params 则是定义在 Params.h 中的结构体该结构体定义了影响链上共识的重要参数,相信大家看到这些参数的名字心情也会很鸡冻滴如下图所示,有 hashGenesisBlock(创世区块)、nSubsidyHalvingInterval (奖励减半时间间隔)、各种BIP启动高度及哈希值、powLimit、fPowAllowMinDifficultyBlocks(工作量证明参数)等等,大家有兴趣可以自行探索。

    2. 获取默认假设有效参数值

    上面结构体中当然也有我们所需要的 defaultAssumeValid 参数,其类型为 uint256,主要是用来存储256位二进制值。

    3. 获取哈希值

    第三步就是将获取到的值通过函数GetHex转换为十六进制

    然后通过调用uint256S函数将变量转换为 uint256 对象。

    /* uint256 from const char *.

    * This is a separate function because the constructor uint256(const char*) can result

    * in dangerously catching uint256(0).

    */

    inline uint256 uint256S(const char *str)

    {

        uint256 rv;

        rv.SetHex(str);

        return rv;

    }

    最后,判断假如hashAssumeValid不为空,则假定该区块所有的父区块都有有效的签名;反之,需要校验所有区块的签名,同时两种情况都要记录日志。

    区块链研习社比特币源码研读班 Jacky

    相关文章

      网友评论

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

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