跳至内容
0%

什么是程序派生地址(PDA)?

发布于 2023年1月25日2 分钟阅读

Program Derived Addresses(PDAs)是 Solana 区块链上具有特殊属性的账户。正确使用 PDA 能让Solana dApp 开发变得快速且高效,因为它们有助于跨程序通信。

本文将解释什么是 PDA、它们解决了哪些问题、其工作原理,以及它们与Solana 账户模型中其他账户的区别。

什么是程序派生地址(pda)?

Program Derived Address 是 Solana 区块链上没有私钥的账户。由于 PDA 不是公钥,其账户地址是通过程序 ID、SHA-512 哈希函数、种子数组和一个特殊的 bump seed 计算得出的。

Solana 上的标准账户是什么?

一个标准的 Solana 账户同时拥有私钥和公钥(各 32 字节),两者共同组成一个密钥对,长度为 64 字节,位于椭圆曲线(ED25519)上。密钥对要有效,必须位于这条曲线上。

以下是 ED25519 椭圆曲线的示意图:

用于 Solana 账户密钥对的 ED25519 椭圆曲线图
Solana 上的 ED25519 椭圆曲线

程序地址是如何派生的?

PDA 需要三个主要组成部分:

  1. 父程序 ID - 创建该 PDA 的父程序的 ID
  2. 种子(Seeds) - 一个字符串数组
  3. Bump Seed - 确保该 PDA 没有私钥

要生成一个有效的 Program Derived Address,需要将父程序的 ID 和种子数组一起输入 SHA-512 哈希函数运算

然而,大约有 50% 的情况下,该哈希运算的结果会落在 ED25519 椭圆曲线 上,形成一个密钥对。由于 PDA 不能有私钥,Program Derived Address 不能落在椭圆曲线上。为防止 PDA 拥有私钥,会使用一个特殊的 bump seed 将哈希结果“撞”离曲线。

bump seed 本质上只是一个数字,从 255 开始。如果即便加上 bump seed,哈希结果仍然落在曲线上,就会再次运行哈希函数,将 bump 依次改为 254、253,以此类推,直到生成的结果不在曲线上为止。

注意:种子可以是任意字符串,但开发者通常会结合父程序的状态变量来使用它们,以此构建出类似哈希表的结构。

PDA 解决了什么问题?

Program Derived Address 通过以编程方式生成交易签名来简化交易确认流程,从而帮助 DeFi 账户等去信任化服务顺畅运行。

以下是一个假设的 PDA 使用场景。

设想一个 Solana 程序,允许用户将某个 NFT 设置为默认头像(PFP)。该程序由两个部分组成:

  1. PFP 程序 - 创建账户以存储用户选择的头像
  2. 核心程序 - 充当用户输入和 PFP 程序之间的代理

若要让 PFP 程序更新用户所选的头像,它需要用自己的私钥对交易签名以完成头像的更改。但这也意味着该程序需要将其私钥存储在链上。

Solana 程序不能用自己的私钥代表自己对交易签名,因为该密钥本身会被存储在链上,对所有人可见。如果这样做,该私钥就可能被用来代表该程序签署交易,从而更改任意用户的头像。

设想 PFP 程序管理着数百万枚 SOL 代币。这样的漏洞会演变成一场重大黑客攻击。Program Derived Address 正是为解决这一问题而生。

PDA 为什么重要?

Program Derived Address 在 Solana 编程中扮演着重要角色,因为它们有助于不同程序之间的通信(跨程序调用),并且可以充当哈希表,用于存储特定数据,而其父程序可以轻松更新和修改这些数据。

1. 存储程序的状态变量

PDA 让 Solana 开发者能够存储并追踪与特定用户相关的一个或一组变量。PDA 最适合的用途是为其父程序存储状态变量或数据,因为默认情况下它已授权父程序代表自己进行修改。

2. 将 PDA 用作哈希表

映射(Mapping)表示一组键值对,用于根据某个键便捷地查找与之关联的信息。在 Solana 开发中,可以利用 PDA 的种子和相应字符串实现同样的效果。

让我们回到之前钱包头像的例子。

一旦用户选定了钱包的 PFP,PFP 程序会取用所选图片和用户的地址作为“种子”,创建一个用于存储用户选择的 PDA。

一旦哈希算法成功找到该 Program Derived Address,其公钥就会“映射”到用户的地址和所选的 NFT 头像上。

通过再提供一个 PDA 作为第三个种子,这种哈希表功能可以得到更好的利用。我们可以把所有可用的头像存储在一个单独的 PDA 中,将每张头像作为其种子传入,最终得到一个存储了所有头像的 PDA。

这样一来,当用户前来选择头像时,这个 PDA 看起来就像一个哈希表,因为通过传入的种子,你可以看出:在众多头像中(PFP 组 PDA),我们作为第一个种子传入的用户地址,选择了我们作为第二个种子传入的那张头像。

在此基础上,还可以进一步构建更深层次的哈希表结构。

将 PDA 用作哈希表的程序流程 - NFT 示例
将 PDA 用作哈希表的程序流程 - NFT 示例

3. 跨程序调用

跨程序调用(Cross Program Invocations,CPI)是指一个程序调用另一个程序中函数的过程。CPI 之所以有用,是因为它们让代码具备更好的可组合性。

回到我们的例子,假设某用户想把头像从 Degen Ape 换成 Solana Monkey Business 头像。

其底层运作方式如下:

用户登录钱包后,核心程序会取用户的地址(公钥),并查找一个已经创建的、其种子中包含该用户公钥的 PDA。

找到之后,核心程序会调用 PFP 程序中名为 changePFP() 的函数(这就是一次跨程序调用),并将核心程序已经“选定”的 PDA 作为参数传入。

函数被调用后,被选中的 PDA 会检查“请求”该变更的账户是否为其父程序。如果不匹配,交易将被拒绝,因为只有父程序才能修改 PDA 的数据。

由于 PFP 程序正是该 PDA 的父程序,因此它被允许将用户所选的头像从 Degen Ape 更改为 SMB 头像。

使用 PDA 进行跨程序调用 - 修改 NFT 图片
使用 PDA 进行跨程序调用 - 修改 NFT 图片

Program Derived Address 让其父程序能够代表自己签名,并可用于存储程序状态、构建哈希表,以及用于跨程序调用。PDA 是 Solana 编程领域中的一个基础性主题,它使快速、高效的 dApp 开发成为可能。

Program Derived Address 常见问题

在使用 Program Derived Address 时,了解 Solana 如何处理交易和数据会很有帮助。账户主要分为两种类型:可执行账户和不可执行账户。

什么是可执行账户?

可执行账户,也被称为程序(programs),类似于 Ethereum 智能合约——一段在账户与其交互时会改变自身状态的代码。

什么是不可执行账户?

不可执行的数据账户仅用于存储数据(例如账户持有的 SOL 数量、NFT、代币余额等),本质上就是某个程序的状态变量。

Solana 的程序数据存储方式与 Ethereum 智能合约有何不同?

Ethereum 与 Solana 之间一个根本性的区别在于可执行代码的存储组织方式。Ethereum 上的智能合约自带“预建”的存储智能合约会将其所有状态变量存储在其中。相比之下,Solana 上的程序没有预建的存储,而是拥有独立的数据账户,用于存放它们想要存储和引用的各种状态变量。

Background gradient

构建区块链应用

Alchemy 将最强大的 Web3 开发者产品和工具与资源、社区及专业支持结合在一起。