newsence

ERC-8039: Programming Smart Account Logic with Zero-Knowledge Circuits

Ethereum Magicians·28 天前

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)為智能帳戶開啟了兩大超能力

  1. 隱私性:在執行邏輯的同時隱藏敏感資訊(私密輸入、隱藏狀態)。

  2. 簡潔性:透過極小的鏈上證明來驗證複雜的離鏈計算。

目前缺失的環節?一個讓智能帳戶在鏈上驗證 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 電路編寫智能帳戶邏輯需要標準化的驗證方式

智能帳戶透過讓帳戶邏輯可編程化徹底改變了以太坊。但它們面臨兩個根本性的限制:

  1. 隱私性:所有邏輯都是公開的 —— 簽署者、門檻、策略和恢復機制在鏈上清晰可見。

  2. 計算成本:複雜邏輯成本高昂 —— 每一項計算消耗的 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 使智能帳戶能夠:

  1. 為每個使用場景選擇最佳證明系統(隱私 vs 簡潔性)。
  2. 隨證明系統進步而升級(更高性能、更低 Gas)。
  3. 支持混合方案(針對不同操作使用不同證明)。
  4. 保持未來兼容性,對接新興技術。
  5. 在單一應用中結合隱私與簡潔性

解決方案: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
}

為何此模式有效:

  1. 開發者熟悉:智能帳戶開發者已理解 ERC-1271。
  2. 不拋出異常(Non-reverting):返回魔術值而非 revert(Gas 高效、具組合性)。
  3. 證明系統無關:透過適配器支持任何證明格式。
  4. 生態系統兼容:遵循既有的標準慣例。

核心介面

/**

  • @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);
      }

三大關鍵功能

  1. verifyProof() - 核心驗證
    用途: 根據公開輸入驗證 ZK 證明。
    設計原則:
  • 不拋出異常:失敗時返回 0x00000000(類似 ERC-1271)。
  • 通用位元組(Generic bytes):支持任何證明格式。
  • View 函數:無狀態更改,Gas 高效。
  • 魔術值:成功時返回 0x534f5876
  1. 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)。
  • 兼容性檢查。
  1. 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 失敗
}

}

流程:

  1. 用戶在離鏈準備 Safe 交易。
  2. M 個簽署者對交易哈希進行簽名(私密)。
  3. 生成 ZK 證明:「M 個簽名有效」。
  4. Safe 調用 zkSignerProxy.isValidSignature(txHash, zkProof)
  5. 代理透過 ERC-8039 調用 verifier.verifyProof()
  6. 若證明有效 → 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;
}

}

流程:

  1. 用戶在離鏈準備 UserOperation。
  2. M 個簽署者對 userOpHash 進行簽名(私密)。
  3. 生成 ZK 證明:「M 個簽名有效」。
  4. EntryPoint 調用 zkValidatorModule.validateUserOp()
  5. 模組透過 ERC-8039 調用 verifier.verifyProof()
  6. 若證明有效 → EntryPoint 執行 UserOperation。

證明系統無關設計

透過 ERC-8039,我們可以使用適配器模式(adapter pattern)來封裝特定證明系統的驗證器:

範例

  1. 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 - 私密門檻簽名";
    }
}
  1. 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 - 私密門檻簽名";
    }
}
  1. 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)
https://ethereum-magicians.org/t/erc-8039-programming-smart-account-logic-with-zero-knowledge-circuits/27919