最近加班到了新高度,晚上终于有点时间了,继续讲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