在 Solana 中,所有数据都存储在称为”账户”(Accounts)的结构中。可以将 Solana 上的数据视为一个公共数据库,其中有一个名为”Accounts”的表,表中的每一条记录就是一个”账户”。
1. 核心要点
- 存储容量:账户最多可以存储 10MiB 的数据,可以是可执行的程序代码或程序状态
- 租金押金:账户需要按比例缴纳租金押金(以 lamports 计,即 SOL 的最小单位),押金金额与存储的数据量成正比,关闭账户时可以完全收回
- 所有权机制:每个账户都有一个程序所有者。只有拥有该账户的程序才能修改其数据或扣除其 lamport 余额,但任何人都可以增加余额
- 特殊账户类型
- Sysvar 账户:存储网络集群状态的特殊账户
- Program 账户:存储智能合约的可执行代码
- Data 账户:由程序创建用于存储和管理程序状态
2. 账户结构
2.1 账户地址
每个 Solana 账户都有一个唯一的 32 字节地址,通常显示为 base58 编码的字符串(例如:14grJpemFaf88c8tiVb77W7TYg2W3ir6pfkKz3YjhhZ5)。
账户与其地址之间的关系类似于键值对,其中地址是用于定位账户链上数据的键。账户地址充当”Accounts”表中每个条目的”唯一标识符”。
地址生成方式:
- Ed25519 公钥地址(最常见)
- 大多数 Solana 账户使用 Ed25519 公钥作为其地址
- 程序派生地址(PDA)
- 可以从程序 ID 和可选输入(种子)确定性地派生的特殊地址
- PDA 是一种特殊的地址类型,不需要私钥就可以使用
2.2 账户类型定义
所有 Solana 账户都具有相同的基础结构:
1 | pub struct Account { |
2.3 字段详解
Lamports 字段
账户的余额(以 lamports 为单位,1 SOL = 10 亿 lamports)。
重要特性:
- 账户必须保持最低 lamport 余额,该余额与存储的数据量成正比(称为”租金”)
- 关闭账户时可以完全收回存储在账户中的 lamport 余额
Data 字段
存储账户任意数据的字节数组,也称为”账户数据”。
不同账户类型的 data 内容:
- 对于程序账户:包含可执行程序代码本身或存储可执行代码的另一个账户的地址
- 对于非可执行账户:通常存储要读取的状态数据
读取账户数据的步骤:
- 使用地址(公钥)获取账户
- 将账户的 data 字段从原始字节反序列化为适当的数据结构(由拥有该账户的程序定义)
Owner 字段
拥有此账户的程序 ID(公钥)。
所有权规则:
- 只有所有者程序才能更改账户的数据或扣除其 lamports 余额
- 程序中定义的指令决定了账户的数据和 lamports 余额如何被更改
Executable 字段
指示账户是否为可执行程序:
true:账户是可执行的 Solana 程序false:账户是存储状态的数据账户
对于可执行账户,owner 字段包含加载器程序的程序 ID。加载器程序是负责加载和管理可执行程序账户的内置程序。
Rent Epoch 字段
**注意:**这是一个已弃用的遗留字段,不再使用。最初用于跟踪账户何时需要支付租金以维护其网络数据,此租金收取机制已被废弃。
3. 租金机制
要在链上存储数据,账户必须保持与存储数据量(字节)成正比的 lamport(SOL)余额。这个余额称为”租金”,但它更像是押金,因为关闭账户时可以收回全部金额。
注意: “租金”一词来自已弃用的机制,该机制会定期从低于租金阈值的账户中扣除 lamports。此机制已不再活跃。
4. 程序所有权
在 Solana 上,”智能合约”被称为”程序”。程序所有权是 Solana 账户模型的关键部分。
4.1 所有者权限
只有所有者程序可以:
- 更改账户的
data字段 - 从账户余额中扣除 lamports
每个程序定义存储在账户 data 字段中的数据结构。程序的指令决定了如何更改这些数据和账户的 lamports 余额。
4.2 System Program(系统程序)
默认情况下,所有新账户都归 System Program 所有。
System Program 的关键功能:
| 功能 | 描述 |
|---|---|
| 空间分配 | 为新账户分配存储空间 |
| 分配程序所有权 | 将账户所有权转移给其他程序 |
| 转账 SOL | 在账户之间转移 lamports |
System Account(系统账户):
- Solana 上的所有”钱包”账户都是由 System Program 拥有的”系统账户”
- 这些账户中的 lamport 余额显示钱包拥有的 SOL 数量
- 只有系统账户可以支付交易费用
- 当首次向新地址发送 SOL 时,会在该地址自动创建一个由 System Program 拥有的账户
- 新创建的系统账户的 owner 字段显示为 System Program 地址:
11111111111111111111111111111111
5. 特殊账户类型
5.1 Sysvar Accounts(系统变量账户)
Sysvar 账户是位于预定义地址的特殊账户,提供对集群状态数据的访问。这些账户会动态更新网络集群的相关数据。
例如,可以通过 Sysvar Clock 账户获取当前的集群时间信息,这些账户的数据可以被获取并反序列化为相应的数据结构。
5.2 Program Account(程序账户)
部署 Solana 程序会创建一个可执行的程序账户,用于存储程序的可执行代码。程序账户由加载器程序(Loader Program)拥有。
关键特点:
- 程序账户可以简单地视为程序本身
- 调用程序的指令时,需要指定程序账户的地址(通常称为”Program ID”)
- 程序账户的
executable字段设置为true
程序部署方式:
- Loader-v3 之前的版本:
- 可执行代码直接存储在程序账户中
- Loader-v3(当前默认):
- 可执行代码存储在单独的”程序数据账户”(Program Data Account)中
- 程序账户只是指向该数据账户
- Solana CLI 默认使用最新的加载器版本
5.3 Buffer Account(缓冲区账户)
Loader-v3 有一种特殊的账户类型,用于在部署或升级期间临时暂存程序的上传。在 Loader-v4 中,仍然有缓冲区,但它们只是普通的程序账户。
5.4 Program Data Account(程序数据账户)
Loader-v3 工作方式与其他 BPF 加载器程序不同:
- 程序账户只包含程序数据账户的地址
- 程序数据账户存储实际的可执行代码
注意: 不要将程序数据账户与程序的数据账户(Data Account)混淆。
5.5 Data Account(数据账户)
在 Solana 上,程序的可执行代码与程序的状态存储在不同的账户中。这类似于操作系统通常为程序及其数据使用单独文件的方式。
为了维护状态,程序定义指令来创建它们拥有的独立账户。每个账户都有自己唯一的地址,可以存储程序定义的任意数据。
重要提示: 只有 System Program 可以创建新账户。System Program 创建账户后,可以将新账户的所有权分配给另一个程序。
创建数据账户的两步过程:
- 调用 System Program:创建账户,然后将所有权转移给自定义程序
- 调用自定义程序:初始化账户数据(按程序指令定义)
示例:创建 Token Mint 账户
- 首先调用 System Program 创建账户并分配空间
- 然后调用 Token Program 初始化 mint 账户的数据
- 需要计算租金豁免所需的最低余额
- 完成后账户由 Token Program 拥有并存储 mint 的相关数据
6. 账户交互实战
6.1 环境准备
1 | # 启动本地验证器 |
6.2 项目设置
1 | # 创建新项目 |
6.3 请求空投与查询余额
通过这个示例,你可以学习如何:
- 连接到 Solana 网络
- 请求空投到指定账户
- 查询账户余额
1 | use solana_client::rpc_client::RpcClient; |
6.4 转账交易
这个示例演示了 Solana 账户之间的转账流程:
- 加载发送方密钥对
- 创建转账指令
- 构建、签名并发送交易
1 | use solana_client::rpc_client::RpcClient; |
7. 总结
- 账户是 Solana 的基础存储单元:所有数据都存储在账户中
- 账户具有统一的结构:所有账户共享相同的基础字段
- 所有权是关键:只有所有者程序可以修改账户数据
- 租金是可恢复的押金:不是真正的费用,关闭账户时可以收回
- 程序和数据分离:可执行代码和状态数据存储在不同的账户中
- System Program 是账户的创建者:所有新账户都由 System Program 创建
Hooray!Solana 账户模型学习完成!!!