递归长度前缀(RLP)是一种核心序列化协议,用于在以太坊中编码和解析数据。本文详细探讨了RLP的工作原理,包括编码和解码规则,以及其在以太坊功能中的重要性。通过示例和流程图,解释了RLP如何将不同类型的数据序列化,确保跨客户端的一致性和高效性。
递归长度前缀(RLP)是一个核心的序列化协议,用于执行层中的数据编码和解析。它旨在序列化数据,并生成所有客户端软件都能读取的结构。它用于交易数据到整个区块链状态的所有内容。该维基页面探讨了RLP的内部结构、编码/解码规则、可用的工具以及其在以太坊功能中的作用。
数据序列化是将数据结构或对象转换为字节流以进行存储、传输或稍后重建的过程。在像以太坊这样的分布式系统中,序列化对于可靠且高效地在网络节点之间传输数据至关重要。使用不同语言编写的客户端需要能够以相同的方式处理数据。传递给其他节点或客户端导出的数据需要具有标准格式。虽然有JSON、XML或Protobuf等常见序列化格式,但以太坊使用自己的协议以实现对嵌套字节数组的简单性和有效性。
以太坊实际上使用了两种格式:RLP和简易序列化(SSZ),后者是共识层使用的更现代标准。
RLP编码算法
以下是一个流程图,描述了RLP编码算法的工作原理。
注意在某些RLP工具中,某些客户端可能会向流程中添加额外的条件案例。这些额外情况不是标准规范的一部分,但对客户端的正确数据序列化很有用,例如与Nethermind客户端节点通信的geth客户端节点。
flowchart TD
A[开始:输入] --> B{输入是否为null、空字符串或false?}
B -->|是| C[编码为单字节 0x80]
B -->|否| D{它是一个单字节并且 < 0x80?}
D -->|是| E[直接编码字节]
D -->|否| F{它是字符串还是列表?}
F -->|字符串| G{字符串长度 <= 55}
G -->|是| H[编码为长度 + 0x80]
G -->|否| I[用 0xB7 + 长度字节编码长度]
F -->|列表| J{总编码项目长度 <= 55}
J -->|是| K[编码为总长度 + 0xC0]
J -->|否| L[用 0xF7 + 长度字节编码总长度]
H --> M[输出编码数据]
I --> M
K --> M
L --> M
E --> M
C --> M
图:RLP编码流程
RLP解码算法
以下是一个流程图,描述了RLP解码算法的工作原理。
flowchart TD
A[开始:编码输入] --> B{检查第一个字节}
B -->|0x00到0x7F| C[直接字节输出]
B -->|0x80| D[解码为空字符串/Null/False]
B -->|0x81到0xB7| E[短字符串]
E --> F[用第一个字节减去 0x80 得到长度]
F --> G[输出下一个“长度”个字节作为字符串]
B -->|0xB8到0xBF| H[长字符串]
H --> I[用第一个字节减去 0xB7 得到长度字节数]
I --> J[读取接下来的“长度字节数”以确定字符串长度]
J --> K[输出下一个“字符串长度”个字节作为字符串]
B -->|0xC0到0xF7| L[短列表]
L --> M[用第一个字节减去 0xC0 得到总编码长度]
M --> N[递归解码下一个“总编码长度”个字节中的每个元素]
B -->|0xF8到0xFF| O[长列表]
O --> P[用第一个字节减去 0xF7 得到长度字节数]
P --> Q[读取接下来的“长度字节数”以确定列表总长度]
Q --> R[递归解码下一个“列表总长度”个字节中的每个元素]
C --> S[输出解码数据]
D --> S
G --> S
K --> S
N --> S
R --> S
图:RLP解码流程
理解如何得到RLP编码需要掌握根据数据的类型和大小应用的具体规则。让我们通过一个示例来探索这些规则,以演示不同类型的数据是如何编码的。
如果你对将字符串转换为十六进制不熟悉,可以参考这个ASCII表。
递归长度前缀(RLP)是一种在以太坊中用于将结构化数据编码为字节序列的基本数据序列化技术。理解如何获得RLP编码需要掌握根据数据类型和大小应用的具体规则。让我们通过一个示例逐步探索这些规则,以演示不同类型的数据是如何编码的。
单字节编码
0x00
和 0x7F
之间(包括)。0x2a
结果为 0x2a
。短字符串编码(1-55字节)
0x80
,后跟字符串本身。0x64, 0x6f, 0x67
)结果为 0x83, 0x64, 0x6f, 0x67
。这里,0x83
是 0x80 + 3
("dog" 的长度)。长字符串编码(超过55字节)
0xb7
加上这个长度数组的长度。0x38
被编码,前面加上 0xb8
(0xb7 + 1
)。结果编码以 0xb8, 0x38
开头,后面跟着字符串的字节。短列表编码(总负载1-55字节)
0xc0
加上编码项目的总长度。["cat", "dog"]
,每个项目首先编码为 0x83, 0x63, 0x61, 0x74
和 0x83, 0x64, 0x6f, 0x67
。总长度为8,因此前缀为 0xc8
(0xc0 + 8 = 0xc8
)。整个编码为 0xc8, 0x83, 0x63, 0x61, 0x74, 0x83, 0x64, 0x6f, 0x67
。长列表编码(总负载超过55字节)
0xf7
加上这个长度数组的长度。["apple", "bread", ...]
,假设负载长度为57,则编码长度 0x39
,前面加上 0xf8
(0xf7 + 1
),后面跟着编码的列表项目。Null、空字符串、空列表和False
0x80
。0xc0
。`,
null,
false),结果为
0x80`。[]
,结果为 0xc0
。RLP解码过程基于编码数据的结构和具体内容:
确定数据类型:
解码单字节:
0x00
到 0x7F
范围内,则字节本身表示解码数据。这种情况比较简单,因为字节是直接编码的。解码字符串和列表:
短字符串(0x80到0xB7):
0x80
和 0xB7
之间,则表示字符串,其长度可以通过从前缀中减去 0x80
直接决定。接下来的字节等于该长度即为字符串。长字符串(0xB8到0xBF):
0xB8
和 0xBF
之间,长度字节数由前缀减去 0xB7
得出。随后的字节表示字符串的长度,接下来的字节表示字符串本身。短列表(0xC0到0xF7):
0xC0
到 0xF7
之间表示一个列表。可以通过从前缀中减去 0xC0
来确定列表的编码数据长度。接下来的字节必须递归解码为单个RLP编码项。长列表(0xF8到0xFF):
0xF8
到 0xFF
之间表示接下来的几字节(由前缀减 0xF7
决定)将说明列表的编码数据长度。接下来这些长度字节后面的数据都需要递归解码为RLP项。RLP解码示例 [0xc8, 0x83, 0x63, 0x61, 0x74, 0x83, 0x64, 0x6f, 0x67]
识别前缀
0xc8
开始。在RLP中,列表的长度前缀从 0xc0
开始。0xc8
和 0xc0
之间的差值为我们提供了列表内容的长度。0xc8 - 0xc0 = 8
解码列表内容
[0x83, 0x63, 0x61, 0x74, 0x83, 0x64, 0x6f, 0x67]
。解码第一个项
0x83
。在RLP中,对于长度在1到55字节之间的字符串,长度前缀从 0x80
开始。因此:0x83 - 0x80 = 3
3
字节。0x63, 0x61, 0x74
,对应ASCII值 "cat"。解码第二个项
0x83
。0x83 - 0x80 = 3
0x64, 0x6f, 0x67
,对应 "dog"。解码输出为 ["cat", "dog"]
。
RLP旨在成为一个高度简约的序列化格式;它的唯一目的是存储嵌套字节数组。与protobuf、BSON和其他现有解决方案不同,RLP并不试图定义任何特定的数据类型,比如布尔值、浮点数、双精度或甚至整数;相反,它只是存在于存储结构中,以嵌套数组的形式,让协议来决定数组的含义。 -- 以太坊的设计原理
RLP是与以太坊一起创建的,并量身定制以满足其特定需求:
在以太坊中,有许多可用于RLP实现的库。这里是一些工具:
- 原文链接: github.com/thogiti/thogi...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!