RLP(Recursive Length Prefix,递归长度前缀)是一种编码算法,用于编码任意的嵌套结构的二进制数据,它是以太坊中数据序列化/反序列化的主要方法,区块、交易等数据结构在持久化时会先经过RLP编码后再存储到数据库中。
定义
RLP编码的定义只处理两类数据:一类是字符串(例如字节数组),一类是列表。字符串指的是一串二进制数据,列表是一个嵌套递归的结构,里面可以包含字符串和列表,例如["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]
就是一个复杂的列表。其他类型的数据需要转成以上的两类,转换的规则不是RLP编码定义的,可以根据自己的规则转换,例如struct
可以转成列表,int
可以转成二进制(属于字符串一类),以太坊中整数都以大端形式存储。
从RLP编码的名字可以看出它的特点:一个是递归,被编码的数据是递归的结构,编码算法也是递归进行处理的;二是长度前缀,也就是RLP编码都带有一个前缀,这个前缀是跟被编码数据的长度相关的,从下面的编码规则中可以看出这一点。
RLP编码规则
- 对于单个字节,如果它的值范围是
[0x00, 0x7f]
,它的RLP编码就是它本身。 - 否则,如果一个字符串的长度是0-55字节,它的RLP编码包含一个单字节的前缀,后面跟着字符串本身,这个前缀的值是**
0x80
**加上字符串的长度。由于被编码的字符串最大长度是55=0x37
,因此单字节前缀的最大值是0x80+0x37=0xb7
,即编码的第一个字节的取值范围是[0x80, 0xb7]
。 - 如果字符串的长度大于55个字节,它的RLP编码包含一个单字节的前缀,后面跟着字符串的长度,后面再跟着字符串本身。这个前缀的值是**
0xb7
**加上字符串长度的二进制形式的字节长度,说的有点绕,举个例子就明白了,例如一个字符串的长度是1024,它的二进制形式是10000000000
,这个二进制形式的长度是2个字节,所以前缀应该是0xb7+2=0xb9
,字符串长度1024=0x400
,因此整个RLP编码应该是xb9x04x00
再跟上字符串本身。编码的第一个字节即前缀的取值范围是[0xb8, 0xbf]
,因为字符串长度二进制形式最少是1个字节,因此最小值是0xb7+1=0xb8
,字符串长度二进制最大是8个字节,因此最大值是0xb7+8=0xbf
。 - 如果一个列表的总长度(列表的总长度指的是它包含的项的数量加它包含的各项的长度之和)是0-55字节,它的RLP编码包含一个单字节的前缀,后面跟着列表中各元素项的RLP编码,这个前缀的值是**
0xc0
**加上列表的总长度。编码的第一个字节的取值范围是[0xc0, 0xf7]
。 - 如果一个列表的总长度大于55字节,它的RLP编码包含一个单字节的前缀,后面跟着列表的长度,后面再跟着列表中各元素项的RLP编码,这个前缀的值是**
0xf7
**加上列表总长度的二进制形式的字节长度。编码的第一个字节的取值范围是[0xf8, 0xff]
。
RLP编码例子
- 字符串
"dog" = [0x83, 'd', 'o', 'g' ]
(规则二) - 列表
["cat","dog"] = [0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g' ]
(规则四) - 空字符串
"" = 0x80
(规则二) - 空列表
[] = [0xc0]
(规则四) - 整数
15('x0f') = 0x0f
(规则一) - 整数
1024('x04 0') = [0x82, 0x04, 0x00]
(规则二) - 列表
[ [], [[]], [ [], [[]] ] ] = [0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]
(规则四) - 字符串
"Lorem ipsum dolor sit amet, consectetur adipisicing elit" = [0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ... , 'e', 'l', 'i', 't']
(规则三)
本文大部分翻译自以太坊github wiki文档,并加入自己的理解。
参考资料:https://github.com/ethereum/wiki/wiki/%5BEnglish%5D-RLP