比特币源码研读(9)-main函数

上文介绍了读取配置文件函数ReadConfigFile。

try

{

    gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));

}

catch (const std::exception& e)

{

    fprintf(stderr,"Error reading configuration file: %s\n", e.what());

    return false;

}

当读取配置文件ReadConfigFile失败时,将进入catch,并打印错误信息,函数返回。

下面我们讲解参数配置。

一,获取网络参数

先介绍一点比特币知识:比特币网络分为主网、测试网以及私有网三种网络,其中主网就是我们现在使用的正式运行的可进行实际交易的网络,在其上我们可以实现物品的交易与服务;测试网顾名思义即为公共测试网络,因为其实测试网,所以,其上的信息是可以重设的;私有网的难度很低,很容易产生块,所以开发者一般在私有网中开展应用的开发与自测试。三个网络的的英文名分别为:

主网:Main network

测试网:Testnet

私有网:Regressiontest

下面我们看代码

// Check for-testnet or -regtest parameter (Params() calls are only valid after thisclause)

try {

    SelectParams(ChainNameFromCommandLine());

} catch (const std::exception& e) {

    fprintf(stderr, "Error:%s\n", e.what());

    return false;

}

注释中提到,要先检查是testnet还是regtext参数,testnet就是测试网络,regtest就是私有网络。

代码中,使用try catch异常处理机制实现比特币的网络设置工作(try catch的介绍见C++异常处理)。

函数SelectParams的输入参数是函数ChainNameFromCommandLine的返回值。那ChainNameFromCommandLine函数是什么呢?返回值又是什么呢?ChainNameFromCommandLine函数的定义在文件chainparamsbase.cpp下,其函数实现如下:

std::string ChainNameFromCommandLine()

{

    bool fRegTest =gArgs.GetBoolArg("-regtest", false);

    bool fTestNet =gArgs.GetBoolArg("-testnet", false);

    if (fTestNet&& fRegTest)

            throwstd::runtime_error("Invalid combination of -regtest and -testnet.");

    if (fRegTest)

        returnCBaseChainParams::REGTEST;

    if (fTestNet)

        returnCBaseChainParams::TESTNET;

    return CBaseChainParams::MAIN;

}

GetBoolArg(const std::string& strArg, bool fDefault)函数,判断是否为strArg,如果是则返回true,如果不是则返回false。

ChainNameFromCommandLine函数流程图如下:


比特币源码研读(9)-main函数_第1张图片

1,首先获取fRegTest与fTestNet参数。

2,如果两个参数都设置了,因为一个比特币程序不可能同时存在两个网络,所以,程序将异常退出,同时抛出异常错误,由之前的try catch模块处理,打印异常错误提示信息;

3,MAIN,TESTNET,REGTEST是静态变量,定义如下:

    const std::string CBaseChainParams::MAIN = "main";//主网

    const std::string CBaseChainParams::TESTNET = "test";//测试网

    const std::string CBaseChainParams::REGTEST = "regtest";//私有网

4,如果设置了私有网,则返回,CBaseChainParams::REGTEST

5,如果设置了测试网,则返回,CBaseChainParams::TESTNET

6,如果都没设置,则返回主网,CBaseChainParams::MAIN

二,网络参数设置

当获得了网络参数以后,将参数传给函数SelectParams,函数在文件chainparams.cpp中,其函数实现如下:

void SelectParams(const std::string& network)

{

    SelectBaseParams(network);//设置globalChainBaseParams

    globalChainParams= CreateChainParams(network);//设置globalChainParams

}

其中有两个函数,SelectBaseParams(network)与CreateChainParams(network)

函数SelectBaseParams(network)在chainparamsbase.cpp中实现,函数实现如下

void SelectBaseParams(const std::string& chain)

{

globalChainBaseParams = CreateBaseChainParams(chain);

}

可以看出,这两个函数:

CreateBaseChainParams(chain);返回globalChainBaseParams

CreateChainParams(network),返回globalChainParams;

两个函数的实现基本是一致的,CreateBaseChainParams是设置网络端口等基础参数,CreateChainParams是设置链路的主要参数

1)设置globalChainBaseParams网络参数(网络端口等基本参数)

调用函数SelectBaseParams(network),这个函数定义在chainparamsbase.cpp中,其函数实现如下:

void SelectBaseParams(const std::string& chain)

{

    globalChainBaseParams = CreateBaseChainParams(chain);

}

该函数调用了函数CreateBaseChainParams(chain),这个函数也定义在chainparamsbase.cpp文件中,其函数实现如下:

std::unique_ptrCreateBaseChainParams(const std::string& chain)

{

    if (chain ==CBaseChainParams::MAIN)

        returnstd::unique_ptr(new CBaseMainParams());

    else if (chain ==CBaseChainParams::TESTNET)

        returnstd::unique_ptr(new CBaseTestNetParams());

    else if (chain ==CBaseChainParams::REGTEST)

        returnstd::unique_ptr(new CBaseRegTestParams());

    else

        throw std::runtime_error(strprintf("%s:Unknown chain %s.", __func__, chain));

}

其中的CBaseChainParams是一个类定义,定义如下:

/**

* CBaseChainParamsdefines the base parameters (shared between bitcoin-cli and bitcoind) * of agiven instance of the Bitcoin system.

*/

class CBaseChainParams

{

    public:

        /** BIP70 chainname strings (main, test or regtest) */

        static conststd::string MAIN;

        static conststd::string TESTNET;

        static conststd::string REGTEST;

        conststd::string& DataDir() const { return strDataDir; }

        int RPCPort()const { return nRPCPort; }

   protected:

        CBaseChainParams() {}

        int nRPCPort;

        std::stringstrDataDir;

};

看其注释,可以知道,这个类定义了比特币客户端的一些基本参数

CreateBaseChainParams函数的流程图如下:


比特币源码研读(9)-main函数_第2张图片

1.如果输入参数是主链MAIN,则创建并一个主链类CBaseMainParams

2.如果输入参数是测试链TESTNET,则创建并一个主链类CBaseTestNetParams

3.如果输入参数是私有链REGTEST,则创建并一个主链类CBaseRegTestParams

4,如果都不是,则打印消息,提示错误。

CBaseMainParams()函数如下:

class CBaseMainParams : public CBaseChainParams

{

    public:

    CBaseMainParams()

    {

        nRPCPort =8332;

    }

};

CBaseTestNetParams函数如下:

class CBaseTestNetParams : public CBaseChainParams

{

    public:

        CBaseTestNetParams()

        {

            nRPCPort =18332;

            strDataDir ="testnet3";

        }

};

CBaseRegTestParams函数如下:

class CBaseRegTestParams : public CBaseChainParams

{

    public:

    CBaseRegTestParams()

        {

            nRPCPort =18443;

            strDataDir ="regtest";

        }

};

可见,CBaseMainParams端口是8332,CBaseTestNetParams端口是18332,CBaseRegTestParams端口是18443.

注意:这里的私有链端口与板卡0.14不一样。0.14的版本是18332.

2) 设置globalChainParams网络参数(当前链的参数)

CreateChainParams的函数结构如下,与CreateBaseChainParams函数 流程是一样的,不在赘述

std::unique_ptr CreateChainParams(conststd::string& chain)

{

    if (chain ==CBaseChainParams::MAIN)

        returnstd::unique_ptr(new CMainParams());

    else if (chain ==CBaseChainParams::TESTNET)

        returnstd::unique_ptr(new CTestNetParams());

    else if (chain ==CBaseChainParams::REGTEST)

        returnstd::unique_ptr(new CRegTestParams());

    throwstd::runtime_error(strprintf("%s: Unknown chain %s.", __func__,chain));

}

其实现与我们刚看到的BaseParams有点类似,只不过其少了Base单词而已,我们可以这样理解,在执行完链的基本参数设置后,比特币程序将设置相应链的主要参数了。从CreatChainParams函数实现我们可以看到,其实现与CreateBaseChainParams是一样的,都是根据链名称获取相应的链参数对象,只不过此处的链路参数类中包含的参数信息更详细些。主链、测试链以及私有链对应的类分别为CMainParams、CTestNetParams、CRegTestParamsstatic,这3个类的定义位于src/chainparams.cpp中,它们均继承了CChainParams类,通过CChainParams可知链参数类主要实现共识参数、CDNSSeedData种子数据、默认端口、创世块信息以及链交易数据等参数的设置。

我们已CMainParams为例,说明共识参数和创世块信息参数(这部分参考菜菜子同学的)

1)共识参数

标注consensus的都是共识参数


比特币源码研读(9)-main函数_第3张图片

consensus.nSubsidyHalvingInterval = 210000;                            //区块奖励减半间隔

consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");  //最小算力

consensus.BIP34Height = 227931;                                                                    //BIP34协议在区块227931生效。

consensus.nPowTargetTimespan = 14 * 24 * 60* 60;                                 // two weeks

consensus.nPowTargetSpacing = 10 * 60;                                    //算力修改间隔,即10分钟

consensus.nMinerConfirmationWindow = 2016;            // nPowTargetTimespan / nPowTargetSpacing

上面三个参数,联合看。

每14天改变一次挖矿难度,每10分钟出一个块,并修改一次算力,也即每2016个区块修改一次挖矿难度

2)创世块参数

genesis = CreateGenesisBlock(1231006505, 2083236893, 0x1d00ffff, 1, 50 * COIN);            //第一个创世块奖励为50个比特币。

至此,程序根据用户输入的网络类型参数完成了比特币运行网络的设置。在这段代码中,我知道了私有网络,以前听得最多的是主网和测试网,而私有网或私有链基本很少听到,在这段代码中我知道了私有链是开发团队在开发时使用的网络,因为其挖矿难度很低,很容易进行程序的调试与功能试验。进而让我明白了一些区块链项目为什么会说在XX时刻要进入测试网阶段,然后再是最终的主网运行阶段。因此区块链开发过程应该是这样的:


比特币源码研读(9)-main函数_第4张图片

今天终于看到比特币的基本配置参数了,从代码层理解了一些挖矿难度调整等信息。

/////////////////////////////////////////////////配置网络参数完毕分析完毕///////////////////////////////////////

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

以下是广告:

我们区块链研习社已创建“区块链研习社币圈交流”小密圈”,在小密圈中,我们将带领大家一起学习区块链的原理与投资,还将提供区块链基本原理解答、交易所注册与交易操作、ICO交易与操作、投资分析、风险分析等内容。

你可能感兴趣的:(比特币源码研读(9)-main函数)