比特币源码研读之六
本文将继续参数处理其他部分源码的研读。
本文主要涉及的源码文件包括:
src/bitcond.cpp、src/util.h、src/util.cpp、src/init.h、src/init.cpp、src/chainparamsbase.h、src/chainparamsbase.cpp、src/chainparams.h、src/chainparams.cpp
一、比特币网络
比特币网络分为主网、测试网以及私有网三种网络,其中主网就是我们现在使用的正式运行的可进行实际交易的网络,在其上我们可以实现物品的交易与服务;测试网顾名思义即为公共测试网络,因为其实测试网,所以,其上的信息是可以重设的;私有网的难度很低,很容易产生块,所以开发者一般在私有网中开展应用的开发与自测试。三个网络的的英文名分别为:
主网:Main network
测试网:Testnet (v3)
私有网:Regression test
二、选择比特币网络
介绍完比特币网络的三种类型后,我们来看如下代码:
//Check for -testnet or -regtest parameter (Params() calls are only valid afterthis clause)
try{
SelectParams(ChainNameFromCommandLine());
} catch (const std::exception&e) {
fprintf(stderr,"Error: %s\n", e.what());
returnfalse;
}
首先我们来看代码注释,其注释的含义为检测-testnet或者-regtest参数。那么这两个参数是什么意思呢?它们分别是我们前面介绍的测试网和私有网。
知道了后面代码的具体功能后,我们就继续对代码进行深入剖析。注释后面的代码通过try catch异常捕捉机制实现比特币网络设置工作。在try代码块中,SelectParams函数以ChainNameFromCommandLine()返回值作为参数,SelectParams函数的声明位于src/chainparams.h中,其参数为字符串类型。那ChainNameFromCommandLine()的返回值是什么呢?
(1)获取网络名称
要知道ChainNameFromCommandLine()的返回值需进入src/chainparamsbase.cpp一看究竟。其函数实现如下:
std::string ChainNameFromCommandLine()
{
boolfRegTest = GetBoolArg("-regtest", false);
boolfTestNet = GetBoolArg("-testnet", false);
if(fTestNet && fRegTest)
throwstd::runtime_error("Invalid combination of -regtest and -testnet.");
if(fRegTest)
returnCBaseChainParams::REGTEST;
if(fTestNet)
returnCBaseChainParams::TESTNET;
returnCBaseChainParams::MAIN;
}
ChainNameFromCommandLine()函数从其名称可以看出,该函数将从命令行中获取链路的名称。其实现流程如图所示。
1)该函数首先获取"-regtest"与"-testnet"参数设置情况;
2)如果两个参数都设置了,因为一个比特币程序不可能同时存在两个网络,所以,程序将异常退出,同时抛出异常错误,由之前的try catch模块处理,打印异常错误提示信息;
3)如果只设置了回归测试,则返回CBaseChainParams::REGTEST,REGTEST为静态字符串常量,代表的是回归测试,与其并行的另两个网络描述字符串也为静态字符串常量,他们均在src/chainparamsbase.h中声明,也同时在src/chainparamsbase.cpp中定义。其声明与定义如下:
声明:
定义:
const std::string CBaseChainParams::MAIN = "main"; //主网
const std::string CBaseChainParams::TESTNET = "test"; //测试网
const std::string CBaseChainParams::REGTEST = "regtest"; //私有网
4)如果只设置了测试网络,,则返回CBaseChainParams::TESTNET;
5)如果都没有设置,则返回,则返回CBaseChainParams::MAIN。
(2)网络基本参数设置
在获得网络名称后,我们将其传给SelectParams函数,该函数的实现位于chainparams.cpp中,其函数实现如下:
void SelectParams(conststd::string& network)
{
SelectBaseParams(network);
pCurrentParams = &Params(network);
}
在该函数中首先调用SelectBaseParams函数,该函数的实现位于chainparamsbase.cpp中,其函数实现内容如下:
void SelectBaseParams(conststd::string& chain)
{
pCurrentBaseParams = &BaseParams(chain);
}
在该函数中,实现了对链参数对象pCurrentBaseParams的赋值,pCurrentBaseParams的类型为CBaseChainParams指针,其定义位于src/chainparams.cpp中,从定义可以看出pCurrentBaseParams为静态全局变量。
static CBaseChainParams* pCurrentBaseParams = 0;
CBaseChainParams类为前面提到的3种区块链基本参数的基类,3种区块链基本参数设置类分别为:CBaseMainParams、CBaseTestNetParams、CBaseRegTestParams,其定义位于src/chainparamsbase.cpp中,具体定义代码如下:
/**
*Main network主网
*/
class CBaseMainParams : publicCBaseChainParams
{
public:
CBaseMainParams()
{
nRPCPort = 8332;
}
};
static CBaseMainParams mainParams;
/**
*Testnet (v3)测试网
*/
class CBaseTestNetParams : publicCBaseChainParams
{
public:
CBaseTestNetParams()
{
nRPCPort = 18332;
strDataDir = "testnet3";
}
};
static CBaseTestNetParams testNetParams;
/*
*Regression test私有链
*/
class CBaseRegTestParams : publicCBaseChainParams
{
public:
CBaseRegTestParams()
{
nRPCPort = 18332;
strDataDir = "regtest";
}
};
static CBaseRegTestParams regTestParams;
从上述定义我们可以看到每个类的构造函数中定义了主链、测试链以及私有链使用的端口与数据目录,其端口分别为8332、18332以及18332。
在完成3种链的定义后,我们再来看BaseParams函数的实现就很容易明白其返回值的意义了:
CBaseChainParams& BaseParams(conststd::string& chain)
{
if (chain == CBaseChainParams::MAIN)
return mainParams;
else if (chain == CBaseChainParams::TESTNET)
return testNetParams;
else if (chain == CBaseChainParams::REGTEST)
return regTestParams;
else
throw std::runtime_error(strprintf("%s: Unknown chain %s.",__func__, chain));
}
BaseParams将返回对应链的基本参数对象,并赋值给pCurrentBaseParams。
(3)主要参数设置
我们最后再来看SelectParams中的pCurrentParams = &Params(network);代码。其实现与我们刚看到的BaseParams有点类似,只不过其少了Base单词而已,我们可以这样理解,在执行完链的基本参数设置后,比特币程序将设置相应链的主要参数了。从Params函数实现我们可以看到,其实现与BaseParams是一样的,都是根据链名称获取相应的链参数对象,只不过此处的链路参数类中包含的参数信息更详细些。主链、测试链以及私有链对应的类分别为CMainParams、CTestNetParams、CRegTestParamsstatic,这3个类的定义位于src/chainparams.cpp中,它们均继承了CChainParams类,通过CChainParams可知链参数类主要实现共识参数、CDNSSeedData种子数据、默认端口、创世块信息以及链交易数据等参数的设置。
这里要着重讲解的是共识参数与创世块信息参数,它们分别为:
Consensus::Params consensus;
CBlock genesis;
因为我们主要使用主网,因此以主网中的参数内容来说明我们经常听到的区块奖励减半、出块时间、创世块奖励等参数是如何设置的。
区块奖励减半间隔:consensus.nSubsidyHalvingInterval = 210000;
算力极限值:consensus.powLimit =uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
算力修改间隔:consensus.nPowTargetSpacing = 10 * 60;即10分钟
创世块genesis = CreateGenesisBlock(1231006505, 2083236893,0x1d00ffff, 1, 50 * COIN);第一个块的奖励为50个比特币
以上这些参数都是我们经常听到的名词,如果我们想创建自己命名的数字货币,只需简单修改这些参数即可,比如把区块奖励减半间隔修改为420000或其他数,创世块中比特币奖励的50该为100或其他数。所以要创建一个自己的数字货币并不难,关键在于看其是否有应用价值。
程序在每个类的定义之后,程序也定义了对应的静态链参数对象。
static CMainParamsmainParams;
staticCTestNetParams testNetParams;
staticCRegTestParams regTestParams;
Params将根据用户设置的链参数名称,将对应的链参数返回给pCurrentParams,从而完成链基本参数与主要参数的实现任务。
至此,程序根据用户输入的网络类型参数完成了比特币运行网络的设置。在这段代码中,我知道了私有网络,以前听得最多的是主网和测试网,而私有网或私有链基本很少听到,在这段代码中我知道了私有链是开发团队在开发时使用的网络,因为其挖矿难度很低,很容易进行程序的调试与功能试验。进而让我明白了一些区块链项目为什么会说在XX时刻要进入测试网阶段,然后再是最终的主网运行阶段。因此区块链开发过程应该是这样的:
以上就是本文的源码研读过程,通过源码的研读确实让我更好地理解区块链网络的运转过程,也让我清晰地明白市场上每个区块链的具体进程,这样可以让我更好地判断每个区块链产品的价值,对我投资区块链资产也有很大的帮助。
我是区块链研习社的菜菜子,我将继续深入研究比特币源码,源码研读系列也将继续更新,敬请期待!
作者:区块链研习社比特币源码研读班 菜菜子
以下是广告:
我们区块链研习社已创建“区块链研习社币圈交流”小密圈”,在小密圈中,我们将带领大家一起学习区块链的原理与投资,还将提供区块链基本原理解答、交易所注册与交易操作、ICO交易与操作、投资分析、风险分析等内容。
目前入圈价格初始定价50元,50人调整一次价格,每次调整幅度为50元!