以太坊区块链中的数据结构

以太坊

以太坊是一个基于交易的状态机,其区块链中的每个区块就对应一个状态,每产生一个区块,以太坊中的状态就会转换到下一个状态。通过状态转换使得运行以太坊中的所有节点保持数据的一致性。以太坊中的区块链分布式存在,但与传统的分布式却截然不同。


从创世块开始,不断产生的交易持续刷新系统当前状态,每当产生一个区块,系统状态就发生一次转换。

以太坊中区块的结构

区块由三大部分组成:区块头(Block Header),叔块(Uncle),交易列表(tx_List)。如下图所示。

  • 区块头由15个字段组成。
  • 叔块其实就是孤块,因以太坊出块速度很快平均十几秒就会打包生成一个块,所以矿工挖矿的竞争性很高,可能同时产出几个都合法的区块,以太坊为了一些安全性起见,允许竞争块也挂在到主链上,同时给与挖出这些孤块的矿工们少许奖励增加工作的公平性。具体参见以太坊中的Ghost协议
  • 交易列表,存储的是本区块中所有的交易内容。

看一下一个实际的区块信息:

"blocks" : [
    {
    "blockHeader" : {
          "bloom" :  "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
                      0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
                      0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
                      0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
                      0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
                      000000000000000",
       "coinbase" : "0x3535353535353535353535353535353535353535",
     "difficulty" : "0x020000",
      "extraData" : "",
       "gasLimit" : "0x05f5e100",
        "gasUsed" : "0x014fa1",
           "hash" : "0x39f4659b079e257df8fd7e699528531e97a6b8a442ca0d11200c4a2f7433c483",
        "mixHash" : "0x7379f33af4ae2db7e293f808a165135d0b1a99572cc96fb9f7d17ef64a751969",
          "nonce" : "0x8e08d7aabeee8773",
         "number" : "0x01",
     "parentHash" : "0xadbef3bf0b3b7b14f6e7b1a45d240ecc863543a279a86c23f60170e8e7a6bcc3",
    "receiptTrie" : "0xb21660268480338c0cd0613358315359b619bd527d5850949c4863cddaec316b",
      "stateRoot" : "0xde4ce9b5b2f88ab1680962c64281224b1743bdf94bd6a9e390ea779ff616c1f7",
      "timestamp" : "0x03e8",
      "transactionsTrie" : "0x56445ba866f3e41851154fb8700dcec8556a178f1833021e030b8a47b494769d",
      "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
                },
    "rlp" : "0xf90308f901f9a0adbef3bf0b3b7b14f6e7b1a45d240ecc863543a279a86c23f60170e8e7a6bcc3a01dcc4de8dec75d7aab85b56
    7b6ccd41ad312451b948a7413f0a142fd40d49347943535353535353535353535353535353535353535a0de4ce9b5b2f88ab168096
    2c64281224b1743bdf94bd6a9e390ea779ff616c1f7a056445ba866f3e41851154fb8700dcec8556a178f1833021e030b8a47b4947
    69da0b21660268480338c0cd0613358315359b619bd527d5850949c4863cddaec316bb901000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000000000000000000000000000000000083020000018405f5e10083014fa18203e880a07379f33af4a
    e2db7e293f808a165135d0b1a99572cc96fb9f7d17ef64a751969888e08d7aabeee8773f90108f90105460183030d4094c305c9010
    78781c232a2a521c2af7980f8385ee980b8a430c8d1da0000000000000000000000000000000000000000000000000000000000000
    0200000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000
    0000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b
    85045b68181585d2833e84879b9709143e1f593f00000001ba021a28cc82b40931239f8653ffa5300e1a506c0ef7fb79a663772caf
    e6558ab44a075af23441f7f176a2770af41142c77b671391209b15d59144e7a1332179b5e14c0",
    "transactions" : [
                        {
            "data" :
                    "0x30c8d1da0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000
                     0000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2833e84879b
                     9709143e1f593f0000000",
        "gasLimit" : "0x030d40",
        "gasPrice" : "0x01",
           "nonce" : "0x46",
               "r" : "0x21a28cc82b40931239f8653ffa5300e1a506c0ef7fb79a663772cafe6558ab44",
               "s" : "0x75af23441f7f176a2770af41142c77b671391209b15d59144e7a1332179b5e14",
              "to" : "0xc305c901078781c232a2a521c2af7980f8385ee9",
               "v" : "0x1b",
           "value" : "0x00"
        }
        ],
       "uncleHeaders" : []
                    }
                   ]

区块头结构

以太坊中每个区块的结构和比特币中每个区块的结构却不同,以太坊中每个区块的结构比比特币种区块的结构更加复杂,太坊中区块的区块头(Block Header)结构定义如下图所示。

这里写图片描述

区块头中每个字段的意义如下:

名称 类型 意义
parentHash common.Hash 父区块的哈希值
UncleHash common.Hash 叔父区块列表的哈希值
Coinbase common.Address 打包该区块的矿工的地址,用于接收矿工费
Root common.Hash 状态树的根哈希值
TxHash common.Hash 交易树的根哈希值
ReceiptHash common.Hash 收据树的根哈希值
Bloom Bloom 交易收据日志组成的Bloom过滤器
Difficulty *Big.Int 本区块的难度
Number *Big.Int 本区块块号,区块号从0号开始算起
GasLimit uint64 本区块中所有交易消耗的Gas上限
GasUsed uint64 本区块中所有交易使用的Gas之和
Time *big.Int 区块产生的unix时间戳
Extra []byte 区块的附加数据
MixDigest common.Hash 哈希值,与Nonce的组合用于工作量计算
Nonce BlockNonce 区块产生时的随机值

和比特币中的区块链一样,以太坊中的区块链仍然使用了parentHash连接前一个区块,通过这个字段,所有的区块最终都能回溯到创始块。

区块头中比较重要的三个字段是Root、TxHash和ReceiptHash三个哈希值。其中Root表示以太坊网络中全部有过转账交易的账户形成的Merkle partial Tree(MPT)的根哈希值。为什么是全部有个转账交易的账户呢,因为我们可以随便创建一个以太坊账户,但是没有发生交易的话,以太坊节点也不会知道这个账户的存在。TxHash是本区块中所有交易形成的merkle tree的根哈希值,ReceiptHash是本区块中所有收据信息构成的merkle tree 的哈希值。这其中涉及到的Merkle tree的相关概念可以参考比特币区块链中的数据结构,里面有merkle tree数据结构的介绍。接下来重点介绍Merkle Partial Tree。

Merkle Patricia Tree(默克尔-帕特里夏树)

比特币是基于交易的分布式账本,而以太坊是基于账户的分布式账本,相比于比特币,以太坊设计的更加复杂。以太坊中会对所有有过转账交易的账户都会建立一个用户状态树,每个运行以太坊客户端的节点都会在本地维护一个用户状态树,当有新的区块打包到区块链上之后,所有的以太坊节点(挖出区块的节点除外)的就会执行最新区块中的交易,对本地用户的状态树进行维护,得到新的用户状态树。用户状态树的信息并不保存在区块中,区块中仅仅保存了最新的状态树组成的根哈希值。

关于MPT的介绍,网上资料众多,但我认为比较容易理解的文章,可参见以太坊MPT原理,和这篇Understand ethereum’s trie,这两篇是网上对于MPT的解释比较到位的文章。
值得说明的是,以太坊中状态树之间的指针式哈希指针,这样从底层的value建立哈希指针,再对其父节点建立哈希指针,以此类推,直到最终的状态树merkle root。
每次发布新的区块链时,每个存有状态树的节点就会根据区块中的交易更新对应的账户树中的内容,每个节点根据区块上的交易独立的修改状态树,最后生成状态树的根哈希值,随后用本地生成的状态树哈希值和新发布区块中的状态树的Root字段进行比较,如果相同,表示账户状态和发布区块的节点的账户状态保持了一致。这也是以太坊中每个节点独立运行却又能维持区块链数据的一致性的方法。

状态树的变化可以用下图所示。每个状态发生改变时,都会新建一个节点,但是原有的状态树仍然会保存,保存原有状态的原因是因为以太坊出快时间是十几秒,在十几秒钟很容易出现多个合法的区块,就会出现分叉,分叉的区块最终合并之后,有一些分叉链上的节点的状态就需要回退,保存历史记录就是为了方便回退。

这里写图片描述

参考阅读:
以太坊的数据结构
以太坊MPT原理

猜你喜欢

转载自blog.csdn.net/t46414704152abc/article/details/81348162
今日推荐