本节主要讲述比特币初始化部分的网络初始化、检测参数完整性、守护程序以及软服务的默认开启。
首先一起来看网络初始化,该部分代码为:
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
if (!SelectParamsFromCommandLine()) { //获取对应的网络对象参数(包括基础参数如文件目录、prc端口及网络参数)
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
return false;}
该代码的位置为src/bitcoind.cpp的AppInit()函数,该代码的功能就是根据配置文件或者命令行中标明的网络(main、test、regtest)获取相应的参数,实现该功能的函数就是SelectParamsFromCommandLine(),下面来分析该函数(位置:src/chainparams.cpp):
bool SelectParamsFromCommandLine()
{
CBaseChainParams::Network network = NetworkIdFromCommandLine();//获取mapArg中标明的网络对象(是主网、、测试网、还是回归测试网)
if (network == CBaseChainParams::MAX_NETWORK_TYPES) //如果mapArg中(配置文件或者命令行)中同时声明了测试与回归测试网,就报错
return false;
SelectParams(network); //获取对应网络的参数
return true;
}
该代码实现了两部分功能, NetworkIdFromCommandLine()函数获取网络对象的实体,然后使用 SelectParams(network)函数获取该网络对象的相关参数。我们首先对 NetworkIdFromCommandLine()函数进行解析(位置:src/chainparamsbase.cpp):
CBaseChainParams::Network NetworkIdFromCommandLine() //获取网络名称
{
bool fRegTest = GetBoolArg("-regtest", false); //查看mapArg中是否声明regtest
bool fTestNet = GetBoolArg("-testnet", false);//查看mapArg中是否声明testnet
if (fTestNet && fRegTest)//若同时声明regtest与testnet就会报错
return CBaseChainParams::MAX_NETWORK_TYPES;
if (fRegTest)//若只声明regtest,返回对应的CBaseChainParams类中对应的参数
return CBaseChainParams::REGTEST;
if (fTestNet)//若只声明testnet,返回对应的CBaseChainParams类中对应的参数
return CBaseChainParams::TESTNET;
return CBaseChainParams::MAIN;//以上两种网络模式若均未声明,那么就是main网,返回对应的CBaseChainParams对象}
本函数实现的功能是返回配置文件或命令行指定网络模式main、test、regtest)的CBaseChainParams对象,CBaseChainParams是定义在chainparamsbase.h中的类,有兴趣可自行查看,该类主要定义网络对象的基础参数,包括特定网络对象的nRPC端口(请自行百度)与文件目录,以及返回这两参数的方法。下面解析本函数,首先使用GetBoolArg()函数查看配置文件或命令行是否指定testnet或者regtest网络,根据指定情况返回对应的CBaseChainParams对象,如果两个均指定返回错误,如果两个均未指定,返回mainnet对应的CBaseChainParams类中的参数。
下面对 SelectParams(network)函数进行解析(位置:src/chainparams.cpp)
void SelectParams(CBaseChainParams::Network network) {
SelectBaseParams(network);//获取网络对象的基础参数,包括RPC端口与文件目录
pCurrentParams = &Params(network);//获取网络对象的网络参数,包括网络端口,创世区块的哈希,挖矿难度等等
}
该函数的作用是获得对应网络类型的相关参数,包括网络的基础参数(如PRC port、文件目录等)与网络连接参数(如网络端口、创世区块信息等),以上功能由 SelectBaseParams(network)与Params(network)两个函数完成,下面对这两个函数进行详解。
** SelectBaseParams(network) 位置:src/chainparamsbase.cpp
void SelectBaseParams(CBaseChainParams::Network network)//根据网络对象,返回对应的基础参数
{
switch (network) {
case CBaseChainParams::MAIN:
pCurrentBaseParams = &mainParams;
break;
case CBaseChainParams::TESTNET:
pCurrentBaseParams = &testNetParams;
break;
case CBaseChainParams::REGTEST:
pCurrentBaseParams = ®TestParams;
break;
default:
assert(false && "Unimplemented network");
return;
}
}
该函数的传入参数为CBaseChainParams::Network类型,用以告知函数需要返回那个网络类型的基础参数。函数功能主要由case语句实现,通过case对参数进行判断,然后返回与之对应的网络基础参数(如:&mainParams,该对象是由CBaseChainParams的自类定义的,其中定义了mainnet中需要的具体的 网络基础参数,类外两个对象定义类似)
**Params(network) 位置:src/chainparams.cpp
CChainParams &Params(CBaseChainParams::Network network) {//根据不同的网络对象,返回对应的网络参数
switch (network) {
case CBaseChainParams::MAIN:
return mainParams;
case CBaseChainParams::TESTNET:
return testNetParams;
case CBaseChainParams::REGTEST:
return regTestParams;
default:
assert(false && "Unimplemented network");
return mainParams;
}
}
该函数的传入参数也为CBaseChainParams::Network类型,用以告知函数返回什么类型的网络连接参数,函数功能的实现与SelectBaseParams(CBaseChainParams::Network network)的实现类似,需要注意的是,该函数返回的是CChainParams类型的对象(CChainParams类主要对网络的连接需要的参数作出了定义,然后由其子类给出各种网络类型的具体定义),如(mainParams,该对象由CChainParams的子类定义,类中给出了mainnet需要的参数的具体的值)
网络初始化部分到这里基本完毕,下面对AppInit剩余部分进行解析(先上代码):
bool fCommandLine = false;
for (int i = 1; i < argc; i++)
if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:")) //判断PRC命令是否以'-',或者''打头,以及'bitcoin:'是命令的开始
fCommandLine = true;
if (fCommandLine)
{
fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n");
exit(1);
}
该部分程序用于检查键入的PRC命令是否合乎规则,IsSwitchChar(argvi)判断命令是否以'-'或者''打头(这里把''也当做正确的命令写法是因为在之前参数处理的时候,我们会把''替换为'-'仅限于Windows),boost::algorithm::istarts_with(argv[i], "bitcoin:"))判断命令开始是否为'bitcoin:',如果不满足以上两条件,就返回错误。下面我们简单看一下IsSwitchChar(argvi)函数:
{
#ifdef WIN32
return c == '-' || c == '/';
#else
return c == '-';
#endif
}
咋样,是不是很简单,传入一个字符c,判断c是不是'-'或者'\'(Windows)
下面继续对AppInit解析:
#ifndef WIN32
fDaemon = GetBoolArg("-daemon", false); //判断是否定义了"-daemon"命令
if (fDaemon)
{
fprintf(stdout, "Bitcoin server starting\n");
// Daemonize
pid_t pid = fork(); //申请子进程
if (pid < 0) //子进程id小于0,报错
{
fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
return false;
}
if (pid > 0) // Parent process, pid is child process id ?//子进程ID大于0,直接返回True
{
return true;
}
// Child process falls through to rest of initialization
pid_t sid = setsid();?//申请新的回话
if (sid < 0)
fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
}
这部分适合守护程序相关的,GetBoolArg("-daemon", false)用于判断mapArgs中知否包含"-daemon"命令,即判断是否用户是否开起了守护程序。如果开启了守护程序,就进入if语句内部。(if语句是通过开启子进程的方式开启守护程序,与区块链架构联系不大,有兴趣者自行百度)
#endif
SoftSetBoolArg("-server", true);
fRet = AppInit2(threadGroup, scheduler);
}
SoftSetBoolArg("-server", true)函数的功能首先判断mapArgs中是否包含了"-server"命令,如果未包含,则加入并赋值为"true",下面来看一下它的代码:
{
if (fValue)
return SoftSetArg(strArg, std::string("1"));//判断mapArgs中是否包含strArg,如果包含返回false,否则将该命令加入,并赋值为“1”
else
return SoftSetArg(strArg, std::string("0"));//判断mapArgs中是否包含strArg,如果包含返回false,否则将该命令加入,并赋值为“0”
else
}
bool SoftSetArg(const std::string& strArg, const std::string& strValue)
{
if (mapArgs.count(strArg)) //判断mapArgs中是否包含strArg
return false;
mapArgs[strArg] = strValue; //向mapArgs中加入strArg命令并将定义好的strValue赋值给命令strArg
return true;
}
第一个函数是我们要讲解的主函数,传入的参数就是strArg(代表某RPC命令)与fValue(代表要给strArg赋的值),程序是一个简单地判断句,判断体中的函数是上面的第二个函数,其实现的功能为判断mapArgs中是否包含命令strArg,如果已经包含直接返回false,如果不包含将该命令加入并将定义好的值strValue赋给该命令(由于该函数比较简单请自行分析), 由此得知 SoftSetBoolArg("-server", true)的意思就是默认开启“server”服务
AppInit2(threadGroup, scheduler)将在后续文章中解析,感谢阅读