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

本文将解释 PDA,它们是什么,它们解决哪些问题,它们如何工作,以及它们与Solana 账户模型中的其他账户有何不同。


 程序派生地址 (PDA) 是 Solana 区块链上具有特殊属性的账户。正确使用 PDA 可以使Solana dApp 开发快速高效,因为它们有助于跨程序通信。

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

程序派生地址是 Solana 区块链上没有私钥的帐户。由于 PDA 不是公钥,因此可以使用程序 ID、SHA-512 哈希函数、种子数组和特殊的碰撞种子来找到帐户的地址。

Solana 上的标准账户是什么?

一个标准的 Solana 帐户既有私钥也有公钥(各 32 个字节),它们共同构成一个密钥对,密钥对长 64 个字节,位于椭圆曲线 (ED25519) 上。为了使密钥对有效,它必须位于此曲线上。 

以下是 ED25519 椭圆曲线的表示:

Solana 上的 ED25519 椭圆曲线
Solana 上的 ED25519 椭圆曲线

程序地址是如何得出的?

PDA 需要三个主要组件:

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

创建有效的程序派生地址需要获取其父程序的 ID、种子数组,并通过 SHA-512 哈希函数运行它们。 

然而,在大约 50% 的情况下,此哈希的结果是位于ED25519 椭圆曲线上的密钥对。由于 PDA 没有私钥,因此程序派生地址不能位于椭圆曲线上。为了防止 PDA 拥有私钥,使用特殊的碰撞种子将哈希结果“撞出”曲线。 

碰撞种子只不过是一个数字,从 255 开始。如果即使使用了碰撞种子,哈希结果仍然位于曲线上,则将再次运行哈希函数,碰撞等于 254,然后是 253 等等,直到生成的结果不在曲线上。

*注意:种子可以是任意字符串,但开发人员在特定于父程序的状态变量的上下文中使用它们来创建类似哈希表的结构。

PDA 能解决哪些问题?

程序派生地址通过以编程方式生成交易签名来简化交易确认,从而帮助 DeFi 账户等无需信任的服务无缝运行。

这是一个假设的 PDA 用例示例。

考虑一个 Solana 程序,它允许用户将 NFT 设置为其默认个人资料图片 (PFP​​)。该程序将由两个程序组成:

  1. PFP 程序- 创建账户来存储用户选择的个人资料图片
  2. 核心程序- 充当用户输入和 PFP 程序之间的代理

PFP 程序若要更新用户选择的个人资料图片,需要使用其私钥签署更改用户个人资料图片的交易。然而,这也意味着程序需要将其私钥存储在链上。

Solana 程序无法使用其私钥代表自己签署交易,因为密钥本身将存储在链上,所有人都可以看到它。如果发生这种情况,私钥可用于代表程序签署交易并更改任何用户的个人资料图片。

想象一下,PFP 程序负责处理数百万个 SOL 代币。这样的漏洞将成为重大黑客攻击。程序派生地址解决了这个问题。

为什么 PDA 很重要?

程序派生地址在 Solana 编程中起着重要作用,因为它们有助于不同程序之间的通信(跨程序调用),并且可以充当存储特定数据的哈希图,其父程序可以轻松更新和更改这些数据。

1. 存储程序的状态变量

PDA 允许 Solana 开发人员存储和跟踪与特定用户相关的一个或一组变量。PDA 的最佳用例是存储其父程序的状态变量或数据,因为默认情况下它已授权父程序代表其进行更改。

2. 使用 PDA 作为 Hashmap

Mapping 表示一组键值对,用于轻松查找与键关联的信息。在 Solana 开发中,可以使用 PDA 的种子和正确的字符串来实现相同的结果。 

让我们回到之前的钱包资料图片示例。

一旦用户选择了其钱包的 PFP,PFP 程序就会获取选定的图像和用户的地址,并将它们用作“种子”来创建用于存储用户选择的 PDA。

一旦哈希算法成功找到程序派生地址,其公钥就会“映射”到用户的地址和选择的 NFT 头像。

通过提供另一个 PDA 作为第三个种子,可以更好地利用哈希图功能。我们可以获取所有可用的个人资料图片并将它们存储在单独的 PDA 中,我们将每张个人资料图片作为其种子传递,最终得到一个存储所有个人资料图片的 PDA。

现在,当用户来选择他们的个人资料图片时,PDA 看起来会像一个哈希图,因为种子已经传递,所以如果您查看它们,您就会知道,在个人资料图片的选择中(PFP 组 PDA),我们作为第一个种子传递的用户地址选择了我们作为第二个种子传递的个人资料图片。 

可以在此示例的基础上创建更深的哈希图结构。

PDA 作为哈希图程序流程 - NFT 示例
PDA 作为哈希图程序流程 - NFT 示例

3. 跨程序调用

跨程序调用 (CPI)是一个程序调用另一个程序中的函数的过程。CPI 非常有用,因为它们可以实现更好的代码可组合性。

回到我们的例子,假设一个用户想要将他们的个人资料图片从 Degen Ape 更改为 Solana Monkey Business 头像。

下面来看看具体发生了什么:

登录钱包后,核心合约将获取用户的地址(公钥)并寻找已创建的 PDA,其种子包括用户的公钥。 

找到之后,核心程序将调用 PFP 程序中名为“changePFP()”的函数(这是一个跨程序调用),该函数将接受已经被核心程序“选择”的 PDA 作为参数。 

一旦调用该函数,所选的 PDA 将检查“请求”进行更改的帐户是否是其父帐户。如果 PDA 不匹配,则交易将被拒绝,因为只有父程序才能修改 PDA 的数据。 

由于 PFP 程序是所选 PDA 的父程序,因此它将被允许将用户选定的个人资料图片从 Degen Ape 更改为 SMB 头像。

使用 PDA 进行跨程序调用 - 更改 NFT 图像
使用 PDA 进行跨程序调用 - 更改 NFT 图像

程序派生地址允许其父程序代表其签名,并可用于存储程序的状态、哈希图和跨程序调用。PDA 是 Solana 编程领域的基础主题,可实现快速高效的 dApp 开发。

程序派生地址常见问题解答

使用程序派生地址时,了解 Solana 如何处理交易和数据可能会有所帮助。两种主要类型的账户是可执行账户和不可执行账户。

什么是可执行帐户?

可执行账户(也称为程序)类似于以太坊智能合约——当账户与其交互时会改变其状态的一段代码。

什么是不可执行帐户?

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

Solana 程序数据存储与以太坊智能合约有何不同?

以太坊和 Solana 之间的一个根本区别在于可执行代码的存储组织方式。以太坊上的智能合约带有“预建”的存储空间,智能合约会将其所有状态变量存储在其中。相比之下,Solana 上的程序没有预建的存储,但它们有单独的数据帐户,用于保存它们想要存储和引用的各种状态变量。

本文由 SlerfTools 翻译自 What is a Program Derived Address (PDA)?