Program Derived Address (PDA)
核心概念
PDA 是 Solana 中一种特殊的地址,具有两个关键特性:
确定性派生: 使用预定义的”种子”和程序 ID 组合,可以确定性地生成地址。就像用固定的配方总能做出同样的菜,给定相同的输入就能得到相同的地址。
程序签名能力: 虽然 PDA 没有私钥,但 Solana 运行时允许程序代表其派生的 PDA 进行签名。这是 PDA 最强大的特性。
为什么需要 PDA?
传统区块链中,程序要控制资产需要管理私钥,这既复杂又不安全。PDA 解决了这个问题:
- 无需私钥管理,程序本身就能控制账户
- 地址可预测,容易查找和管理
- 类似链上的哈希映射,用种子映射到地址
派生机制
PDA 派生需要三个输入:
可选种子: 任意数据,如字符串 "user_profile" 或用户地址。可以使用多个种子组合,例如 ["vault", user_address, "v1"]。
Bump 种子: 一个 0-255 的数字,用于确保地址落在 Ed25519 曲线之外(即没有私钥)。系统从 255 开始递减查找,第一个有效的值称为”规范 bump”。
程序 ID: 派生 PDA 的程序地址。只有这个程序能代表该 PDA 签名。
规范 Bump(Canonical Bump)的重要性
同一组种子可能有多个 bump 值能生成有效的 PDA,但这些 PDA 的地址各不相同。
安全风险:
如果程序不验证是否使用规范 bump,攻击者可能:
- 使用非规范 bump 创建”影子账户”
- 传入这个影子 PDA 地址而非预期的账户
- 绕过程序的安全检查和业务逻辑
- 导致资金损失、状态错误或权限绕过
**注意:**始终使用规范 bump,并将其存储在账户数据中供后续验证。
关键要点
- 派生 PDA 只是计算地址,不会自动创建账户
- 账户必须通过程序指令显式创建
- PDA 账户的所有权属于系统程序,数据所有权属于创建它的程序
- 种子设计要考虑唯一性和可预测性
Cross Program Invocation (CPI)
核心概念
CPI 是一个程序调用另一个程序的指令,类似于 API 调用 API。这是 Solana 程序可组合性的基础。
当程序 A 通过 CPI 调用程序 B 时,原始交易中的权限会自动传递给程序 B,实现了安全的程序间协作。
权限传递机制
假设用户发起交易调用程序 A:
- 用户钱包是签名者且可写
- 某个数据账户只读
- 某个代币账户可写
当程序 A 通过 CPI 调用程序 B 时:
- 程序 B 可以使用用户钱包的签名权限
- 程序 B 可以读取数据账户
- 程序 B 可以写入代币账户
- 程序 B 还能继续 CPI 调用程序 C
这种自动权限传递确保了安全性和便利性的平衡。
调用深度限制
Solana 限制 CPI 调用深度为 4 层:
- 第 1 层:用户原始交易
- 第 2-5 层:最多 4 次 CPI 调用
超过限制会导致交易失败。这个设计防止无限递归,控制计算成本。
两种 CPI 类型
普通 CPI: 只传递原始交易中的签名者权限。适用于简单的中介场景,如用户通过程序 A 向程序 B 转账。
带 PDA 签名的 CPI: 程序可以代表其控制的 PDA 签名。这需要提供 PDA 的派生种子,Solana 运行时会验证 PDA 的有效性后授予签名权限。
安全考虑
执行 CPI 前必须验证:
- 账户所有权是否正确
- 账户状态是否有效
- 调用者是否有权限
- PDA 派生是否使用规范 bump
总结
PDA 提供了确定性地址生成和程序签名能力,使程序能够拥有和控制账户。
CPI 实现了程序间的可组合性,通过权限传递机制实现安全的跨程序调用。
Hooray!Solana PDA 与 CPI 小节完成!!!