Go-ethereum 源码解析之 core/types/block.go

Go-ethereum 源码解析之 core/types/block.go

// Package types contains data types related to Ethereum consensus.
package types

import (
    "encoding/binary"
    "io"
    "math/big"
    "sort"
    "sync/atomic"
    "time"
    "unsafe"

    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/crypto/sha3"
    "github.com/ethereum/go-ethereum/rlp"
)

var (
    EmptyRootHash  = DeriveSha(Transactions{})
    EmptyUncleHash = CalcUncleHash(nil)
)

// 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

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

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

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

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

//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go

// 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"`
}

// field type overrides for gencodec
type headerMarshaling struct {
    Difficulty *hexutil.Big
    Number     *hexutil.Big
    GasLimit   hexutil.Uint64
    GasUsed    hexutil.Uint64
    Time       *hexutil.Big
    Extra      hexutil.Bytes
    Hash       common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
}

// Hash returns the block hash of the header, which is simply the keccak256 hash of its
// RLP encoding.
func (h *Header) Hash() common.Hash {
    return rlpHash(h)
}

// 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 common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
}

func rlpHash(x interface{}) (h common.Hash) {
    hw := sha3.NewKeccak256()
    rlp.Encode(hw, x)
    hw.Sum(h[:0])
    return h
}

// 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
}

// 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{}
}

// [deprecated by eth/63]
// StorageBlock defines the RLP encoding of a Block stored in the
// state database. The StorageBlock encoding contains fields that
// would otherwise need to be recomputed.
type StorageBlock Block

// "external" block encoding. used for eth protocol, etc.
type extblock struct {
    Header *Header
    Txs    []*Transaction
    Uncles []*Header
}

// [deprecated by eth/63]
// "storage" block encoding. used for database.
type storageblock struct {
    Header *Header
    Txs    []*Transaction
    Uncles []*Header
    TD     *big.Int
}

// 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) *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))
        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))
        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)}
}

// CopyHeader creates a deep copy of a block header to prevent side effects from
// modifying a header variable.
func CopyHeader(h *Header) *Header {
    cpy := *h
    if cpy.Time = new(big.Int); h.Time != nil {
        cpy.Time.Set(h.Time)
    }
    if cpy.Difficulty = new(big.Int); h.Difficulty != nil {
        cpy.Difficulty.Set(h.Difficulty)
    }
    if cpy.Number = new(big.Int); h.Number != nil {
        cpy.Number.Set(h.Number)
    }
    if len(h.Extra) > 0 {
        cpy.Extra = make([]byte, len(h.Extra))
        copy(cpy.Extra, h.Extra)
    }
    return &cpy
}

// 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,
    })
}

// [deprecated by eth/63]
func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
    var sb storageblock
    if err := s.Decode(&sb); err != nil {
        return err
    }
    b.header, b.uncles, b.transactions, b.td = sb.Header, sb.Uncles, sb.Txs, sb.TD
    return nil
}

// TODO: copies

func (b *Block) Uncles() []*Header          { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }

func (b *Block) Transaction(hash common.Hash) *Transaction {
    for _, transaction := range b.transactions {
        if transaction.Hash() == hash {
            return transaction
        }
    }
    return nil
}

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() *big.Int       { return new(big.Int).Set(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) 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)
}

type writeCounter common.StorageSize

func (c *writeCounter) Write(b []byte) (int, error) {
    *c += writeCounter(len(b))
    return len(b), nil
}

func CalcUncleHash(uncles []*Header) common.Hash {
    return rlpHash(uncles)
}

// 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
}

// Hash returns the keccak256 hash of b's header.
// The hash is computed on the first call and cached thereafter.
func (b *Block) Hash() common.Hash {
    if hash := b.hash.Load(); hash != nil {
        return hash.(common.Hash)
    }
    v := b.header.Hash()
    b.hash.Store(v)
    return v
}

type Blocks []*Block

type BlockBy func(b1, b2 *Block) bool

func (self BlockBy) Sort(blocks Blocks) {
    bs := blockSorter{
        blocks: blocks,
        by:     self,
    }
    sort.Sort(bs)
}

type blockSorter struct {
    blocks Blocks
    by     func(b1, b2 *Block) bool
}

func (self blockSorter) Len() int { return len(self.blocks) }
func (self blockSorter) Swap(i, j int) {
    self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
}
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }

func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }

Appendix A. 总体批注

文件 core/types/block.go 主要定义了区块相关的数据结构,如区块头 Header,区块 Block。

1. type BlockNonce [8]byte

Block 随机数的封装器。

  • 通过函数 EncodeNonce() 将 uint64 类型的值转换为 BlockNonce 类型的值。
  • 通过方法 Uint64() 将 BlockNonce 类型的值转换为 uint64 类型的值。
  • 通过方法 MarshalText() 将 BlockNonce 类型的值编码为 16 进制的字符串,前面带 0x 前缀。
  • 通过方法 UnmarshalText() 将已经编码后的 input 解码到 BlockNonce 类型的值中。

2. type Header struct

Header 用于描述以太坊区块链中的区块头。

  • ParentHash common.Hash: 父区块的哈希

  • UncleHash common.Hash: 叔区块列表的哈希

  • Coinbase common.Address: 矿工地址

  • Root common.Hash: 状态树的根哈希。

  • TxHash common.Has: 事务树的根哈希。

  • ReceiptHash common.Hash: 收据树的根哈希。

  • Bloom Bloom: 布隆过滤器。

  • Difficulty *big.Int: 目标哈希的难度值。

  • Number *big.Int: 区块编号。创始区块的编号为 0.

  • GasLimit uint64: 单个区块可消耗的 gas 上限。

  • GasUsed uint64: 当前区块消耗的 gas 总量。

  • Time *big.Int: 区块创建的时间戳。

  • Extra []byte: 额外数据。可提供一定程度的扩展能力。

  • MixDigest common.Hash: ???

  • Nonce BlockNonce: 使区块头哈希满足目标难度的随机数。

  • 通过方法 Hash() 返回区块头的哈希,哈希只是简单地计算区块头 RLP 编码之后的 keccak256 哈希值。

  • 方法 Size() 返回由所有内部内容使用的内存。被用于评估和限制各种缓存所需的内存消耗。

3. type Body struct

Body 对象是一起存储和移动一个区块的数据内容(事务列表、叔区块列表)的简单(可变地、不安全)容器。

  • Transactions []*Transaction: 事务列表
  • Uncles []*Header: 叔区块列表

4. type Block struct

Block 对象表示以太坊区块链中的整个区块。

  • header *Header: 区块头
  • uncles []*Header: 步区块头列表
  • transactions Transactions: 事务列表

// caches

  • hash atomic.Value

  • size atomic.Value

  • td *big.Int: 被包 core 用于存储链到当前区块(包含)的总难度

// These fields are used by package eth to track inter-peer block relay.

  • ReceivedAt time.Time

  • ReceivedFrom interface{}

  • 通过函数 NewBlock() 创建一个新区块。对输入数据进行深度拷贝,以避免新创建的区块受到参数的变化所影响。

  • 通过函数 NewBlockWithHeader() 根据给定的区块头数据创建一个新的区块。对区块头数据进行了深度拷贝,以避免对区块头的修改会影响到新创建的区块。

  • 通过方法 DecodeRLP() 从 RLP 流中解码出 Block。

  • 通过方法 EncodeRLP() 将 Block 编码成 RLP 流。

  • 通过方法 Uncles() 返回叔区块列表。

  • 通过方法 Transactions() 返回事务列表。

  • 通过方法 Transaction() 返回给定事务哈希对应的事务。

  • 通过方法 Number() 返回区块编号。

  • 通过方法 GasLimit() 返回区块的 header.GasLimit。

  • 通过方法 GasUsed() 返回区块的 header.GasUsed。

  • 通过方法 Difficulty() 返回区块的难度。

  • 通过方法 Time() 返回区块的打包时间。

  • 通过方法 NumberU64() 返回区块编号。

  • 通过方法 MixDigest() 返回区块的 header.MixDigest

  • 通过方法 Nonce() 返回区块的随机数,采用大端编码。

  • 通过方法 Bloom() 返回区块的 header.Bloom。

  • 通过方法 Coinbase() 返回区块的矿工地址。

  • 通过方法 Root() 返回区块的状态树的根哈希。

  • 通过方法 ParentHash() 返回父区块的哈希。

  • 通过方法 TxHash() 返回区块的事务树的根哈希。

  • 通过方法 ReceiptHash() 返回区块的收据树的根哈希。

  • 通过方法 UncleHash() 返回区块的叔区块列表的哈希。

  • 通过方法 Extra() 返回区块的 header.extraData 的深度拷贝。

  • 通过方法 Header() 返回区块头的深度拷贝。

  • 通过方法 Body() 返回区块的非区块头内容。

  • 通过方法 Size() 返回区块采用 RLP 编码后的存储大小。

  • 通过方法 WithSeal() 返回一个新区块,除了区块头用已经被验证的替换之外,其它的都用原来的。

  • 通过方法 WithBody() 返回一个新区块,事务列表和叔区块列表用给定的参数替换,区块头用原区块的。

  • 通过方法 Hash() 返回区块头的 keccak256 哈希。

5. type StorageBlock Block

StorageBlock 定义了存储于状态数据库中的 Block 的 RLP 编码。

  • 通过方法 DecodeRLP() 从 RLP 流中解码出 StorageBlock。

6. type extblock struct

extblock 表示外部区块编码,用于 eth 协议等。

  • Header *Header: 区块头
  • Txs []*Transaction: 事务列表
  • Uncles []*Header: 叔区块列表

7. type storageblock struct

storageblock 表示区块编码,用于 database。

  • Header *Header: 区块头
  • Txs []*Transaction: 事务列表
  • Uncles []*Header: 叔区块列表
  • TD *big.Int: 到当前区块的总难度

8. type writeCounter common.StorageSize

writeCounter 是 common.StorageSize 的包装器,并实现了 io.Writer 接口。而 common.StorageSize 又是 float64 的包装器,并实现了 Stringer 接口。

  • 通过函数 Write() 实现了 io.Writer 接口,计算输入的字节数。
  • 通过函数 CalcUncleHash() 返回叔区块列表的哈希。

9. type Blocks []*Block

Blocks 是 Block 列表的包装器。

10. type BlockBy func(b1, b2 *Block) bool

BlockBy 是函数 func(b1, b2 *Block) bool 的包装器。

  • 通过方法 Sort() 对 Blocks 排序。

11. type blockSorter struct

blockSorter 是区块列表的排序器,封装了待排序对象 Blocks 和元素比较器。

  • blocks Blocks: 待排序对象

  • by func(b1, b2 *Block) bool: 比较两个元素的大小

  • 通过方法 Len() 返回列表中的元素个数。

  • 通过方法 Swap() 交换列表中的两个元素。

  • 通过方法 Less() 返回第 i 个元素是否小于第 j 个元素。

12. 辅助函数

  • 函数 rlpHash() 先对输入 x 进行 RLP 编码,再返回其 Keccak256 哈希。
  • 函数 CalcUncleHash() 计算叔区块列表的哈希。
  • 函数 CopyHeader() 创建了区块头的一个深度拷贝,以避免对区块头修改而产生的副作用。
  • 函数 Number() 返回区块 b1 的编号是否比区块 b2 的编号小。

Appendix B. 详细批注

var

  • EmptyRootHash:表示由空事务列表构成的 MPT 树的根哈希。
  • EmptyUncleHash:表示空叔块列表的哈希。

type BlockNonce [8]byte

64 位(8 个字节)的哈希,与 mix-hash 组合用于证明已经对一个区块进行了足够的计算。

使区块头哈希满足目标难度的随机数。

  1. func EncodeNonce(i uint64) BlockNonce
    函数 EncodeNonce() 将 uint64 类型的值转换为 BlockNonce 类型的值。

  2. func (n BlockNonce) Uint64() uint64
    方法 Uint64() 将 BlockNonce 类型的值转换为 uint64 类型的值。

  3. func (n BlockNonce) MarshalText() ([]byte, error)
    方法 MarshalText() 将 BlockNonce 类型的值编码为 16 进制的字符串,前面带 0x 前缀。

  4. func (n *BlockNonce) UnmarshalText(input []byte) error
    方法 UnmarshalText() 将已经编码后的 input 解码到 BlockNonce 类型的值中。

type Header struct

用于描述以太坊区块链中的区块头。

  • ParentHash common.Hash: 父区块的哈希
  • UncleHash common.Hash: 叔区块列表的哈希
  • Coinbase common.Address: 矿工地址
  • Root common.Hash: 状态树的根哈希。
  • TxHash common.Has: 事务树的根哈希。
  • ReceiptHash common.Hash: 收据树的根哈希。
  • Bloom Bloom: 布隆过滤器。
  • Difficulty *big.Int: 目标哈希的难度值。
  • Number *big.Int: 区块编号。创始区块的编号为 0.
  • GasLimit uint64: 单个区块可消耗的 gas 上限。
  • GasUsed uint64: 当前区块消耗的 gas 总量。
  • Time *big.Int: 区块创建的时间戳。
  • Extra []byte: 额外数据。可提供一定程度的扩展能力。
  • MixDigest common.Hash: ???
  • Nonce BlockNonce: 使区块头哈希满足目标难度的随机数。
  1. func (h *Header) Hash() common.Hash
    方法 Hash() 返回区块头的哈希,哈希只是简单地计算区块头 RLP 编码之后的 keccak256 哈希值。
  • 哈希值的具体计算由 rlpHash() 函数实现。
  1. func (h *Header) Size() common.StorageSize
    方法 Size() 返回由所有内部内容使用的内存。被用于评估和限制各种缓存所需的内存消耗。

func rlpHash(x interface{}) (h common.Hash)

函数 rlpHash() 先对输入 x 进行 RLP 编码,再返回其 Keccak256 哈希。

func CalcUncleHash()

函数 CalcUncleHash() 计算叔区块列表的哈希。

type Body struct

Body 对象是一起存储和移动一个区块的数据内容(事务列表、叔区块列表)的简单(可变地、不安全)容器。

  • Transactions []*Transaction: 事务列表
  • Uncles []*Header: 叔区块列表

type Block struct

Block 对象表示以太坊区块链中的整个区块。

  • header *Header: 区块头
  • uncles []*Header: 步区块头列表
  • transactions Transactions: 事务列表

// caches

  • hash atomic.Value

  • size atomic.Value

  • td *big.Int: 被包 core 用于存储链到当前区块(包含)的总难度

// These fields are used by package eth to track inter-peer block relay.

  • ReceivedAt time.Time
  • ReceivedFrom interface{}
  1. func (b *Block) DeprecatedTd() *big.Int
    此方法已被弃用。

方法 DeprecatedTd() 返回 b.td。

  1. func NewBlock(header Header, txs []Transaction, uncles []Header, receipts []Receipt) *Block
    函数 NewBlock() 创建一个新区块。对输入数据进行深度拷贝,以避免新创建的区块受到参数的变化所影响。

参数 header 中的字段 TxHash, UncleHash, ReceiptHash, and Bloom 被忽略,并根据参数 txs, uncles, and receipts 重新进行计算。

主要实现的具体细节如下:

  • 计算新区块头的事务列表的根哈希,并拷贝事务列表
  • 计算新区块头的收据列表的根哈希,并根据收据列表创建布隆过滤器
  • 计算新区块头的叔区块列表的根哈希,并拷贝叔区块。
  1. func NewBlockWithHeader(header *Header) *Block
    函数 NewBlockWithHeader() 根据给定的区块头数据创建一个新的区块。对区块头数据进行了深度拷贝,以避免对区块头的修改会影响到新创建的区块。

  2. func (b *Block) DecodeRLP(s *rlp.Stream) error
    方法 DecodeRLP() 从 RLP 流中解码出 Block。

主要实现的具体细节如下:

  • 使用 extblock 作为 Block 和其 RLP 编码之间的中间数据结构
  • 将 Block 的 RLP 流解码到临时对象 extblock
  • 从临时对象 extblock 恢复 Block 各字段
  1. func (b *Block) EncodeRLP(w io.Writer) error
    方法 EncodeRLP() 将 Block 编码成 RLP 流。

主要实现的具体细节如下:

  • 使用 extblock 作为 Block 和其 RLP 编码之间的中间数据结构
  • 创建临时对象 extblock,并从 Block 中的拷贝同名字段
  • 将临时对象 extblock 编码成 RLP 流
  1. func (b Block) Uncles() []Header
    方法 Uncles() 返回叔区块列表。

  2. func (b *Block) Transactions() Transactions
    方法 Transactions() 返回事务列表。

  3. func (b *Block) Transaction(hash common.Hash) *Transaction
    方法 Transaction() 返回给定事务哈希对应的事务。

  4. func (b *Block) Number() *big.Int
    方法 Number() 返回区块编号。

  5. func (b *Block) GasLimit() uint64
    方法 GasLimit() 返回区块的 header.GasLimit。

  6. func (b *Block) GasUsed() uint64
    方法 GasUsed() 返回区块的 header.GasUsed。

  7. func (b *Block) Difficulty() *big.Int
    方法 Difficulty() 返回区块的难度。

  8. func (b *Block) Time() *big.Int
    方法 Time() 返回区块的打包时间。

  9. func (b *Block) NumberU64() uint64
    方法 Number() 返回区块编号。

  10. func (b *Block) MixDigest() common.Hash
    方法 MixDigest() 返回区块的 header.MixDigest

  11. func (b *Block) Nonce() uint64
    方法 Nonce() 返回区块的随机数,采用大端编码。

  12. func (b *Block) Bloom() Bloom
    方法 Bloom() 返回区块的 header.Bloom。

  13. func (b *Block) Coinbase() common.Address
    方法 Coinbase() 返回区块的矿工地址。

  14. func (b *Block) Root() common.Hash
    方法 Root() 返回区块的状态树的根哈希。

  15. func (b *Block) ParentHash() common.Hash
    方法 ParentHash() 返回父区块的哈希。

  16. func (b *Block) TxHash() common.Hash
    方法 TxHash() 返回区块的事务树的根哈希。

  17. func (b *Block) ReceiptHash() common.Hash
    方法 ReceiptHash() 返回区块的收据树的根哈希。

  18. func (b *Block) UncleHash() common.Hash
    方法 UncleHash() 返回区块的叔区块列表的哈希。

  19. func (b *Block) Extra() []byte
    方法 Extra() 返回区块的 header.extraData 的深度拷贝。

  20. func (b *Block) Header() *Header
    方法 Header() 返回区块头的深度拷贝。

  21. func (b *Block) Body() *Body
    方法 Body() 返回区块的非区块头内容。

  22. func (b *Block) Size() common.StorageSize
    方法 Size() 返回区块采用 RLP 编码后的存储大小。

主要实现的具体细节如下:

  • 先查看缓存字段 size,如果有缓存则返回缓存的大小
  • 采用 RLP 编码,并计算编码后的大小
  • 更新缓存字段 size
  1. func (b *Block) WithSeal(header *Header) *Block
    方法 WithSeal() 返回一个新区块,除了区块头用已经被验证的替换之外,其它的都用原来的。

主要实现的具体细节如下:

  • 新区块的字段 header 用已被验证的区块头(参数 header)替换
  • 新区块的字段 transactions 拷贝于原区块的字段 transactions
  • 新区块的字段 uncles 拷贝于原区块的字段 uncles
  1. func (b Block) WithBody(transactions []Transaction, uncles []*Header) *Block
    方法 WithBody() 返回一个新区块,事务列表和叔区块列表用给定的参数替换,区块头用原区块的。

主要实现的具体细节如下:

  • 新区块的字段 header 由原区块的 header 填充,使用了函数 CopyHeader()
  • 新区块的字段 transactions 由参数 transactions 填充,使用了函数 copy()
  • 新区块的字段 uncles 由参数 uncles 填充,使用了函数 CopyHeader()
  1. func (b *Block) Hash() common.Hash
    方法 Hash() 返回区块头的 keccak256 哈希。

主要实现的具体细节如下:

  • 先尝试从缓存字段 hash 中恢复,如果成功直接返回
  • 获取区块头的哈希并缓存

type StorageBlock Block

被 eth/63 弃用。

StorageBlock 定义了存储于状态数据库中的 Block 的 RLP 编码。

  1. func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error
    被 eth/63 弃用。

方法 DecodeRLP() 从 RLP 流中解码出 StorageBlock。

主要实现的具体细节如下:

  • 使用 storageblock 作为 StorageBlock 和其 RLP 编码之间的中间数据结构
  • 将 StorageBlock 的 RLP 流解码到临时对象 storageblock
  • 从临时对象 storageblock 恢复 StorageBlock 各字段

type extblock struct

extblock 表示外部区块编码,用于 eth 协议等。

  • Header *Header: 区块头
  • Txs []*Transaction: 事务列表
  • Uncles []*Header: 叔区块列表

type storageblock struct

被 eth/63 弃用。

storageblock 表示区块编码,用于 database。

  • Header *Header: 区块头
  • Txs []*Transaction: 事务列表
  • Uncles []*Header: 叔区块列表
  • TD *big.Int: 到当前区块的总难度

func CopyHeader(h *Header) *Header

函数 CopyHeader() 创建了区块头的一个深度拷贝,以避免对区块头修改而产生的副作用。

主要实现的具体细节如下:

  • 拷贝字段 Time
  • 拷贝字段 Difficulty
  • 拷贝字段 Number
  • 拷贝字段 Extra

type writeCounter common.StorageSize

writeCounter 是 common.StorageSize 的包装器,并实现了 io.Writer 接口。而 common.StorageSize 又是 float64 的包装器,并实现了 Stringer 接口。

  1. func (c *writeCounter) Write(b []byte)
    函数 Write 实现了 io.Writer 接口。

主要实现的具体细节如下:

  • 只是对输入 b 进行了计数

func CalcUncleHash(uncles []*Header) common.Hash

函数 CalcUncleHash() 返回叔区块列表的哈希。

主要实现的具体细节如下:

  • 调用方法 rlpHash() 进行具体的哈希计算。

type Blocks []*Block

Blocks 是 Block 列表的包装器。

type BlockBy func(b1, b2 *Block) bool

BlockBy 是函数 func(b1, b2 *Block) bool 的包装器。

  1. func (self BlockBy) Sort(blocks Blocks)
    方法 Sort() 对 Blocks 排序。

主要实现的具体细节如下:

  • BlockBy 是具体的排序函数
  • Blocks 是待排序的对象
  • 调用函数 sort.Sort() 进行最终的排序

type blockSorter struct

blockSorter 是区块列表的排序器,封装了待排序对象 Blocks 和元素比较器。

  • blocks Blocks: 待排序对象
  • by func(b1, b2 *Block) bool: 比较两个元素的大小
  1. func (self blockSorter) Len() int
    方法 Len() 返回列表中的元素个数。

  2. func (self blockSorter) Swap(i, j int)
    方法 Swap() 交换列表中的两个元素。

  3. func (self blockSorter) Less(i, j int) bool
    方法 Less() 返回第 i 个元素是否小于第 j 个元素。

func Number(b1, b2 *Block) bool

函数 Number() 返回区块 b1 的编号是否比区块 b2 的编号小。

Reference

  1. https://github.com/ethereum/go-ethereum/blob/master/core/types/block.go

Contributor

  1. Windstamp, https://github.com/windstamp
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容