RLP序列化
RLP序列化(Recursive Length Prefix 递归长度前缀)
科大讯飞星火 AI大语言模型:
RLP编码,全称是Recursive Length Prefix,是一种在区块链系统中常用的数据编码方式。它是由以太坊的开发者Gavin Wood提出的,用于解决在区块链中存储和传输数据的问题。
RLP编码的主要特点是它可以处理任意长度的数据(实际上支持的最大字符串长度为2^64,相当之大所以说任意),并且可以递归地处理嵌套的数据结构。这使得它非常适合于处理复杂的数据结构,如区块链中的交易信息、智能合约等。
RLP编码的基本规则是:对于每一个数据项,首先将其转换为字节数组,然后计算字节数组的长度,并将长度编码为一个或多个字节(如果长度小于55,则直接编码为一个字节;如果长度大于等于55,则先编码长度的长度,然后再编码长度本身)。最后,将长度编码和字节数组连接起来,就得到了这个数据项的RLP编码。
RLP编码的一个优点是它的解码过程非常简单:只需要从左到右扫描编码结果,根据第一个字节的值就可以确定下一个数据项的长度,然后取出相应长度的字节数组,就得到了这个数据项的原始数据。
以太坊 RECURSIVE-LENGTH PREFIX (RLP) SERIALIZATION :
递归长度前缀的目的在于,对任意嵌套的二进制数据数组进行编码,而递归长度前缀是用于序列化以太坊执行层中对象的主要编码方法。 递归长度前缀的唯一目的是对结构进行编码;而对特定数据类型(例如字符串、浮点数)进行编码的工作,则留给高阶协议;但正递归长度前缀整数必须以不带前导零的大端序二进制形式表示(从而使整数值零相当于空字节数组)。 带有前导零的反序列化正整数被视为无效。 字符串长度的整数表示也必须以这种方式编码,有效载荷中的整数也是如此。
为什么还要提出一种新的编码?
对象序列化方法有很多种,如JSON编码,但这些编码结果都比较大,所以提出RLP。
编码
RLP编码前需要将对象映射成字节数组或列表。对于int类型需要去掉前导零,使用大端模式。
Rules
数据类型 | 条件(逐行匹配) | RLP | RLP首字节范围 |
---|---|---|---|
字节数组X | len(X)=1 && X∈[0x00, 0x7f] | X | [0x00, 0x7f] |
字节数组X | len(X)∈[0, 55] | 0x80+len(X), X... | [0x80, 0xb7] |
字节数组X | len(X)>55 | 0xb7+len(len(X)), len(X), X... | [0xb8, 0xbf] |
列表I | len(RLP(I))∈[0, 55] | 0xc0+len(RLP(I)), RLP(I)... | [0xc0, 0xf7] |
列表I | len(RLP(I))>55 | 0xf7+len(len(RLP(I))), len(RLP(I)), RLP(I)... | [0xf8, 0xff] |
len()返回int64,最大8字节。表格中RLP列中的len(X)返回的数据长度是整数类型,且以大端模式的最小字节数组表示。
任意X,len(X) ∈[0, 2^64],len(len(X))∈[1, 8]。
Examples
- 整数
0 = [0x80]
- 整数
1 = [0x01]
- 整数
1024('0x04,0x00') = [0x82, 0x04, 0x00]
- 字符串
"" = ['0x80']
- 字符串
"d" = ['d']
- 字符串
"dog" = [0x83, 'd', 'o', 'g']
- 56位字符串
"Lorem ipsum dolor sit amet, consectetur adipisicing elit" = [ 0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ... , 'e', 'l', 'i', 't' ]
- 列表
[] = [0xc0]
- 列表
["cat","dog"] = [0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g']
- 嵌套列表
[ [], [[]], [ [], [[]] ] ] = [0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]
- 大列表
["cat", "Lorem ipsum dolor sit amet, consectetur adipisicing elit"] = [0xf8, 0x3e, 0x83, 'c', 'a', 't', 0xb8, 0x38, 'L', 'o', 'r', ..., 't']
Code
def rlp_encode(input):
if isinstance(input,str):
if len(input) == 1 and ord(input) < 0x80:
return input
return encode_length(len(input), 0x80) + input
elif isinstance(input, list):
output = ''
for item in input:
output += rlp_encode(item)
return encode_length(len(output), 0xc0) + output
def encode_length(L, offset):
if L < 56:
return chr(L + offset)
elif L < 256**8:
BL = to_binary(L)
return chr(len(BL) + offset + 55) + BL
raise Exception("input too long")
def to_binary(x):
if x == 0:
return ''
return to_binary(int(x / 256)) + chr(x % 256)
解码
根据编码,二进制数组结果的形式为{prefix, [size], [data]},其中prefix在五中规则中分别占用
128,56,8,56,8
个值,正好1字节,由此通过RLP编码的首字节(前缀)即可判断类型
Rules
首字节prefix | 类型 | 解码规则 |
---|---|---|
[0, 127] | 字节数组 | 无需解码,直接返回 |
[128, 183] | 字节数组 | 无需递归解码,len=prefix-128,首字节后len个字节 |
[184, 191] | 字节数组 | 无需递归解码,len_len=prefix-183,首字节后len_len个字节大端模式编码后的整数为字节数组的长度len,再往后取len字节 |
[192, 247] | 列表 | 需递归解码,len=len(RLP(I))=prefix-192,取首字节后len个字节为列表RLP编码值,编码值首字节为列表第一个元素的prefix,递归解码 |
[248, 255] | 列表 | 需递归解码,len_len=len(len(RLP(I)))=prefix-247,取首字节后len_len个字节为列表RLP编码值的长度len,再往后取len字节即为RLP编码值,编码值首字节为列表第一个元素的prefix,递归解码 |
Code
def rlp_decode(input):
if len(input) == 0:
return
output = ''
(offset, dataLen, type) = decode_length(input)
if type is str:
output = instantiate_str(substr(input, offset, dataLen))
elif type is list:
output = instantiate_list(substr(input, offset, dataLen))
output += rlp_decode(substr(input, offset + dataLen))
return output
def decode_length(input):
length = len(input)
if length == 0:
raise Exception("input is null")
prefix = ord(input[0])
if prefix <= 0x7f:
return (0, 1, str)
elif prefix <= 0xb7 and length > prefix - 0x80:
strLen = prefix - 0x80
return (1, strLen, str)
elif prefix <= 0xbf and length > prefix - 0xb7 and length > prefix - 0xb7 + to_integer(substr(input, 1, prefix - 0xb7)):
lenOfStrLen = prefix - 0xb7
strLen = to_integer(substr(input, 1, lenOfStrLen))
return (1 + lenOfStrLen, strLen, str)
elif prefix <= 0xf7 and length > prefix - 0xc0:
listLen = prefix - 0xc0;
return (1, listLen, list)
elif prefix <= 0xff and length > prefix - 0xf7 and length > prefix - 0xf7 + to_integer(substr(input, 1, prefix - 0xf7)):
lenOfListLen = prefix - 0xf7
listLen = to_integer(substr(input, 1, lenOfListLen))
return (1 + lenOfListLen, listLen, list)
raise Exception("input does not conform to RLP encoding form")
def to_integer(b):
length = len(b)
if length == 0:
raise Exception("input is null")
elif length == 1:
return ord(b[0])
return ord(substr(b, -1)) + to_integer(substr(b, 0, -1)) * 256