比特币源码初级阅读一(3)

本节主要讲述比特币初始化部分的网络初始化、检测参数完整性、守护程序以及软服务的默认开启。

首先一起来看网络初始化,该部分代码为:

       // 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 = &regTestParams;
        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)将在后续文章中解析,感谢阅读

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容