Geth源码学习之共识与奖励

计算挖矿奖励

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

// AccumulateRewards credits the coinbase of the given block with the mining
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
// Select the correct block reward based on chain progression
// 根据不同的阶段,获取静态的区块奖励
blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) {
blockReward = ByzantiumBlockReward
}
if config.IsConstantinople(header.Number) {
blockReward = ConstantinopleBlockReward
}
// Accumulate the rewards for the miner and any included uncles
reward := new(big.Int).Set(blockReward)
r := new(big.Int)
for _, uncle := range uncles {
r.Add(uncle.Number, big8) // r = uncle.Number + 8
r.Sub(r, header.Number) // r = r - header.Number
r.Mul(r, blockReward) // r = r * blockReward
r.Div(r, big8) // r = r / 8
state.AddBalance(uncle.Coinbase, r) // 挖出叔父区块的miner获得静态的(2个)奖励
//叔父区块number 当前区块number r.Add(uncle.Number, big8) r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) 挖出叔父区块的奖励 blockReward
//1 2 9 7 14 1.75 八分之七 2
//1 3 9 6 12 1.5 八分之六 2
//1 4 9 5 10 1.25 八分之五 2
//1 5 9 4 8 1 八分之四 2
//1 6 9 3 6 0.75 八分之三 2
//1 7 9 2 4 0.5 八分之二 2
//1 8 9 1 2 0.25 八分之一 2
//1 9 9 0 0 0 八分之零 2
//1 10 9 -1 -2 -0.25 八分之零 2
// 叔块距离当前区块越远,能够得到的奖励越少,最多不超过8个区块
// 不会出现负数的原因应该是在添加叔父区块时有判断,只有距离在8个区块之内的区块才能算是叔父区块,其他的不让添加
r.Div(blockReward, big32) // 当前挖到区块的miner每添加一个叔块获得1/32的静态(2个)的奖励
reward.Add(reward, r)
}
state.AddBalance(header.Coinbase, reward)
}

解析源码中叔父区块的挖矿者获得的奖励

blockReward=2,不会出现负数的原因应该是在添加叔父区块时有判断,只有距离在8个区块之内的区块才能算是叔父区块,其他的不让添加

叔父区块number 当前区块number r.Add(uncle.Number, big8) r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, big8) 挖出叔父区块的奖励 blockReward
1 2 9 7 14 1.75 八分之七 2
1 3 9 6 12 1.5 八分之六 2
1 4 9 5 10 1.25 八分之五 2
1 5 9 4 8 1 八分之四 2
1 6 9 3 6 0.75 八分之三 2
1 7 9 2 4 0.5 八分之二 2
1 8 9 1 2 0.25 八分之一 2
1 9 9 0 0 0 八分之零 2
1 10 9 -1 -2 -0.25 八分之零 2

块头验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

// VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
// If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake {
return nil
}
// Short circuit if the header is known, or its parent not
number := header.Number.Uint64()
// 根据区块号和区块头的hash获取区块信息,只有区块高度为number的区块头的hash等于指定hash才返回数据,否则返回nil
if chain.GetHeader(header.Hash(), number) != nil {
return nil
}
// 获取父区块,父区块不能为nil
parent := chain.GetHeader(header.ParentHash, number-1)
if parent == nil {
return consensus.ErrUnknownAncestor
}
// Sanity checks passed, do a proper verification
return ethash.verifyHeader(chain, header, parent, false, seal, time.Now().Unix())
}


// verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
// See YP section 4.3.4. "Block Header Validity"
func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool, unixNow int64) error {
// Ensure that the header's extra-data section is of a reasonable size
// 块头的扩展数据的字符长度不能超过32
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
}
// Verify the header's timestamp
if !uncle {
// 如果块头的时间超过了当前时间+15秒,抛异常;
if header.Time > uint64(unixNow+allowedFutureBlockTimeSeconds) {
return consensus.ErrFutureBlock
}
}
// 如果块头的时间小于父区块的时间,也抛异常
if header.Time <= parent.Time {
return errOlderBlockTime
}
// Verify the block's difficulty based on its timestamp and parent's difficulty
// 根据父区块的难度和当前区块的时间获取期望的难度
expected := ethash.CalcDifficulty(chain, header.Time, parent)
// 如果当前区块的难度和计算的难度不一致,则抛异常
if expected.Cmp(header.Difficulty) != 0 {
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
}
// Verify that the gas limit is <= 2^63-1
// 区块的gas总量不能超过2^63-1
cap := uint64(0x7fffffffffffffff)
if header.GasLimit > cap {
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, cap)
}
// Verify that the gasUsed is <= gasLimit
// 使用的gas不能超过gas上限
if header.GasUsed > header.GasLimit {
return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit)
}

// Verify that the gas limit remains within allowed bounds
// 当前区块的gas上限与父区块的gas上限相减取绝对值
diff := int64(parent.GasLimit) - int64(header.GasLimit)
if diff < 0 {
diff *= -1
}
limit := parent.GasLimit / params.GasLimitBoundDivisor
// 此处的整体意思是每次gas费的上限增加不能超过上一个区块的gas上限的1024分之一;也不能少于区块的gas最小上限5000
// 转账最少都需要21000的gas费,gas费怎么会少于5000呢?
if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
}
// Verify that the block number is parent's +1
// 这个校验没有用吧,因为上面的父区块的获取方式是在当前区块高度的基础上-1;所以只要能获取到父区块父区块的高度肯定比当前区块少1
// 因为这个方法中的父区块是传入的,而不是在当前方法查询的,所以有这个判断是没问题的,但是可以把这个放在gasLimit的前面,如果这个父区块不是当前区块的父区块就没有必要验证gasLimit了
if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
return consensus.ErrInvalidNumber
}
// Verify the engine specific seal securing the block
if seal {
// 验证nonce是否符合难度
if err := ethash.verifySeal(chain, header, false); err != nil {
return err
}
}
// If all checks passed, validate any special fields for hard forks
if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil {
return err
}
if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil {
return err
}
return nil
}

难度计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

// calcDifficultyByzantium is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules.
// Specification EIP-649: https://eips.ethereum.org/EIPS/eip-649
// 数字3000000是为了计算难度炸弹,目前的区块高度是12184295;2^(12184295-3000000)/100000 = 2^91 = 2.47588e+27!!!
calcDifficultyByzantium = makeDifficultyCalculator(big.NewInt(3000000))


// makeDifficultyCalculator creates a difficultyCalculator with the given bomb-delay.
// the difficulty is calculated with Byzantium rules, which differs from Homestead in
// how uncles affect the calculation
func makeDifficultyCalculator(bombDelay *big.Int) func(time uint64, parent *types.Header) *big.Int {
// Note, the calculations below looks at the parent number, which is 1 below
// the block number. Thus we remove one from the delay given
bombDelayFromParent := new(big.Int).Sub(bombDelay, big1)
return func(time uint64, parent *types.Header) *big.Int {
// https://github.com/ethereum/EIPs/issues/100.
// algorithm:
// diff = (parent_diff +
// (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99))
// ) + 2^(periodCount - 2)

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

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

// (2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9
x.Sub(bigTime, bigParentTime)
x.Div(x, big9)
if parent.UncleHash == types.EmptyUncleHash {
x.Sub(big1, x)
} else {
x.Sub(big2, x)
}
// max((2 if len(parent_uncles) else 1) - (block_timestamp - parent_timestamp) // 9, -99)
// 最多减少的难度不能超过99个单元,一个单元 = parent_diff / 2048;即最多减少的难度不能大于99*parent_diff / 2048
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
// parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99))
y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
x.Mul(y, x)
x.Add(parent.Difficulty, x)

// minimum difficulty can ever be (before exponential factor)
// 最小难度不能少于131072,即100000000000000000
if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
// calculate a fake block number for the ice-age delay
// Specification: https://eips.ethereum.org/EIPS/eip-1234
// 如果已经过了bombDelayFromParent区块高度,则计算直到父区块为止过去了多少个区块了
fakeBlockNumber := new(big.Int)
if parent.Number.Cmp(bombDelayFromParent) >= 0 {
fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, bombDelayFromParent)
}
// for the exponential factor
// 每隔100000(十万)个区块,periodCount增加1,即每隔十万个区块难度炸弹的难度翻一倍
periodCount := fakeBlockNumber
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
}
}