1. 交易的签名
  2. 理解收据receipt
  3. 理解区块
  4. 理解交易
  5. blockchain核心
  6. forkId 解读
  7. 布隆过滤器原理
  8. 交易池分析
  9. oracle 原理和实现
  10. TxList 解读
  11. MPT树
  12. 区块同步
  13. geth源码学习——介绍
  14. How Geth starts its server

结构定义

区块

img

首先我们需要明白区块的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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 //链的总难度,与最长链原则相关

//与 p2p 功能相关

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

这些参数的具体含义需要读者有一定的基础,例如理解什么是区块头、叔块、区块哈希、区块大小。读者可阅读官方文档

区块头

区块头用于构成链式结构和快速验证。

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
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 uint64 `json:"timestamp" gencodec:"required"`
//为未来共识机制调整预留的额外字段
Extra []byte `json:"extraData" gencodec:"required"`
//验证 PoW 时,从 Nonce 计算出的中间数据,可以减少验证时的计算量。
//并且可以防止故意发送错误的验证信息导致验证者遭受拒绝服务攻击。
MixDigest common.Hash `json:"mixHash"`
// 验证挖矿时的随机数,请了解挖矿过程。
Nonce BlockNonce `json:"nonce"`

// BaseFee was added by EIP-1559 and is ignored in legacy headers.
BaseFee *big.Int `json:"baseFeePerGas" rlp:"optional"`
}

这里需要读者有理解如何成链的流程,如果不熟悉,可以阅读我以前学习比特币时的区块链基础

其次,出现了布隆过滤器,后面的源码解读的文章将会详细介绍。关于 MPT 树的介绍即将在《以太坊的数据组织》中给出。

初始值和辅助变量

区块内可能没有交易,被称作空区块;区块也可能没有引用叔块,对于这两种情况都有自己的默认哈希值。

当交易树为空和收据树为空时的根哈希相同,如下定义

1
2
3
4
5
6
var (
//区块中不含有交易时的 MPT 树根哈希
EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
//引用的叔块不含有交易时的哈希,请自行理解叔块
EmptyUncleHash = rlpHash([]*Header(nil))
)

为了证明矿工成功挖矿,定义了使得满足难度要求的随机数为

1
2
3
4
// A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a sufficient amount of computation has been carried
// out on a block.
type BlockNonce [8]byte

但是,恶意的矿工可能大量的发送错误的随机数,导致其他节点大量地验证错误信息,造成 DDoS 攻击,因此提出了区块头的 MixDigest 成员,它是从随机数生成的中间过程的哈希,能够减少验证的资源消耗。因为计算MixDigest 需要消耗资源,一定程度上防止了 DDos。

1
2
3
//验证 PoW 时,从 Nonce 计算出的中间数据,可以减少验证时的计算量。
//并且可以防止故意发送错误的验证信息导致验证者遭受拒绝服务攻击。
MixDigest common.Hash `json:"mixHash"`

区块体

区块的主体是交易和引用的叔块,用于暂存数据。

1
2
3
4
5
6
// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
Transactions []*Transaction
Uncles []*Header
}

函数

区块随机数的赋值

区块的随机数定义了一系列的类型转换和赋值函数

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
//整数转换成 BlockNonce

// EncodeNonce converts the given integer to a block nonce.
func EncodeNonce(i uint64) BlockNonce {
var n BlockNonce
binary.BigEndian.PutUint64(n[:], i)
return n
}

//BlockNonce 转成整数

// Uint64 returns the integer value of a block nonce.
func (n BlockNonce) Uint64() uint64 {
return binary.BigEndian.Uint64(n[:])
}

//BlockNonce 转换成十六进制字节

// MarshalText encodes n as a hex string with 0x prefix.
func (n BlockNonce) MarshalText() ([]byte, error) {
return hexutil.Bytes(n[:]).MarshalText()
}

//检查 input 的字节是否和所需的类型的相同,相同时用 input 设置 BlockNonce 的值

// UnmarshalText implements encoding.TextUnmarshaler.
func (n *BlockNonce) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
}

垃圾信息检查

由于区块头有动态的类型,为了防止填充垃圾信息,源码中通过占用的内存长度检查。

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
//反射类型用于获取运行时信息,这里获取 header 的内存的字节数,和动态类型相关
var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())

//获取区块头占用的内存大小,用于估计占用内存和限制内存消耗

// Size returns the approximate memory used by all internal contents. It is used
// to approximate and limit the memory consumption of various caches.
func (h *Header) Size() common.StorageSize {
return headerSize + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen())/8)
}

//检查字段是否合理以及动态类型所占用的内存是否合理

// SanityCheck checks a few basic things -- these checks are way beyond what
// any 'sane' production values should hold, and can mainly be used to prevent
// that the unbounded fields are stuffed with junk data to add processing
// overhead
func (h *Header) SanityCheck() error {
if h.Number != nil && !h.Number.IsUint64() {
return fmt.Errorf("too large block number: bitlen %d", h.Number.BitLen())
}
if h.Difficulty != nil {
if diffLen := h.Difficulty.BitLen(); diffLen > 80 {
return fmt.Errorf("too large block difficulty: bitlen %d", diffLen)
}
}
if eLen := len(h.Extra); eLen > 100*1024 {
return fmt.Errorf("too large block extradata: size %d", eLen)
}
if h.BaseFee != nil {
if bfLen := h.BaseFee.BitLen(); bfLen > 256 {
return fmt.Errorf("too large base fee: bitlen %d", bfLen)
}
}
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
// NewBlock creates a new block. The input data is copied,
// changes to header and to the field values will not affect the
// block.
//
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles
// and receipts.
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block {
b := &Block{header: CopyHeader(header), td: new(big.Int)}

// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
b.header.TxHash = EmptyRootHash
} else {
b.header.TxHash = DeriveSha(Transactions(txs), hasher) //生成交易树的根哈希
b.transactions = make(Transactions, len(txs))
copy(b.transactions, txs) //复制交易到区块
}

if len(receipts) == 0 {
b.header.ReceiptHash = EmptyRootHash
} else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher) //生成收据树根哈希
b.header.Bloom = CreateBloom(receipts) //根据收据创建布隆过滤器
}

if len(uncles) == 0 {
b.header.UncleHash = EmptyUncleHash
} else {
b.header.UncleHash = CalcUncleHash(uncles) //生成叔块哈希
b.uncles = make([]*Header, len(uncles))
for i := range uncles {
b.uncles[i] = CopyHeader(uncles[i])
}
}

return b
}

// NewBlockWithHeader creates a block with the given header data. The
// header data is copied, changes to header and to the field values
// will not affect the block.
func NewBlockWithHeader(header *Header) *Block {
return &Block{header: CopyHeader(header)}
}

区块的编码

存储时,按照 MPT 树的需求,也需要进行 RLP 编码,暂时不必深入,后续可能单独作解读。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// DecodeRLP decodes the Ethereum
func (b *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock
_, size, _ := s.Kind()
if err := s.Decode(&eb); err != nil {
return err
}
b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
b.size.Store(common.StorageSize(rlp.ListSize(size)))
return nil
}

// EncodeRLP serializes b into the Ethereum RLP block format.
func (b *Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, extblock{
Header: b.header,
Txs: b.transactions,
Uncles: b.uncles,
})
}

获取区块属性

为了方便快速获取区块属性,设置了许多的方法,通过名字都能直观地了解含义。

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
func (b *Block) Uncles() []*Header          { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }

func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
func (b *Block) GasLimit() uint64 { return b.header.GasLimit }
func (b *Block) GasUsed() uint64 { return b.header.GasUsed }
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
func (b *Block) Time() uint64 { return b.header.Time }

func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() }
func (b *Block) MixDigest() common.Hash { return b.header.MixDigest }
func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) }
func (b *Block) Bloom() Bloom { return b.header.Bloom }
func (b *Block) Coinbase() common.Address { return b.header.Coinbase }
func (b *Block) Root() common.Hash { return b.header.Root }
func (b *Block) ParentHash() common.Hash { return b.header.ParentHash }
func (b *Block) TxHash() common.Hash { return b.header.TxHash }
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash }
func (b *Block) UncleHash() common.Hash { return b.header.UncleHash }
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) }

func (b *Block) BaseFee() *big.Int {
if b.header.BaseFee == nil {
return nil
}
return new(big.Int).Set(b.header.BaseFee)
}

//这里一般都是复制
func (b *Block) Header() *Header { return CopyHeader(b.header) }

// Body returns the non-header content of the block.
func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} }

// Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previsouly cached value.
func (b *Block) Size() common.StorageSize {
if size := b.size.Load(); size != nil {
return size.(common.StorageSize)
}
c := writeCounter(0)
rlp.Encode(&c, b)
b.size.Store(common.StorageSize(c))
return common.StorageSize(c)
}

检索交易

交易一旦生成,不管是否被打包上链,都会生成交易哈希,用于检索。成块后可以方便的找到对应的区块高度,然后通过哈希值搜索交易。

1
2
3
4
5
6
7
8
9
//根据哈希搜索交易
func (b *Block) Transaction(hash common.Hash) *Transaction {
for _, transaction := range b.transactions {
if transaction.Hash() == hash {
return transaction
}
}
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
//seal 相当于铅封,类比区块头。生成用传入的区块头替代,主体不变的区块

// WithSeal returns a new block with the data from b but the header replaced with
// the sealed one.
func (b *Block) WithSeal(header *Header) *Block {
cpy := *header

return &Block{
header: &cpy,
transactions: b.transactions,
uncles: b.uncles,
}
}

//替代区块主体,生成新的区块

// WithBody returns a new block with the given transaction and uncle contents.
func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
block := &Block{
header: CopyHeader(b.header),
transactions: make([]*Transaction, len(transactions)),
uncles: make([]*Header, len(uncles)),
}
copy(block.transactions, transactions)
for i := range uncles {
block.uncles[i] = CopyHeader(uncles[i])
}
return block
}

参考:https://www.codeleading.com/article/3301657376/