win10下开发部署Dapp(5):Ethereum块结构以及RLP编码
2018-03-20 17:40
344 查看
以太坊的块分为块头与交易列表两大部分,使用RLP(Recursive Length Prefix,递归长度前缀)将原数据编码。我们先学习RLP,再结合一个私链上的以太坊原数据块,分析以太坊块结构。(这里使用go-ethereum代码库,分析以太坊链上的块的结构。主要的代码位于 core/types目录下)
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 + [b]长度字节长度。此时,前缀字节的范围为[0xf8, 0xff]。
简单来说,当我们拿到一段RLP数据,仅用第一字节的值即可判断出原始数据的类型:
接下来通过go代码看Ethereum的块头结构
Header中共有15个字段,这15个字段会按照声明的顺序依次编码到block的RLP数据中。我们拿到一段只有块头的RLP编码:
经过分段后,我们可以清楚的看到块头的哥哥字段的具体数据:
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 + [b]长度字节长度。此时,前缀字节的范围为[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 |
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
相关文章推荐
- win10下开发部署Dapp(0):从零开始
- Win10下开发部署Dapp(4):solidity快速入门
- win10下开发部署Dapp(1):搭建私链
- win10下开发部署Dapp(2):编写、部署第一个合约
- win10下开发部署Dapp(6):发布ERC20标准的token
- 织梦dedeCMS二次开发文档手册 程序目录详解以及数据表结构字段
- [毕业生的商业软件开发之路]C#表达式以及判断语法结构
- 百度地图开发之定位以及反地理编码获取周围地理位置
- 如何部署Android开发环境以及解决部署中遇到的问题
- C\S结构的插件式开发思想以及向B\S结构的架构延伸(一)
- Dubbox 的编译 部署 以及开发
- 1.TomCat配置以及JavaWeb开发的目录结构
- Servlet的部署开发细节以及注意事项
- [毕业生的商业软件开发之路]C#表达式以及判断语法结构 推荐
- [绍棠] iOS开发- 文件共享(利用iTunes导入文件, 并且显示已有文件) 以及 iOS App与iTunes文件传输的方法和对iOS App文件结构的说明
- iOS开发之利用MVVM框架来优化项目结构。对Controller瘦身以及MVC向MVVM框架的迁移。
- C\S结构的插件式开发思想以及向B\S结构的架构延伸(二)
- C\S结构的插件式开发思想以及向B\S结构的架构延伸
- linux下部署JavaWeb应用以及mysql目录结构