什么是 RLP
递归长度前缀(RLP)序列化,是以太坊执行层用于数据编码和解析的核心协议,将字节数组和嵌套列表转换为统一的字节流,以确保用不同编程语言编写的节点客户端能相互通信,达成一致。
与常见的 JSON 或 XML 格式不同,RLP 不会去定义数据到底是数字、布尔值还是字符串。它的唯一目的是存储嵌套的字节数组结构。,至于这些字节代表什么意思(是账户余额还是合约代码),则交由以太坊的其他协议层去解释。这种极简让它在处理嵌套数据时非常高效
RLP 编码如何工作?
RLP 的名字里有“长度前缀”,这解释了它的工作原理。一串 RLP 编码的数据,**第一个字节(前缀)**像是说明书,告诉计算机接下来的数据是什么类型、有多长:
-
单字节:如果数据很小,直接编码。
-
字符串:如果是短字符串,前缀会告诉电脑“后面有几个字节是文本”;如果是长字符串,则会有更复杂的前缀来记录长度。
-
列表:它甚至可以把多个字符串打包成一个“列表”,甚至列表里还可以嵌套列表(这就是“递归”的含义)。
RLP 协议将一个字节(0x00 到 0xff)的取值范围进行了切分,以便解码器仅通过看第一个字节就能判断出数据的类型:
-
0x00 - 0x7f:用于直接表示单个字节本身。这意味着如果数据本身就是一个很小的数字(小于 128),它不需要任何前缀,直接存储即可。
-
0x80:这个特定的数值被保留用来表示空字符串、null 或 false。
-
0x81 - 0xb7:这就是为什么例子中要加上 0x80 的原因。这个范围专门用于长度在 1 到 55 字节之间的字符串。
RLP 编码例子
以编码字符串 “dog” 为例:
-
转换为十六进制: 字符串 “dog” 对应的 ASCII/十六进制值为 0x64, 0x6f, 0x67。
-
确定长度: “dog” 的长度是 3 个字节。
-
应用规则: 根据 RLP 的“短字符串”规则(长度在 1-55 字节之间),编码结果为:0x80 + 字符串长度,再接上原始数据。(对于长度为 1-55 字节的字符串,前缀应该是 0x80 + 字符串长度,当以太坊节点看到 0x83 时,它知道这个数大于 0x80 且小于 0xb7,于是它会减去 0x80 得到 3,从而知道接下来的 3 个字节是一个字符串!)
-
计算前缀:0x80 + 3 = 0x83。
-
最终编码: 0x83, 0x64, 0x6f, 0x67。
如果是一个列表 [“cat”, “dog”]:
-
“cat” 编码为 0x83, 0x63, 0x61, 0x74。
-
“dog” 编码为 0x83, 0x64, 0x6f, 0x67。
两个编码项的总长度为 8 字节
什么是SSZ?它和RLP有什么区别?
SSZ (Simple Serialize) 是以太坊的一种“简单序列化”协议。
使用层面: 以太坊的执行层的 RLP 不同,SSZ 是用于以太坊共识层的更现代的标准。
功能特性: SSZ 支持默克尔化(Merkleization),默克尔化允许对复杂的数据结构进行哈希计算,并生成默克尔证明(Merkle Proofs)。这对于共识层非常关键,因为它让节点能够高效地验证数据的某一部分,而不需要下载或处理整个数据集。
相比之下,RLP 的设计初衷极其简约,其唯一目的是存储嵌套的字节数组,并不原生支持这种高效的片段验证机制。
RLP如何处理非常长的数据或嵌套列表?
当数据(字符串或列表)的长度超过 55 字节时,RLP 采用长度的长度这种两级前缀机制:
长字符串: 首先将数据的实际长度转换为大端序(big-endian)格式。前缀计算公式为 0xb7 + 长度字节数组的长度。
例子: 如果一个字符串长 56 字节(0x38),由于 56 占 1 个字节,前缀就是 0xb7 + 1 = 0xb8。最终编码以 0xb8, 0x38… 开头。
长列表: 逻辑类似,前缀计算公式为 0xf7 + 长度字节数组的长度。
对于嵌套结构采用递归方式:
-
编码时,先将列表内部的每一个元素独立进行 RLP 编码。
-
将这些编码后的字节流拼接在一起,计算总长度。
-
根据这个总长度,为整个序列加上列表前缀(0xc0 系列或 0xf7 系列)。
-
解码时,计算机会先通过第一个字节确定列表的总范围,然后递归地解码其中的每一个元素,直到还原出完整的嵌套结构。