以太坊中的RLP编码

文章分为2部分, 第一部分是综合整理已有资料而生成的参考文档, 第二部分是python版以太坊代码中的源码实现分析.

RLP概述

RLP (递归长度前缀)适用于任意二进制数据数组的编码。是以太坊中数据序列化/反序列化的主要方法,区块、交易等数据结构在 网络传输和持久化 时会先经过RLP编码后再存储到数据库中。

相比json编码, RLP编码占用空间小,几乎没有冗余信息。

RLP编码的定义只处理两类数据:

  • 二进制数据的数组(字符串,字节数组,整形等)

  • 列表(二进制数据的数组的数组,即一个嵌套递归的结构,里面可以包含字符串和列表)

    例如

    ["cat",["puppy","cow"],"horse",[[]],"pig",[""],"sheep"]
    
    

    就是一个复杂的列表。

其他类型的数据需要转成以上的两类结构,例如struct可以转成列表,int可以转成字符串,也可以不转,字典可以转换成例如 : [[k1,v1],[k2,v2]...]

RLP编码规则

总体可以概括为: 内容 (单字节) , 前缀+内容 (总长<55) , 或 前缀+长度+内容 (总长>55)

  1. 规则1(内容). . [0x00, 0x7f] 范围内的 单个字节 , RLP 编码内容就是字节内容本身。

例子:

  • ‘a’ = 0x61
  • 整数 15('\x0f') = 0x0f
  1. 规则2(前缀+内容). . 0-55字节长度的字符串,RLP编码是 前缀(0x80+len(字符串))+字符串内容

例子:

  • abc编码结果是0x83 0x61 0x62 0x63,其中0x83=0x80+len("abc")。
  • 整数 1024('\x04\00') = [0x82, 0x04, 0x00]
  • 空字符串 "" = 0x80
  • 字符串 "dog" = [0x83, 'd', 'o', 'g' ]
  1. 规则3(前缀+长度+内容) . >55字节长度字符串, RLP编码是 前缀(0xb7+len(len(字符串)))+len(字符串)+字符串内容

例子:

  • 字符串 "Lorem ipsum dolor sit amet, consectetur adipisicing elit" = [0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ... , 'e', 'l', 'i', 't']
  1. 规则4(前缀+内容). . 列表的总长度(列表的总长度指的是它包含的项的数量加它包含的各项的长度之和)是0-55字节,它的RLP编码是 前缀(0xc0+len(列表总))+列表中各元素项的RLP编码 ,前缀取值范围是 [0xc0, 0xf7]

例子:

  • 列表 ["cate","dog"] = [0xc9, 0x84, 'c', 'a', 't', 'e',0x83, 'd', 'o', 'g' ]

    0xc9 = 0xc0+ 1+4+1+3 (1:字符串长度的长度, 4:字符串长度, 1:字符串长度的长度,3:字符串长度)

  • 列表 [ [], [[]], [ [], [[]] ] ] = [0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0]

  • 规则5(前缀+长度+内容). ** ** 列表的总长度大于55字节,它的RLP编码是 前缀(0xf7+len(len(列表总)))+len(列表总)+列表中各元素项的RLP编码 ,前缀取值范围是 [0xf8, 0xff]

    例子:

    • 列表 ["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"] = [0xf8 0x58 0xb3 'T','h','e',...

      0xf8=0xf7+1 (1:列表总长度的长度)

      0x58=0x56+1+1 (0x56:列表总长度 1+1: 2个字符串长度的长度)

      0xb3=0x80+0x33 (33: 字符串长度)

综合编码的例子:

["abc",["The length of this sentence is more than 55 bytes, ", "I know it because I pre-designed it"]]

= [0xf8 0x5e 0x83 0x61 98 99 248 88 179 84 104 101 32 108 101 110 103 116 104 32 111 102 32 116 104 105 115 32 115 101 110 116 101 110 99 101 32 105 115 32 109 111 114 101 32 116 104 97 110 32 53 53 32 98 121 116 101 115 44 32 163 73 32 107 110 111 119 32 105 116 32 98 101 99 97 117 115 101 32 73 32 112 114 101 45 100 101 115 105 103 110 101 100 32 105 116]

0xf8=0xf7+1 (1:列表总长度的长度)

0x5e=90 + 1 +2 +1 (90:字符串总长度 1:第一个字符串前缀长度 2: 第二个字符串前缀+长度 1: 第三个字符串前缀)

RLP解码规则

  1. 如果f∈ [0,128), 那么它是一个字节本身。

2. 如果f∈[128,184),那么它是一个长度不超过55的byte数组,数组的长度为 l=f-128

3. 如果f∈[184,192),那么它是一个长度超过55的数组,长度本身的编码长度 ll=f-183 ,然后从第二个字节开始读取长度为ll的bytes,按照BigEndian编码成整数l,l即为数组的长度。

4. 如果f∈(192,247],那么它是一个编码后总长度不超过55的列表,列表长度为 l=f-192 。递归使用规则1~4进行解码。

5. 如果f∈(247,256],那么它是编码后长度大于55的列表,其长度本身的编码长度 ll=f-247 ,然后从第二个字节读取长度为ll的bytes,按BigEndian编码成整数l,l即为子列表长度。然后递归根据解码规则进行解码。

以太坊中的RLP编解码

python版以太坊中, 关于RLP编解码独立出来一个专门的项目: https://github.com/ethereum/pyrlp

主要流程包括 序列化和编码 , 以及 **解码和反序列化. **

总入口为:

  • encode(obj, sedes=None, infer_serializer=True)

    obj : 待编码的对象

    sedes : 表示显式地指定编码对象类型(例如 binary, list 等)

    infer_serializer : 默认根据obj类型自动进行序列化, 如果置为false,表示对对象直接进行rlp编码

  • decode(rlp, sedes=None, strict=True, **kwargs)

项目分为几大功能模块, 按照执行流程依次如下:

  • codec/lazy: 实现RLP编码解码的接口定义和实现

    • encode流程
      1. 根据obj类型进行序列化: item = infer_sedes(obj).serialize(obj)
      2. infer_sedes 返回支持RLP编码的类型: big_endian_int; binary; List ,如果是dict, 需要先转化为List类型. 详见下面.
      3. serialize 调用对应类型的序列化接口, 执行序列化.详见下面.
      4. 编码: encode_raw(item) ,编码规则同上描述. 对于List类型, 对每个元素都进行 递归编码 : payload = b''.join(encode_raw(x) for x in item)
      5. 根据长度生成编码前缀: prefix = length_prefix(len(payload), prefix_offset) , 规则同上描述.
      6. 返回前缀和编码内容: return prefix + payload
  • sedes: 定义了可以序列化/反序列化的对象类型,并且顶一个各自的序列化/反序列化的方法:

    • Binary: 确定长度的二进制数据

      序列化方法: str_to_bytes(obj)

    • BigEndianInt: 确定长度的大端二进制数据

      序列化方法: b'\x00' * max(0, <序列化长度> - len(int_to_big_endian(obj))) + int_to_big_endian(obj)

    • List: 每个成员都需要支持序列化

      序列化方法: [sedes.serialize(element) for element, sedes in zip(obj, self)]

  • utils: 数据类型之间的相互转换的接口

    • decode_hex(s): 将16进制编码的字节数组串转换为字符串 例如: "616263" => "abc"
    • encode_hex(s): 将字符串转换成16进制编码的字节数组串, 例如: "abc" => "616263"
    • int_to_big_endian(value): 整形数据转换为大端字节编码的字符串
    • big_endian_to_int(value): 大端字节编码的字符串转换为整形数据
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 字符集和编码简介 在编程中常常可以见到各种字符集和编码,包括ASCII,MBCS,Unicode等字符集。确切的说...
    兰山小亭阅读 8,453评论 0 13
  • GitHub上介绍(解码部分为本人编辑): https://github.com/ethereum/wiki/wi...
    AlbertGou阅读 3,038评论 1 3
  • 1.恭请的玛尼石今天收到了,心里满满的感恩!拿简单仔细的剪开快递盒子,哇,客服好仔细,好认真,为了减少运输过程中的...
    碧霞阅读 382评论 0 0
  • 支付宝的9.9版上线之后引来吐槽不断,似乎是打算在社交+电商这条路一路走到黑。关于支付宝和微信之争已有数不胜数的分...
    产品超人阅读 2,895评论 3 19