返回 SC 笔记
SC Day 33

Solana - 架构总览 - Account 模型/Program/Instruction/Transaction + EVM 对比

### 一、Solana 架构总览

2026-05-03
第二阶段:框架实战
SolanaAccountModelProgramInstructionTransactionEVM对比

日期: 2026-05-03 方向: Rust/Solana 阶段: 第二阶段:框架实战 标签: #Solana #AccountModel #Program #Instruction #Transaction #EVM对比


今日目标

  1. 理解 Solana 独特的 Account 模型(Data Accounts vs Program Accounts)
  2. 掌握 Program、Instruction、Transaction 三者的关系
  3. 理解 Signers 和 PDA(Program Derived Address)的基本概念
  4. 通过 EVM 对比表深入理解 Solana 与 Ethereum 的架构差异
  5. 实操 Solana CLI 基础命令(keygen、devnet SOL、查询账户)

核心概念

一、Solana 架构总览

Solana 的架构与 Ethereum 有根本性差异。理解这些差异是后续 Solana 开发的基础。

Ethereum 模型:
┌──────────────────┐
│   Smart Contract  │  ← 代码 + 状态 存储在同一个地方
│   ├── code        │
│   ├── storage     │
│   └── balance     │
└──────────────────┘

Solana 模型:
┌──────────────┐     ┌──────────────┐
│   Program     │     │ Data Account  │
│   (只有代码)   │ ←→ │ (只有数据)     │
│   不可变执行码 │     │  可变状态      │
└──────────────┘     └──────────────┘
     ↑                      ↑
   代码与数据分离!

二、Account 模型深度解析

在 Solana 中,一切都是 Account。每个 Account 都有如下结构:

pub struct Account {
    /// 这个账户有多少 lamports(SOL 的最小单位,1 SOL = 1e9 lamports)
    pub lamports: u64,

    /// 这个账户存储的数据(字节数组)
    pub data: Vec<u8>,

    /// 这个账户的"拥有者"程序
    pub owner: Pubkey,

    /// 这个账户是否可执行(是否是程序)
    pub executable: bool,

    /// 这个账户开始能使用的 epoch
    pub rent_epoch: u64,
}

2.1 两种账户类型

特性Data Account(数据账户)Program Account(程序账户)
executablefalsetrue
data 字段存储状态数据存储编译后的 BPF 字节码
可修改 data是(由 owner 程序修改)否(升级需要特殊机制)
类比 EVMStorage slotsContract bytecode
创建成本需要支付 rent需要支付 rent + 部署费用

2.2 所有权模型(Ownership)

这是 Solana 最核心的安全模型之一:

规则 1: 只有账户的 owner 程序才能修改其 data
规则 2: 只有账户的 owner 程序才能扣减其 lamports
规则 3: 任何程序都可以给账户增加 lamports
规则 4: System Program 拥有所有新创建的普通账户
创建流程:
System Program → 创建账户 → 设置 owner 为你的程序

状态修改:
用户签名 → Transaction → 你的程序(owner)→ 修改 data account

2.3 Rent(租金机制)

Solana 上存储数据需要支付"租金"。如果账户余额足以覆盖 2 年租金,则 Rent Exempt(免租),数据永久存储。

Rent 计算:
rent_per_byte_per_year ≈ 0.00000348 SOL

例如存储 165 bytes 的 Token Account:
  rent = 165 * 0.00000348 * 2 = ~0.00114840 SOL ≈ 0.00203928 SOL (含安全余量)

实际使用 solana CLI 查询:
  solana rent 165
  → Rent per byte-year: 0.00000348 SOL
  → Rent per epoch: ...
  → Rent-exempt minimum: 0.00203928 SOL

三、Program(程序)

Solana 的 Program 就是其他链上的 "Smart Contract",但有关键区别:

// Solana Program 的入口函数签名
pub fn process_instruction(
    program_id: &Pubkey,        // 当前程序的 ID(地址)
    accounts: &[AccountInfo],   // 这条指令需要的所有账户
    instruction_data: &[u8],    // 指令附带的数据
) -> ProgramResult {
    // 解析 instruction_data 确定要执行什么操作
    // 修改 accounts 中的数据
    Ok(())
}

3.1 系统内置程序

程序Program ID功能
System Program11111111111111111111111111111111创建账户、转 SOL
Token ProgramTokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DASPL Token 操作
Token 2022TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb新版 Token 标准
Associated TokenATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL管理 ATA
BPF LoaderBPFLoaderUpgradeab1e11111111111111111111111部署/升级程序
Sysvar多个地址提供链上时间、费用等信息

3.2 Program 的关键特性

1. 无状态: Program 自身不存储数据,数据在 Data Accounts 中
2. 确定性: 同样的输入一定产生同样的输出
3. 并行执行: 不同 accounts 的 transactions 可以并行处理
   (这是 Solana 高性能的关键!)
4. CPI: Cross-Program Invocation,程序之间可以互相调用

四、Instruction(指令)与 Transaction(交易)

4.1 层次结构

Transaction(交易)
├── Message
│   ├── Header(签名者数量等元信息)
│   ├── Account Keys(这笔交易涉及的所有账户地址列表)
│   ├── Recent Blockhash(防止重放)
│   └── Instructions[](指令列表)
│       ├── Instruction #1
│       │   ├── program_id_index(调用哪个程序)
│       │   ├── account_indices[](使用 keys 中的哪些账户)
│       │   └── data(传给程序的数据)
│       ├── Instruction #2
│       │   └── ...
│       └── ...
└── Signatures[](所有签名者的签名)

4.2 Instruction 详解

每个 Instruction 包含三部分:

pub struct Instruction {
    /// 要调用的程序地址
    pub program_id: Pubkey,

    /// 这条指令需要的账户列表及其权限
    pub accounts: Vec<AccountMeta>,

    /// 传给程序的数据(编码后的参数)
    pub data: Vec<u8>,
}

pub struct AccountMeta {
    pub pubkey: Pubkey,
    pub is_signer: bool,     // 是否需要签名
    pub is_writable: bool,   // 是否可写
}

4.3 一个实际的 Transaction 例子

场景: Alice 向 Bob 转 1 SOL

Transaction:
  Signatures: [Alice 的签名]
  Message:
    Account Keys: [Alice, Bob, System Program]
    Instructions:
      - Instruction #0:
          program_id: System Program
          accounts: [
            { pubkey: Alice, is_signer: true, is_writable: true },
            { pubkey: Bob, is_signer: false, is_writable: true }
          ]
          data: Transfer { lamports: 1_000_000_000 }

4.4 多指令 Transaction(原子性)

这是 Solana 的一个巨大优势——一个 Transaction 可以包含多条 Instructions,它们原子执行

场景: 在 DEX 中先 approve 再 swap(在 Ethereum 需要两笔交易)

Solana Transaction:
  Instructions:
    #1: Token Program → approve(user, dex, 1000 USDC)
    #2: DEX Program → swap(USDC → SOL)

  → 两条指令原子执行:要么都成功,要么都失败
  → 不存在 "approve 了但 swap 失败" 的状态

五、Signers 与 PDA

5.1 Signers(签名者)

在 Transaction 中标记为 is_signer: true 的账户必须在 Signatures 中提供签名。这保证了只有私钥持有者才能授权操作。

5.2 PDA(Program Derived Address)初探

PDA 是 Solana 中没有私钥的地址,由程序控制:

// PDA 的生成
let (pda, bump) = Pubkey::find_program_address(
    &[b"vault", user.key.as_ref()],  // seeds
    program_id,                       // 程序 ID
);
PDA 的特点:
1. 不在 ed25519 曲线上 → 没有对应的私钥
2. 只有生成它的程序可以"代签" → 程序控制的账户
3. 由 seeds + program_id 确定性生成 → 可重复查找
4. bump seed 保证地址不在曲线上

用例:
- Vault 金库账户(程序控制的资金池)
- 用户的程序专属状态账户
- 权限管理(PDA 作为 authority)

六、EVM vs Solana 对比表

维度Ethereum (EVM)Solana (SVM)
账户模型代码+状态绑定在合约中代码与数据分离
状态存储Contract storage slots独立的 Data Accounts
程序/合约Smart Contract(有状态)Program(无状态)
执行模型串行执行并行执行(Sealevel)
Gas/费用Gas(动态,可能很贵)固定低费用 + priority fee
最小货币单位wei (1 ETH = 1e18 wei)lamport (1 SOL = 1e9 lamports)
区块时间~12 秒~400 毫秒
确认时间~12 秒(1 确认)~400ms(乐观确认)
编程语言Solidity / VyperRust / C / Python(Seahorse)
代币标准ERC20 (合约)SPL Token (系统程序)
NFT 标准ERC721 / ERC1155Metaplex Token Metadata
合约交互直接调用函数Instruction + Accounts
合约间调用External CallCPI (Cross-Program Invocation)
地址格式0x + 40 hex chars (20 bytes)Base58 编码 (32 bytes)
重放保护nonce(递增)recent_blockhash(时效)
原子操作单合约调用原子整个 Transaction 原子
存储成本一次性 GasRent(或 Rent-exempt 永久)
升级机制Proxy 模式BPF Loader 原生支持

关键架构差异深度解读

差异 1: 代码与数据分离

Ethereum:
  Contract A 的所有 storage 都在 Contract A 的 storage trie 下
  → 读写 storage 只需要知道 slot number
  → 但不同合约的 storage 无法并行处理

Solana:
  Program A 管理多个 Data Accounts
  → 每个 account 是独立的
  → 操作不同 accounts 的 transactions 可以并行
  → 但需要提前声明使用哪些 accounts

差异 2: Transaction 中声明所有账户

Ethereum:
  tx.to = Contract Address
  → 运行时才知道会读写哪些 storage slots
  → 无法提前调度并行执行

Solana:
  Transaction 中必须列出所有要用到的 accounts
  → Runtime 在执行前就知道哪些 transactions 冲突
  → 不冲突的 transactions 可以并行执行
  → 这就是 Sealevel 并行执行引擎的基础!

差异 3: 费用模型

Ethereum:
  费用 = gasUsed × gasPrice
  动态定价,网络拥堵时暴涨(可能 $50-200 一笔交易)
  EIP-1559: baseFee(销毁)+ priorityFee(给矿工)

Solana:
  基础费用 = 5000 lamports / signature ≈ $0.00025
  + 计算单元费用(通常很低)
  + priority fee(可选,给验证者小费加速)
  总计通常 < $0.01

代码实战

Solana CLI 基础操作

安装 Solana CLI

# macOS / Linux
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"

# 或使用 agave (新版 Solana 客户端)
sh -c "$(curl -sSfL https://release.anza.xyz/v2.1.0/install)"

# 验证安装
solana --version

生成密钥对

# 生成新密钥对(默认存储在 ~/.config/solana/id.json)
solana-keygen new

# 输出:
# Wrote new keypair to /home/user/.config/solana/id.json
# pubkey: 7nQz3...
# Save this seed phrase to recover your keypair:
# abandon abandon abandon ...

# 查看当前公钥
solana-keygen pubkey

# 从种子短语恢复
solana-keygen recover

# 生成自定义前缀的地址(vanity address)
solana-keygen grind --starts-with abc:1

配置网络

# 查看当前配置
solana config get

# 切换到 devnet(开发测试用)
solana config set --url https://api.devnet.solana.com

# 切换到 testnet
solana config set --url https://api.testnet.solana.com

# 切换到 mainnet
solana config set --url https://api.mainnet-beta.solana.com

# 使用自定义 RPC
solana config set --url https://your-rpc-provider.com

获取 Devnet SOL

# 空投 devnet SOL(每次最多 2 SOL)
solana airdrop 2

# 指定地址空投
solana airdrop 2 <PUBKEY>

# 查看余额
solana balance

# 查看指定地址余额
solana balance <PUBKEY>

查询账户信息

# 查看账户详细信息
solana account <PUBKEY>

# 输出示例:
# Public Key: 7nQz3...
# Balance: 2 SOL
# Owner: 11111111111111111111111111111111  (System Program)
# Executable: false
# Rent Epoch: 361

# 查看程序账户
solana account TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
# Owner: BPFLoaderUpgradeab1e11111111111111111111111
# Executable: true

# 查看交易详情
solana confirm <TX_SIGNATURE> -v

# 查看最近的区块
solana block-height
solana slot

转账

# 转 SOL
solana transfer <RECIPIENT_PUBKEY> 0.5

# 带 memo
solana transfer <RECIPIENT_PUBKEY> 0.1 --with-memo "Hello Solana!"

# 查看交易记录
solana transaction-history <PUBKEY>

用 Rust 查询 Solana 账户(预览)

use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::str::FromStr;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 连接 devnet
    let client = RpcClient::new("https://api.devnet.solana.com".to_string());

    // 查询账户信息
    let pubkey = Pubkey::from_str("Your_Pubkey_Here")?;
    let account = client.get_account(&pubkey)?;

    println!("Balance: {} lamports ({} SOL)",
        account.lamports,
        account.lamports as f64 / 1e9
    );
    println!("Owner: {}", account.owner);
    println!("Data length: {} bytes", account.data.len());
    println!("Executable: {}", account.executable);

    // 查询余额
    let balance = client.get_balance(&pubkey)?;
    println!("Balance via get_balance: {} SOL", balance as f64 / 1e9);

    // 获取最新 blockhash
    let blockhash = client.get_latest_blockhash()?;
    println!("Latest blockhash: {}", blockhash);

    // 获取 slot
    let slot = client.get_slot()?;
    println!("Current slot: {}", slot);

    Ok(())
}

Cargo.toml 依赖:

[dependencies]
solana-client = "2.1"
solana-sdk = "2.1"

关键要点总结

Solana 架构核心三要素

1. Account Model(账户模型)
   → 一切皆账户,代码和数据分离
   → Owner 机制保证安全性
   → Rent 机制管理存储成本

2. Program(程序)
   → 无状态,只有逻辑
   → 通过 CPI 互相调用
   → 系统程序提供基础功能

3. Transaction(交易)
   → 必须声明所有涉及的账户
   → 可包含多条 Instructions(原子执行)
   → 不冲突的 Transactions 并行处理

Solana 高性能的秘密

提前声明 Accounts → Runtime 知道 Tx 冲突图 → Sealevel 并行执行

Ethereum:  Tx1 → Tx2 → Tx3 → Tx4  (串行)
Solana:    Tx1 ─→ ─→ ─→
           Tx2 ─→ ─→       (不冲突的并行执行)
           Tx3 ─→ ─→ ─→

常见误区

  1. 误区:Solana 的 Program 就是 Ethereum 的 Smart Contract

    • 关键区别:Solana Program 是无状态的,所有状态存在独立的 Data Accounts 中
    • 一个 Program 可以管理百万个 Data Accounts
  2. 误区:Solana 只是"更快的 Ethereum"

    • 架构层面完全不同(Account model、并行执行、fees)
    • 开发范式也完全不同(需要提前声明所有 accounts)
  3. 误区:Solana 不需要考虑 Gas 优化

    • 虽然费用低,但有 Compute Unit 限制(默认 200K CU per instruction)
    • 复杂操作可能超出 CU 限制需要优化
  4. 误区:PDA 是可选的高级功能

    • 事实:PDA 是 Solana 开发的基础,几乎每个程序都会用到
    • 没有 PDA 就无法实现程序控制的账户

面试关联

Q1: Solana 与 Ethereum 的账户模型有什么区别?

简短回答:Ethereum 把代码和状态绑定在一起(Smart Contract = code + storage),Solana 把它们分开(Program = code, Data Account = state)。这种分离使 Solana 能实现交易并行处理。

详细回答

  • Ethereum:每个合约有自己的 storage trie,状态和代码紧耦合。EVM 串行执行交易。
  • Solana:Program 只包含代码,状态分布在独立的 Data Accounts 中。Transaction 必须声明所有涉及的 accounts,使 runtime 知道哪些交易冲突,从而并行执行不冲突的交易。
  • trade-off:Solana 的并行性带来性能优势,但开发者需要手动管理 accounts,增加了开发复杂度。

Q2: 为什么 Solana 的 Transaction 需要提前声明所有 Accounts?

答案:这是 Sealevel 并行执行引擎的前提。通过提前知道每笔交易会读写哪些 accounts,runtime 可以构建冲突图,把不冲突的交易分配到不同线程并行执行。如果像 EVM 一样运行时才知道访问了哪些状态,就无法实现这种并行化。

Q3: 解释 Solana 中 Rent 的作用和 Rent Exempt 的含义。

答案:Solana 账户需要支付"租金"来维持链上存储。如果账户余额不足以覆盖 2 年租金,则可能在 epoch 结束时被垃圾回收。Rent Exempt 意味着账户余额 >= 2 年租金,数据将永久保存。当前实践中,几乎所有账户都设为 Rent Exempt。


参考资源

  1. Solana 官方文档 - Core Concepts — 账户模型、程序、交易
  2. Solana Cookbook — 实用代码示例
  3. Solana CLI 参考 — 命令行工具文档
  4. Sealevel 并行执行引擎 — 技术深度
  5. Solana vs Ethereum 对比 — 开发者入口
  6. Helius - Solana 开发教程 — 高质量 Solana 博客
  7. Solana Playground — 浏览器内 Solana 开发环境