浅析RLP-以太坊的数据编码方式

概述

RLP(RecursiveLength Prefix)是以太坊中序列化数据的编码方式。
既然是编码方式,下面结合代码详细看看RLP的奥秘:

官方定义:

  • RLP encoding is defined as follows:

    • For a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding.
    • Otherwise, if a string is 0-55 bytes long, the RLP encoding consists of a single byte with value 0x80 plus the length of the string followed by the string. The range of the first byte is thus [0x80, 0xb7].
    • If a string is more than 55 bytes long, the RLP encoding consists of a single byte with value 0xb7 plus the length in bytes of the length of the string in binary form, followed by the length of the string, followed by the string. For example, a length-1024 string would be encoded as xb9x04x00 followed by the string. The range of the first byte is thus [0xb8, 0xbf].
    • If the total payload of a list (i.e. the combined length of all its items being RLP encoded) is 0-55 bytes long, the RLP encoding consists of a single byte with value 0xc0 plus the length of the list followed by the concatenation of the RLP encodings of the items. The range of the first byte is thus [0xc0, 0xf7].
    • If the total payload of a list is more than 55 bytes long, the RLP encoding consists of a single byte with value 0xf7 plus the length in bytes of the length of the payload in binary form, followed by the length of the payload, followed by the concatenation of the RLP encodings of the items. The range of the first byte is thus [0xf8, 0xff].

编码规则

[0x00, 0x7f]范围内的单字节数据

字面意思: 对于[0x00, 0x7f]范围内的单字节数据,RLP编码后的数据和编码之前是一样的。

有没有想过为什么是0x7f呢? 打开ASCII编码表会惊奇的发现ASCII编码的最大值就是0x7f, 所以RLP这个编码在[0x00, 0x7f]之内复用了ASCII编码,就是把数据当成ASCII编码使用了。

下面用以太坊的Python版本看看是不是这样:

浅析RLP-以太坊的数据编码方式


长度在55个字节以内的字符串

字面意思: RLP编码包含一个单字节的前缀,后面跟着字符串本身,这个前缀的值是0x80加上字符串的长度。
想想为什么是0xb7的上界? 0x37的10进制数就是55, 所以单字节前缀表示的最大值就是0x80+0x37=0xb7, 事实上这个前缀表示的长度是以字节为单位的。

下面依然用Python验证一下:

浅析RLP-以太坊的数据编码方式

上面的截图中"hello"占用5个字节,所以前缀变成了: 0x85。


长度超过55字节的字符串

字面意思: RLP编码包含一个单字节的前缀,后面跟着字符串的长度,后面再跟着字符串本身; 其中前缀的值是0xb7加上字符串长度的二进制形式的字节长度。
结构像这个样子: | 前缀 | 字符串长度 | 字符串本身 |

前缀的计算举一个例子: 如果一个字符串长度是156个字节(26个英文字母连续输入6遍), 用2进制表示十进制的156==10011100, 1个字节就可以表示156了。
所以前缀是: 0xb7+1 = 0xb8
长度是: 0x9c
则结果就是 | 0xb8 | 0x9c | 重复6遍的字母表 |

下面我们用Python验证一下:

浅析RLP-以太坊的数据编码方式


以上是对单字节 和 字符串数据的编码说明, 接下来的两项编码规则是对list而言的。
开始之前,需要定义什么是list的总长度: 包含的各项的长度之和 + 包含的项的数量
例如: ["abc","def","ghi"] 的总长度是: 3+9=12(0xc)


list总长度为0 ~ 55字节

字面意思: RLP编码包含一个单字节的前缀,后面跟着列表中各元素项的RLP编码, 前缀的值是0xc0加上列表的总长度。
前缀取值范围是[0xc0, 0xf7], 同理 0xf7== 十进制(192+55), 其中 0xc0的十进制是 192。

以["abc","def","ghi"] 为例, 其RLP编码为: | 0xcc | 三个字符串的RLP |

Python验证:

浅析RLP-以太坊的数据编码方式


list总长度大于55字节

字面意思: RLP编码包含: 一个单字节的前缀, 后面跟list的长度,后面再跟list中各元素项的RLP编码; 前缀的值是0xf7加上list总长度的二进制形式的字节长度(和规则3表达的计算方式相同)。
第一个前缀的取值范围是: [0xf8, 0xff], 则这个规则能表示的list最大长度是: 8字节表示的最大整数(0xff = 0xf7+0x08)。

以一个总长度超过55字节的list为例:
我们看看存储了6次a~z的list怎么表达呢? ["abc....z", "abc...z", "abc...z","abc....z", "abc...z", "abc...z"]
按照上面的规则描述, list总长度是: 0xa2(16进制), 10100010(2进制,1字节可表示), 162(10进制)

则: 前缀: 0xf7+0x01=0xf8

  总长度: 0xa2

如果是12次a~z的list呢? 总长度: 324,需要2字节表示(00000001 01000100)。
则: 前缀: 0xf7+0x02=0xf9

  总长度: 0x0144

Python验证一下:

浅析RLP-以太坊的数据编码方式

如果细致一点, 你会发现上面的测试用0x01D代表了0x0144, 没错十六进制的0x44就是对应ASCII的D字母。


代码分析

上一篇:【Hexo异常】fatal: in unpopulated submodule '.deploy_git'


下一篇:蚂蚁大规模 Sigma 集群 Etcd 拆分实践