主页 > imtoken手机钱包 > 以太坊区块的生成

以太坊区块的生成

imtoken手机钱包 2023-01-18 09:37:20

从名字上看,区块(Block)也是区块链系统中的核心概念。简单来说,区块链就是将区块连接成一条链,区块存储在各种包中。信息。在以太坊中,各种交易信息都存储在区块中。一个块可以包含多个事务或不包含事务。本文主要解决以下问题:

以太坊区块

以太坊的区块中存储了很多信息,其中最重要的就是区块的区块信息和区块中包含的交易信息。

我们可以在go-ethereum的源码中看到区块结构定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

// Block represents an entire block in the Ethereum blockchain.
type Block struct {
header *Header
uncles []*Header
transactions Transactions

// caches
hash atomic.Value
size atomic.Value

// Td is used by package core to store the total difficulty
// of the chain up to and including the block.
td *big.Int

// These fields are used by package eth to track
// inter-peer block relay.
ReceivedAt time.Time
ReceivedFrom interface{}
}

它具有以下主要属性:

Header结构是块中包含的信息,定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

// Header represents a block header in the Ethereum blockchain.
type Header struct {
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"`
Coinbase common.Address `json:"miner" gencodec:"required"`
Root common.Hash `json:"stateRoot" gencodec:"required"`
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"`
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"`
Bloom Bloom `json:"logsBloom" gencodec:"required"`
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Number *big.Int `json:"number" gencodec:"required"`
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Time *big.Int `json:"timestamp" gencodec:"required"`
Extra []byte `json:"extraData" gencodec:"required"`
MixDigest common.Hash `json:"mixHash" gencodec:"required"`
Nonce BlockNonce `json:"nonce" gencodec:"required"`
}

从代码中我们可以看到一个block包含以下信息:

提示:

布鲁姆的角色

以太坊被设计成能够快速检索事件,而在区块链上的存储成本非常高。我们也希望不会有太多重复的数据(交易列表、交易生成记录等)。 Bloom旨在解决这个问题。当一个区块产生时,该区块中包含的所有合约相关地址以及所有交易产生的记录索引都会记录在Bloom中,以供后续查找和索引。这些信息将存储在Header结构(Bloom字段)中,而不是存储在块的数据段中,这样可以节省空间。当应用层的应用想要检索合约中的数据时,只需要在block中查找header信息,看block中是否包含与合约相关的记录。

如何验证 MixDigest 和 Nonce

我们知道目前以太坊使用的挖矿算法是 PoW(工作量证明)。简单来说,这种方法就像猜数字一样。例如,100,000 个数字中有 5 个可行解。即使矿已经被挖了,这个算法要求可以调整猜解的难度。一般难度会很高,也不是那么容易猜出来的,但是很容易验证解是否正确。 Nonce值实际上是矿工猜测的解决方案。验证过程如下:预处理后的Header和Nonce值做一个类似SHA3的运算,生成一个128B的Mix(Mix0),用来计算来自DAG(Ethernet)的数据。伪方方用于挖矿算法的随机数据集,当前大小约2GB以上),取出哪一页数据(128B大小),在以太坊专用混合中取出DAG页和Mix方法,会生成下一个Mix(Mix1),这个过程会重复64次,直到得到一个Mix64。Mix64会被处理成32B的数据,称为Mix Digest。Mix Digest会与一个叫做 Target Threshold 的值(如果 Mix Digest 的值小于 Threshold,则认为挖矿成功,如果大于该值,则表示失败。

块打包过程

交易产生后,由以太坊节点广播到网络,交易被放入交易池(txPool),矿工对交易进行验证,放入被打包的区块在交易被包含在矿工中后,矿工开始挖矿过程(PoW)。当矿工赢得挖矿比赛后,矿工的区块数据就可以写入区块链了。

交易池中有很多交易。矿工如何从交易池中选择交易?实际上,交易池会对每一笔交易进行排序以太坊官网,矿工费(gas)最高的交易会排在第一位。因此,矿工会优先选择奖励高的交易打包成区块。这就是为什么高gas值的交易处理得更快的原因。

块容量

以太坊的区块大小与比特币的区块大小不同,目前为 1MB。因此,一个比特币区块可以包含多少交易取决于区块的大小和每笔交易的大小。一个区块中所有交易的总和不能超过区块大小。但是,以太坊并没有固定的区块大小限制,但这如何决定一个区块可以包含多少交易呢?

以太坊的区块中有一个gasLimit,它代表了该交易可以包含在区块中的gas值的上限。以太坊上的每一笔交易都会消耗gas值,一个区块中包含的所有交易的gas总和不能超过该区块的gasLimit。所以通过这种方式我们可以控制一个区块的交易数量。

如果交易池中没有待处理的交易,矿工会直接进入挖矿过程,依然会获得挖矿奖励(5 Ether),区块仍然会被打包广播,但区块不会包含交易。

阻塞时间

在比特币中,一个区块大约在 10 分钟内发生。根据以太坊白皮书,以太坊生成一个区块大约需要 12 秒。这个时间是块时间。出块时间与挖矿难度有关。比特币的难度调整有相应的算法,该算法会将出块时间维持在10分钟左右。以太坊也有相应的难度调整算法。

我们可以在以太坊的源码中找到计算难度的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// CalcDifficulty 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.
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
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)
}
}

我们可以看到代码有三个版本,分别应用于不同版本的以太坊。我们先看一下 Homestead 版本中的代码:

我们可以看到代码有三个版本以太坊官网,分别应用于不同版本的以太坊。我们先看一下 Homestead 版本中的代码:

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

func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).Set(parent.Time)

x := new(big.Int)
y := new(big.Int)

x.Sub(bigTime, bigParentTime)
x.Div(x, big10)
x.Sub(big1, x)

if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
x.Mul(y, x)
x.Add(parent.Difficulty, x)

if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
periodCount := new(big.Int).Add(parent.Number, big1)
periodCount.Div(periodCount, expDiffPeriod)

if periodCount.Cmp(big1) > 0 {
y.Sub(periodCount, big2)
y.Exp(big2, y, nil)
x.Add(x, y)
}
return x
}

我们从代码中可以看出,以太坊的难度值是根据当前区块的出块时间,调整后续的难度值。

具体公式如下:

1

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

其中2^(periodCount - 2)又称“难度炸弹”,公式如下:

1

2^(periodCount - 2) = 2**((block_number // expDiffPeriod) - 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

func calcDifficultyByzantium(time uint64, parent *types.Header) *big.Int {
bigTime := new(big.Int).SetUint64(time)
bigParentTime := new(big.Int).Set(parent.Time)

x := new(big.Int)
y := new(big.Int)

x.Sub(bigTime, bigParentTime)
x.Div(x, big9)
if parent.UncleHash == types.EmptyUncleHash {
x.Sub(big1, x)
} else {
x.Sub(big2, x)
}
if x.Cmp(bigMinus99) < 0 {
x.Set(bigMinus99)
}
y.Div(parent.Difficulty, params.DifficultyBoundDivisor)
x.Mul(y, x)
x.Add(parent.Difficulty, x)

if x.Cmp(params.MinimumDifficulty) < 0 {
x.Set(params.MinimumDifficulty)
}
fakeBlockNumber := new(big.Int)
if parent.Number.Cmp(big2999999) >= 0 {
fakeBlockNumber = fakeBlockNumber.Sub(parent.Number, big2999999) // Note, parent is 1 less than the actual block number
}
periodCount := fakeBlockNumber
periodCount.Div(periodCount, expDiffPeriod)

if periodCount.Cmp(big1) > 0 {
y.Sub(periodCount, big2)
y.Exp(big2, y, nil)
x.Add(x, y)
}
return x
}

算法如下:

1

diff = (parent_diff + (parent_diff / 2048 * max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99))) + 2^(periodCount - 2)

以太坊定义了一个最低难度值,MinimumDifficulty,定义在protocol_params.go源文件中,值为131072,即以太坊的最低难度,创世区块的难度值。

本文版权归作者罗远航所有,采用 Attribution-NonCommercial 3.0 License。任何人都可以转载和分享,但未经许可不得用于商业目的;转载请注明出处。感谢您的合作!