以太坊源码剖析(1)-RLP编码

luca · · 1314 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前言

RLP(Recursive Length Prefix,递归长度前缀)是一种编码算法,用于编码任意的嵌套结构的二进制数据,它是以太坊中数据序列化/反序列化的主要方法,区块、交易等数据结构在持久化时会先经过RLP编码后再存储到数据库中,RLP编码的定义只处理两类数据:一类是字符串(例如字节数组),一类是列表。

RLP与JSON的比较

话不多说,直接上代码

package main

import (
    "encoding/json"
    "fmt"
    "rlp"
)

type Pig struct {
    Name    string
    Gender  uint8
    Address string
}

func main() {
    pig := &Pig{Name: "piggy", Gender: 2, Address: "England"}
    json_bytes, _ := json.Marshal(pig)
    fmt.Println(json_bytes)
    fmt.Println(string(json_bytes))
    fmt.Println("length:", len(json_bytes))
    //output
    //[123 34 78 97 109 101 34 58 34 112 105 103 103 121 34 44 34 71 101 110 100 101 114 34 58 50 44 34 65 100 100 114 101 115 115 34 58 34 69 110 103 108 97 110 100 34 125]
    //{"Name":"piggy","Gender":2,"Address":"England"}
    //length: 47

    rlp_bytes, _ := rlp.EncodeToBytes(pig)
    fmt.Println(rlp_bytes)
    fmt.Println("length:", len(rlp_bytes))
    //output
    //[207 133 112 105 103 103 121 2 135 69 110 103 108 97 110 100]
    //length: 16
}

从上面的输出我们可以看到,同一个结构体,JSON编码需要47个字节,而RLP编码只需要16个字节,{"Name":"piggy","Gender":2,"Address":"England"}中的Name,Gender,Address都是不需要的。当然JSON编码也有自己的优点,这里不展开说明。

定义

RLP 编码函数接受一个item。定义如下:

  • 将一个字符串作为一个item(比如,一个 byte 数组)
  • 一组item列表(list)作为一个item

例如,一个空字符串可以是一个item,一个字符串"cat"也可以是一个item,一个含有多个字符串的列表也行,列表里面嵌套列表也可以,比如这样的["monkey",["tony","kong"],"horse",[[]],"pig",[""],"fish"]。

RLP 编码定义如下:

类型 首字节范围 编码内容
[0x00, 0x7f]的单个字节 [0x00, 0x7f] 字节内容本身
0-55 字节长的字符串 [0x80, 0xb7] 0x80加上字符串长度,后跟字符串二进制内容
超过55个字节的字符串 [0xb8, 0xbf] 0xb7加上字符串长度的长度,后跟字符串二进制内容
0-55个字节长的列表(所有项的组合长度) [0xc0, 0xf7] 0xc0加上所有的项的RLP编码串联起来的长度得到的单个字节,后跟所有的项的RLP编码的串联组成。
列表的内容超过55字节 [0xf8, 0xff] 0xC0加上所有的项的RLP编码串联起来的长度的长度得到的单个字节,后跟所有的项的RLP编码串联起来的长度的长度,再后跟所有的项的RLP编码的串联组成

例子

  • 字符串 "pig" = [ 0x83, 'p', 'i', 'g' ]
  • 列表 [ "pig", "dog" ] = [ 0xc8, 0x83, 'p', 'i', 'g', 0x83, 'd', 'o', 'g' ]
  • 空字符串 ('null') = [ 0x80 ]
  • 空列表 = [ 0xc0 ]
  • 数字12 ('x0c') = [ 0x0c ]
  • 数字 1024 ('x04x00') = [ 0x82, 0x04, 0x00 ]
  • 嵌套列表 [ [], [[]], [ [], [[]] ] ] = [ 0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0 ]
  • 字符串 "Lorem ipsum dolor sit amet, consectetur adipisicing elit" = [ 0xb8, 0x38, 'L', 'o', 'r', 'e', 'm', ' ', ... , 'e', 'l', 'i', 't' ]

RLP分析

以上我们可以看出RLP编码的设计思想,就是通过首字节快速判断一串编码的类型,充分利用了一个字节的存储空间,将0x7f以后的值赋予了新的含义,RLP最大的优点是在充分利用字节的情况下,同时支持列表结构,也就是说可以很轻易的利用RLP存储一个树状结构。

程序处理RLP编码时也非常容易,根据首字节就可以判断出这段编码的类型,同时调用不同的方法进行解码,和JSON编码类似,支持嵌套的结构,通过递归调用可以将整个RLP快速还原成一颗树,或者转译成一个JSON结构,便于其他程序使用。

RLP使用首字节存储长度的位数,再用后续的字节表明整体字符串的长度,根据规则二计算,RLP可以支持的单个最大字符串长度为2的64次方,这无疑是个天文数字,再加上嵌套规则,所以理论上RLP可以编码任何数据。


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:luca

查看原文:以太坊源码剖析(1)-RLP编码

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1314 次点击  ∙  1 赞  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传