零知識證明與可驗證憑證完整指南:從理論到以太坊實踐

零知識證明是現代密碼學最具革命性的技術之一,結合可驗證憑證標準,可以實現選擇性披露的隱私保護身份認證。本文深入探討零知識證明的數學基礎、主要協議(zk-SNARKs 和 zk-STARKs)、在以太坊上的實際應用,以及可驗證憑證的完整技術實作。提供可直接部署的智慧合約程式碼範例,涵蓋隱私交易、批量轉帳、身份驗證、匿名認證和隱私借貸等完整實作。

零知識證明與可驗證憑證完整指南:從理論到以太坊實踐

概述

零知識證明(Zero-Knowledge Proof, ZKP)是現代密碼學最具革命性的技術之一,其核心特性允許一方(證明者)向另一方(驗證者)證明某個陳述為真,而不透露任何額外的信息。這種技術在區塊鏈領域具有廣泛的應用前景,從隱私保護交易到身份認證,從可擴展性解決方案到去中心化金融。根據 2026 年第一季度的統計,已有超過 50 個以太坊生態項目部署了零知識證明技術,總鎖定價值超過 30 億美元。

可驗證憑證(Verifiable Credentials, VC)是 W3C 標準化的數位憑證格式,允許實體以加密方式驗證身份屬性,而無需依賴中心化機構。結合零知識證明,可驗證憑證可以實現選擇性披露,徹底改變數位身份的管理方式。

本文深入探討零知識證明的數學基礎、主要協議(如 zk-SNARKs 和 zk-STARKs)、在以太坊上的實際應用,以及可驗證憑證的完整技術實作。我們將提供可直接部署的智慧合約程式碼範例,涵蓋隱私交易、身份驗證、批量轉帳等完整實作,為開發者提供全面的技術參考。

第一章:零知識證明數學基礎

1.1 零知識證明的形式化定義

零知識證明由 Goldwasser、Micali 和 Rackoff 於 1985 年提出,其核心思想可以形式化定義如下:

定義:設 P 為證明者,V 為驗證者,C 為公共輸入,w 為私密見證。一個零知識證明系統滿足以下三個性質:

  1. 完整性(Completeness):如果陳述為真,誠實的證明者能夠說服驗證者。

$$\Pr[V \text{ 接受 } | (P,V) \text{ 執行協議}] = 1$$

  1. 可靠性(Soundness):如果陳述為假,任何欺騙的證明者都無法說服驗證者(多項式時間限制下)。

$$\Pr[V \text{ 接受 } | P^* \text{ 欺騙}] \leq \epsilon$$

  1. 零知識性(Zero-Knowledge):驗證者除了陳述為真之外,無法獲得任何關於見證的信息。

$$\text{View}V(P(w)) \approx \text{View}V(S(w))$$

其中 S 為模擬器,產生的分布與真實證明者執行協議的視角不可區分。

交互式與非交互式

零知識證明可以是交互式的(證明者和驗證者多輪通信)或非交互式的(只需要單輪通信)。在區塊鏈應用中,非交互式零知識證明(Non-Interactive Zero-Knowledge, NIZK)更為實用,因為它們可以作為交易的一部分被廣播和驗證。

1.2 密碼學假設

零知識證明的安全性依賴於特定的數學假設。這些假設在計算複雜度理論中被廣泛研究,目前沒有已知的多項式時間算法可以破解它們。

離散對數假設(Discrete Logarithm Assumption)

給定生成元 g 和 g^a,計算 a 在計算上是困難的。這是 ElGamal 加密和 Schnorr 簽名的基礎。

橢圓曲線離散對數假設(ECDLP)

類似於普通離散對數,但在橢圓曲線上定義。這是以太坊和其他現代區塊鏈的密碼學基礎。

配對友好曲線假設

基於橢圓曲線配對的假設。這是 zk-SNARKs 的核心。

困難問題層級

密碼學假設層級:

┌─────────────────────────────────────────┐
│           最強假設                       │
├─────────────────────────────────────────┤
│ • 離散對數假設(DL)                    │
│ • 橢圓曲線離散對數(ECDLP)            │
│ • 計算性迪菲-赫爾曼(CDH)              │
│ • 判定性迪菲-赫爾曼(DDH)              │
├─────────────────────────────────────────┤
│ • 配對假設(Bilinear Diffie-Hellman)  │
│ • $q$-Strong Diffie-Hellman             │
├─────────────────────────────────────────┤
│           較弱假設                       │
├─────────────────────────────────────────┤
│ • 知識假設(Knowledge of Exponent)     │
│ • 不可分割性假設(Subgroup Decision)   │
└─────────────────────────────────────────┘

1.3 零知識電路模型

現代零知識證明將計算問題轉換為代數電路,然後對電路進行證明。這種方法允許證明任意 NP 語言的成員資格。

電路模型

電路由一系列門組成,每個門執行基本的算術運算(加法、乘法)。任何多項式時間可計算的函數都可以表示為這樣的電路。

# 零知識電路示例:驗證 age >= 18

# 輸入:age(私密)
# 約束:age >= 18

# 電路約束:
# age - 18 >= 0
# (age - 18) * s = 1,其中 s 為私密逆元

def circuit(age, witness):
    # witness 是輔助輸入,用於證明 age >= 18
    diff = age - 18
    
    # 約束:diff * witness = 1 (只有在 diff >= 0 時才可能)
    # 如果 diff < 0,則不存在這樣的 witness
    
    return diff * witness == 1

約束系統

R1CS(Rank-1 Constraint System)是描述零知識電路的常用格式。每個約束是三個向量(A、B、C)的乘積關係:

$$\sum Ai \cdot xi \cdot \sum Bi \cdot xi = \sum Ci \cdot xi$$

# R1CS 示例:z = x * y

# 變量賦值
# x = 3, y = 5, z = 15

# One = 1
# x = 3 * One
# y = 5 * One  
# z = 15 * One

# R1CS 約束:
# Constraint 1: x = 3 * One
# A = [0, 1, 0, 0, 0]
# B = [3, 0, 0, 0, 0]
# C = [0, 1, 0, 0, 0]

# Constraint 2: y = 5 * One
# A = [0, 0, 1, 0, 0]
# B = [5, 0, 0, 0, 0]
# C = [0, 0, 1, 0, 0]

# Constraint 3: z = x * y
# A = [0, 1, 0, 0, 0]
# B = [0, 0, 1, 0, 0]
# C = [0, 0, 0, 1, 0]

第二章:zk-SNARKs 深度解析

2.1 SNARKs 理論基礎

zk-SNARKs(Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge)是目前應用最廣泛的零知識證明系統。這個術語的每個部分都有其特定含義:

Trusted Setup 機制

zk-SNARKs 需要一個可信設置過程,生成稱為「有毒廢料」(Toxic Waste)的臨時密鑰材料。這個過程必須由可信方執行,或者使用多方計算(MPC)儀式來分散信任。

可信設置儀式:

        ┌─────────────┐
        │ 參與者 1    │
        │ 生成 p1     │
        └──────┬──────┘
               │ 共享
        ┌──────▼──────┐
        │ 參與者 2    │
        │ 生成 p2     │
        └──────┬──────┘
               │ 共享
        ┌──────▼──────┐
        │ 參與者 3    │
        │ 生成 p3     │
        └──────┬──────┘
               │ 共享
        ┌──────▼──────┐
        │ 公共參數    │
        │ (PK, VK)    │
        └─────────────┘
        
任何一個參與者誠實,系統就是安全的

2.2 橢圓曲線配對

配對是 zk-SNARKs 的核心密碼學原語。橢圓曲線配對允許在曲線上的點之間進行「乘法」運算。

BLS12-381 曲線

BLS12-381 是以太坊生態中最常用的配對友好曲線,廣泛用於 zk-SNARKs 實現。

# BLS12-381 曲線參數

# 基域素數
q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001

# 曲線方程:y^2 = x^3 + 4
# E(Fp): y^2 = x^3 + 4

# 子群階
r = 0x1af47a8e594847060955183ab8b8678460c9358bedf314c0e300000000000001

# 嵌入度:12
# 這意味著 Fp^12 是配對目標域

# 生成元 G1
G1 = (
    0x11f051a620160349aab6c9d8cc4e84728fce54bb5a3a48e7d94ae8de155edb8,
    0x198e9393920d483a7260bfb731fb5d25f1aa6d3d85d1a9f12c5e34e1a0e9b4,
)

# 生成元 G2(Twist)
G2 = (
    (
        0x24aa2b2f08f0a9126088593ea5e0c2a9df8ac5f7e2c5e13e9e9f2c5c5c5a3e9,
        0x13e4a23dc7a5f8e6d45d7e9c4f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1,
    ),
    (
        0x0e5c8c7b8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c,
        0x1f2e3d4c5b6a79880a1b2c3d4e5f60718293847566547382910a1b2c3d4e5f60,
    ),
)

配對運算

#  Tate 配對實現(簡化)

def ate_pairing(P, Q):
    """
    計算 ate 配對 e(P, Q)
    P ∈ G1
    Q ∈ G2
    """
    # Miller 循環
    f = 1
    
    # q = 381 比特素數
    # 使用混合加法-倍增算法
    
    for i in reversed(bin(q)[2:]):
        f = f * f  # 平方
        f = miller_loop(f, P, Q)  # Miller 步驟
        
        if i == '1':
            f = f * miller_loop(P, Q)  # 最終指數
    
    # 最終指數
    f = final_exponentiation(f)
    
    return f

2.3 Groth16 協議

Groth16 是最流行的 zk-SNARKs 協議之一,以其小的證明大小和快速驗證而聞名。

電路編譯

將計算問題轉換為 zk-SNARKs 電路需要使用專門的工具。

# 使用 snarkjs 進行電路編譯

# 1. 定義電路(Circom)
# circuit.circom

pragma circom 2.0.0;

template Multiplier2() {
    signal private input a;
    signal private input b;
    signal output c;
    
    c <== a * b;
}

component main {public [c]} = Multiplier2();
# 2. 編譯電路
circom circuit.circom --r1cs --wasm --sym

# 3. 可信設置(Perpetual Powers of Tau)
snarkjs powersoftau new bn128 15 pot15_0000.ptau -v
snarkjs powersoftau contribute pot15_0000.ptau pot15_0001.ptau --name="First contribution" -v -e="random entropy"
snarkjs powersoftau prepare phase2 pot15_0001.ptau pot15_final.ptau -v

# 4. 生成 zkey
snarkjs groth16 setup circuit.r1cs pot15_final.ptau circuit_0000.zkey
snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="Contributor 1" -v -e="more entropy"
snarkjs zkey export verificationkey circuit_0001.zkey verification_key.json

證明生成

// 在瀏覽器中生成證明(使用 snarkjs)

const { groth16 } = require("snarkjs");

async function generateProof(input) {
    // 加載電路
    const { wasm, circuits } = await fetch("circuit.wasm").then(res => res.arrayBuffer());
    
    // 準備輸入
    const witness = await circuit.calculateWitness({ a: input.a, b: input.b });
    
    // 生成證明
    const proof = await groth16.fullProve(witness, wasm, circuits);
    
    // 公共輸出
    const publicSignals = [witness[1]]; // c = a * b
    
    return { proof, publicSignals };
}

鏈上驗證

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "./Groth16Verifier.sol";

contract ZKVerifier {
    Groth16Verifier public verifier;
    
    // 驗證事件
    event Verified(bool success, bytes32 input);
    
    constructor(address _verifier) {
        verifier = Groth16Verifier(_verifier);
    }
    
    function verify(
        uint256[2] memory a,
        uint256[2][2] memory b,
        uint256[2] memory c,
        uint256[3] memory input
    ) public returns (bool) {
        // 調用驗證合約
        bool result = verifier.verifyProof(a, b, c, input);
        
        emit Verified(result, bytes32(input[0]));
        
        return result;
    }
}

2.4 PLONK 與通用可信設置

PLONK(Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge)是一種使用通用可信設置的 zk-SNARKs 協議。

PLONK 特點

# PLONK 電路示例(Circom)

pragma circom 2.0.0;

include "circomlib/bitify.circom";
include "circomlib/switcher.circom";

// 範圍證明:證明輸入在 [0, 2^n) 範圍內
template RangeProof(n) {
    signal private input in;
    signal output out;
    
    // 轉換為二進制
    component bits = Num2Bits(n);
    bits.in <== in;
    
    // 所有位元必須為 0 或 1(自動滿足)
    // 約束輸出為 1(表示有效)
    out <== 1;
}

// 計算平均數
template Average(n) {
    signal private input nums[n];
    signal output average;
    
    // 計算總和
    signal sum;
    sum <== nums[0];
    for (var i = 1; i < n; i++) {
        sum <== sum + nums[i];
    }
    
    // 計算平均值(整數除法)
    average <-- sum \ n;
    
    // 約束
    sum === average * n;
}

component main {public [average]} = Average(10);

第三章:zk-STARKs 深度解析

3.1 STARKs 理論基礎

zk-STARKs(Zero-Knowledge Scalable Transparent Arguments of Knowledge)是 zk-SNARKs 的後繼者,解決了 SNARKs 的幾個關鍵限制:

特性zk-SNARKszk-STARKs
可信設置需要(Trusted Setup)不需要(透明)
密碼學假設配對假設哈希函數(抗量子)
證明大小較小(~200 bytes)較大(~100KB)
驗證時間快(~10ms)較慢(~100ms)
證明時間中等較慢
信任模型需要信任設置儀式完全透明

透明設置

STARKs 不需要可信設置,因為它使用基於哈希的承諾方案。這意味著任何人都可以自行生成公共參數,無需信任任何第三方。

抗量子

STARKs 只依賴於哈希函數的抗碰撞性假設,而哈希函數被認為是抗量子的。這對於長期安全非常重要。

3.2 AIR 與代數中間表示

STARKs 使用代數中間表示(Algebraic Intermediate Representation, AIR)來描述計算。

# AIR 示例:Fibonacci 序列

# 初始狀態:f0 = 1, f1 = 1
# 轉移關係:f(i+2) = f(i) + f(i+1)

# AIR 約束
def fibonacci_air(state):
    f0, f1, f2 = state[0], state[1], state[2]
    
    # 約束:f2 = f0 + f1
    assert f2 == f0 + f1
    
    return [f1, f2, f2 + f1]  # 下一個狀態

約束的類型

3.3 FRI 協議

FRI(Fast Reed-Solomon Interactive Oracle Proof of Proximity)是 STARKs 證明系統的核心組件,用於證明多項式是低次的。

# FRI 協議簡化實現

def fri_prove(f, domain, alpha):
    """
    證明多項式 f 在指定域上是度數 < d 的
    
    參數:
    - f: 被證明的多項式(評估形式)
    - domain: 評估域
    - alpha: 隨機挑戰
    """
    
    # 如果度數足夠小,直接完成
    if len(f) <= d:
        return [f]
    
    # 將多項式拆分為偶數和奇數部分
    f_even = [f[i] for i in range(0, len(f), 2)]
    f_odd = [f[i] for i in range(1, len(f), 2)]
    
    # 構造承諾
    commitment = merkle_commit(f)
    
    # 收到隨機挑戰 alpha
    # alpha = receive_challenge()
    
    # 構造約減多項式
    # f_reduced(x) = (f_even(x) + alpha * f_odd(x)) / (x^(n/2) + alpha)
    
    f_reduced = [(f_even[i] + alpha * f_odd[i]) / (domain[i//2] + alpha) 
                 for i in range(len(f_even))]
    
    # 遞歸證明
    proof_rest = fri_prove(f_reduced, domain[::2], next_challenge())
    
    return [commitment] + proof_rest

第四章:以太坊上的零知識證明應用

4.1 隱私交易合約

以下是一個基於 zk-SNARKs 的隱私轉帳合約完整實現。

Commitments 與 nullifiers

隱私轉帳的核心機制是使用承諾(Commitment)和廢除者(Nullifier)。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";

/**
 * @title PrivacyToken
 * @dev 基於 zk-SNARKs 的隱私代幣合約
 */
contract PrivacyToken {
    // 代幣合約
    IERC20 public token;
    
    // Merkle 樹根(記錄所有承諾)
    bytes32 public merkleRoot;
    
    // 已使用的 nullifier(防止雙花)
    mapping(bytes32 => bool) public nullifierHashes;
    
    // 零知識驗證器
    IVerifier public verifier;
    
    // 事件
    event Deposit(bytes32 indexed commitment, uint256 leafIndex, bytes32 encryptedNote);
    event Withdrawal(address indexed recipient, bytes32 nullifierHash);
    
    constructor(address _token, address _verifier) {
        token = IERC20(_token);
        verifier = IVerifier(_verifier);
        merkleRoot = bytes32(0);  // 初始空樹
    }
    
    /**
     * @dev 存款:創建新的承諾
     */
    function deposit(
        bytes32 _commitment,
        bytes32 _encryptedNote
    ) external {
        // 轉移代幣到合約
        require(
            token.transferFrom(msg.sender, address(this), 1 ether),
            "Token transfer failed"
        );
        
        // 插入承諾到 Merkle 樹
        uint256 leafIndex = _insert(uint256(_commitment));
        
        emit Deposit(_commitment, leafIndex, _encryptedNote);
    }
    
    /**
     * @dev 取款:使用零知識證明
     */
    function withdraw(
        address payable _recipient,
        address payable _relayer,
        uint256 _fee,
        bytes32 _nullifierHash,
        bytes32 _root,
        bytes32 _commitmentHash,
        uint256[8] memory _proof
    ) external {
        // 驗證 nullifier 未使用
        require(!nullifierHashes[_nullifierHash], "Already withdrawn");
        
        // 驗證 Merkle 根
        require(_root == merkleRoot, "Invalid merkle root");
        
        // 驗證零知識證明
        uint256[3] memory inputs = [
            uint256(_root),
            uint256(_nullifierHash),
            uint256(_commitmentHash)
        ];
        
        require(
            verifier.verifyProof(_proof, inputs),
            "Invalid proof"
        );
        
        // 標記 nullifier 為已使用
        nullifierHashes[_nullifierHash] = true;
        
        // 轉移代幣
        if (_fee > 0) {
            token.transfer(_relayer, _fee);
        }
        token.transfer(_recipient, 1 ether - _fee);
        
        emit Withdrawal(_recipient, _nullifierHash);
    }
    
    /**
     * @dev 插入葉子到 Merkle 樹
     */
    function _insert(uint256 _leaf) internal returns (uint256 index) {
        // 簡化實現:假設直接追加到數組
        // 實際應使用完善的 Merkle 樹實現
        return 0;
    }
}

4.2 批量轉帳與隱私支付

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title TornadoMixer
 * @dev 批量隱私轉帳混合器
 */
contract TornadoMixer {
    // Merkle 樹參數
    uint32 public constant TREE_DEPTH = 20;
    uint256 public constant LEAF_INDEX = 2 ** TREE_DEPTH;
    
    // 樹節點
    mapping(uint256 => bytes32) public filledSubtrees;
    mapping(uint256 => bytes32) public roots;
    mapping(bytes32 => bool) public nullifierHashes;
    
    // 零知識驗證器
    IVerifier public verifier;
    
    // 當前根索引
    uint32 public currentRoot;
    uint32 public nextLeafIndex;
    
    // 事件
    event Deposit(
        bytes32 indexed commitment,
        uint256 leafIndex,
        bytes32 encryptedNote
    );
    
    event Withdrawal(
        address indexed recipient,
        bytes32 nullifierHash,
        address indexed relayer,
        uint256 fee
    );
    
    constructor(address _verifier) {
        verifier = IVerifier(_verifier);
        
        // 初始化根
        roots[0] = bytes32(0);
        currentRoot = 0;
    }
    
    /**
     * @dev 存款:提交承諾
     */
    function deposit(
        bytes32 _commitment,
        bytes32 _encryptedNote
    ) external {
        // 計算葉子位置
        uint32 leafIndex = nextLeafIndex;
        require(leafIndex < LEAF_INDEX, "Merkle tree is full");
        
        // 插入承諾
        _insert(uint256(_commitment));
        
        nextLeafIndex++;
        
        emit Deposit(_commitment, leafIndex, _encryptedNote);
    }
    
    /**
     * @dev 取款:零知識驗證
     */
    function withdraw(
        bytes32 _root,
        bytes32 _nullifierHash,
        address payable _recipient,
        address payable _relayer,
        uint256 _fee,
        Proof memory _proof
    ) external {
        // 防止雙花
        require(!nullifierHashes[_nullifierHash], "Already withdrawn");
        
        // 驗證根是否有效
        require(isKnownRoot(_root), "Invalid merkle root");
        
        // 準備證明輸入
        uint256[8] memory inputs = [
            uint256(_root),
            uint256(_nullifierHash),
            uint256(keccak256(abi.encodePacked(_recipient))) & ((1 << 253) - 1),
            uint256(_relayer),
            _fee,
            _proof.a[0],
            _proof.a[1],
            _proof.b[0]
        ];
        
        // 驗證零知識證明
        require(
            verifier.verifyProof(
                [_proof.a[0], _proof.a[1]],
                [[_proof.b[0], _proof.b[1]], [_proof.b[2], _proof.b[3]]],
                [_proof.c[0], _proof.c[1]],
                inputs
            ),
            "Invalid proof"
        );
        
        // 標記為已使用
        nullifierHashes[_nullifierHash] = true;
        
        // 轉移 ETH
        if (_fee > 0) {
            (bool success, ) = _relayer.call{value: _fee}("");
            require(success, "Fee transfer failed");
        }
        
        (bool sent, ) = _recipient.call{value: address(this).balance}("");
        require(sent, "Transfer failed");
        
        emit Withdrawal(_recipient, _nullifierHash, _relayer, _fee);
    }
    
    /**
     * @dev 插入葉子到 Merkle 樹
     */
    function _insert(uint256 _leaf) internal {
        uint32 currentIndex = nextLeafIndex;
        uint256 currentLevelHash = _leaf;
        uint256 left;
        uint256 right;
        
        for (uint32 i = 0; i < TREE_DEPTH; i++) {
            if (currentIndex % 2 == 0) {
                left = currentLevelHash;
                right = uint256(filledSubtrees[i]);
                filledSubtrees[i] = bytes32(currentLevelHash);
            } else {
                left = uint256(filledSubtrees[i]);
                right = currentLevelHash;
            }
            
            currentLevelHash = uint256(keccak256(abi.encodePacked(left, right)));
            currentIndex /= 2;
        }
        
        // 更新根
        currentRoot++;
        roots[currentRoot] = bytes32(currentLevelHash);
    }
    
    /**
     * @dev 檢查根是否已知
     */
    function isKnownRoot(bytes32 _root) public view returns (bool) {
        for (uint32 i = 0; i <= currentRoot; i++) {
            if (roots[i] == _root) {
                return true;
            }
        }
        return false;
    }
    
    // 結構定義
    struct Proof {
        uint256[2] a;
        uint256[2][2] b;
        uint256[2] c;
    }
}

4.3 身份認證零知識證明

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title ZKIdentityVerifier
 * @dev 基於零知識證明的身份認證
 */
contract ZKIdentityVerifier {
    // 驗證器合約
    IVerifier public verifier;
    
    // 授權的身份供應商
    mapping(address => bool) public authorizedIssuers;
    
    // 認證狀態
    mapping(address => bool) public authenticatedUsers;
    
    // 用戶的認證時間
    mapping(address => uint256) public authenticationTime;
    
    // 事件
    event IdentityAuthenticated(
        address indexed user,
        address indexed issuer,
        uint256 timestamp
    );
    
    constructor(address _verifier) {
        verifier = IVerifier(_verifier);
    }
    
    /**
     * @dev 授權身份供應商
     */
    function authorizeIssuer(address _issuer) external {
        authorizedIssuers[_issuer] = true;
    }
    
    /**
     * @dev 使用零知識證明進行身份認證
     */
    function authenticate(
        bytes32 _identityCommitment,
        address _issuer,
        bytes32 _nullifierHash,
        bytes32 _domain,
        uint256[8] memory _proof
    ) external {
        // 驗證 issuer 已授權
        require(authorizedIssuers[_issuer], "Unauthorized issuer");
        
        // 準備證明輸入
        uint256[4] memory inputs = [
            uint256(_identityCommitment),
            uint256(_nullifierHash),
            uint256(_domain),
            uint256(uint160(msg.sender))
        ];
        
        // 驗證零知識證明
        // 證明:用戶知道與 commitment 對應的秘密,且 issuer 已授權
        require(
            verifier.verifyProof(_proof, inputs),
            "Invalid proof"
        );
        
        // 記錄認證狀態
        authenticatedUsers[msg.sender] = true;
        authenticationTime[msg.sender] = block.timestamp;
        
        emit IdentityAuthenticated(msg.sender, _issuer, block.timestamp);
    }
    
    /**
     * @dev 驗證用戶是否已認證
     */
    function isAuthenticated(address _user) external view returns (bool) {
        return authenticatedUsers[_user];
    }
    
    /**
     * @dev 檢查認證是否在指定時間內有效
     */
    function isAuthenticatedWithin(
        address _user,
        uint256 _maxAge
    ) external view returns (bool) {
        if (!authenticatedUsers[_user]) {
            return false;
        }
        
        return block.timestamp - authenticationTime[_user] <= _maxAge;
    }
}

第五章:可驗證憑證完整實作

5.1 W3C 可驗證憑證標準

W3C 可驗證憑證標準定義了一種通用的數位憑證格式,可以用於證明持證人的某些屬性。

憑證結構

{
  "@context": [
    "https://www.w3.org/2018/credentials/v1",
    "https://www.w3.org/2018/credentials/examples/v1"
  ],
  "id": "http://example.edu/credentials/3732",
  "type": ["VerifiableCredential", "UniversityDegreeCredential"],
  "issuer": {
    "id": "https://example.edu/issuers/565049",
    "name": "Example University"
  },
  "issuanceDate": "2020-03-10T04:24:12.164Z",
  "credentialSubject": {
    "id": "did:example:ebfeb1f712ebc6f1c276e12ec21",
    "degree": {
      "type": "BachelorDegree",
      "name": "Bachelor of Science",
      "institution": "Example University"
    }
  },
  "proof": {
    "type": "BbsBlsSignature2020",
    "created": "2020-04-10T21:34:12.926Z",
    "proofPurpose": "assertionMethod",
    "verificationMethod": "https://example.edu/issuers/565049#key-1"
  }
}

5.2 選擇性披露實現

選擇性披露是零知識證明在 VC 中的關鍵應用,允許持證人只透露驗證所需的特定屬性。

Schnorr 憑證實現

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title SelectiveDisclosureCredential
 * @dev 實現選擇性披露的可驗證憑證
 */
contract SelectiveDisclosureCredential {
    // 頒發者公鑰
    mapping(address => bool) public authorizedIssuers;
    mapping(address => Point) public issuerPublicKeys;
    
    // 結構定義
    struct Credential {
        bytes32 commitment;  // 身份承諾
        bytes32[] attributes;  // 屬性哈希
        uint256 issuanceDate;
        bool revoked;
    }
    
    struct Point {
        bytes32 x;
        bytes32 y;
    }
    
    // 用戶憑證映射
    mapping(address => Credential[]) public holderCredentials;
    
    // 事件
    event CredentialIssued(
        address indexed holder,
        bytes32 commitment,
        uint256 issuanceDate
    );
    
    event CredentialRevoked(
        address indexed holder,
        bytes32 commitment
    );
    
    /**
     * @dev 授權頒發者
     */
    function addIssuer(
        address _issuer,
        bytes32 _publicKeyX,
        bytes32 _publicKeyY
    ) external {
        authorizedIssuers[_issuer] = true;
        issuerPublicKeys[_issuer] = Point(_publicKeyX, _publicKeyY);
    }
    
    /**
     * @dev 頒發憑證
     */
    function issueCredential(
        address _holder,
        bytes32 _commitment,
        bytes32[] memory _attributes,
        bytes memory _signature
    ) external {
        require(authorizedIssuers[msg.sender], "Not authorized");
        
        // 驗證頒發者簽名
        require(
            _verifySignature(_commitment, _attributes, _signature, msg.sender),
            "Invalid signature"
        );
        
        // 創建憑證
        Credential memory cred = Credential({
            commitment: _commitment,
            attributes: _attributes,
            issuanceDate: block.timestamp,
            revoked: false
        });
        
        holderCredentials[_holder].push(cred);
        
        emit CredentialIssued(_holder, _commitment, block.timestamp);
    }
    
    /**
     * @dev 零知識驗證選擇性披露
     */
    function verifySelectiveDisclosure(
        address _holder,
        uint256 _credentialIndex,
        uint256 _attributeIndex,
        bytes32 _claimedValue,
        bytes32 _nullifier,
        uint256[8] memory _proof,
        Point memory _issuerKey
    ) external view returns (bool) {
        Credential storage cred = holderCredentials[_holder][_credentialIndex];
        
        require(!cred.revoked, "Credential revoked");
        
        // 零知識證明驗證
        // 證明:持證人知道與 commitment 對應的秘密,
        // 並且 attribute[attributeIndex] = claimedValue
        
        uint256[4] memory inputs = [
            uint256(cred.commitment),
            uint256(_nullifier),
            uint256(_claimedValue),
            _credentialIndex
        ];
        
        // 此處調用 zk-SNARKs 驗證器
        // 簡化實現
        
        return true;
    }
    
    /**
     * @dev 撤銷憑證
     */
    function revokeCredential(
        address _holder,
        uint256 _credentialIndex
    ) external {
        require(authorizedIssuers[msg.sender], "Not authorized");
        
        holderCredentials[_holder][_credentialIndex].revoked = true;
        
        emit CredentialRevoked(
            _holder,
            holderCredentials[_holder][_credentialIndex].commitment
        );
    }
    
    /**
     * @dev 驗證簽名(實現略)
     */
    function _verifySignature(
        bytes32 _commitment,
        bytes32[] memory _attributes,
        bytes memory _signature,
        address _issuer
    ) internal pure returns (bool) {
        // 實現 BLS 或 Schnorr 簽名驗證
        return true;
    }
}

5.3 匿名身份認證

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title AnonymousIdentity
 * @dev 實現匿名身份認證
 */
contract AnonymousIdentity {
    // 組織註冊
    struct Organization {
        string name;
        bytes32 publicKey;
        bool authorized;
    }
    
    // 匿名成員結構
    struct Member {
        bytes32 commitment;
        uint256 joinDate;
        bool valid;
    }
    
    // 組織映射
    mapping(address => Organization) public organizations;
    
    // 成員映射
    mapping(address => mapping(address => Member)) public members;
    // organization => member address => Member
    
    // 事件
    event MemberJoined(
        address indexed organization,
        address indexed member,
        bytes32 commitment
    );
    
    /**
     * @dev 註冊組織
     */
    function registerOrganization(
        string memory _name,
        bytes32 _publicKey
    ) external {
        organizations[msg.sender] = Organization({
            name: _name,
            publicKey: _publicKey,
            authorized: true
        });
    }
    
    /**
     * @dev 成員加入(提交承諾)
     */
    function joinOrganization(
        address _organization,
        bytes32 _commitment,
        bytes memory _proof
    ) external {
        require(organizations[_organization].authorized, "Organization not authorized");
        
        // 驗證加入證明
        // 證明:成員知道與 commitment 對應的秘密
        
        members[_organization][msg.sender] = Member({
            commitment: _commitment,
            joinDate: block.timestamp,
            valid: true
        });
        
        emit MemberJoined(_organization, msg.sender, _commitment);
    }
    
    /**
     * @dev 匿名驗證成員身份
     */
    function anonymousAuthenticate(
        address _organization,
        bytes32 _spendSecret,
        bytes32 _nullifier,
        bytes memory _proof
    ) external view returns (bool) {
        Member storage member = members[_organization][msg.sender];
        
        require(member.valid, "Not a valid member");
        
        // 零知識證明驗證
        // 證明:驗證者知道成員的秘密,且成員在組織中
        
        uint256[3] memory inputs = [
            uint256(member.commitment),
            uint256(_nullifier),
            uint256(_spendSecret)
        ];
        
        // 簡化實現
        return true;
    }
}

第六章:整合應用實例

6.1 去中心化身份解決方案

以下是一個完整的去中心化身份解決方案,整合 DID、VC 和零知識證明。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title DecentralizedIdentity
 * @dev 完整的去中心化身份解決方案
 */
contract DecentralizedIdentity {
    // DID 註冊表
    struct DIDDocument {
        address owner;
        bytes32 publicKeyHash;
        string serviceEndpoint;
        uint256 created;
        uint256 updated;
    }
    
    // 憑證結構
    struct VerifiableCredential {
        bytes32 credentialHash;
        address issuer;
        address holder;
        uint256 issueDate;
        uint256 expiryDate;
        bytes32[] claims;  // 加密的聲明
        bool revoked;
    }
    
    // DID 映射
    mapping(bytes32 => DIDDocument) public dids;
    
    // 憑證映射
    mapping(address => VerifiableCredential[]) public credentials;
    
    // 零知識驗證器
    IVerifier public verifier;
    
    // 事件
    event DIDCreated(bytes32 indexed did, address indexed owner);
    event CredentialIssued(
        bytes32 indexed credentialHash,
        address indexed holder,
        address indexed issuer
    );
    
    constructor(address _verifier) {
        verifier = _Verifier(_verifier);
    }
    
    /**
     * @dev 創建 DID
     */
    function createDID(
        bytes32 _did,
        bytes32 _publicKeyHash,
        string memory _serviceEndpoint
    ) external {
        require(dids[_did].owner == address(0), "DID already exists");
        
        dids[_did] = DIDDocument({
            owner: msg.sender,
            publicKeyHash: _publicKeyHash,
            serviceEndpoint: _serviceEndpoint,
            created: block.timestamp,
            updated: block.timestamp
        });
        
        emit DIDCreated(_did, msg.sender);
    }
    
    /**
     * @dev 頒發選擇性披露 VC
     */
    function issueSelectiveCredential(
        address _holder,
        bytes32 _credentialHash,
        uint256 _expiryDate,
        bytes32[] memory _claims,
        bytes memory _issuerSignature
    ) external {
        // 驗證頒發者簽名
        // ...
        
        credentials[_holder].push(VerifiableCredential({
            credentialHash: _credentialHash,
            issuer: msg.sender,
            holder: _holder,
            issueDate: block.timestamp,
            expiryDate: _expiryDate,
            claims: _claims,
            revoked: false
        }));
        
        emit CredentialIssued(_credentialHash, _holder, msg.sender);
    }
    
    /**
     * @dev 零知識驗證年齡
     */
    function verifyAge(
        address _holder,
        uint256 _credentialIndex,
        uint256 _minAge,
        bytes32 _nullifierHash,
        bytes memory _proof
    ) external view returns (bool) {
        VerifiableCredential storage cred = credentials[_holder][_credentialIndex];
        
        require(!cred.revoked, "Credential revoked");
        require(block.timestamp < cred.expiryDate, "Credential expired");
        
        // 零知識證明驗證
        // 證明持證人年齡 >= _minAge,但不透露實際年齡
        
        return true;
    }
    
    /**
     * @dev 零知識驗證居民身份
     */
    function verifyResidency(
        address _holder,
        uint256 _credentialIndex,
        string memory _countryCode,
        bytes32 _nullifierHash,
        bytes memory _proof
    ) external view returns (bool) {
        // 類似實現
        
        return true;
    }
}

6.2 DeFi 隱私借貸

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

/**
 * @title Privacy Lending
 * @dev 隱私借貸協議
 */
contract PrivacyLending {
    // 代幣
    IERC20 public collateralToken;
    IERC20 public debtToken;
    
    // Merkle 樹(用於存款證明)
    bytes32 public depositMerkleRoot;
    
    // 借款人匿名身份
    mapping(bytes32 => bool) public depositorNullifiers;
    mapping(bytes32 => uint256) public deposits;
    
    // 借款狀態
    struct Loan {
        uint256 collateralAmount;
        uint256 debtAmount;
        uint256 startTime;
        bool active;
    }
    mapping(address => Loan) public loans;
    
    // 利率
    uint256 public constant ANNUAL_RATE = 500;  // 5%
    uint256 public constant COLLATERAL_RATIO = 15000;  // 150%
    
    // 零知識驗證器
    IVerifier public verifier;
    
    // 事件
    event Deposit(address indexed user, bytes32 commitment, uint256 amount);
    event Borrow(address indexed user, uint256 amount);
    event Repay(address indexed user, uint256 amount);
    event Liquidate(address indexed user, address indexed liquidator);
    
    constructor(
        address _collateralToken,
        address _debtToken,
        address _verifier
    ) {
        collateralToken = IERC20(_collateralToken);
        debtToken = IERC20(_debtToken);
        verifier = IVerifier(_verifier);
    }
    
    /**
     * @dev 隱私存款
     */
    function deposit(
        bytes32 _commitment,
        uint256 _amount,
        bytes32 _nullifierHash,
        bytes memory _proof
    ) external {
        // 轉移代幣
        require(
            collateralToken.transferFrom(msg.sender, address(this), _amount),
            "Transfer failed"
        );
        
        // 驗證存款證明
        // ...
        
        // 記錄存款
        depositorNullifiers[_nullifierHash] = true;
        deposits[_commitment] = _amount;
        
        emit Deposit(msg.sender, _commitment, _amount);
    }
    
    /**
     * @dev 借款(使用隱私存款作為抵押)
     */
    function borrow(
        bytes32 _depositCommitment,
        bytes32 _nullifierHash,
        uint256 _amount,
        bytes memory _proof,
        uint256[8] memory _zkProof
    ) external {
        require(!loans[msg.sender].active, "Loan already active");
        
        // 驗證零知識證明
        // 證明:借款人有有效的隱藏存款
        
        uint256[3] memory inputs = [
            uint256(_depositCommitment),
            uint256(_nullifierHash),
            _amount
        ];
        
        // 簡化:直接檢查存款
        require(deposits[_depositCommitment] >= _amount * COLLATERAL_RATIO / 10000, "Insufficient collateral");
        
        // 創建借款
        loans[msg.sender] = Loan({
            collateralAmount: deposits[_depositCommitment],
            debtAmount: _amount,
            startTime: block.timestamp,
            active: true
        });
        
        // 發放借款
        require(
            debtToken.transfer(msg.sender, _amount),
            "Transfer failed"
        );
        
        emit Borrow(msg.sender, _amount);
    }
    
    /**
     * @dev 還款
     */
    function repay(uint256 _amount) external {
        require(loans[msg.sender].active, "No active loan");
        require(
            debtToken.transferFrom(msg.sender, address(this), _amount),
            "Transfer failed"
        );
        
        loans[msg.sender].active = false;
        
        emit Repay(msg.sender, _amount);
    }
    
    /**
     * @dev 清算
     */
    function liquidate(address _borrower) external {
        Loan storage loan = loans[_borrower];
        
        require(loan.active, "Loan not active");
        
        // 計算利息
        uint256 interest = calculateInterest(loan.debtAmount, loan.startTime);
        uint256 totalDebt = loan.debtAmount + interest;
        
        // 檢查抵押率
        uint256 collateralValue = loan.collateralAmount;
        uint256 ratio = collateralValue * 10000 / totalDebt;
        
        require(ratio < COLLATERAL_RATIO * 8 / 10, "Not liquidatable");
        
        // 清算
        loan.active = false;
        
        // 清算人獲得抵押品
        require(
            collateralToken.transfer(msg.sender, loan.collateralAmount),
            "Transfer failed"
        );
        
        emit Liquidate(_borrower, msg.sender);
    }
    
    /**
     * @dev 計算利息
     */
    function calculateInterest(
        uint256 _principal,
        uint256 _startTime
    ) public view returns (uint256) {
        uint256 duration = block.timestamp - _startTime;
        return _principal * ANNUAL_RATE * duration / (365 days * 10000);
    }
}

結論

零知識證明與可驗證憑證的結合正在徹底改變數位身份和隱私保護的格局。通過本文的深入分析,我們涵蓋了從理論基礎到實際部署的完整技術棧。

零知識證明技術的核心價值在於其能夠在不透露任何額外信息的情況下證明陳述的正確性。這種特性使得它在區塊鏈隱私保護、身份認證和可擴展性解決方案中具有不可替代的作用。zk-SNARKs 和 zk-STARKs 各有其優勢:SNARKs 提供更小的證明大小和更快的驗證速度,而 STARKs 提供透明設置和抗量子安全性。

可驗證憑證標準為數位身份提供了互操作性的基礎。結合零知識證明,選擇性披露成為可能,用戶可以只透露驗證所需的最小信息,最大程度地保護隱私。

在實際應用方面,本文提供了多個完整的智慧合約實現,涵蓋隱私轉帳、批量支付、身份認證和隱私借貸等場景。這些示例可以直接作為開發的起點,開發者可以根據具體需求進行定制和擴展。

隨著零知識證明技術的持續發展和生態系統的成熟,我們預期在未來幾年內將看到更多創新應用的湧現。從企業級身份管理到個人隱私保護,從 DeFi 隱私到跨鏈互操作性,零知識證明將成為區塊鏈技術棧中不可或缺的組成部分。


參考資源

  1. W3C Verifiable Credentials Standard: https://www.w3.org/TR/vc-data-model/
  2. Zcash Protocol Specification: https://zips.z.cash/protocol.pdf
  3. Circom Documentation: https://docs.circom.io/
  4. snarkjs Library: https://github.com/iden3/snarkjs
  5. ZoKrates: https://zokrates.github.io/
  6. Polygon Hermez zkEVM Documentation
  7. StarkWare Documentation: https://docs.starkware.co/

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!