如何使用零知識證明在以太坊上構建隱私應用程式

以太坊在設計上是極度公開的。任何擁有區塊鏈瀏覽器的人都可以看到每個地址、餘額、交易、合約呼叫和事件。當你需要可驗證性時,這種透明度非常有用。但當用戶需要投票、申領、提款或證明會員身分,而不希望將每個操作都連結回同一個錢包時,這就會成為一個問題。
匿名會員制是一種可重複使用的模式,為以太坊上一大類隱私應用程式提供動力。人們首先註冊,然後在不透露自己是哪位成員的情況下,證明他們屬於該群體。零知識證明是註冊錢包和操作錢包之間的橋樑,而這座橋樑不會透露是誰跨越了它。
周邊的產品會改變,但隱私骨架保持不變。
透過匿名投票解釋該模式
該模式包含三個部分。一個承諾會註冊每位成員。一棵默克爾樹將這些承諾變成一個群體。一個證明和一個作廢者(nullifier)讓一位成員可以操作一次,而不會透露是哪位成員進行了操作。
第一步:註冊
每位投票者在鏈下建立兩個私密值:秘密(secret)和作廢者(nullifier)。投票者將這些值雜湊成一個公開的承諾,然後在鏈上註冊該承諾。
承諾是公開的註冊紀錄。秘密和作廢者是投票者稍後需要的私密票據(note)。如果遺失了票據,投票者就無法證明會員身分。如果洩漏了票據,其他人可能就可以代替該用戶投票。
因為承諾是一個雜湊,觀察者無法還原其中的私密值。承諾表示「有人註冊了」,但不會透露稍後誰將使用該註冊。
第二步:建立群體
隨著越來越多投票者註冊,應用程式會將他們的承諾收集到一棵默克爾樹中。默克爾樹將一長串的值壓縮成單一雜湊,稱為根(root)。只要更改列表中的任何值,雜湊就會改變,因此根可以作為整個集合防篡改的摘要。
這棵樹就是你的匿名集。如果樹中有十個用戶,觀察者可以將後續的操作縮小到這十個人之一。如果樹中有一萬個用戶,就很難將操作連結到單一個人。一個匿名集很小的隱私應用程式通常不太具有隱私性,即使密碼學是正確的。
第三步:匿名操作
當投票開始時,投票者不應該使用註冊承諾的同一個錢包進行投票。從註冊錢包投票會將投票直接連結回註冊者,從而破壞隱私保護工作。相反地,投票者會建立一個零知識證明。該陳述被編碼為一個電路,表示:「我知道能產生已註冊承諾的私密值,並且我正在揭露這次投票正確的作廢者雜湊。」
該證明能說服驗證者合約該陳述為真。它不會透露秘密、作廢者,或使用了哪個承諾。
作廢者是用來防止重複投票的機制。在提交證明的同時,投票者會發布一個作廢者雜湊。投票合約在接受投票後會儲存該雜湊。如果同一個私密票據再次用於同一次投票,它會產生相同的作廢者雜湊,合約就會拒絕第二次投票。結合證明,這使得合約只知道某個已註冊的投票者操作了一次,而不知道是哪一位。
可重複使用的閘門
同樣的證明與作廢者組合不僅適用於投票。撇開投票的情境,你得到的是一個用於智能合約函式的隱私閘門。
在函式執行之前,合約會檢查默克爾根、驗證證明、確認作廢者雜湊未被使用過,並將公開輸入綁定到正確的應用程式、鏈、投票、申領或提款。如果這些檢查通過,它會將作廢者標記為已使用,並執行函式的其餘部分。
將這個閘門放在投票之前,你就得到了匿名投票。將它放在空投申領之前,你就得到了匿名申領。將它放在提款函式之前,你就得到了混幣器風格提款流程的核心。相同的承諾樹、相同的作廢者概念、相同的證明模式。改變的只有函式主體和周邊的應用程式邏輯。
什麼在哪裡運行
私密工作通常在鏈下進行。用戶儲存票據,客戶端應用程式建立見證並運行證明者來產生證明。索引器會追蹤承諾和默克爾根。捆綁器會在鏈上傳播用戶操作(UserOperation),並由 ERC-4337 代付合約贊助燃料,因此一個全新的錢包不需要先從用戶已知的錢包獲取 ETH。
公開的強制執行發生在鏈上。驗證者合約會檢查證明。應用程式合約會檢查有效的根和未使用的作廢者、儲存作廢者雜湊,並執行公開操作。
敏感的使用者體驗(UX)在於票據的處理。請將秘密和作廢者視為金鑰。不要將它們放入分析、日誌、URL、錯誤報告或一般的伺服器端遙測中。一旦票據洩漏,無論證明有多強大,隱私都會蕩然無存。
工具已經跟上
你不需要手動編寫底層的密碼學程式碼。常見的做法是使用高階零知識語言編寫電路,產生一個 Solidity 驗證者,然後從應用程式合約中呼叫該驗證者。
適合的技術堆疊取決於工作需求。Circom 搭配 snarkjs 是應用程式級別電路行之有效的途徑。Noir 搭配 Barretenberg 是一條較新且對開發者友善的途徑。Halo2 和 gnark 是較低階的電路函式庫。像 RISC Zero 或 SP1 這樣的 zkVM 可以證明一般的程式,但證明的成本可能比小型的自訂電路更高。
對於匿名會員制,在編寫自己的電路之前,請先尋找現有的協定。Semaphore 將群組會員制和基於作廢者的防止重複使用機制打包成合約和 JavaScript 函式庫。對於私密投票和治理,MACI 是專門的途徑,因為它增加了防勾結的特性。成熟的協定通常比新的電路更安全。
僅有證明是不夠的
如果錢包流程洩漏了關聯性,即使是完美的證明也會失效。從錢包 A 註冊,稍後又從錢包 A 進行操作,任何觀察者都能將這些交易連結起來。在操作前夕從錢包 A 轉帳給錢包 B,這筆資金交易也會造成同樣的問題。
這就是為什麼捆綁器和代付合約如此重要。操作錢包應該是全新的,而且它不應該需要從用戶試圖與該操作分開的錢包中接收 ETH。
同樣的問題也存在於鏈下。從同一個 IP 地址、RPC 供應商或工作階段提交註冊和操作交易,可能會削弱電路提供的隱私。前端可能會透過分析、本機儲存和支援日誌發生洩漏。零知識證明隱藏了證明內部的值,但它並不會隱藏交易周圍的所有事物。
公開輸入是隱私應用程式失敗的另一個地方。在電路中標記為公開、作為事件發出、包含在呼叫資料(calldata)中,或由合約儲存的任何內容都是可見的。審查公開輸入時,應像審查 Solidity 合約上的存取控制一樣仔細。
這對開發者帶來了什麼改變
在以太坊上實現隱私是可行的。開發者可以將這些組件組合成真實的應用程式。這個技術堆疊包含:用於私密陳述的電路、用於檢查證明的驗證者、用於公開規則的應用程式合約、用於默克爾資料的索引器,以及用於無關聯提交和燃料贊助的捆綁器加代付合約。
困難的部分在於產品設計、金鑰管理、中繼資料衛生、稽核,以及擴大匿名集。只要其中任何一個環節出錯,證明所提供的隱私就會消失。
進一步閱讀
- 零知識證明 (ethereum.org) (opens in a new tab)
- Semaphore 文件 (opens in a new tab)
- MACI 文件 (opens in a new tab)
- Circom 文件 (opens in a new tab)
- Noir 文件 (opens in a new tab)
- Halo2 手冊 (opens in a new tab)
- gnark 文件 (opens in a new tab)
- RISC Zero 文件 (opens in a new tab)
- SP1 文件 (opens in a new tab)
- EIP-4337:透過 EntryPoint 合約實現帳戶抽象化 (opens in a new tab)
頁面最後更新: 2026年5月28日