在我发表了第十一篇源码研读分享之后,社友大硕给我打赏了,而且还说了一句倒出了我心声的话:“菜菜子,你的工作成果能让后来者节省很多时间,很有价值,谢谢分享!”。是的,我在中的Sia教程、比特币源码研读系列在我最初编写的时候,只要有2个目的:一是为了保留自己的学习记录,以便后面可以随时回顾自己所学知识,减去重复学习的时间;二是可以让后来者少走弯路,提升学习效率。就是基于这2个原因,我才会坚持写比特币源码研读系列(在这期间我还会穿插),让自己和更多的人节省时间,提高效率,说大点是可以促进比特币和区块链的快速发展!希望我的文章真正能帮到大家,同时,也欢迎大家给我提出宝贵意见,这也能对我成长提供帮助。
本文将继续开展应用程序参数交互源码部分(AppInitParameterInteraction)的研读与分析。
本文主要涉及的源码文件包括:
src/bitcond.cpp、src/init.h、src/init.cpp、src/util.h、src/util.cpp、src/validation.h、src/uint256.h、src/uint256.cpp、consensus/Params.h
一、检测点参数提示
检测点参数信息获取代码如下:
fCheckpointsEnabled =GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
检测点设置参数checkpoints,在程序中其默认值为DEFAULT_CHECKPOINTS_ENABLED,该常量在src/validation.h中定义,其默认值为true,即需要进行检测点检查。那何为checkpoints,它的作用是什么呢?带着这两个疑问我在网上搜索了一番,找了一篇解释得比较全面的英文帖子(https://bitcoin.stackexchange.com/questions/1797/what-are-checkpoints):
首先是提问者的给出的疑问(Question):
What are checkpoints?
I often read that checkpoints protect the network from a 51% attack because an attacker cannot reverse transactions madebefore the last checkpoint.
How exactly does this checkpoint mechanismwork? And who creates the checkpoints?
从其提问中我们可以得知checkpoints,即检测点,是用于保护网络不受全网51%算力攻击,因为攻击者是不能逆转最后一个检测点前发生的那些交易的。
同时,提问者又提出问题:检测点的工作机制是怎么样的呢?又是谁创建了检测点?
回答是这样的:
The checkpoints are hard coded into the standard client. The concept is, that the standard client will accept all transactions up to the checkpoint as valid and irreversible. If anyone tries to fork the blockchain starting from a block before the checkpoint, the client will not accept the fork. This makes those blocks "set in stone".
这段话的含义是这样的:检测点是通过硬编码方式写入标准客户端的。意味着标准客户端将在检测点之前将接受所有有效交易,这些交易将是不可逆的。如果任何人试图在检测点前从一个区块对区块链进行分叉,客户端将不会接受这个分叉,这使得那些区块一成不变(set in stone)。
从上述提问与回答我们可以看出checkpoint的重要性,通过checkpoint可以防范区块链被恶意分叉,保护比特币网络的正常运行。
我们再来看看fCheckpointsEnabled的使用情况,该变量为全局变量,声明于src/validation.h,定义于src/validation.cpp中,并且其默认为true。主要在AcceptBlockHeader与TestBlockValidity中使用,这两个函数的具体解释将在后续实际使用时进行详细说明,但我们通过其函数名也可以看出检测点主要是对区块相关内容的检测。
二、哈希假定有效参数
我们首先来看哈希假定有效参数的处理代码:
hashAssumeValid= uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
if (!hashAssumeValid.IsNull())
LogPrintf("Assumingancestors of block %s have valid signatures.\n",hashAssumeValid.GetHex());
else
LogPrintf("Validating signatures for all blocks.\n");
在该源码中,我们可以看到assumevalid参数的默认值可以通过chainparams.GetConsensus().defaultAssumeValid.GetHex()获得,这个默认值的获得过程包括3个步骤:
(1)获得链上共识参数:通过chainparams.GetConsensus()获得链上共识参数(chainparams在前面的文章中已多次提起,此处就不再赘述),返回类型为Consensus::Params,其详细定义我们可以在consensus/Params.h中找到,Params为一个结构体,该结构体主要定义了影响链上共识的重要参数,比如:创世块(hashGenesisBlock)、奖励减半时间间隔(nSubsidyHalvingInterval)、各种BIP启动时的区块高度、工作量证明参数(powLimit、fPowAllowMinDifficultyBlocks等)、难度调整间隔计算函数(DifficultyAdjustmentInterval)以及默认假定有效对象(defaultAssumeValid);
(2)默认假定有效对象:默认假定有效对象在Consensus::Params中定义uint256 defaultAssumeValid,其类型uint256是一个类,定义于src/uint256.h中,其基类为模板类base_blob,主要用于存储固定大小不透明二进制数值模板,在其后实现了uint160与uint256两个子类,分别实现了160位与256位二进制数值存储类,所以在此处我们可以看到默认假定有效对象主要是需要存储二进制值,而通过base_blob与uint256可以发现该二进制数为区块的哈希值;
(3)获取哈希值:最后一步就是获取哈希值的十六进值,其获取方式通过GetHex()实现。
assumevalid参数的值也可以由用户在命令行中输入,输入形式为包含64位数的十六进制值,其样式如下:
0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec
在获得十六进制值后,我们通过uint256S函数将其转换为uint256对象,该函数定义于src/unit256.h中。其函数实现如下:
inline uint256 uint256S(const char *str)
{
uint256 rv;
rv.SetHex(str);
return rv;
}
最后判断hashAssumeValid的有效性,如果不为空,则假定该哈希值对应区块的所有父区块都具备有效的签名,否则需要验证所有区块的签名,并将判断信息输入在日志中。hashAssumeValid主要在src/Validation.cpp的ConnectBlock函数中使用。
三、交易池大小限定参数
交易池大小限定参数设置代码比较简单,主要是设定交易池最大与最小容量,其代码如下:
// mempool limits
int64_t nMempoolSizeMax =GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
int64_t nMempoolSizeMin =GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000* 40;
if (nMempoolSizeMax < 0 ||nMempoolSizeMax < nMempoolSizeMin)
return InitError(strprintf(_("-maxmempool must be at least %dMB"), std::ceil(nMempoolSizeMin / 1000000.0)));
在上述代码中DEFAULT_MAX_MEMPOOL_SIZE常量定义于src/policy.h中,其代表的是交易池最大存储容量(MB)数值,通过其定义我们可以看到其最大容量为300MB。
/** Default for -maxmempool,maximum megabytes of mempool memory usage */
static const unsigned int
DEFAULT_MAX_MEMPOOL_SIZE = 300;
而交易池最小存储容量对应的默认值为DEFAULT_DESCENDANT_SIZE_LIMIT常量,定义于src/validation.h,其单位为KB,通过其定义我们可以看到其最小存储容量为101KB。
/** Default for-limitdescendantsize, maximum kilobytes of in-mempool descendants */
static const unsigned int
DEFAULT_DESCENDANT_SIZE_LIMIT = 101;
以上就是本次源码研读的内容,在这篇研读文章中我们知道了检测点、哈希假定有效、共识信息参数以及交易池大小范围等内容,可以说我们离比特币源码的核心越来越近了!后面的难度也会随之增加,但只要坚持研读就好,一遍不行就看两遍,不会就的就去学,只要通过努力我们一定能把比特币源码读懂读透的,因为《精通比特币》的作者就是这么过来的,他可是技术大牛啊,连他都愿意花了几个月的时间专注比特币源码的分析,进而掌握比特币的运行原理,我们还有什么理由不跟随着他的脚步前行呢!
区块链研习社比特币源码研读班 菜菜子
以下是广告:
我们区块链研习社已创建“区块链研习社币圈交流”小密圈”,在小密圈中,我们将带领大家一起学习区块链的原理与投资,还将提供区块链基本原理解答、交易所注册与交易操作、ICO交易与操作、投资分析、风险分析等内容。
目前入圈价格初始定价50元,50人调整一次价格,每次调整幅度为50元!