本文由 AI 生成,请独立核实重要信息。

Solana 账户模型:技术指南

Crypto Wiki|Oct 9, 2026|★★★★★★4.5 (500 人评分)
AI 摘要

Learn how Solana's account model works: data storage, ownership rules, rent exemption, PDAs, and how it enables parallel execution.

本 Solana 账户模型:技术指南解释了程序所依赖的运行时字段和访问规则,包括所有权、可执行账户、代币账户、租金豁免、程序派生地址、关联代币账户以及跨程序调用。

租金处理、账户大小限制和客户端 API 等实现细节可能会发生变化。开发人员在部署前应查阅当前的 Solana 文档并查询当前的网络参数。

Solana 上的每笔交易都会涉及一个账户。您持有的每个代币、部署的每个程序、铸造的每个 NFT:所有这些都存在于账户中。在您编写一行 Solana 代码之前,了解账户的工作原理不是可选的背景知识。它是其他一切的前提。

将 Solana 想象成一个文件柜。每个抽屉里放着一个文件夹,每个文件夹都有一个标签(其地址)、内容(其数据)、一个余额印章,以及封面上的一个部门名称,指示哪个程序有权编辑其中的内容。无论您是从以太坊迁移过来的开发人员,还是评估 Solana 架构的研究员,或者是正在构建第一个链上状态管理心智模型的学生,本指南都涵盖了全貌:账户剖析、账户类型、所有权规则、租金机制、程序派生地址、与以太坊的并排对比,以及用于实际开发的注释代码。

Solana 账户模型:技术指南概览

Solana 账户模型是 Solana 区块链的数据存储和状态管理架构。Solana 上的每一项状态,包括代币余额、程序逻辑、用户数据和系统配置,都存储在由唯一 256 位公钥标识的离散账户中。有关规范规范,请参阅 Solana 官方账户文档)。

Solana 上的每一项状态都存在于账户中。程序不存储自己的数据;它们在执行期间读取和写入传入它们的账户。这一核心架构决策将 Solana 的设计与大多数其他区块链区别开来。

文件柜类比直接对应于数据结构:柜子是 Solana 区块链,文件夹是账户,每个文件夹的标签是其公钥(链上地址),文件夹的内容是数据字段,余额印章是 lamports 字段,封面上的部门印章是所有者字段,记录了哪个程序拥有写入权限。只有该印章上命名的部门才能更改其中的内容。

如果您是从以太坊转过来的,这个区别非常重要:以太坊智能合约在内部存储其自身状态,而 Solana 程序是无状态的。完整对比见下方的专用章节。

Solana 账户剖析

每个 Solana 账户,无论存储的是 SOL、程序代码还是应用程序状态,都由相同的五个字段构建而成。

每个 Solana 账户的五个字段

Solana 上的每个账户都恰好持有五个字段,每个字段都有不同的功能。在查看代码之前,用通俗的语言理解这些字段会使技术细节更容易内化。

lamports:此账户的 SOL 余额,以 Lamports 表示(Solana 的最小单位,将在下一节解释)。在文件柜类比中,这是印在文件夹封面上的货币价值。

data:一个原始字节数组(Vec<u8>),存储所属程序需要的任何状态。对于程序账户,此字段保存编译后的 BPF 字节码。对于数据账户,它保存序列化的应用程序状态。程序定义自己的序列化格式(通常为 Borsh)来对该字段进行结构化数据的读写。文件柜的等效物是文件夹内的实物内容。

owner:获授权修改此账户数据字段的程序公钥。其他程序无法对其进行写入。这是文件夹封面上的部门印章。

executable:一个布尔标志。当设置为 true 时,账户存储程序代码且可以执行。当为 false 时,账户是一个数据容器。

rent_epoch:一个遗留字段,以前用于跟踪租金收取时间。此字段正在被弃用,对于豁免租金的账户(这是生产标准)没有操作意义。

每个 Solana 账户都由衍生自 ed25519 密钥对的唯一 256 位公钥标识,该公钥既是其地址也是其链上标识符。截至最近的协议升级,单个账户的最大数据字段大小为 10 MB;请通过 Solana 的账户文档,) 验证当前限制,因为该值在 Solana 的历史中曾发生过变化。

Lamports:Solana 的最小单位

Lamport 是 SOL 的最小单位:1 SOL 等于 1,000,000,000 Lamports (10⁹)。该单位以计算机科学家 Leslie Lamport 的名字命名,他在分布式系统和容错方面的研究构成了许多现代分布式计算理论的基础。Lamports 之所以存在,是因为所有链上算术(包括租金计算、交易费用和账户余额)都必须使用整数算术,以避免浮点精度误差。用 Lamports 表示数值使每次计算都精确无误。

Solana 账户等同于钱包吗?

Solana 账户和钱包不是一回事。您所认为的钱包(手机上的 Phantom 或 Solflare 应用程序)是管理私钥的链下软件。链上实体是持有您的 SOL 余额的系统账户。您的钱包控制一个系统账户;它本身不是一个账户。代币账户、程序账户和数据账户没有钱包等效物:它们纯粹是链上数据容器,没有任何消费者钱包应用程序会直接代表它们。

Solana 账户分为四类,每一类都由其所有者程序和可执行标志定义。

  1. 系统账户:由系统程序所有;用于 SOL 存储和用户钱包
  2. 程序账户:存储编译后的 BPF 字节码的可执行账户
  3. 数据/状态账户:由程序所有;存储应用程序特定的状态
  4. 代币账户:由 SPL 代币程序拥有的专门数据账户(关联代币账户是其规范子集)

系统账户

系统账户由系统程序所有,用户钱包也是系统账户。系统程序是 Solana 的原生程序,负责三个核心操作:创建新账户(分配链上数据空间)、分配账户所有权(将所有者字段设置为指定程序)以及在账户之间转账 SOL。Solana 上的每个新账户最初都是由系统程序拥有的系统账户。

系统程序有一个广为人知的地址:11111111111111111111111111111111(base58 编码中的 32 个 1)。有关其完整指令集,请参阅 系统程序文档)。当开发人员为其程序创建数据账户时,他们会调用系统程序的“创建账户”指令,指定数据大小、豁免租金的 Lamport 充值以及新所有者程序的公钥。

程序账户

程序账户存储已编译的 BPF 字节码,而不是状态。这些账户的“可执行”标志设置为 true。程序是无状态的:当程序运行时,它通过交易指令接收一个要读取或写入的账户列表。它在调用之间不维护任何内部状态。如果您来自以太坊,您可能知道链上可执行代码是智能合约,但 Solana 程序的工作方式从根本上不同,因为它们不携带自己的状态。程序账户由 BPF Loader 程序拥有。一旦部署,程序账户就是不可变的,除非在部署期间指定了升级权限密钥对。

数据和状态账户

数据账户包含程序在执行期间读取和写入的应用程序状态。这些账户的“可执行”标志为 false。data 字段存储使用 Borsh 序列化的程序定义的状态。示例包括用户在 DeFi 协议中的借贷仓位、NFT 元数据或应用程序计数器。所有者程序是唯一能够写入此账户 data 字段的实体,此规则在所有权部分有详细介绍。

代币账户和关联代币账户

代币账户是由 SPL Token Program 拥有的数据账户,每个账户为特定钱包持有特定代币的余额。一个钱包地址可以持有多个代币账户,每个账户代表一种它持有的代币类型。

关联代币账户 (ATA) 是特定 [钱包地址, 代币地址] 对的规范代币账户。ATA 是作为程序派生地址 (PDA) 确定性生成的,使用钱包地址和代币地址作为种子。这确保每个钱包对每种代币类型都有且仅有一个“官方”账户。ATA 是 Phantom 和 Solflare 等钱包、交易所和协议使用的标准:当您在钱包 UI 中收到代币时,它会进入您该代币的 ATA。

如果给定 [钱包, 代币] 对的 ATA 不存在,则必须先创建它才能接收代币,而创建需要支付一笔免租金的 Lamport 充值。这就是为什么某些交易在钱包 UI 中包含少量“账户创建费”的原因。有关完整的 ATA 规范,请参阅 SPL Associated Token Account 文档.)。ATA 是代币账户,但并非所有代币账户都是 ATA,并且在调试代币相关程序时,这种区别很重要。

账户所有权:Solana 如何控制数据访问

Solana 中的账户所有权是一种写入权限机制,而不是资产所有权概念。

所有权规则

只有账户的所有者程序才能修改其 data 字段。任何其他程序都无法写入,无论用户在交易级别授权什么。AccountInfo 中的 owner 字段存储拥有此权限的程序的公钥。非所有者程序可以自由读取 data 字段,但写入需要成为指定的拥有者。

System Program 在账户创建期间分配所有权。当开发者为其程序创建数据账户时,Create Account 指令会将 owner 字段设置为其程序的公钥。所有权可以重新分配,但只能由当前所有者程序使用 Assign 指令进行。任何外部方都无法单方面转让所有权。

所有者 vs. 权限:关键区别

所有者和权限是在 Solana 账户管理中的两个不同概念。所有者是账户 owner 字段中记录的程序,意味着该程序可以写入 data 字段。权限(或签名者)是可以为账户签名交易的密钥对。

以代币账户为例:您的钱包的私钥签署交易(您是权限人),但 SPL Token Program 是您代币账户的所有者,也是唯一能够修改 data 字段中代币余额的实体。您可以授权一个移动代币的交易,但 data 字段的实际写入是由 Token Program 执行的,而不是直接由您执行。混淆这两个概念会导致新接触 Solana 的开发者出现大多数账户所有权调试错误。

所有权实践:一个 DEX 示例

一个 DEX 程序只能写入它直接拥有的账户。要将代币从用户的代币账户(由 SPL Token Program 拥有,而不是 DEX)转账,DEX 不能直接写入该账户。程序可以在同一交易中调用其他程序,这种模式称为跨程序调用 (CPI),允许 DEX 指示 Token Program 代用户移动代币。DEX 发送一个 CPI 到 Token Program,提供用户的签名授权,然后 Token Program 执行写入用户代币账户 data 字段的操作。这就是程序所有权模型如何扩展到程序间交互的方式。有关完整演练,请参阅 Solana 开发者的跨程序调用 (CPI) 指南。

账户租金和免租金

Solana 租金是一种存储成本机制,而不是定期费用。

什么是租金?

租金是 Solana 对链上存储定价的机制。每个账户必须维持一个与其数据大小成比例的最低 SOL 余额,以保持在链上活跃。大多数竞争性内容遗漏的纠正点:租金实际上不是定期费用。生产标准是免租金,即预先充值足够的 SOL 以支付大约两年的存储费用,就能使账户永久免除任何租金扣除。绝大多数已部署的账户都处于免租金状态。

免租金:实际标准

免租金是所有已部署 Solana 账户的生产标准。免租金的最低余额约为 6,960 Lamports 乘以账户的数据大小(以字节为单位),再加上一个零字节账户的基本金额,约为 890,880 Lamports。

一个工作示例:一个 100 字节的数据账户大约需要 890,880 + (6,960 × 100) = 1,586,880 Lamports,或约 0.00159 SOL。这些数字是近似值,可能会发生变化。使用 Solana CLI 命令 solana rent <bytes> 或 Solana 租金文档.) 验证当前值。一项将租金转换为交易费模型的提议尚未在此文写作时激活;请查阅当前文档以获取最新的协议状态。

当前的账户创建通常需要免租金资金。开发者应查询当前的 RPC 要求,而不是依赖于旧的租金收集行为。这是导致账户被“删除”的担忧行为。

免租金账户永远不会被运行时自动关闭。生产账户始终有足够的资金达到免租金阈值,因此垃圾回收不会影响它们。有意关闭账户是一种单独的、经过深思熟虑的模式:程序可以关闭数据账户以收回为该账户充值的 Lamport。例如,在一个 DeFi 协议中,关闭一个借贷仓位会将为该仓位数据账户充值的 SOL 退还给用户。这是一种常见的协议设计模式,而不是非自愿删除。

程序派生地址 (PDA) 详解

程序派生地址解决了特定问题:程序如何在没有人持有相应私钥的情况下控制账户。

什么是程序派生地址?

程序派生地址 (PDA) 是一个确定性生成的账户地址,通过 SHA-256 哈希算法从程序 ID 和一个或多个种子值派生而来。PDA 被故意放置在 ed25519 椭圆曲线之外,这意味着它们没有对应的私钥。只有拥有者程序才能为 PDA 地址的账户签名交易。有关完整规范,请参阅 Solana 的 PDA 文档)。

所有常规 Solana 账户地址都位于 ed25519 椭圆曲线上。PDA 则被刻意放置在该曲线之外。这种“离轴” (off-curve) 特性并非缺陷,而是一种设计机制,使得私钥在数学上成为不可能。没有任何人、硬件钱包或密钥管理系统能够生成 PDA 的私钥,因为曲线上根本不存在这样的密钥。

为何 PDA 至关重要:程序控制的托管

没有私钥的后果是,只有拥有该 PDA 的程序才能为其交易授权。这实现了去信任的程序控制托管:程序拥有对账户的控制权,而无需依赖任何人来保管私钥。

相比之下,常规的密钥对账户由持有私钥的人控制。如果私钥丢失,访问权限就随之消失;如果私钥被盗,账户就会被攻破。PDA 完全消除了这种依赖。程序的逻辑就是托管人,是可审计的代码而非个人。

三个真实的 PDA 使用案例

PDA 在 Solana 程序中以三种重复模式出现。

DEX 托管账户:去中心化交易所程序将代币存放在 PDA 中,直到交易双方都满足所需条件。没有任何受信任的中间人持有该托管账户的私钥。程序的逻辑控制释放,任何参与者都无法在协议规则之外抽干资金。

任何个人都无法对 NFT 系列的铸造权限进行“跑路” (rug pull)。NFT 铸造权限的工作原理是由 Metaplex 程序通过派生自铸造地址的 PDA 来控制铸造。由于该 PDA 不存在私钥,系列收藏的铸造规则在部署后仅由程序代码强制执行。

程序如何在不维护中心化索引的情况下存储每个用户的数据?每用户状态存储使用派生自 [program_id, user_pubkey] 的唯一 PDA。这会生成一个确定的、特定于用户的账户地址,用于存储该用户的游戏分数、借贷仓位或质押记录。程序始终知道在哪里可以找到每个用户的数据。

函数 findProgramAddress() 和 createProgramAddressSync() 通过使用递增的 bump seed 测试候选地址,直到找到一个脱离曲线的地址,从而定位有效的离轴地址。

什么时候应该使用 PDA?

只要您的程序需要控制账户且不依赖人工私钥时,就请使用 PDA。如果账户权限应该是程序逻辑而非个人,请使用 PDA。这一规则涵盖了托管账户、协议金库、铸造权限以及程序本身必须跨多个用户拥有和管理的任何状态。

Solana 与 Ethereum:账户模型的对比

Solana 和以太坊账户模型的核心区别在于状态存储的位置。

在以太坊中,智能合约是有状态的:它们在内部与代码一起存储自己的状态。Solidity 合约在合约存储中保留自己的映射和状态变量,并由以太坊虚拟机 (EVM) 管理。在 Solana 中,程序是无状态的;所有状态都存在于外部账户中,程序在执行期间读取和写入这些账户。以太坊在 EVM 中运行程序,而 Solana 使用基于 BPF 的运行时,针对交易中预先声明的账户执行程序。

维度Solana以太坊
状态存储位置外部数据账户内部合约存储
代码/数据分离强制执行:程序是无状态的未强制执行:合约存储自身状态
账户创建显式:开发者指定大小、lamports、所有者隐式:EVM 在内部管理存储
存储成本模型租金豁免:与数据大小成比例的 SOL 充值Gas 费:每次 SSTORE 操作的成本
执行模型并行 (Sealevel):非重叠账户同时运行顺序 (EVM):一次一个交易
开发者复杂度较高:账户、所有权、租金需显式管理较低:状态管理由 EVM 处理

这种权衡是双向的。以太坊的有状态模型具有真正的开发者体验优势:心智模型更简单,无需显式账户管理,对于许多用例来说认知负荷较低。开发者可以编写并部署 ERC-20 合约,而无需考虑数据账户大小、租金豁免充值或 PDA 派生。

Solana 的无状态模型以牺牲这种简单性为代价,换取了执行并行性和关注点分离。每笔交易所需的账户声明是 Sealevel 调度算法的输入,该算法能够同时执行互不冲突的交易。其他 Layer-1 区块链(如 Aptos 和 Sui)采用了各自的基于资源的资源状态分离变体,每种变体都有不同的权衡面。以太坊的方法和 Solana 的方法都没有绝对的优劣;它们反映了针对不同工作负载的不同设计优先级。

为什么这种架构很重要:Sealevel 和并行执行

无状态程序不是一种设计限制;它们是使并行执行成为可能的机制。

从账户模型设计到性能的因果链如下:首先,Solana 程序是无状态的。其次,由于程序不携带状态,每笔交易在执行开始前必须声明它将访问的所有账户(包括读取和写入)。第三,Solana 的并行执行引擎 Sealevel 在调度前分析这些账户访问列表。第四,访问非重叠账户集的交易彼此之间没有数据依赖关系,可以在多个处理器核心上同时执行。第五,结果是一个能够每秒处理数万笔交易的网络。

Anatoly Yakovenko 在设计 Solana 架构时,将并行交易处理作为一个明确目标,而将程序逻辑与账户状态分离正是实现这一目标的关键。账户模型与 Solana 的其他架构创新协同工作,包括历史证明 (PoH)——Solana 的加密时钟机制。要了解 PoH 如何对这种吞吐量做出贡献,请参阅 Solana 架构说明中关于历史证明工作原理的部分。

使用账户进行构建:开发者实现

在程序对其进行读取或写入之前,Solana 程序处理的每个账户都必须经过创建、注资并分配给所有者。

使用系统程序创建账户

Solana 程序使用 Rust(一种系统编程语言)编写,并在部署前编译为 BPF 字节码。在 Solana 上创建账户遵循以下步骤,每个步骤对应系统程序“创建账户”指令中的一个参数:

  1. from_pubkey:确定为创建账户提供资金的账户(付款人)
  2. new_account_pubkey:指定新账户的地址
  3. lamports:根据数据大小计算免租金余额金额
  4. space:设置要为账户分配的数据大小(以字节为单位)
  5. owner:指定将拥有并写入此账户的程序公钥
// 使用系统程序创建一个新的数据账户
let create_account_ix = system_instruction::create_account(
    &payer.pubkey(),         // from_pubkey: 谁为此账户提供资金
    &new_account.pubkey(),   // new_account_pubkey: 账户地址
    rent_exempt_balance,     // lamports: 足以豁免租金的金额
    data_size as u64,        // space: 为账户数据分配的字节数
    &program_id,             // owner: 控制此账户的程序
);

此指令确立了账户所有权。执行后,只有位于 program_id 的程序才能写入此账户的数据字段。

使用 Anchor 框架

许多 Solana 程序是使用 Anchor 框架构建的,该框架提供了用于定义账户限制和程序指令的结构化宏。请参阅 Anchor 文档) 以获取完整的框架参考。

// Anchor:使用 #[derive(Accounts)] 宏定义账户约束
#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,                        // 如果此账户不存在,则创建它
        payer = user,                // 用户支付免租金押金
        space = 8 + MyData::LEN,    // 8 字节鉴别器 + 数据大小
    )]
    pub my_account: Account<'info, MyData>,     // 新的数据账户
    #[account(mut)]
    pub user: Signer<'info>,                    // 交易签名者和付款者
    pub system_program: Program<'info, System>, // 账户创建必需
}

Anchor 会自动处理账户验证的样板代码,在您的指令逻辑运行之前验证所有权和签名者状态。在调试所有权错误或设计 PDA 结构时,理解底层账户模型仍然是必要的,因为 Anchor 的宏是对本文档所述的相同机制的抽象。

相关的 Solana 架构资源

正在寻找更短的实现路径的开发者可以使用面向开发者的账户模型指南;关于代币程序的约定在Solana 程序库概述中有所介绍。SOL 价格页面 和 SOL/USDT 现货市场 涉及的是市场访问,而不是账户所有权或程序权限。


常见问题解答:Solana 账户模型:技术指南

Solana 中的账户是什么?

Solana 账户是一个链上数据容器,由唯一的 256 位公钥标识。每个账户都存储一份 Lamports 形式的 SOL 余额、一个数据字段(一个保存程序状态或字节码的字节数组)、一个所有者字段(被授权修改数据的程序的公钥)以及一个可执行标志,指示该账户是否存储程序代码。

在以太坊中,智能合约在其代码的旁边存储自己的状态。在 Solana 中,程序是无状态的;所有状态都存在于程序在执行期间读取和写入的外部账户中。这种分离使得 Solana 的 Sealevel 运行时能够检测非重叠的账户访问模式并同时执行多个事务,从而实现大规模的并行处理。

所有生产环境的 Solana 账户都已充值至免租金门槛,使其永久免除存储费用。租金是底层机制:账户必须根据其数据大小维持最低 SOL 余额才能保持链上活跃。高于该门槛充值的免租金账户永远不会被运行时自动关闭。

程序派生地址 (PDA) 是一个确定性生成的账户地址,它使用 SHA-256 散列算法从程序 ID 和种子值派生而来。PDA 无法从 ed25519 椭圆曲线生成,因此没有对应的私钥。只有拥有该账户的程序才能为 PDA 地址的账户签名交易,从而实现对托管账户、铸币授权和用户特定状态存储的无信任程序控制。

当前账户创建通常需要免租金充值。开发者应查询当前的 RPC 要求,而不是依赖于旧的租金收集行为。妥善部署的生产账户始终充值至免租金门槛,且从不会被运行时自动删除。程序也可以有意关闭账户以收回 SOL 押金,这是 DeFi 协议中的常见模式。

Lamports 是 Solana 区块链上 SOL 的最小单位:1 SOL 等于 1,000,000,000 Lamports (10⁹)。Lamports 以分布式计算先驱 Leslie Lamport 的名字命名,用于链上的所有 SOL 余额、交易费用和租金计算。以 Lamports 为单位表示金额可以避免在链上算术运算中使用小数 SOL 值时出现的浮点精度错误。

系统程序是 Solana 的原生程序,负责三项核心操作:创建新账户(分配链上数据空间)、分配账户所有权(将所有者字段设置为指定程序)以及在系统账户之间转移 SOL。每个新的 Solana 账户都始于一个系统账户。开发者调用系统程序的 Create Account 指令来为其程序配置数据账户。

Solana 账户有三种主要类型:(1)系统账户,由系统程序拥有,用于 SOL 余额和用户钱包;(2)程序账户,是存储已编译 BPF 字节码的可执行账户;(3)数据/状态账户,由程序拥有,存储应用程序特定的状态。代币账户是属于 SPL Token Program 的数据账户的一个特殊子集。

Solana 的账户模型比以太坊的无状态模型需要更多的前期思考,但这种认知投入正是实现并行执行的关键。

  • Solana 上的每一块状态都存在于一个账户中;程序在执行期间读取和写入传递的账户,而不是内部存储。
  • 每个账户有五个字段:lamports、data、owner、executable 和旧的 rent_epoch。
  • 只有所有者程序可以修改账户的数据字段;这是 Solana 的主要访问控制机制。
  • 生产账户已充值至免租金门槛,且从不会被运行时自动垃圾回收。
  • 程序派生地址通过使用没有私钥的离曲线地址,实现了无信任的程序控制托管。
  • Solana 的 Sealevel 运行时通过分析非冲突事务中声明的账户访问列表来实现事务执行的并行化。

准备好在此基础上进行构建了吗?继续阅读:

  • Solana 程序如何工作:无状态执行的开发者指南
  • 用于构建 Solana 程序的 Anchor 框架指南
  • 程序间调用 (CPI) 详解:Solana 程序如何相互调用
  • 历史证明 (Proof of History) 在 Solana 架构中的工作原理

有关账户大小、租金计算和运行时行为的最新协议规范,请直接查阅 Solana 官方账户文档。