首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

RLP 递归长度前缀

RLP(recursive length prefix):递归长度前缀。

RLP编码是以太坊中主要的序列化格式,它的使用无处不在:区块、交易、账户状态以及线路协议消息。

RLP旨在成为高度简化的序列化格式,它唯一的目的是存储嵌套的字节数组。不同于protobuf、BSON等现有的解决方案,RLP并不定义任何指定的数据类型,如Boolean、floa、double或者integer。它仅仅是以嵌套数组的形式存储结构,并将其留给协议来确定数组的含义。RLP也没有明确支持map集合,半官方的建议是采用 [[k1, v1], [k2, v2], ...] 的嵌套数组来表示键值对集合,k1,k2 ... 按照字符串的标准排序。

与RLP具有相同功能的方案是或,它们是一直被使用的算法。然而,以太坊中,更偏向于使用RLP,因为:(1)它易于实现;(2)绝对保证字节的一致性。许多语言的Map集合没有明确的排序,并且浮点格式有很多特殊情况,这可能造成相同数据却导致不同编码和hash值。通过内部开发协议,我们能确保它是带着这些目标设计的(这是一般原则,也适用于代码的其他部分,如VM)。BitTorrent使用的编码方式也许可以替代RLP。不过它采用的是十进制的编码方式,与采用二进制的RLP相比,稍微逊色了点。

RLP定义

RLP编码功能只处理两类数据:字符串(字节数组)和列表(list)。

可以是:空字符串""、包含单词"cat"的字符串、包含任意数量字符串的列表(如,["cat","dog"])以及更复杂的数据结构。请注意,“字符串”将表示为“一定数量字节的二进制数据”的同义词。

RLP编码规则

对于值在范围内的单个字节,编码就是本身。

如果一个字符串的长度是0-55字节,其RLP编码是前缀再拼接字符串本身,前缀的值是加上字符串的长度。前缀取值范围是。

如果一个字符串的长度大于55字节,编码结果为:。本规则下前缀的取值范围是;

以上3个规则是针对字符串的,接下来的两个规则针对列表的。由于列表是任意嵌套的,因此列表的编码是递归的,先编码最里层列表,再逐步往外层列表编码。如果列表长度小于55,编码结果第一位是加列表长度的编码的长度,然后依次连接各子列表的编码。本规则下前缀的取值范围是。

如果列表长度超过55,编码结果第一位是加列表长度的编码长度,然后是列表长度本身的编码,最后依次连接各子列表的编码。编码的第一个字节的取值范围是。

代码如下:

defrlp_encode(input):ifisinstance(input,str):

iflen(input) ==1andord(input)

else:returnencode_length(len(input),0x80) + input

elifisinstance(input,list):

output =''foritemininput: output += rlp_encode(item)

returnencode_length(len(output),0xc0) + output

defencode_length(L,offset):ifL

returnchr(L + offset)

elifL

returnchr(len(BL) + offset +55) + BL

else:

raiseException("input too long")

defto_binary(x):ifx ==:

return''else:returnto_binary(int(x /256)) + chr(x %256)RLP解码规则

根据RLP编码规则和过程,RLP解码的输入一律视为二进制字符数组,其过程如下:

根据输入首字节数据,解码数据类型、实际数据长度和位置;

根据类型和实际数据,解码不同类型的数据;

继续解码剩余的数据;

其中,解码数据类型、实际数据类型和位置的规则如下:

如果首字节(prefix)的值在[0x00, 0x7f]范围之间,那么该数据是字符串,且字符串就是首字节本身;

如果首字节的值在[0x80, 0xb7]范围之间,那么该数据是字符串,且字符串的长度等于首字节减去,且字符串位于首字节之后;

如果首字节的值在[0xb8, 0xbf]范围之间,那么该数据是字符串,且字符串的长度的字节长度等于首字节减去,数据的长度位于首字节之后,且字符串位于数据的长度之后;

如果首字节的值在[0xc0, 0xf7]范围之间,那么该数据是列表,在这种情况下,需要对列表各项的数据进行递归解码。列表的总长度(列表各项编码后的长度之和)等于首字节减去,且列表各项位于首字节之后;

如果首字节的值在[0xf8, 0xff]范围之间,那么该数据为列表,列表的总长度的字节长度等于首字节减去,列表的总长度位于首字节之后,且列表各项位于列表的总长度之后;

代码如下:

defrlp_decode(input):iflen(input) ==:

returnoutput =''(offset, dataLen, type) = decode_length(input)

iftypeisstr: output = instantiate_str(substr(input, offset, dataLen))

eliftypeislist: output = instantiate_list(substr(input, offset, dataLen)) output + rlp_decode(substr(input, offset + dataLen))

returnoutput

defdecode_length(input):length = len(input)

iflength ==:

raiseException("input is null") prefix = ord(input[])

ifprefix

return(,1, str)

elifprefix prefix -0x80: strLen = prefix -0x80return(1, strLen, str)

elifprefix prefix -0xb7andlength > prefix -0xb7+ to_integer(substr(input,1, prefix -0xb7)): lenOfStrLen = prefix -0xb7strLen = to_integer(substr(input,1, lenOfStrLen))

return(1+ lenOfStrLen, strLen, str)

elifprefix prefix -0xc0: listLen = prefix -0xc0;

return(1, listLen, list)

elifprefix prefix -0xf7andlength > prefix -0xf7+ to_integer(substr(input,1, prefix -0xf7)): lenOfListLen = prefix -0xf7listLen = to_integer(substr(input,1, lenOfListLen))

return(1+ lenOfListLen, listLen, list)

else:

raiseException("input don't conform RLP encoding form")

defto_integer(b)length=len(b)iflength== 0:raiseException("input is null")

eliflength ==1:

returnord(b[])

else:

returnord(substr(b, -1)) + to_integer(substr(b,, -1)) *256参考链接

ethereum/wiki:RLP

RLP编码和解码

以太坊源码学习—RLP编码

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180601G1KK2L00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券