---
title: "什麼是 program derived address (PDA)？"
description: "了解什麼是 Program Derived Address、PDA 與一般帳戶地址的差異，以及 PDA 的應用場景"
---

# 什麼是 program derived address (PDA)？

Program Derived Addresses（PDAs）是 Solana 區塊鏈上具有特殊屬性的帳戶。正確使用 PDA 可以讓 [Solana dApp 開發變得快速](https://www.alchemy.com/overviews/learn-solana-development)且高效，因為它們有助於跨程式通訊。

本文將說明 PDA 是什麼、解決了哪些問題、如何運作，以及它們與[Solana 帳戶模型](https://www.alchemy.com/overviews/solana-account-model)中其他帳戶的差異。

## **什麼是 program derived address（pda）？**

**Program Derived Address 是 Solana 區塊鏈上一種沒有私鑰的帳戶**。由於 PDA 不是公鑰，因此該帳戶的位址是透過程式 ID、SHA-512 雜湊函式、種子陣列（seed array）以及一個特殊的 bump seed 計算出來的。

### **什麼是 Solana 上的標準帳戶？**

**一個標準的 Solana 帳戶同時具有私鑰與公鑰（各 32 位元組），兩者共同組成一組金鑰對（keypair），長度為 64 位元組，並位於一條橢圓曲線（ED25519）上**。金鑰對要有效，就必須位於此曲線上。

以下是 ED25519 橢圓曲線的示意圖：

<ImageBlock
  src="https://media.alchemy.com/1703701173-elliptic-curve-on-solana.png"
  alt="用於 Solana 帳戶金鑰對的 ED25519 橢圓曲線圖"
  width={1600}
  height={900}
  caption="Solana 上的 ED25519 橢圓曲線"
/>

## **程式位址是如何推導出來的？**

PDA 需要三個主要元件：

1. **Parent Program ID** - 建立該 PDA 的父程式的 ID
1. **Seeds** - 一組字串陣列
1. **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，依此類推，直到得出的結果不在曲線上為止。

注意：種子可以是任意字串，但開發者通常會依父程式狀態變數的特定情境來使用它們，藉此建立類似雜湊表（hashmap）的結構。

## **PDA 解決了哪些問題？**

**Program derived address 透過以程式化方式產生交易簽章，簡化了交易確認流程，讓 DeFi 帳戶等去信任化服務得以順暢運作。**

以下是一個 PDA 使用情境的假設範例。

假設有一個 Solana 程式，讓使用者可以將某個 NFT 設定為預設大頭貼（PFP）。這個程式會由兩個程式組成：

1. **PFP 程式** - 建立帳戶來儲存使用者所選擇的大頭貼
1. **Core 程式** - 作為使用者輸入與 PFP 程式之間的代理

若要讓 PFP 程式更新使用者選擇的大頭貼，它需要用自己的私鑰來簽署變更大頭貼的交易。但這也代表該程式必須將私鑰儲存在鏈上。

Solana 程式無法用自己的私鑰為自己簽署交易，因為該金鑰本身會儲存在鏈上，所有人都看得到。若真是如此，該私鑰就可能被用來代表該程式簽署交易，進而更改任何使用者的大頭貼。

試想，如果這個 PFP 程式管理著數百萬顆 SOL token，這樣的漏洞將會演變成一場重大的駭客事件。Program Derived Address 正是為了解決這個問題而存在。

## **為什麼 PDA 很重要？**

Program Derived Address 在 Solana 程式設計中扮演關鍵角色，因為它們有助於不同程式之間的通訊（Cross Program Invocation），也可以作為雜湊表，用來儲存父程式能輕鬆更新與變更的特定資料。

### **1. 儲存程式的狀態變數**

PDA 讓 Solana 開發者能夠儲存並追蹤與特定使用者相關的變數或一組變數。PDA 最典型的用途，就是為其父程式儲存狀態變數或資料，因為它預設就已授權父程式代表它進行變更。

### **2. 將 PDA 當作雜湊表使用**

Mapping 代表一組鍵值對（key-value pairs），用來透過鍵快速找到相關聯的資訊。在 Solana 開發中，透過 PDA 的種子與正確的字串，也能達成同樣的效果。

**讓我們回到前面提到的錢包大頭貼範例。**

當使用者選定他們錢包的 PFP 後，PFP 程式會拿選定的圖片與使用者的位址，將這兩者作為「種子」來建立一個 PDA，用來儲存使用者的選擇。

一旦雜湊演算法成功找到這個 Program Derived Address，其公鑰就會「對應」到使用者的位址與所選的 NFT 頭像。

如果再多提供一個 PDA 作為第三個種子，這個雜湊表功能還能發揮得更好。我們可以將所有可選的大頭貼收集起來，儲存在另一個獨立的 PDA 中，並把每一張大頭貼作為它的種子，最終得到一個儲存所有大頭貼的 PDA。

這樣一來，當使用者來選擇他們的大頭貼時，這個 PDA 看起來就會像一個雜湊表，因為透過這些種子，你可以看出：從一組大頭貼選項（PFP 群組 PDA）中，某個作為第一個種子傳入的使用者位址，選擇了作為第二個種子傳入的那張大頭貼。

這個範例可以進一步延伸，建構出更深層的雜湊表結構。

<ImageBlock
  src="https://media.alchemy.com/1703701259-pda-as-hashmap-program-flow.jpeg"
  alt="PDA 作為 hashmap 的程式流程 - NFT 範例"
  width={1024}
  height={768}
  caption="PDA 作為 hashmap 的程式流程 - NFT 範例"
/>

### **3. 跨程式呼叫（Cross program invocations）**

[Cross Program Invocation（CPI）](https://www.alchemy.com/overviews/cross-program-invocation)是指一個程式呼叫另一個程式中函式的過程。CPI 之所以有用，是因為它們讓程式碼有更好的組合性（composability）。

回到我們的範例，假設某位使用者想把他的大頭貼從 Degen Ape 換成 Solana Monkey Business 頭像。

**以下是背後實際發生的流程：**

當使用者登入錢包時，core 合約會取得使用者的位址（公鑰），並尋找一個已經建立、種子中包含該使用者公鑰的 PDA。

找到之後，core 程式會呼叫 PFP 程式中一個名為「changePFP()」的函式（這就是一次 Cross-Program Invocation），並將 core 程式已經「選定」的那個 PDA 作為參數傳入。

一旦這個函式被呼叫，被選定的 PDA 會檢查「要求」進行變更的帳戶是否為其父程式。如果 PDA 不相符，交易就會被拒絕，因為只有父程式才能修改該 PDA 的資料。

由於 PFP 程式是這個被選定的 PDA 的父程式，因此它有權將使用者選定的大頭貼從 Degen Ape 改為 SMB 頭像。

<ImageBlock
  src="https://media.alchemy.com/1703701345-cross-program-invocation-with-pda.jpeg"
  alt="使用 PDA 進行跨程式呼叫 - 變更 NFT 圖片"
  width={1600}
  height={901}
  caption="使用 PDA 進行跨程式呼叫 - 變更 NFT 圖片"
/>

Program Derived Address 讓其父程式能夠代表它進行簽署，並可用於儲存程式的狀態、建立雜湊表，以及進行跨程式呼叫。PDA 是 Solana 程式設計領域中的基礎主題，能讓 dApp 開發變得快速且高效。

## **Program derived address 常見問題**

在處理 Program Derived Address 時，了解 Solana 如何處理交易與資料會很有幫助。帳戶主要有兩種類型：可執行（executable）與不可執行（non-executable）。

### **什麼是可執行帳戶？**

可執行帳戶，也稱為 **_程式（programs）_**，類似於 Ethereum 的智能合約——一段程式碼，當某個帳戶與它互動時，會改變其狀態。

### **什麼是不可執行帳戶？**

不可執行的資料帳戶單純用來儲存資料（例如帳戶擁有的 SOL 數量、NFT、token 餘額等），基本上就是程式的狀態變數。

### **Solana 程式的資料儲存方式與 Ethereum 智能合約有何不同？**

Ethereum 與 Solana 之間一個根本性的差異，在於可執行程式碼的儲存方式。Ethereum 上的智能合約本身就「內建」了[儲存所有狀態變數的 storage](https://www.alchemy.com/docs/smart-contract-storage-layout)。相較之下，Solana 上的程式並沒有內建的 storage，而是擁有各自獨立的資料帳戶，用來持有並參照它們想要儲存的各種狀態變數。
