为了账号安全,请及时绑定邮箱和手机立即绑定

以太坊源码研究之RLP编码

标签:
ZBrush

这是以太坊源码研究的第一篇文章。基本上来说,我写什么内容,说明我正好在学习什么内容,并没有固定的顺序。之所以先写RLP编码,是因为在一开始研究以太坊交易结构的时候,就遇上RLP编码了,所谓拣上不如撞上,就从它开始吧。

RLP(Recursive Length Prefix)是以太坊中广泛运用的一种编码方法,用于序列化以太坊中各类对象。RLP可以把任意嵌套的二进制数据数组,都编码成为一个“扁平”的无嵌套的byte数组。

任意嵌套的二进制数据数组,可能是一个空字符串“”,可能是一个整数比如36,也可能是一个非常复杂的数据结构,比如["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]。对于这些千变万化的数据,RLP到底是怎么进行编码的呢?其主要规则如下:

  1. 如果是单个字节,并且值在[0x00, 0x7f]范围内,则RLP编码就是它自己。这个好理解,不用解释。

  2. 如果是长度不超过55的字符串,那么RLP编码的第一个字节用来指定字符串长度,其值为0x80加上字符串长度,之后再跟整个字符串。因此,第一个字节值范围从0x80到0xb7,空字符串时为0x80,长度55的字符串为0x80+0x37=0xb7。
    例:“dog"编码后为4个字节:[0x83, 'd', 'o', 'g']

  3. 如果是长度超过55的字符串,这时字符串的长度可能会很长,甚至超过一个字节。RLP规定这种情况下,从第二个字节开始存储字符串的长度,将长度所花的字节数再加上0xb7的和作为第一个字节,字符串长度之后再跟字符串的内容。
    例:"Lorem ipsum dolor sit amet, consectetur adipisicing elit"字符串一共56个字节,编码结果为58字节:[ 0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ... , 'e', 'l', 'i', 't' ]。其中,0x38即56为字符串长度,0xb8为0xb7+1,1就是0x38这个长度本身的长度也就是1个字节。

  4. 如果是长度不超过55的列表,那么RLP编码的第一个字节是0xc0加上列表长度,后跟列表内容。因此,第一个字节值范围从0xc0到0xf7。
    例: [ "cat", "dog" ]列表,编码后为[ 0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g' ]。其中0xc8为0xc0+8,8就是两个字符串的总长度。

  5. 如果是长度超过55的列表,那么RLP编码的第一个字节是0xf7加上列表长度的字节长度,后跟列表总长度,再跟列表项目内容的串联。因此,第一个字节的范围[0xf8, 0xff]。

再举个稍微复杂的例子:[ [ ], [ [ ] ], [ [ ], [ [ ] ] ] ] 的编码结果是 [ 0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0 ]。

编码过程用python语言来描述是下面这样的:

def rlp_encode(input):
    if isinstance(input,str):        if len(input) == 1 and ord(input) < 0x80: return input        else: 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) + outputdef 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    else:         raise Exception("input too long")def to_binary(x):
    if x == 0:        return ''
    else: 
        return to_binary(int(x / 256)) + chr(x % 256)

下面再看解码。解码其实就是编码的逆过程,每次读入第一个字节时判断其类型,计算出长度和偏移量,再解构具体数字。其具体规则如下:

  1. 如果第一个字节的范围是[0x00,0x7f],则数据是一个字符串,且字符串本身就是第一个字节;

  2. 如果第一个字节的范围是[0x80,0xb7],则数据是一个字符串,其长度等于第一个字节减去0x80,字符串内容在第一个字节之后;

  3. 如果第一个字节的范围是[0xb8,0xbf],则数据是一个字符串,并且字节长度等于第一个字节减去0xb7,字符串长度在第一个字节之后,字符串跟随长度串;

  4. 如果第一个字节的范围是[0xc0,0xf7],则数据是一个列表,其总长度等于第一个字节减去0xc0,列表中各项目的RLP编码的串联在第一个字节之后;

  5. 如果第一个字节的范围是[0xf8,0xff],则数据是一个列表,字节长度等于第一个字节减去0xf7,列表总长度在第一个字节之后,再接下来是所有项目的RLP编码串联。

例子就不再举了。解码过程的python代码如下:

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 outputdef 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)    else:        raise Exception("input don't conform 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])    else: return ord(substr(b, -1)) + to_integer(substr(b, 0, -1)) * 256



作者:魏兆华
链接:https://www.jianshu.com/p/f960553742ae


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消