以太坊源码研读0x07 Block

前面看了以太坊的交易模块,而交易都是要打包在区块上的。Block是Eth上存储价值信息的核心数据结构之一。

一个完整的Block大概包括以下几部分:

  • 1.所有账户的相关活动,都是以Transaction格式存储,每个Block有一个Tx的列表

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

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

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

  • 5.Block结构体基本可分为Header和Body两个部分

废话少说撸代码

Block结构

// 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.
    // totalDifficulty 区块总难度  当前区块难度值 = td - lastBlock.td
    td *big.Int

    // These fields are used by package eth to track
    // inter-peer block relay.
    // 出块时间
    ReceivedAt   time.Time
    // 区块体
    ReceivedFrom interface{}
}

一个Block的唯一标识符就是它的hash,这里的hash是指其Header内容的RLP哈希值。在第一次计算后会缓存到hash值里。

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

这里每一个Block都有一个BlockHeader,Header是Block的核心,它的成员变量全都是公共的,可以很方便的向调用者提供关于Block属性的操作。

// Header represents a block header in the Ethereum blockchain.
type Header struct {
    // 父区块hash,即链上上一个区块的Hash
    ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"`
    // 叔块集合uncles的RLP哈希值
    UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"`
    // 挖出区块的矿工地址
    Coinbase    common.Address `json:"miner"            gencodec:"required"`
    // MPT状态树根哈希
    Root        common.Hash    `json:"stateRoot"        gencodec:"required"`
    // 交易树根节点RLP哈希值
    TxHash      common.Hash    `json:"transactionsRoot" gencodec:"required"`
    // 收据树根节点RLP哈希值
    ReceiptHash common.Hash    `json:"receiptsRoot"     gencodec:"required"`
    // Bloom过滤器(Filter),用来快速判断一个参数Log对象是否存在于一组已知的Log集合中
    Bloom       Bloom          `json:"logsBloom"        gencodec:"required"`
    // 区块难度
    Difficulty  *big.Int       `json:"difficulty"       gencodec:"required"`
    // 区块序号,相当于Bitcoin的Height
    Number      *big.Int       `json:"number"           gencodec:"required"`
    // 区块内所有Gas消耗的理论上限
    GasLimit    uint64         `json:"gasLimit"         gencodec:"required"`
    // 区块内所有Transaction执行时所实际消耗的Gas总和
    GasUsed     uint64         `json:"gasUsed"          gencodec:"required"`
    // 出块时间
    Time        *big.Int       `json:"timestamp"        gencodec:"required"`
    // 额外数据
    Extra       []byte         `json:"extraData"        gencodec:"required"`
    // 用于POW
    MixDigest   common.Hash    `json:"mixHash"          gencodec:"required"`
    // 用于POW 结合MixDigest生成区块哈希值
    Nonce       BlockNonce     `json:"nonce"            gencodec:"required"`
}

此外,以太坊将一个Block中的交易集合和叔块集合单独封装到一个Body结构中,因为他们相对于Header需要更多的内存空间,在传输和验证时为了节省时间可以和Header分开进行。

// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
// Body可以理解为Block里的数组成员集合,它相对于Header需要更多的内存空间,
// 所以在数据传输和验证时,往往与Header是分开进行的。
type Body struct {
    Transactions []*Transaction
    Uncles       []*Header
}

我们注意到,这里相比Bitcoin多了一个叔块(uncle)的概念。这里可以参考官方对叔块的解释。

叔块,顾名思义就是跟自己的父区块在一个高度上。我们知道相比Bitcoin,Eth将出块时间缩短到了15s左右。这样在庞大的P2P网络中就增大了同时出现同一高度区块的概率,这样就有可能使得大批矿工因为产生这样的区块而得不到奖励。因此,以太坊引入了叔块的概念,一个叔块该满足的条件为:

  • 1.叔块必须是B的第K层祖先,2<= k <= 7
  • 2.叔块不能是B的祖先
  • 3.叔块必须拥有合法的block Header
  • 4.叔块必须是不曾被包含进区块链过的

这样,以太坊的激励机制就有所改变。当一个矿工挖到一个普通区块B时,若该区块拥有叔块,该矿工将除固定区块奖励外额外再得到固定区块奖励/32*count(uncles)的奖励。
同时,曾经挖出叔块的矿工也将获得(Number(uncle)+8-Number(B))*固定区块奖励/8的叔块奖励。我们举个简单的小🌰:

A挖到一个Number为20的Block,该区块由两个叔块uncle1(B挖出Number=18)和uncle2(C挖出Number=13),此时的奖励分配为:

  • A = 3 + 3/32*2 = 3.1875eth

  • B = (18 + 8 - 20) * 3 / 8 = 3*6/8 = 2.25eth

  • C = (13 + 8 - 20) * 3 / 8 = 3*1/8 = 0.375eth

关于区块奖励的源码今天不是重点,这里只是稍微提一下。

Block基本操作

首先来看添加新Block的操作,代码逻辑清晰简单。

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

至此,关于Block数据结构的源码就看完了。

更多以太坊源码解析请移驾全球最大同性交友网,觉得有用记得给个小star哦😯😯😯

.
.
.
.

互联网颠覆世界,区块链颠覆互联网!

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

推荐阅读更多精彩内容