RLP(RecursiveLength Prefix)是以太坊中序列化数据的编码方式。
既然是编码方式,下面结合代码详细看看RLP的奥秘:
官方定义:
RLP encoding is defined as follows:
字面意思: 对于[0x00, 0x7f]范围内的单字节数据,RLP编码后的数据和编码之前是一样的。
有没有想过为什么是0x7f呢? 打开ASCII编码表会惊奇的发现ASCII编码的最大值就是0x7f, 所以RLP这个编码在[0x00, 0x7f]之内复用了ASCII编码,就是把数据当成ASCII编码使用了。
下面用以太坊的Python版本看看是不是这样:
字面意思: RLP编码包含一个单字节的前缀,后面跟着字符串本身,这个前缀的值是0x80加上字符串的长度。
想想为什么是0xb7的上界? 0x37的10进制数就是55, 所以单字节前缀表示的最大值就是0x80+0x37=0xb7, 事实上这个前缀表示的长度是以字节为单位的。
下面依然用Python验证一下:
上面的截图中"hello"占用5个字节,所以前缀变成了: 0x85。
字面意思: RLP编码包含一个单字节的前缀,后面跟着字符串的长度,后面再跟着字符串本身; 其中前缀的值是0xb7加上字符串长度的二进制形式的字节长度。
结构像这个样子: | 前缀 | 字符串长度 | 字符串本身 |
前缀的计算举一个例子: 如果一个字符串长度是156个字节(26个英文字母连续输入6遍), 用2进制表示十进制的156==10011100, 1个字节就可以表示156了。
所以前缀是: 0xb7+1 = 0xb8
长度是: 0x9c
则结果就是 | 0xb8 | 0x9c | 重复6遍的字母表 |
下面我们用Python验证一下:
以上是对单字节 和 字符串数据的编码说明, 接下来的两项编码规则是对list而言的。
开始之前,需要定义什么是list的总长度: 包含的各项的长度之和 + 包含的项的数量
例如: ["abc","def","ghi"] 的总长度是: 3+9=12(0xc)
字面意思: RLP编码包含一个单字节的前缀,后面跟着列表中各元素项的RLP编码, 前缀的值是0xc0加上列表的总长度。
前缀取值范围是[0xc0, 0xf7], 同理 0xf7== 十进制(192+55), 其中 0xc0的十进制是 192。
以["abc","def","ghi"] 为例, 其RLP编码为: | 0xcc | 三个字符串的RLP |
Python验证:
字面意思: 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验证一下:
如果细致一点, 你会发现上面的测试用0x01D代表了0x0144, 没错十六进制的0x44就是对应ASCII的D字母。