go-ethereum修改挖矿周期

    现在以太坊公链使用geth挖矿周期大概是15秒出一个新块。在自己基于go-ethereum源码来建立私有链的时候,如果想加快挖矿出块周期,应该修改哪些地方呢?许多人的第一想法是修改创世配置文件genesis.json文件中的difficulty,把它改成一个较小的值,比如0x10000。在电脑上跑起来后,发现挖矿基本上是1秒出几个块,这有点太快了。于是改大,改成0x20000,发现出块还是1s几个块。这种是凑的方法不太靠谱。

    以太坊采用PoW工作量证明机制来进行挖矿,最简单的理解就是寻找一个整数nonce,使得哈希值sha3(data+nonce)<M/difficulty。difficulty越大,挖矿难度越大,反之挖矿越容易。查看以太坊中计算难度值的函数,在go-ethereum/consensus/ethash/consensus.go中:

func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
	//return big.NewInt(0x100000);
	next := new(big.Int).Add(parent.Number, big1)
	switch {
	case config.IsByzantium(next):
		return calcDifficultyByzantium(time, parent)
	case config.IsHomestead(next):
		return calcDifficultyHomestead(time, parent)
	default:
		return calcDifficultyFrontier(time, parent)
	}
}

    对于1.8.1版本,使用的calcDifficultyHomestead函数:

func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
	// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md
	// algorithm:
	// diff = (parent_diff +
	//         (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
	//        ) + 2^(periodCount - 2)

	bigTime := new(big.Int).SetUint64(time)
	bigParentTime := new(big.Int).Set(parent.Time)

	// holds intermediate values to make the algo easier to read & audit
	x := new(big.Int)
	y := new(big.Int)

	// 1 - (block_timestamp - parent_timestamp) // 10
	x.Sub(bigTime, bigParentTime)
	x.Div(x, big10)
	x.Sub(big1, x)

	// max(1 - (block_timestamp - parent_timestamp) // 10, -99)
	if x.Cmp(bigMinus99) < 0 {
		x.Set(bigMinus99)
	}
	// (parent_diff + parent_diff // 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
	y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
	x.Mul(y, x)
	x.Add(parent.Difficulty, x)

	// minimum difficulty can ever be (before exponential factor)
	if x.Cmp(params.MinimumDifficulty) < 0 {
		x.Set(params.MinimumDifficulty)
	}
	// for the exponential factor
	periodCount := new(big.Int).Add(parent.Number, big1)
	periodCount.Div(periodCount, expDiffPeriod)

	// the exponential factor, commonly referred to as "the bomb"
	// diff = diff + 2^(periodCount - 2)
	if periodCount.Cmp(big1) > 0 {
		y.Sub(periodCount, big2)
		y.Exp(big2, y, nil)
		x.Add(x, y)
	}
	return x
}

    看函数注释,计算difficulty的算法是:

    diff = (parent_diff +
         (parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99))
        ) + 2^(periodCount - 2)

分为3部分,第一项是parent_diff,父区块的难度值;第二项是难度调整值parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99),第三项是难度炸弹,难度炸弹=2^(blockNumber/100000-2),每过10万个区块难度增加2。

    关键是第二项难度调整,Y=parent_diff / 2048 * max(1 - (block_timestamp - parent_timestamp) // 10, -99),这个表达式看起来这么长,乍一看不知所云。认真分析一下,deltaTime=block_timestamp - parent_timestamp,及当前区块和前一个区块的产生时间差。//操作符是先除再向下去整的意思。当deltaTime<10s时,Y=parrent_diff/2048;当10<deltaTime<20时,Y=0;当Y>=20时,Y=-parrent_diff/2048。这样一看就很明了了。这是采用自控里面的负反馈调节的思路。当时间差小于10s时,增大难度让时间变长。当时间差介于10s到20s时,难度不变。当时间差大于20s时,减小难度来让挖矿更容易。平均挖矿时间就是10~20之间,最终平均时间大概15s。

    决定周期的就是这个除数10,如果将除数10改成2,则挖矿周期将在2~4秒之间。只需要这样修改:

     x.Div(x, big10)     ==> x.Div(x,big.NewInt(2))  

    看起来好简单,关键是理清这个逻辑。

猜你喜欢

转载自blog.csdn.net/liuzhijun301/article/details/80006761
今日推荐