深入浅出以太坊 RLP 编码,原理/用法与实践

 :2026-03-13 2:24    点击:1  

在以太坊的世界里,数据的高效、紧凑序列化是保障网络性能和一致性的基石,RLP(Recursive Length Prefix,递归长度前缀)编码正是以太坊中用于序列化任意嵌套数据结构的核心算法,无论是交易、区块状态,还是账户信息,其底层存储和传输都离不开 RLP 的身影,本文将深入探讨 RLP 的编码原理、详细用法及其在以太坊中的实际应用。

什么是 RLP?为何需要它

RLP 的设计目标非常明确:简洁、高效且能够序列化任意嵌套的二进制数据结构,这里的“任意嵌套”指的是列表可以包含列表,形成复杂的树形结构,以太坊选择 RLP 而非其他序列化方案(如 JSON、Protocol Buffers),主要因为它:

  1. 简洁高效:RLP 的编码方式非常紧凑,只包含必要的信息,没有额外的元数据开销,适合区块链对存储和带宽的严格要求。
  2. 易于实现:算法逻辑相对简单,便于在以太坊客户端(如 geth、parity)等多种编程语言中实现。
  3. 通用性强:能够处理字符串和列表两种基本类型,并通过递归嵌套表示复杂数据。

RLP 的核心原理

RLP 的核心思想是:对于数据项(字符串或列表),其编码由两部分组成:数据本身和一个长度前缀(Length Prefix),用于指示数据的长度,从而解码时能够正确分割数据。

RLP 只处理两种数据类型:

  1. 字符串(String):字节数组,在以太坊中,数字通常也以其字节数组形式表示(如大端序)。
  2. 列表(List):一个有序的数据项集合,其中数据项可以是字符串或列表本身,列表可以嵌套。

1 字符串的 RLP 编码规则

对于一个字符串(字节数组),其编码规则取决于其长度 len

  • 情况 1:0 <= len <= 55 字节

    • 编码结果:单个字节(值为 0x80 + len) + 字符串本身。
    • 示例:空字符串 ,长度为 0,编码为 0x80
    • 示例:"dog"(ASCII 编码为 [0x64, 0x6f, 0x67]),长度为 3,编码为 0x83 + 'd' + 'o' + 'g' = 0x83646f67
  • 情况 2:56 <= len < 2^56 字节(即长度本身需要 1 到 8 字节表示)

    • 编码结果:单个字节(值为 0xb7 + len 的字节数) + 长度本身的 RLP 编码(大端序) + 字符串本身。
    • 示例:一个长度为 56 的字符串 s
      • len = 56len 的字节数 = 1(因为 56 < 25
        随机配图
        6)。
      • 第一个字节:0xb7 + 1 = 0xb8
      • 长度本身的编码:0x38(56 的十六进制)。
      • 编码结果:0xb8 + 0x38 + s(共 1 + 1 + 56 = 58 字节)。
  • 情况 3:len >= 2^56 字节(理论上以太坊中不会出现如此大的字符串)

    • 编码结果:单个字节(值为 0xb7 + len 的字节数) + 长度本身的 RLP 编码(大端序,使用尽可能多的字节) + 字符串本身。
    • (此情况在实际以太坊应用中极少遇到,了解即可)

2 列表的 RLP 编码规则

对于一个列表,其编码规则如下:

  1. 计算列表中所有数据项(字符串或列表)的 RLP 编码后的总长度 total_len
  2. 根据 total_len 的值,选择前缀字节:
    • 情况 1:0 <= total_len <= 55 字节
      • 编码结果:单个字节(值为 0xc0 + total_len) + 各数据项 RLP 编码的串联。
    • 情况 2:56 <= total_len < 2^56 字节
      • 编码结果:单个字节(值为 0xf7 + total_len 的字节数) + total_len 本身的 RLP 编码(大端序) + 各数据项 RLP 编码的串联。
    • 情况 3:total_len >= 2^56 字节(同样,以太坊中罕见)
      • 编码结果:单个字节(值为 0xf7 + total_len 的字节数) + total_len 本身的 RLP 编码(大端序) + 各数据项 RLP 编码的串联。

列表编码示例:

  • 列表 ["cat", "dog"]

    • "cat" 的 RLP 编码:0x83636174
    • "dog" 的 RLP 编码:0x83646f67
    • total_len = len("cat" RLP) + len("dog" RLP) = 4 + 4 = 8 字节。
    • total_len = 8 <= 55,所以前缀字节为 0xc0 + 8 = 0xc8
    • 列表 RLP 编码:0xc8 + 0x83636174 + 0x83646f67 = 0xc88363617483646f67
  • 嵌套列表 ["cat", ["dog"]]

    • "cat" 的 RLP 编码:0x83636174
    • ["dog"] 的 RLP 编码:
      • "dog" 的 RLP 编码:0x83646f67
      • 内层列表 total_len = 4,前缀字节 0xc0 + 4 = 0xc4
      • 内层列表 RLP 编码:0xc483646f67
    • 外层列表 total_len = len("cat" RLP) + len(["dog"] RLP) = 4 + 5 = 9 字节。
    • 外层列表前缀字节:0xc0 + 9 = 0xc9
    • 外层列表 RLP 编码:0xc9 + 0x83636174 + 0xc483646f67 = 0xc983636174c483646f67

以太坊中的 RLP 用法

RLP 在以太坊中无处不在,主要用于以下几个方面:

  1. 区块(Block)的序列化

    • 一个区块包含多个字段:父区块哈希、叔父区块哈希列表、Coinbase 地址、根状态、交易列表、收据列表、日志布隆过滤器、难度、时间戳、数字签名、额外数据等。
    • 这些字段通过 RLP 编码成一个字节数组,这就是我们通常所说的“区块头”中的“区块体”(实际上区块头本身也有 RLP 结构)的 RLP 编码,或者整个区块的 RLP 编码用于节点间同步。
  2. 交易(Transaction)的序列化

    • 以太坊交易包含 nonce、gas 价格、gas 限制、接收方地址、金额、数据、签名等信息。
    • 这些字段被打包成一个结构化的数据,然后通过 RLP 编码,形成网络上传输的交易数据,一个简单的转账交易(无 data 字段)的 RLP 编码会包含上述关键字段的序列化串联。
  3. 状态存储(State Storage)

    以太坊的状态树(Merkle Patricia Trie)的节点键和值都是通过 RLP 编码的,账户对象(包含 nonce、余额、存储根代码哈希)本身就是一个列表或结构化数据,其序列化依赖于 RLP,存储在合约中的键值对,其键和值在存入状态树前也会进行 RLP 编码。

  4. 收据(Receipt)的序列化

    交易执行后生成的收据(包含状态根、gas 使用量、日志等)也需要通过 RLP 编码后存入区块的收据列表中。

  5. 其他数据结构

    如 Uncle 列表、区块头中的 ExtraData 字段等,

本文由用户投稿上传,若侵权请提供版权资料并联系删除!