以太坊CPP代码分析(三)

本部分针对在ethashseal工程中Ethash类分析,Ethash类主要完成工作量证明相关工作


Ethash.h的代码在 https://github.com/ethereum/cpp-ethereum/blob/develop/libethashseal/Ethash.h

1、难度计算

难度(Difficulty)一词来源于区块链技术的先驱比特币,用来度量挖出一个区块平均需要的运算次数。挖矿本质上就是在求解一个谜题,不同的电子币设置了不同的谜题。比如比特币使用SHA-256、莱特币使用Scrypt、以太坊使用Ethash。一个谜题的解的所有可能取值被称为解的空间,挖矿就是在这些可能的取值中寻找一个解。

这些谜题都有如下共同的特点:

  • 没有比穷举法更有效的求解方法
  • 解在空间中均匀分布,从而使每一次穷举尝试找到一个解的概率基本一致
  • 解的空间足够大,保证一定能够找到解

假设现在有一种电子币,解所在的空间为0-99共100个数字,谜题为x<100。这个谜题非常简单,空间中的任何一个数字都能满足。如果想让谜题更难以求解该怎么做呢?把谜题改成x<50,现在空间中只有一半的数字能满足了,也就是说,现在的难度比原来大了。并且我们还能知道难度大了多少,原来求解平均要尝试1次,现在求解平均要尝试2次了,也就是说,x<50的难度是x<100的2/1=2倍。同理,如果谜题变成x<10,难度就是x<100的100/10=10倍。

现在谜题多了个参数Difficulty,谜题变成了x<Difficulty,上面的100、50、10,都是Difficulty的取值,这个参数Difficulty就是我们常说的难度(Difficulty)

难度(Difficulty)通过控制合格的解在空间中的数量来控制平均求解所需要尝试的次数,也就可以间接的控制产生一个区块需要的时间,这样就可以使区块以一个合理而稳定的速度产生。

当挖矿的人很多,单位时间能够尝试更多次时,难度就会增大,当挖矿的人减少,单位时间能够尝试的次数变少时,难度就降低。这样产生一个区块需要的时间就可以做到稳定。

有了难度,我们还需要一种算法,用来确定和调整合理的难度值是多少。


以太坊的目前难度计算公式

block_diff = parent_diff + 难度调整 + 难度炸弹

难度调整 = parent_diff // 2048 * MAX(1 - (block_timestamp - parent_timestamp) // 10, -99)

难度炸弹 = INT(2**((block_number // 100000) - 2))

parent_timestamp:上一个区块产生的时间

parent_diff:上一个区块的难度

block_timestamp:当前区块产生的时间

block_number:当前区块的序号


完整公式如下

block_diff = parent_diff + parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) / 10, -99) + int(2^((block.number / 100000) - 2))

----------------------------------------------------------------------------------------------------

符号//整数除法

计算a//b时,先计算a/b,然后取不大于a/b的最大整数。

例如:

-11.3 // 5 = -3

11.3 // 5 = 2


以太坊(Ethereum ETH)是如何计算难度的,可以看到在ethash类中有一个难度计算函数

u256 calculateDifficulty(BlockHeader const& _bi, BlockHeader const& _parent) const;

传入参数:

   BlockHeader const& _bi,        当前区块头

  BlockHeader const& _parent  上一个区块头

代码中涉及以太坊分叉的信息,先说明一下:

     以太坊的创始人们为它设定了4个发展阶段:Frontier,Homestead,Metropolis(Byzantium和Constantinople),Serenity,阶段之间的转换需要通过硬分叉的方式实现。

c++代码并没严格遵循公式

block_diff = parent_diff + parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) / 10, -99) + int(2^((block.number / 100000) - 2))

因为代码里涉及很多分叉的处理,不同分叉使用不同的难度计算

u256 Ethash::calculateDifficulty(BlockHeader const& _bi, BlockHeader const& _parent) const
{
	const unsigned c_expDiffPeriod = 100000;

	if (!_bi.number())     //如果当前区块号为空,则抛出创世区块不能计算错误
		throw GenesisBlockCannotBeCalculated();
	auto const& minimumDifficulty = chainParams().minimumDifficulty; //区块链上的最小难度
	auto const& difficultyBoundDivisor = chainParams().difficultyBoundDivisor;
	auto const& durationLimit = chainParams().durationLimit;

	bigint target;	// stick to a bigint for the target. Don't want to risk going negative.
	if (_bi.number() < chainParams().homesteadForkBlock) //如果当前区块号小于Homestead分叉号
		// Frontier-era difficulty adjustment
		target = _bi.timestamp() >= _parent.timestamp() + durationLimit ? _parent.difficulty() - (_parent.difficulty() / difficultyBoundDivisor) : (_parent.difficulty() + (_parent.difficulty() / difficultyBoundDivisor));
	else
	{
		bigint const timestampDiff = bigint(_bi.timestamp()) - _parent.timestamp();
		bigint const adjFactor = _bi.number() < chainParams().byzantiumForkBlock ?
			max<bigint>(1 - timestampDiff / 10, -99) : // Homestead-era difficulty adjustment
			max<bigint>((_parent.hasUncles() ? 2 : 1) - timestampDiff / 9, -99); // Byzantium-era difficulty adjustment

		target = _parent.difficulty() + _parent.difficulty() / 2048 * adjFactor;
	}

	bigint o = target;
	unsigned exponentialIceAgeBlockNumber = unsigned(_parent.number() + 1);

	// EIP-649 modifies exponentialIceAgeBlockNumber
	if (_bi.number() >= chainParams().byzantiumForkBlock)  //如果当前区块号大于拜占庭分叉区块号
	{
		if (exponentialIceAgeBlockNumber >= 3000000)
			exponentialIceAgeBlockNumber -= 3000000;
		else
			exponentialIceAgeBlockNumber = 0;
	}

	unsigned periodCount = exponentialIceAgeBlockNumber / c_expDiffPeriod;
	if (periodCount > 1)
		o += (bigint(1) << (periodCount - 2));	// latter will eventually become huge, so ensure it's a bigint.

	o = max<bigint>(minimumDifficulty, o);
	return u256(min<bigint>(o, std::numeric_limits<u256>::max()));
}


2、难度校验

难度校验在libethash/internal.h 文件中实现

代码地址:https://github.com/ethereum/cpp-ethereum/blob/develop/libethash/internal.h

// Returns if hash is less than or equal to boundary (2^256/difficulty)
static inline bool ethash_check_difficulty(
	ethash_h256_t const* hash,
	ethash_h256_t const* boundary
)
{
	// Boundary is big endian
	for (int i = 0; i < 32; i++) {
		if (ethash_h256_get(hash, i) == ethash_h256_get(boundary, i)) {
			continue;
		}
		return ethash_h256_get(hash, i) < ethash_h256_get(boundary, i);
	}
	return true;
}

可以看到该段代码hash表示mixhash, boundary表示难度,均为32个字节256位二进制,

通过循环32次,每次取一个字节比较,如果有字节不同,则返回比较结果,否则进行下一个字节比较



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
paho.mqtt.cpp是一个用于MQTT(Message Queuing Telemetry Transport)协议的C++库。它提供了一组用于与MQTT代理进行通信的函数和类。这个库是基于paho.mqtt.c库封装实现的,可以在C++项目环境下作为组件使用。 如果我们想要对paho.mqtt.cpp代码进行分析,我们可以通过查看paho.mqtt.cpp.zip压缩包中的工程文件来了解其内部结构和功能。该压缩包包含了VS2019编译好的paho.mqtt.cpp的工程文件。 在代码分析过程中,我们可以查看头文件和源文件,了解库的类、函数和成员变量的定义和实现。通过分析这些代码,我们可以了解库的功能和使用方法。同时,我们还可以参考相关博客或教程,以获取更详细的信息和示例代码。 需要注意的是,为了能够正确使用paho.mqtt.cpp库,我们需要先安装paho.mqtt.c库。这样才能在C++项目中正确使用paho.mqtt.cpp库的功能。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [VS2019编译完成的paho.mqtt.cpp库](https://download.csdn.net/download/zhh763984017/12195122)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [基于paho.mqtt.c用C++封装实现mqtt](https://download.csdn.net/download/qq_25662827/77133536)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值