概述
在 Ethereum 中,RLP 算法被用于编码任意嵌套的字节数组,下面是一个任意嵌套字节数组的例子:
1 | ["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"] |
RLP 算法用来编码 ( 序列化 ) 其结构,使其变为字节序列 ( 二进制串 ) 。下面是一个简单的转化例子 ( 使用 <
和 >
表示序列和数组区分 ) :
1 | [ [], [[]], [ [], [[]] ] ] = < 0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0 > |
上面例子中,RLP 算法将嵌套的数组转化为一个字节序列 ( 编码 ),可以方便的进行存储和传输,同时,该字节序列还记录了原始数据的结构及其中的数据,可以方便地将数据还原出来,这个过程称为解码。本文将对编码算法进行说明。
算法流程
字符串 ( 无嵌套的字节数组 ) 编码
- 对于字符串中的一个字节的值,如果其值在
[0x00, 0x7f]
之间 ( 即[0, 127]
,为 ASCII 表中的字符的取值范围 ),其 RLP 编码值为其本身。
例如,对于 "dog"
中的每个字符,则有:
1 | RLP('d') = 'd' |
完成上述步骤后再考虑字符串的长度:
- 如果一个字符串长度
str_len
在 0 到 55 字节,还需要在字符串每个字符编码结束后的结果前添加一个字节,这个字节的值为0x80 + str_len
( 即128 + str_len
,128 为偏移值 )。 - 如果一个字符串长度
str_len
大于 55 ( 0x37 ) 字节,则需要:- 首先得到字符串长度十六进制表示的字节数
len_of_str_len
。 - 在每个字节编码结束后的结果前添加两个部分,一个是
0x80 + 0x37 + len_of_str_len
, 另一个是str_len
的字节编码形式结果。
- 首先得到字符串长度十六进制表示的字节数
例如,字符串 "dog"
,长度为 3 ( 0x03 ),对于每个字符编码得到的结果为 ['d', 'o', 'g']
,由于其长度为 3 个字节 小于 55 字节,在其结果前添加 0x80 + str_len = 0x80 + 0x03 = 0x83
,所以有 ( len 函数用来输出输入的字节数 ):
1 | len("dog") = 3 = 0x03 |
另一个例子是字符串 "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
,其长度为 56 ( 0x38,一个字节 ) 大于 55,首先计算长度值 56 的字节数为 1:
1 | str_len = len("Lorem ipsum dolor sit amet, consectetur adipisicing elit") |
然后在每个字符编码结束后的结果前添加两部分,第一部分为 0x80 + 0x37 + 长度值的字节数
( 占用一个字节 ),第二部分为 长度值
( 占 长度值的字节数
个字节 ),所以有:
1 | RLP("Lorem ipsum dolor sit amet, consectetur adipisicing elit") |
列表编码
对于列表使用下面规则进行编码:
- 首先需要对列表中的字符串和列表均进行编码 ( 字符串编码使用上一节的规则 ) 得到的列表是内部无嵌套的字节数组。
- 如果列表长度(字节数)
list_len
在 0 到 55 字节,在列表中内容的 RLP 编码结果前添加一个字节,其值为0xc0 + list_len
。 - 如果列表长度
list_len
大于 55 字节,则需要:- 首先得到列表长度十六进制表示的字节数
len_of_list_len
。 - 在每个字节编码结束后的结果前添加两个部分,一个是
0xc0 + 0x37 + len_of_list_len
, 另一个是list_len
的字节编码形式结果。
- 首先得到列表长度十六进制表示的字节数
以字符串数组 ["dog", "dog"]
举例,先对 dog 进行编码得到:
1 | RLP("dog") = < 0x83, 'd', 'o', 'g' > |
当前的字符串转化结果为:
1 | ["dog", "dog"] -> < 0x83, 'd', 'o', 'g', 0x83, 'd', 'o', 'g' > |
此时编码字节数为 8 ( 0x08 ) 个字节,小于 55 个字节,因此只需添加一个字节,其内容为 0xc0 + list_len
,所以有:
1 | RLP(["dog", "dog"]) = RLP([RLP("dog"), RLP("dog")]) |
对于较长的数组操作相似使用 3 中描述的方式添加前缀,不再赘述。
总结
对于字符串使用下面规则进行编码:
- 对于字符串中的一个字节的值,如果其值在
[0x00, 0x7f]
之间 ( 即[0, 127]
,为 ASCII 表中的字符的取值范围 ),其 RLP 编码值为其本身。 - 如果一个字符串长度
str_len
在 0 到 55 字节,还需要在字符串每个字符编码结束后的结果前添加一个字节,这个字节的值为0x80 + str_len
( 即128 + str_len
,128 为偏移值 )。 - 如果一个字符串长度
str_len
大于 55 ( 0x37 ) 字节,则需要:- 首先得到字符串长度十六进制表示的字节数
len_of_str_len
。 - 在每个字节编码结束后的结果前添加两个部分,一个是
0x80 + 0x37 + len_of_str_len
, 另一个是str_len
的字节编码形式结果。
- 首先得到字符串长度十六进制表示的字节数
对于列表使用下面规则进行编码:
- 首先需要对列表中的字符串和列表均进行编码 ( 字符串编码使用上一节的规则 ) 得到的列表是内部无嵌套的字节数组。
- 如果列表长度(字节数)
list_len
在 0 到 55 字节,在列表中内容的 RLP 编码结果前添加一个字节,其值为0xc0 + list_len
。 - 如果列表长度
list_len
大于 55 字节,则需要:- 首先得到列表长度十六进制表示的字节数
len_of_list_len
。 - 在每个字节编码结束后的结果前添加两个部分,一个是
0xc0 + 0x37 + len_of_list_len
, 另一个是list_len
的字节编码形式结果。
- 首先得到列表长度十六进制表示的字节数