13. 程序升级与不可变性

程序升级

软件总是需要修补的

在以太坊的早期,智能合约一旦部署就无法修改。如果发现了 Bug,或者想要添加新功能,开发者必须部署一个全新的合约,然后通知所有用户迁移资金和数据到新地址。这不仅麻烦,而且风险极高。

Solana 吸取了教训,从 BPF Loader Upgradeable (v3) 开始,将“可升级性”作为默认选项。


1. 可升级架构:外壳与内核

Solana 的可升级程序由两个账户组成,实现了接口与实现的分离

  1. Program Account (程序账户)

    • 角色:外壳 / 代理 / 入口。
    • 地址:这就是众所周知的 Program ID (如 Tokenkeg...)。
    • 内容:它不存代码!它只存储一个指针,指向实际存放代码的地方。
    • 特点地址永远不变。用户和前端永远只跟这个地址交互。
  2. ProgramData Account (数据账户)

    • 角色:内核 / 实现。
    • 内容:存储实际编译好的 .so 字节码 (ELF)。
    • 权限:记录了 Upgrade Authority (升级管理员)。只有拥有这个私钥的人才能修改这里的字节码。

当用户调用程序时,运行时加载器会先找到 Program Account,顺藤摸瓜找到 ProgramData Account,加载里面的字节码并执行。


2. 升级流程

升级过程对用户是完全透明的,因为 Program ID 没有变。

  1. 编译新代码:修复 Bug,编译出新的 .so 文件。
  2. 写入 Buffer:将新代码上传到一个临时的 Buffer 账户(和初次部署一样)。
  3. 执行升级:管理员发送 Upgrade 指令。
    • Loader 会将 Buffer 中的新字节码替换掉 ProgramData Account 中的旧字节码。
    • Buffer 账户随之关闭,租金退还。
python
# 使用代码进行升级 import pathlib import pxsol ada = pxsol.wallet.Wallet(pxsol.core.PriKey.int_decode(0x01)) program_pubkey = pxsol.core.PubKey.base58_decode('DVapU9kv...K5E') # 读取新版本的字节码 new_code = pathlib.Path('target/deploy/solana_storage_v2.so').read_bytes() # 发起升级交易 ada.program_update(program_pubkey, new_code) print("✅ 程序已升级到 v2")

3. 放弃权限:通往去中心化

可升级是一把双刃剑。虽然方便了修复 Bug,但也意味着开发者可以随时“作恶”(比如修改代码卷走资金)。

在 DeFi 项目成熟后,为了赢得社区信任,开发者通常会放弃升级权限,使程序变得不可变 (Immutable)

操作很简单:将 Upgrade Authority 设置为 None (null)。 一旦执行,没人(包括开发者自己)能再修改代码。程序将永远按照既定逻辑运行,直到 Solana 链停止运转。

bash
# 命令行放弃权限 solana program set-upgrade-authority <PROGRAM_ID> --final
RUSTPlayground
EDITOR ACTIVE
Initializing RUST Environment...

热更新实验室

Admin Actions
Current Authority: AdminWallet
Allows upgrading code to fix bugs.
Program Account (ID)
ProgKey...111
Points to Data
ProgramData Account
Bytecode:v1.0
Authority:
Active
Buffer Account
New Bytecode
v.0