Solana 60 天课程

2025年02月27日更新 89 人订阅
原价: ¥ 66 限时优惠
专栏简介 开始 Solana - 安装与故障排除 Solana 和 Rust 中的算术与基本类型 Solana Anchor 程序 IDL Solana中的Require、Revert和自定义错误 Solana程序是可升级的,并且没有构造函数 Solidity开发者的Rust基础 Rust不寻常的语法 Rust 函数式过程宏 Rust 结构体与属性式和自定义派生宏 Rust 和 Solana 中的可见性与“继承” Solana时钟及其他“区块”变量 Solana 系统变量详解 Solana 日志、“事件”与交易历史 Tx.origin、msg.sender 和 onlyOwner 在 Solana 中:识别调用者 Solana 计算单元与交易费用介绍 在 Solana 和 Anchor 中初始化账户 Solana 计数器教程:在账户中读写数据 使用 Solana web3 js 和 Anchor 读取账户数据 在Solana中创建“映射”和“嵌套映射” Solana中的存储成本、最大存储容量和账户调整 在 Solana 中读取账户余额的 Anchor 方法:address(account).balance 功能修饰符(view、pure、payable)和回退函数在 Solana 中不存在的原因 在 Solana 上实现 SOL 转账及构建支付分配器 使用不同签名者修改账户 PDA(程序派生地址)与 Solana 中的密钥对账户 理解 Solana 中的账户所有权:从PDA中转移SOL Anchor 中的 Init if needed 与重初始化攻击 Solana 中的多重调用:批量交易与交易大小限制 Solana 中的所有者与权限 在Solana中删除和关闭账户与程序 在 Anchor 中:不同类型的账户 在链上读取另一个锚点程序账户数据 在 Anchor 中的跨程序调用(CPI) SPL Token 的运作方式 使用 Anchor 和 Web3.js 转移 SPL Token Solana 教程 - 如何实现 Token 出售 基础银行教程 Metaplex Token 元数据工作原理 使用Metaplex实施代币元数据 使用 LiteSVM 进行时间旅行测试 Solana Token-2022 标准规范 生息代币第一部分 计息代币第二部分 Solana 指令自省 Solana 中的 Ed25519 签名验证 Solana - Switchboard 预言机使用 原生Solana:程序入口与执行 原生 Solana :读取账户数据 原生 Solana :Borsh 序列化 原生 Solana:使用 invoke 和 invoke signed 进行跨程序调用 原生 Solana :创建存储账户 (一) 原生 Solana:创建存储账户 二 原生 Solana: 函数分发 原生 Solana:关键安全检查 Rust 程序到 SBF 编译 sBPF 虚拟机和指令集介绍 跟踪 sBPF 指令执行和计算成本 Solana 程序执行与输入序列化 指令处理器和运行时设置 sBPF 内存布局和寄存器约定 使用 sBPF 汇编读取 Solana 指令输入 Solana 系统调用:sBPF 汇编中的日志记录

原生 Solana :Borsh 序列化

本文详细介绍了Solana生态系统中Borsh序列化机制的工作原理。文章解释了序列化与反序列化的概念,Borsh作为Solana标准序列化格式的特点(确定性、紧凑性),以及如何处理固定长度和可变长度数据类型。同时,通过代码示例展示了如何在Solana Native程序中序列化和反序列化数据,并提及了手动读取数据和访问账户元数据的方法。

原生 Solana:Borsh 序列化

在之前的教程中,我们学习了如何读取传递给程序的账户。我们看到调用 account.try_borrow_data() 会将账户的 data 字段作为一个原始字节切片(例如 [0x01, 0x00, 0x00, 0x00])的引用。

Solana 将所有账户数据存储为字节。为了使用像 Rust 结构体这样的高级数据结构,我们使用序列化将结构体转换为字节,以便在链上存储,并在读取时将这些字节反序列化回结构体。Solana 使用 Borsh 作为其标准序列化格式。

本文解释了 Borsh 序列化如何工作以及如何解释这些原始字节。

在本教程中,我们将展示以下内容:

  • 什么是序列化以及 Borsh 序列化在 Solana 中如何工作
  • 如何读取和解释序列化的账户数据
  • 当你尝试读取没有数据的账户时会是什么样子

什么是序列化和反序列化?

序列化是将数据结构(如 Rust 结构体或字符串)转换为可以存储或传输的字节序列的过程。反序列化是相反的过程——将这些字节转换回原始数据结构。

例如,如果你有一个计数器值为 42(存储为 u64)的结构体,序列化会将其转换为 8 个字节:[0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]。第一个字节 0x2A 以小端序存储值的最低有效字节,其余 7 个字节为零,因为 u64 在内存中存储为 8 个字节。稍后,当你需要读取这些数据时,反序列化会将这些字节转换回你的结构体。

什么是 Borsh 序列化?

Borsh(用于哈希的二进制对象表示序列化器)是一种序列化格式,它定义了将 Rust 结构体转换为字节并返回的规则。Solana 使用 Borsh 作为其标准序列化格式,因为它具有以下特点:

  • 确定性:相同的数据总是产生相同的字节(相同的字节也总是产生相同的数据)
  • 紧凑性:它有效地存储数据,字段之间没有额外的元数据或填充(固定大小类型使用其标准字节大小,可变长度类型仅使用所需空间加上 4 字节的长度前缀)

borsh Rust crate 实现了 Solana 程序使用的序列化格式。

Solana 程序使用 Borsh 序列化指令数据和链上账户数据。对于账户,Borsh 将 Rust 结构体及其字段(字符串、整数、布尔值、向量等)转换为存储在账户的 data 字段中的原始字节。

Borsh 序列化如何工作

在原生 Rust 程序中,我们定义结构体来表示我们想要存储的账户数据(类似于你在 Anchor 中定义账户结构体的方式)。然后 Borsh 将这些结构体序列化为填充账户 data 字段的字节。序列化时,结构体字段在账户的 data 字段中按声明顺序连续排列。

基本流程如下图所示:

一个显示序列化流程的图表

此图表显示:

  1. 序列化:你的 Rust 结构体(例如 CounterData { count: 42 })使用 Borsh 转换为原始字节
  2. 存储:这些字节存储在链上账户的 data 字段中
  3. 反序列化:读取账户时,Borsh 将这些字节转换回你的结构体

Borsh 序列化后的 Solana 账户数据是什么样子的?

在 Anchor 中,序列化被抽象化了,但在原生 Rust 程序中,我们为结构体添加 #[derive(BorshSerialize, BorshDeserialize)] 属性。这会告诉 Borsh 库在编译时自动生成序列化和反序列化代码。

例如,这是一个 CounterData 结构体,它将计数器值存储为 u64

#[derive(BorshSerialize, BorshDeserialize)]
pub struct CounterData {
    pub count: u64,
}

如果 count 值为 42,BorshSerialize 属性会将此结构体序列化为:

一个图表,显示 Borsh 如何序列化值 43。

具体过程如下:

  • Borsh 接收我们的计数器值(42)并将其转换为十六进制:十进制 42 = 十六进制 0x2A
  • 由于 count 被定义为 u64,Borsh 使用 8 个字节以小端序格式表示它
  • 0x2A 之后的 7 个字节为零,因为 u64 占用 8 个字节

Borsh 如何序列化可变长度数据

在查看更复杂的示例之前,让我们了解 Borsh 如何处理没有固定长度的数据类型,例如字符串和向量。

u64boolu8 等固定数据类型不同,它们无论值如何都使用相同数量的字节,StringVec<T> 等可变长度数据类型的大小取决于其内容。

对于可变长度类型,Borsh 使用长度前缀:它首先写入数据的大小,然后是实际数据。这告诉 Borsh 在反序列化时需要读取多少字节(没有它,Borsh 将不知道一个字段在哪里结束,另一个在哪里开始)。

其工作原理如下:

  1. 首先,Borsh 将数据的长度以 u32(4 字节)的小端序格式序列化
  2. 然后,它序列化实际的数据字节

例如,如果我们有一个字符串“hi”:

  • 首先,Borsh 将长度以 u32 的小端序序列化:[0x02, 0x00, 0x00, 0x00] (2 字节)。由于长度存储为 u32,字符串的最大理论大小为 2^32 - 1 字节
  • 最后,Borsh 序列化“hi”的实际 UTF-8 字节:[0x68, 0x69]

这给我们带来了最终结果:

一个图表,显示 Borsh 如何序列化字符串“hi”。

这个长度前缀有助于 Borsh 在反序列化时准确地知道可变长度类型需要读取多少字节。

如何序列化具有多个字段的 Solana 账户结构体?

假设我们有一个 UserData 结构体,包含多个字段来表示存储账户的用户信息:

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论