大家好,欢迎来到IT知识分享网。
以太坊的块分为块头与交易列表两大部分,使用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
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/21431.html