掌握递归长度前缀(RLP)序列化 - 以太坊执行层的综合指南

  • thogiti
  • 发布于 2024-05-03 12:47
  • 阅读 46

递归长度前缀(RLP)是一种核心序列化协议,用于在以太坊中编码和解析数据。本文详细探讨了RLP的工作原理,包括编码和解码规则,以及其在以太坊功能中的重要性。通过示例和流程图,解释了RLP如何将不同类型的数据序列化,确保跨客户端的一致性和高效性。

递归长度前缀(RLP)是一个核心的序列化协议,用于执行层中的数据编码和解析。它旨在序列化数据,并生成所有客户端软件都能读取的结构。它用于交易数据到整个区块链状态的所有内容。该维基页面探讨了RLP的内部结构、编码/解码规则、可用的工具以及其在以太坊功能中的作用。

以太坊中的数据序列化

数据序列化是将数据结构或对象转换为字节流以进行存储、传输或稍后重建的过程。在像以太坊这样的分布式系统中,序列化对于可靠且高效地在网络节点之间传输数据至关重要。使用不同语言编写的客户端需要能够以相同的方式处理数据。传递给其他节点或客户端导出的数据需要具有标准格式。虽然有JSON、XML或Protobuf等常见序列化格式,但以太坊使用自己的协议以实现对嵌套字节数组的简单性和有效性。

以太坊实际上使用了两种格式:RLP和简易序列化(SSZ),后者是共识层使用的更现代标准。

RLP算法工作原理

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编码规则

理解如何得到RLP编码需要掌握根据数据的类型和大小应用的具体规则。让我们通过一个示例来探索这些规则,以演示不同类型的数据是如何编码的。

如果你对将字符串转换为十六进制不熟悉,可以参考这个ASCII表

RLP编码规则的详细说明及示例

递归长度前缀(RLP)是一种在以太坊中用于将结构化数据编码为字节序列的基本数据序列化技术。理解如何获得RLP编码需要掌握根据数据类型和大小应用的具体规则。让我们通过一个示例逐步探索这些规则,以演示不同类型的数据是如何编码的。

单字节编码

  • 条件:如果输入是单字节,并且其值在 0x000x7F 之间(包括)。
  • 编码:字节直接编码,不发生变化。
  • 示例:直接编码字节 0x2a 结果为 0x2a

短字符串编码(1-55字节)

  • 条件:如果字符串(或字节数组)的长度在1到55字节之间。
  • 编码:输出是字符串长度加 0x80,后跟字符串本身。
  • 示例:编码字符串 "dog"(0x64, 0x6f, 0x67)结果为 0x83, 0x64, 0x6f, 0x67。这里,0x830x80 + 3("dog" 的长度)。

长字符串编码(超过55字节)

  • 条件:如果字符串长度超过55字节。
  • 编码:字符串长度以大端格式表示为字节数组,前面加上 0xb7 加上这个长度数组的长度。
  • 示例:对于长度为56的字符串,长度 0x38 被编码,前面加上 0xb80xb7 + 1)。结果编码以 0xb8, 0x38 开头,后面跟着字符串的字节。

短列表编码(总负载1-55字节)

  • 条件:如果列表项目的总编码负载在1到55字节之间。
  • 编码:列表前加上 0xc0 加上编码项目的总长度。
  • 示例:对于列表 ["cat", "dog"],每个项目首先编码为 0x83, 0x63, 0x61, 0x740x83, 0x64, 0x6f, 0x67。总长度为8,因此前缀为 0xc80xc0 + 8 = 0xc8)。整个编码为 0xc8, 0x83, 0x63, 0x61, 0x74, 0x83, 0x64, 0x6f, 0x67

长列表编码(总负载超过55字节)

  • 条件:如果列表项目的总编码负载超过55字节。
  • 编码:类似于长字符串,负载长度以大端格式编码,前面加上 0xf7 加上这个长度数组的长度。
  • 示例:对于列表 ["apple", "bread", ...],假设负载长度为57,则编码长度 0x39,前面加上 0xf80xf7 + 1),后面跟着编码的列表项目。

Null、空字符串、空列表和False

  • 空字符串、Null和False的规则:编码为单字节 0x80
  • 空列表的规则:编码为 0xc0
  • 示例:
    • 编码空字符串或NULL值或false(`,null,false),结果为0x80`。
    • 编码空列表 [],结果为 0xc0

RLP解码规则

RLP解码过程基于编码数据的结构和具体内容:

确定数据类型

  • 编码数据的第一个字节(前缀)决定了后续数据的类型和长度。这个字节在指导解码过程中至关重要。

解码单字节

  • 如果前缀字节在 0x000x7F 范围内,则字节本身表示解码数据。这种情况比较简单,因为字节是直接编码的。

解码字符串和列表

  • 解码的复杂性在于字符串和列表,它们的长度各异,可能包含嵌套结构。

短字符串(0x80到0xB7)

  • 如果前缀字节在 0x800xB7 之间,则表示字符串,其长度可以通过从前缀中减去 0x80 直接决定。接下来的字节等于该长度即为字符串。

长字符串(0xB8到0xBF)

  • 对于较长的字符串,如果前缀字节在 0xB80xBF 之间,长度字节数由前缀减去 0xB7 得出。随后的字节表示字符串的长度,接下来的字节表示字符串本身。

短列表(0xC0到0xF7)

  • 类似于短字符串,前缀在 0xC00xF7 之间表示一个列表。可以通过从前缀中减去 0xC0 来确定列表的编码数据长度。接下来的字节必须递归解码为单个RLP编码项。

长列表(0xF8到0xFF)

  • 对于长列表,前缀在 0xF80xFF 之间表示接下来的几字节(由前缀减 0xF7 决定)将说明列表的编码数据长度。接下来这些长度字节后面的数据都需要递归解码为RLP项。

RLP解码示例 [0xc8, 0x83, 0x63, 0x61, 0x74, 0x83, 0x64, 0x6f, 0x67]

  • 识别前缀

    • 序列以字节 0xc8 开始。在RLP中,列表的长度前缀从 0xc0 开始。0xc80xc0 之间的差值为我们提供了列表内容的长度。
    • 0xc8 - 0xc0 = 8
    • 这告诉我们接下来的8个字节是列表的一部分。
  • 解码列表内容

    • 本示例中的列表内容为 [0x83, 0x63, 0x61, 0x74, 0x83, 0x64, 0x6f, 0x67]
    • 我们将逐字节解码此内容,以提取各个项。
  • 解码第一个项

    • 列表内容的第一个字节是 0x83。在RLP中,对于长度在1到55字节之间的字符串,长度前缀从 0x80 开始。因此:
    • 0x83 - 0x80 = 3
    • 这告诉我们第一个字符串的长度为 3 字节。
    • 接下来的三个字节为 0x63, 0x61, 0x74,对应ASCII值 "cat"。
    • 我们现在解码了第一个项:"cat"。
  • 解码第二个项

    • 解码第一个项后,序列中的下一个字节是另一个 0x83
    • 按照之前的规则:
    • 0x83 - 0x80 = 3
    • 这表示下一个字符串的长度也是3字节。
    • 随后的三个字节为 0x64, 0x6f, 0x67,对应 "dog"。
    • 我们现在解码了第二个项:"dog"。
  • 解码输出为 ["cat", "dog"]

以太坊中RLP的必要性

RLP旨在成为一个高度简约的序列化格式;它的唯一目的是存储嵌套字节数组。与protobuf、BSON和其他现有解决方案不同,RLP并不试图定义任何特定的数据类型,比如布尔值、浮点数、双精度或甚至整数;相反,它只是存在于存储结构中,以嵌套数组的形式,让协议来决定数组的含义。 -- 以太坊的设计原理

RLP是与以太坊一起创建的,并量身定制以满足其特定需求:

  • 简约设计:它纯粹关注存储结构,而不强加数据类型定义。
  • 一致性:在不同实现之间保证字节完美一致,对于区块链操作所需的确定性特性至关重要。

RLP工具

在以太坊中,有许多可用于RLP实现的库。这里是一些工具:

资源

  • 原文链接: github.com/thogiti/thogi...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
thogiti
thogiti
https://thogiti.github.io/