ERC-8039: Programming Smart Account Logic with Zero-Knowledge Circuits
ERC-8039 為智能帳戶引入了一個標準化的鏈上零知識證明驗證介面,使各種證明系統都能實現更強的隱私性與簡潔性。此提案在保持證明系統不可知論與前向相容性的同時,促進了可證明所有權與複雜策略驗證的發展。
類別: 標準軌道、帳戶抽象、零知識證明
作者: Walid Khemiri, Ismail Amara
狀態: 討論中
相關連結: [ERC-8039 草案]( Add ERC: ZK Proof Verification for Smart Accounts by khemiriwalid · Pull Request #1238 · ethereum/ERCs · GitHub )
首次實作: GitHub - MicrochainLabs/microchain-zk-signers · GitHub
摘要 (Abstract)
智能帳戶透過鏈上代碼實現了可編程的帳戶邏輯。零知識證明(Zero-knowledge proofs)為智能帳戶開啟了兩大超能力:
-
隱私性:在執行邏輯的同時隱藏敏感資訊(私密輸入、隱藏狀態)。
-
簡潔性:透過極小的鏈上證明來驗證複雜的離鏈計算。
目前缺失的環節?一個讓智能帳戶在鏈上驗證 ZK 證明的標準化介面。
我們推出了 ERC-8039:智能帳戶的 ZK 證明驗證 —— 這是一個與證明系統無關(proof-system-agnostic)的介面,使智能帳戶能夠使用零知識電路來編寫身份驗證、授權、恢復及任何與帳戶相關的邏輯。遵循已驗證的 ERC-1271 模式,ERC-8039 提供了一個統一介面,可與任何證明系統(Groth16、PLONK、HONK 等)協作,實現:
隱私使用場景:
私密身份驗證:證明簽名有效性,而不洩露簽署者身份或門檻要求。
隱私保護策略:執行支出限制、Gas 價格、時間鎖,同時保持這些約束條件隱藏。
機密恢復:透過隱藏的守護者(guardians)和機制進行帳戶恢復。
憑證驗證:在不洩露數據的情況下證明年齡、身份或信用評分。
簡潔性使用場景:
複雜策略驗證:在離鏈執行複雜規則,並在鏈上進行廉價驗證。
歷史狀態驗證:證明關於過去區塊鏈狀態的屬性(協處理器模式)。
信任最小化計算:在離鏈執行任意邏輯並進行鏈上驗證。
基礎設施:
證明系統無關:無需更改帳戶邏輯即可切換或支持多種證明系統。
向前兼容:適用於任何 ZK 框架(Noir、Circom、SP1、RISC0)及未來的證明系統。
架構圖:
引入「可證明所有權」(Provable Ownership)
ERC-8039 的一個關鍵應用是可編程所有權 —— 透過零知識電路邏輯而非鏈上代碼來定義帳戶所有權。正如智能合約從「可編程貨幣」演變為「可編程所有權」(多簽、時間鎖、DAO 治理),ERC-8039 開啟了下一個演變:可證明所有權。
演變歷程:
傳統 EOA: 所有權 = 私鑰
(單點故障)
↓
可編程所有權: 所有權 = 智能合約代碼
(多簽、策略、治理)
但所有邏輯皆為公開
↓
可證明所有權: 所有權 = ZK 電路代碼
(具備相同功能,且完全隱私)
由 ERC-8039 實現 ✓
範例:
| 所有權模型 | 傳統(公開) | 可證明(透過 ERC-8039 實現隱私) |
|---|---|---|
| 多重簽名 | 5 分之 3 簽署者在鏈上可見 | 5 分之 3 簽署者隱藏在 ZK 電路中 |
| 加權投票 | 「Alice: 40%, Bob: 30%」公開 | 權重隱藏,僅證明達到門檻 |
| 時間鎖 | 「區塊 X 前不可提款」可見 | 時間約束在滿足前保持隱藏 |
| 基於角色的權限 | 「CEO: 無限制, 員工: 1k」公開 | 角色與限額完全隱私 |
為何這很重要:
傳統的可編程所有權犧牲了隱私 —— 競爭對手可以看到你的簽署者,攻擊者知道該針對誰,組織結構完全暴露。透過 ERC-8039 實現的可證明所有權在提供相同編程能力的同時,確保了完全的機密性 —— 證明授權有效,而不必洩露是誰、如何或為何授權。
動機 (Motivation)
問題所在:使用 ZK 電路編寫智能帳戶邏輯需要標準化的驗證方式
智能帳戶透過讓帳戶邏輯可編程化徹底改變了以太坊。但它們面臨兩個根本性的限制:
-
隱私性:所有邏輯都是公開的 —— 簽署者、門檻、策略和恢復機制在鏈上清晰可見。
-
計算成本:複雜邏輯成本高昂 —— 每一項計算消耗的 Gas 與其複雜度成正比。
零知識電路解決了這兩個問題:
鏈上邏輯: 若 (邏輯 == 鏈上代碼) { 可編程 && 公開 && 昂貴 }
ZK 電路邏輯: 若 (邏輯 == ZK 電路代碼) { 可編程 && 隱私 && 簡潔 }
使用場景維度 1:隱私(隱藏資訊)
ZK 電路透過將敏感資訊保留在離鏈,實現了私密帳戶邏輯:
- 私密身份驗證(可證明所有權)
- 傳統:具備公開簽署者的 3-of-5 多簽。
- ZK 電路:具備隱藏簽署者、權重和門檻的 3-of-5 多簽。
- 隱私保護策略
- 傳統:每日最高支出 $10k(公開限制,競爭對手可見)。
- ZK 電路:每日最高支出 $10k(私密限制,保護企業隱私)。
- 機密恢復
- 傳統:2-of-3 守護者(公開地址,易受社交工程攻擊)。
- ZK 電路:2-of-3 守護者(隱藏身份,透過隱匿實現安全)。
- 憑證驗證
- 傳統:將憑證存儲在鏈上(公開,永久暴露)。
- ZK 電路:證明憑證而不洩露內容(隱私,選擇性披露)。
使用場景維度 2:簡潔性(高效驗證)
ZK 電路實現了對複雜離鏈計算的Gas 高效驗證:
- 複雜策略驗證(離鏈計算)
- 傳統:在鏈上評估 50 條策略規則(約 50 萬 Gas)。
- ZK 電路:在離鏈證明 50 條規則皆滿足(驗證約需 35 萬 Gas)。
- 優勢:Gas 節省隨複雜度增加(100 條規則 = 便宜 10 倍)。
- 歷史狀態驗證(協處理器模式)
- 傳統:無法訪問舊狀態(僅當前狀態可用)。
- ZK 電路:證明關於任何歷史狀態的屬性(例如:「帳戶在第 1M 個區塊時餘額 > X」)。
- 優勢:無需鏈上狀態歷史即可開啟基於時間的策略。
- 信任最小化計算
- 傳統:在鏈上執行複雜算法(可能超過區塊 Gas 上限)。
- ZK 電路:在離鏈執行,在鏈上證明正確性(始終能放入區塊)。
- 優勢:任意計算複雜度,固定驗證成本。
關鍵洞察:簡潔性在以下情況最有價值:
- 邏輯複雜(規則多、計算深)。
- 需要歷史數據(過去狀態、時間序列)。
- 計算量超過區塊 Gas 上限。
關鍵依賴
ZK 電路邏輯無法獨立運作 —— 證明必須由智能合約在鏈上驗證。這是使隱私和簡潔性都能達到去信任化的關鍵依賴。
挑戰: 每個證明系統都有不同的驗證介面:
// Groth16 (Circom)
function verifyProof(
uint256[2] calldata a,
uint256[2][2] calldata b,
uint256[2] calldata c,
uint256[] calldata input
) external view returns (bool);
// PLONK (多種實作)
function verify(
bytes calldata proof,
uint256[] calldata publicInputs
) external view returns (bool);
// UltraHonk (Noir/Barretenberg)
function verify(
bytes calldata proof,
bytes32[] calldata publicInputs
) external view returns (bool);
// SP1 zkVM
function verifyProof(
bytes32 programVKey,
bytes calldata publicValues,
bytes calldata proofBytes
) external view;
若沒有標準介面:
- 帳戶合約與特定證明系統高度耦合。
- 不更換合約就無法升級證明系統。
- 無法支持多種證明系統。
- 每種帳戶類型都需要自定義的驗證器集成。
- 生態系統工具無法跨實作運作。
有了 ERC-8039:
- 帳戶可與任何證明系統協作。
- 透過適配器合約升級證明系統。
- 同時支持多種證明系統。
- 所有帳戶採用統一的集成模式。
- 生態系統工具具備通用性。
這些證明可以使用以下技術生成:
- Groth16(驗證最快,證明最小)。
- PLONK(通用設置,可更新)。
- HONK(無需信任設置,高效)。
- STARKs(後量子安全,透明)。
- zkVMs(SP1, RISC0 —— 任意程序執行)。
ERC-8039 使智能帳戶能夠:
- 為每個使用場景選擇最佳證明系統(隱私 vs 簡潔性)。
- 隨證明系統進步而升級(更高性能、更低 Gas)。
- 支持混合方案(針對不同操作使用不同證明)。
- 保持未來兼容性,對接新興技術。
- 在單一應用中結合隱私與簡潔性。
解決方案:ERC-8039
ERC-8039 遵循已驗證的 ERC-1271 簽名驗證模式:
// ERC-1271: 簽名驗證
interface IERC1271 {
function isValidSignature(
bytes32 hash,
bytes memory signature
) external view returns (bytes4 magicValue);
// 有效時返回: 0x1626ba7e
}
// ERC-8039: 證明驗證
interface IERC8039 {
function verifyProof(
bytes calldata publicInputs,
bytes calldata proof
) external view returns (bytes4 magicValue);
// 有效時返回: 0x534f5876
}
為何此模式有效:
- 開發者熟悉:智能帳戶開發者已理解 ERC-1271。
- 不拋出異常(Non-reverting):返回魔術值而非 revert(Gas 高效、具組合性)。
- 證明系統無關:透過適配器支持任何證明格式。
- 生態系統兼容:遵循既有的標準慣例。
核心介面
/**
-
@title IERC8039
-
@notice 智能帳戶驗證 ZK 證明的標準介面
-
魔術值: bytes4(keccak256("verifyProof(bytes,bytes)")) = 0x534f5876
/
interface IERC8039 {
/*- @notice 驗證零知識證明
- @dev 證明無效時絕不可 revert;應返回 0x00000000
- @param publicInputs 公開輸入 (instance)
- @param proof 序列化的證明 (evidence)
- @return magicValue 有效則為 0x534f5876,否則為 0x00000000
*/
function verifyProof(
bytes calldata publicInputs,
bytes calldata proof
) external view returns (bytes4 magicValue);
/**
- @notice 返回此驗證器支持的證明類型
- @dev 格式: keccak256("system-implementation")
- @return proofType 證明系統標識符
*/
function getProofType() external pure returns (bytes32 proofType);
/**
- @notice 返回人類可讀的元數據
- @dev 描述正在證明的陳述/關係
- @return metadata 應用名稱、用途及版本
*/
function metadata() external pure returns (string memory metadata);
}
三大關鍵功能
verifyProof()- 核心驗證
用途: 根據公開輸入驗證 ZK 證明。
設計原則:
- 不拋出異常:失敗時返回
0x00000000(類似 ERC-1271)。 - 通用位元組(Generic bytes):支持任何證明格式。
- View 函數:無狀態更改,Gas 高效。
- 魔術值:成功時返回
0x534f5876。
getProofType()- 技術識別
用途: 識別證明系統的實作方式。
格式:keccak256(“system-implementation”)
範例:
- keccak256("groth16-circom") // 透過 Circom/SnarkJS 的 Groth16
- keccak256("groth16-gnark") // 透過 Gnark (Go) 的 Groth16
- keccak256("plonk-circom") // 透過 Circom 的 PLONK
- keccak256("honk-barretenberg") // 透過 Noir/Aztec 的 UltraHonk
- keccak256("sp1") // SP1 zkVM (Rust)
- keccak256("risc0") // RISC0 zkVM (Rust)
使用場景: - 驗證證明格式是否符合預期。
- 根據證明系統路由驗證邏輯。
- 自動化工具與內省(introspection)。
- 兼容性檢查。
metadata()- 人類可讀描述
用途: 描述正在證明的內容。
格式:“應用名稱 v版本 - 用途”
範例:
- “ZK MultiSig v1.0.0 - 私密門檻簽名”
- “Max Gas Price v1.0.0 - 私密 Gas 價格限制”
- “Spending Limit v2.0.0 - 私密每日支出上限”
- “Social Recovery v1.0.0 - 隱藏守護者恢復”
- “Time-Locked Recovery v1.5.0 - 私密時間鎖機制”
- “Age Verification v1.0.0 - 證明年齡 > 18”
使用場景: - UI 顯示與調試。
- 審計追蹤與日誌記錄。
- 文檔與合規性。
- 面向用戶的應用程序。
職責分離
這三個函數提供了完整的上下文:
getProofType() → 技術上下文(「如何驗證」—— 證明系統)
↓
verifyProof() → 驗證(「是否通過?」)
↓
metadata() → 語義上下文(「證明什麼」—— 應用用途)
ERC-8039 如何實現隱私且高效的帳戶邏輯
架構概覽
實作案例 1:zkSafe
連結:GitHub - MicrochainLabs/microchain-zk-signers · GitHub
使用 ZK 代理合約作為所有者的 Safe 帳戶。
// Safe 配置
contract Safe {
address[] owners = [zkSignerProxy]; // 單一所有者:ZK 代理
uint256 threshold = 1;
}
// ZK 簽署者代理 (使用 ERC-8039)
contract ZKSignerProxy {
IERC8039 public immutable verifier; // ERC-8039 驗證器
bytes32 public signersRoot;
constructor(address _verifier) {
verifier = IERC8039(_verifier);
}
// ERC-1271: Safe 調用此函數來驗證簽名
function isValidSignature(
bytes32 hash,
bytes memory signature
) external view returns (bytes4) {
// 準備公開輸入
bytes memory publicInputs = abi.encode(
hash,
stateRoot
);
// 使用 ERC-8039 驗證證明
bytes4 result = verifier.verifyProof(publicInputs, signature);
// 將 ERC-8039 結果轉換為 ERC-1271 魔術值
if (result == 0x534f5876) {
return 0x1626ba7e; // ERC-1271 成功
}
return 0xffffffff; // ERC-1271 失敗
}
}
流程:
- 用戶在離鏈準備 Safe 交易。
- M 個簽署者對交易哈希進行簽名(私密)。
- 生成 ZK 證明:「M 個簽名有效」。
- Safe 調用
zkSignerProxy.isValidSignature(txHash, zkProof)。 - 代理透過 ERC-8039 調用
verifier.verifyProof()。 - 若證明有效 → Safe 執行交易。
實作案例 2:zkNexus (ERC-7579)
連結:GitHub - MicrochainLabs/microchain-zk-signers · GitHub
帶有 ZK 驗證器模組的 Biconomy Nexus。
// ZK 驗證器模組 (使用 ERC-8039)
contract ZKValidatorModule {
IERC8039 public immutable verifier; // ERC-8039 驗證器
bytes32 public signersRoot;
constructor(address _verifier) {
verifier = IERC8039(_verifier);
}
// ERC-7579: EntryPoint 調用此函數來驗證 UserOps
function validateUserOp(
UserOperation calldata userOp,
bytes32 userOpHash
) external returns (uint256) {
// 準備公開輸入
bytes memory publicInputs = abi.encode(
userOpHash,
signersRoot,
);
// 使用 ERC-8039 驗證證明
bytes4 result = verifier.verifyProof(publicInputs, userOp.signature);
// 將 ERC-8039 結果轉換為 ERC-7579 驗證代碼
if (result == 0x534f5876) {
return VALIDATION_SUCCESS;
}
return VALIDATION_FAILED;
}
}
流程:
- 用戶在離鏈準備 UserOperation。
- M 個簽署者對 userOpHash 進行簽名(私密)。
- 生成 ZK 證明:「M 個簽名有效」。
- EntryPoint 調用
zkValidatorModule.validateUserOp()。 - 模組透過 ERC-8039 調用
verifier.verifyProof()。 - 若證明有效 → EntryPoint 執行 UserOperation。
證明系統無關設計
透過 ERC-8039,我們可以使用適配器模式(adapter pattern)來封裝特定證明系統的驗證器:
範例
- HONK 適配器 (Noir/Barretenberg)
contract HonkProofVerifier is IERC8039 {
HonkVerifier public immutable honkVerifier;
function verifyProof(
bytes calldata publicInputs,
bytes calldata proof
) external view returns (bytes4 magicValue) {
// 將公開輸入解碼為 bytes32[] (HONK 格式)
bytes32[] memory decodedInputs = abi.decode(publicInputs, (bytes32[]));
// 調用原生 HONK 驗證器
bool isValid = honkVerifier.verify(proof, decodedInputs);
// 返回 ERC-8039 魔術值
if (isValid) {
magicValue = 0x534f5876;
}
}
function getProofType() external pure returns (bytes32) {
return keccak256("honk-barretenberg");
}
function metadata() external pure returns (string memory) {
return "ZK MultiSig v1.0.0 - 私密門檻簽名";
}
}
- Groth16 適配器 (Circom)
contract Groth16ProofVerifier is IERC8039 {
IGroth16Verifier public immutable groth16Verifier;
function verifyProof(
bytes calldata publicInputs,
bytes calldata proof
) external view returns (bytes4 magicValue) {
// 解碼 Groth16 證明組件 (a, b, c)
(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c) =
abi.decode(proof, (uint256[2], uint256[2][2], uint256[2]));
// 將公開輸入解碼為 uint256[]
uint256[] memory inputs = abi.decode(publicInputs, (uint256[]));
// 調用原生 Groth16 驗證器
bool isValid = groth16Verifier.verifyProof(a, b, c, inputs);
// 返回 ERC-8039 魔術值
if (isValid) {
magicValue = 0x534f5876;
}
}
function getProofType() external pure returns (bytes32) {
return keccak256("groth16-circom");
}
function metadata() external pure returns (string memory) {
return "ZK MultiSig v1.0.0 - 私密門檻簽名";
}
}
- SP1 zkVM 適配器
contract SP1ProofVerifier is IERC8039 {
ISP1Verifier public immutable sp1Verifier;
bytes32 public immutable programVKey;
function verifyProof(
bytes calldata publicInputs,
bytes calldata proof
) external view returns (bytes4 magicValue) {
// SP1 驗證器在失敗時會 revert,使用 try/catch
try sp1Verifier.verifyProof(programVKey, publicInputs, proof) {
// 成功:證明有效
magicValue = 0x534f5876;
} catch {
// 失敗:返回 0x00000000 (預設值)
}
}
function getProofType() external pure returns (bytes32) {
return keccak256("sp1");
}
function metadata() external pure returns (string memory) {
return "SP1 zkSafe v1.0.0 - 基於 Rust 的門檻簽名";
}
}
安全考量
證明重放攻擊 (Proof Replay Attacks)
應用程序必須在電路設計中包含重放防範機制。
帳戶特定綁定
// 公開輸入包含帳戶地址
bytes memory publicInputs = abi.encode(
messageHash,
stateRoot,
address(this) // 將證明與此帳戶綁定
);
基於 Nonce 的防範
// 公開輸入包含遞增的 nonce
bytes memory publicInputs = abi.encode(
messageHash,
signersRoot,
stateRoot,
nonce++ // 每個證明使用唯一的 nonce
);
不可變驗證器
// 使驗證器不可變
contract ZKSignerProxy {
IERC8039 public immutable verifier; // 無法更改
constructor(address _verifier) {
verifier = IERC8039(_verifier);
}
}
公開輸入操縱
攻擊者可能修改公開輸入,使無效證明看起來有效。
生態系統影響
1. 標準化的 ZK 集成
在 ERC-8039 之前:
// 每個帳戶都需要自定義集成
contract CustomAccount {
HonkVerifier honkVerifier;
Groth16Verifier groth16Verifier;
// ... 為每個系統編寫自定義驗證邏輯
}
有了 ERC-8039:
// 通用集成
contract StandardAccount {
IERC8039 verifier; // 可與任何證明系統協作
}
2. 生態系統工具
錢包與介面:
// 通用的證明驗證檢查
interface IWallet {
async verifyProof(
verifierAddress: Address,
publicInputs: Bytes,
proof: Bytes
): Promise<boolean> {
const verifier = IERC8039__factory.connect(verifierAddress);
const result = await verifier.verifyProof(publicInputs, proof);
return result === "0x534f5876";
}
}
SDK 與庫:
// 證明系統無關的 SDK
class ProofVerifier {
async verify(verifier: IERC8039, publicInputs: any, proof: any) {
// 可與任何符合 ERC-8039 的驗證器協作
const result = await verifier.verifyProof(
encodePublicInputs(publicInputs),
proof
);
return result === "0x534f5876";
}
}
1 則貼文 - 1 位參與者
[閱讀全文](https://ethereum-magicians.org/t/erc-8039-programming-smart-account-logic-with-zero-knowledge-circuits/27919)