win10下开发部署Dapp(5):Ethereum块结构以及RLP编码

  以太坊的块分为块头与交易列表两大部分,使用RLP(Recursive Length Prefix,递归长度前缀)将原数据编码。我们先学习RLP,再结合一个私链上的以太坊原数据块,分析以太坊块结构。(这里使用go-ethereum代码库,分析以太坊链上的块的结构。主要的代码位于 core/types目录下)

RLP

RLP编码的目的是为了能够嵌套任意数量、层数的二进制数据,至于里面的二进制数据的具体类型是整数还是浮点数、字符串,则留给上层协议处理。

RLP中共有两种数据类型:

  • 字节串。(Github上原文是string,应翻译成字符串,但实质上是一个字符数组,此处简称字节串)。
  • 列表。列表内的每一个元素,要么是一个列表,要么是字节串。很明显,列表的定义是递归的。

字节串跟列表有不同的编码规则。具体如下:

  • 对于值在该[0x00, 0x7f]范围内的单个字节,该字节内的值即为其自己的RLP编码。
  • 长度为0-55的字节串,RLP编码由两部分组成: [前缀字节] [ 字节串] ,前缀字节的值为 0x80 + 字节串长度。因此,第一个字节的范围[0x80, 0xb7]。反过来,当我们拿到一段首字节在[0x80, 0xb7]范围内的RLP数据时,我们就可以肯定,这段RLP数据内存放着一个0-55长度的字节串。
  • 长度超过55的字节串,RLP编码由三大部分构成:[前缀字节] [长度字节] [字节串]。前缀字节长度为1,值为:0xb7 + 长度字节的长度。长度字节里自然是存放原始字节串的长度,原始串越长,长度字节占用空间越多,最多8字节,故第一个字节的范围[0xb8, 0xbf]。例如,长度为1024(0x400)的字符串将被编码为[0xb9] [0x0400] […..1024byte字节串…..]。
  • 以上三个规则是针对字节(串)的,接下来两个则针对列表。如果一个列表内的所有元素的RLP编码顺序拼接后的长度在[0,55]之间,则该列表的RLP编码由两部分组成: [前缀字节] [ 列表元素RLP的拼接] 。本规则类似于第二条规则,前缀字节的值为:0xc0 + 第二部分的长度.此时,前缀字节的范围为[0xc0, 0xf7]。
  • 列表内的所有元素的RLP编码顺序拼接后的长度超过55,RLP编码由三大部分构成:[前缀字节] [长度字节] [列表元素RLP的拼接]。本规则类似于第三条规则,前缀字节值:0xf7 + 长度字节长度。此时,前缀字节的范围为[0xf8, 0xff]。

简单来说,当我们拿到一段RLP数据,仅用第一字节的值即可判断出原始数据的类型:

前缀字节值N的范围 原数据类型
[0x00, 0x7f] 单字节数据,原始数据的值即为N
[0x80, 0xb7] 长度为0-55的字节串,具体长度为:N - 0x80
[0xb8, 0xbf] 长度大于55的字节串,具体长度存储于首字节之后的m个字节中,m = N - 0xb7
[0xc0, 0xf7] 长度为0-55的列表(准确的说应该是所有元素的RLP编码的长度之和为0-55的列表),具体长度为:N - 0xc0
[0xf8, 0xff] 长度大于55的列表,具体长度存储于首字节之后的m个字节中,m = N - 0xf7

接下来通过go代码看Ethereum的块头结构

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

Header中共有15个字段,这15个字段会按照声明的顺序依次编码到block的RLP数据中。我们拿到一段只有块头的RLP编码:

0xf90211a0b1fc9aeb47e1beb1fa57e42ddf57374e51b77b907d8af294f7e73790774b8420a01dcc4de8dec75d7aab85b567
b6ccd41ad312451b948a7413f0a142fd40d4934794a56974882eb32f7782c51755887f130e0b9e0f40a012e8ab925c8e0a7e
931081061df222b5ecc786e3ac6f578d2be3da3a0dbac5bba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001
622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083
0b31c8823fac8347e7c480845a9f927298d783010703846765746885676f312e398777696e646f7773a0cff35326dc5bbdbe
5e6011808cfe73ea6d531fa65c3ded2fa001a00eb328a63e885a5dee2c9d65d7be

经过分段后,我们可以清楚的看到块头的哥哥字段的具体数据:

f9      //块头长度大于55的列表,具体长度存于接下来的m个字节中,m = 0xf9 - 0xf7 = 2
0211    //块头长度为0x211=529,也就是接下来的529字节全是块头里的元素的RLP编码
a0      //长度小于55的字节串,具体长度为:0xa0 - 0x80 = 0x20 = 32byte, 即:接下来32字节为ParentHash
b1fc9aeb47e1beb1fa57e42ddf57374e51b77b907d8af294f7e73790774b8420
a0      //接下来32字节为UncleHash
1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
94      //长度小于55的字节串,具体长度为:0x94 - 0x80 = 0x14 = 20byte, 即:接下来32字节为CoinBase
a56974882eb32f7782c51755887f130e0b9e0f40
a0      //Root Hash
12e8ab925c8e0a7e931081061df222b5ecc786e3ac6f578d2be3da3a0dbac5bb
a0      //TxHash
56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
a0      //ReceiptHash
56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
b9      //长度大于55的字节串,具体长度存于接下来的m个字节中,m = b9 - 0xb7 = 2
0100    //字节串长度为0x100=256byte, Bloom
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
83      //长度小于55的字节串,具体长度为 0x83 - 0x80 = 3, Difficulty
0b31c8
82      //再往下懒得写了
3fac
83
47e7c4
80
84
5a9f9272
98
d783010703846765746885676f312e398777696e646f7773
a0
cff35326dc5bbdbe5e6011808cfe73ea6d531fa65c3ded2fa001a00eb328a63e885a5dee2c9d65d7be

你可能感兴趣的:(BlockChain,Go)