【以太坊源码解析】-区块数据结构

jincheng828 · · 948 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

区块数据结构

在区块链中,区块是存储有价值信息的块。这是任何一种加密货币的本质。除此之外,区块还包含一些技术信息,比如它的版本、当前时间戳和前一区块的散列值(哈希值)

Block(区块)是Ethereum的核心数据结构之一

* 所有账户的相关活动,以交易(Transaction)的格式存储,每个Block有一个交易对象的列表

* 每个交易的执行结果,由一个Receipt对象与其包含的一组Log对象记录

* 所有交易执行完后生成的Receipt列表,存储在Block中(经过压缩加密)

* 不同Block之间,通过前向指针ParentHash一个一个串联起来成为一个单向链表,BlockChain 结构体管理着这个链表

* Block结构体基本可分为Header和Body两个部分

Block: 表示以太坊区块链中的一个完整块

type Block struct {
    header       *Header
    uncles       []*Header        // 块头(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{}        // 块体(Body)
}
在存储区块信息时,会将区块头和区块体分开进行存储。因此在区块的结构体中,能够看到Header和Body两个结构体

Header: 表示以太坊区块链中的块头

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

Body: 以太坊区块链中的交易信息

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

Header部分

Header是Block的核心,它的成员变量全都是公共的,可以很方便的向调用者提供关于Block属性的操作。Header的成员变量全都很重要,值得细细理解:

  • ParentHash:指向父区块(parentBlock)的指针。除了创世块(Genesis Block)外,每个区块有且只有一个父区块。
  • UncleHash:Block结构体的成员uncles的RLP哈希值。uncles是一个Header数组,它的存在,颇具匠心。
  • Coinbase:挖掘出这个区块的作者地址。在每次执行交易时系统会给与一定补偿的Ether,这笔金额就是发给这个地址的。
  • Root:StateDB中的“state Trie”的根节点的RLP哈希值。Block中,每个账户以stateObject对象表示,账户以Address为唯一标示,其信息在相关交易(Transaction)的执行中被修改。所有账户对象可以逐个插入一个Merkle-PatricaTrie(MPT)结构里,形成“state Trie”。
  • TxHash: Block中 “tx Trie”的根节点的RLP哈希值。Block的成员变量transactions中所有的tx对象,被逐个插入一个MPT结构,形成“tx Trie”。
  • ReceiptHash:Block中的 "Receipt Trie”的根节点的RLP哈希值。Block的所有Transaction执行完后会生成一个Receipt数组,这个数组中的所有Receipt被逐个插入一个MPT结构中,形成"Receipt Trie"。
  • Bloom:Bloom过滤器(Filter),用来快速判断一个参数Log对象是否存在于一组已知的Log集合中。
  • Difficulty:区块的难度。Block的Difficulty由共识算法基于parentBlock的Time和Difficulty计算得出,它会应用在区块的‘挖掘’阶段。
  • Number:区块的序号。Block的Number等于其父区块Number +1。
  • GasLimit:区块内所有Gas消耗的理论上限。该数值在区块创建时设置,与父区块有关。具体来说,根据父区块的GasUsed同GasLimit * 2/3的大小关系来计算得出。
  • GasUsed:区块内所有Transaction执行时所实际消耗的Gas总和。
  • Time:区块“应该”被创建的时间。由共识算法确定,一般来说,要么等于parentBlock.Time + 10s,要么等于当前系统时间。
  • Nonce:一个64bit的哈希数,它被应用在区块的"挖掘"阶段,并且在使用中会被修改

Body结构体

Block的成员变量td 表示的是整个区块链表从源头创世块开始,到当前区块截止,累积的所有区块Difficulty之和,td 取名totalDifficulty。从概念上可知,某个区块与父区块的td之差,就等于该区块Header带有的Difficulty值。

Body可以理解为Block里的数组成员集合,它相对于Header需要更多的内存空间,所以在数据传输和验证时,往往与Header是分开进行的。

Uncles是Body非常特别的一个成员,从业务功能上说,它并不是Block结构体必须的,它的出现当然会占用整个Block计算哈希值时更长的时间,目的是为了抵消整个Ethereum网络中那些计算能力特别强大的节点会对区块的产生有过大的影响力,防止这些节点破坏“去中心化”这个根本宗旨。官方描述可见ethereum-wiki

Block的唯一标识符

Block对象的唯一标识符,就是它的(RLP)哈希值。需要注意的是,Block的哈希值,等于其Header成员的(RLP)哈希值

// core/types/block.go
// 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()    // 调用Head成员的Hash
    b.hash.Store(v)
    return v
}
Block的成员hash会缓存上一次Header计算出的哈希值,以避免不必要的计算
// 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)
}
func rlpHash(x interface{}) (h common.Hash) {
   hw := sha3.NewKeccak256()
   rlp.Encode(hw, x)
   hw.Sum(h[:0])
   return h
}

添加新块:

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

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:jincheng828

查看原文:【以太坊源码解析】-区块数据结构

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

948 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传