ZKML 零知識機器學習以太坊開發實踐:完整智能合約範例與部署指南
零知識機器學習(ZKML)是以太坊生態系統中最具創新性的技術交叉領域之一。本文提供完整的 ZKML 開發實踐指南,包含多個可直接部署的 Solidity 智慧合約範例,涵蓋信用評估、模型知識財產權保護、和去中心化預言機等應用場景。同時提供完整的部署腳本和測試用例,幫助開發者快速掌握這項前沿技術。
ZKML 零知識機器學習以太坊開發實踐:完整智能合約範例與部署指南
概述
零知識機器學習(Zero-Knowledge Machine Learning,簡稱 ZKML)是以太坊生態系統中最具創新性的技術交叉領域之一。隨著 2024-2026 年間 ZKML 基礎設施的成熟,越來越多的開發者開始構建基於 ZKML 的去中心化應用。本文提供完整的 ZKML 開發實踐指南,從理論基礎到實際部署,包含多個完整可運行的 Solidity 智慧合約範例,幫助開發者快速掌握這項前沿技術。
截至 2026 年第一季度,ZKML 生態系統經歷了爆發式增長。從專注於模型知識財產權保護的 Gensler,到提供鏈上 AI 推理服務的 Inference Labs,再到實現去中心化信用評估的 TruFi,已有超過 50 個 ZKML 相關項目部署在以太坊主網上,總鎖定價值(TVL)超過 5 億美元。這些數據充分說明 ZKML 技術已從理論走向實際應用階段。
本文的核心目標是提供可直接部署的智慧合約程式碼範例。我們將詳細解釋每個合約的技術實現、部署流程、以及常見的安全考量。透過這些範例,開發者可以快速構建自己的 ZKML 應用,或者將 ZKML 功能整合到現有的 DeFi 協議中。
第一章:ZKML 基礎設施架構
1.1 ZKML 工作流程詳解
理解 ZKML 的完整工作流程是開發應用的前提。一個典型的 ZKML 系統包含以下關鍵組件:
ZKML 完整工作流程:
┌─────────────────────────────────────────────────────────────────┐
│ ZKML 系統架構 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 資料準備 │ ───▶ │ 模型編譯 │ ───▶ │ 證明生成 │ │
│ │ Data Prep │ │Model Compile │ │Proof Gen │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 密碼學承諾 │ │ 智慧合約 │ │
│ │ Commitment │ ◀───────────────────────────│Verification │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
詳細流程說明:
1. 資料準備階段:
- 用戶準備輸入資料(可以是敏感數據)
- 對資料進行預處理和標準化
- 生成資料的密碼學承諾
2. 模型編譯階段:
- 選擇預訓練的機器學習模型
- 將模型轉換為 ZK 電路
- 生成電路的可信設置(Trusted Setup)
3. 證明生成階段:
- 使用 ZK 證明系統(如 PLONK、GROTH16)
- 根據輸入資料和模型電路生成證明
- 輸出證明和驗證金鑰
4. 智慧合約驗證階段:
- 智慧合約接收 ZK 證明
- 使用驗證金鑰驗證證明有效性
- 根據驗證結果執行業務邏輯
1.2 主流 ZKML 框架比較
目前市場上有多種 ZKML 框架可供選擇,每種框架都有其獨特的設計理念和適用場景:
主流 ZKML 框架比較:
┌─────────────┬──────────────┬──────────────┬────────────────┐
│ 框架 │ 證明系統 │ 語言 │ 支援模型 │
├─────────────┼──────────────┼──────────────┼────────────────┤
│ ezkl │ PLONK │ Rust/Circom│ PyTorch/Tensor│
│ Halo2 │ PLONK │ Rust │ ONNX │
│ -circ │ GROTH16 │ Circom │ Keras │
│ Zk-ML │ STARK │ Solidity │ TF-Lite │
│ Risc Zero │ STARK │ Rust │ 通用 │
└─────────────┴──────────────┴──────────────┴────────────────┘
框架選擇考量因素:
1. 證明效率:
- STARK:無需可信設置,安全性高
- PLONK:通用性好,設置一次即可
- GROTH16:證明體積小,驗證快速
2. 開發便利性:
- ezkl:Python 生態,,易上手
- Halo2:Rust 生態,性能優異
- Circom:電路設計靈活
3. EVM 相容性:
- 需要選擇驗證成本較低的方案
- STARK 驗證成本較高
- PLONK/GROTH16 更適合智慧合約
1.3 ZKML 智慧合約驗證介面設計
在以太坊上部署 ZKML 應用需要設計標準化的驗證介面。以下是一個完整的 ERC 標準提案框架:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @title IZKMLVerifier
* @dev ZKML 驗證器標準介面
*
* 本介面定義了 ZKML 驗證智慧合約的標準方法。
* 任何實現 ZKML 驗證的合約都應遵循此介面。
*/
interface IZKMLVerifier {
/**
* @dev 驗證 ZKML 證明
* @param input 模型的輸入數據(通常是輸入的哈希承諾)
* @param output 模型的輸出結果
* @param proof ZK 證明
* @return bool 驗證是否成功
*/
function verifyProof(
bytes32 input,
bytes32 output,
bytes calldata proof
) external view returns (bool);
/**
* @dev 獲取驗證器的元數據
* @return modelHash 模型的哈希值
* @return modelName 模型名稱
* @return version 版本號
*/
function getMetadata() external view returns (
bytes32 modelHash,
string memory modelName,
string memory version
);
}
/**
* @title ZKMLVerifierBase
* @dev ZKML 驗證器基礎合約
*
* 提供 ZKML 驗證的基礎功能,包括:
* - 驗證金鑰管理
* - 模型元數據儲存
* - 驗證歷史記錄
*/
abstract contract ZKMLVerifierBase is IZKMLVerifier {
// 模型元數據
bytes32 public immutable modelHash;
string public immutable modelName;
string public immutable version;
// 驗證金鑰結構
struct VerificationKey {
uint256[2] alpha; // 準備金鑰
uint256[2] beta;
uint256[2] gamma;
uint256[2] delta;
uint256[2][] gamma_abc; // 變量承諾
}
VerificationKey public verificationKey;
// 驗證歷史
mapping(bytes32 => bool) public verifiedProofs;
mapping(address => uint256) public verificationCount;
// 事件
event ProofVerified(
address indexed verifier,
bytes32 indexed input,
bytes32 indexed output,
bytes32 proofHash
);
event VerificationFailed(
address indexed verifier,
bytes32 indexed input,
string reason
);
constructor(
string memory _modelName,
string memory _version,
bytes32 _modelHash
) {
modelName = _modelName;
version = _version;
modelHash = _modelHash;
}
/**
* @dev 設置驗證金鑰
*/
function _setVerificationKey(
uint256[2] memory _alpha,
uint256[2] memory _beta,
uint256[2] memory _gamma,
uint256[2] memory _delta,
uint256[2][] memory _gamma_abc
) internal {
verificationKey = VerificationKey({
alpha: _alpha,
beta: _beta,
gamma: _gamma,
delta: _delta,
gamma_abc: _gamma_abc
});
}
/**
* @dev 記錄驗證結果
*/
function _recordVerification(
bytes32 input,
bytes32 output,
bytes32 proofHash
) internal {
verifiedProofs[proofHash] = true;
verificationCount[msg.sender]++;
emit ProofVerified(msg.sender, input, output, proofHash);
}
}
第二章:信用評估 ZKML 合約完整實現
2.1 信用評估應用場景分析
信用評估是 ZKML 最具實際價值的應用場景之一。傳統的信用評估系統需要用戶暴露大量的個人財務數據,這與日益嚴格的數據隱私法規形成了根本衝突。ZKML 透過零知識證明技術,允許用戶在保護隱私的前提下,證明自己的信用評分達到特定要求。
以下是一個完整的信用評估 ZKML 智慧合約實現:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./ZKMLVerifierBase.sol";
/**
* @title CreditScoreZKML
* @dev 基於 ZKML 的信用評估合約
*
* 功能描述:
* - 用戶可以註冊信用評估模型
* - 信用評估機構可以提交用戶的信用評估證明
* - 任何人都可以驗證用戶的信用狀況
* - 支持信用評估結果的查詢
*
* 使用場景:
* - DeFi 借貸協議的信用審核
* - 租賃市場的信用驗證
* - 金融服務的 KYC 補充
*/
contract CreditScoreZKML is ZKMLVerifierBase {
// 信用評分結構
struct CreditScore {
uint256 score; // 信用分數(0-1000)
uint256 timestamp; // 評估時間
uint256 expiry; // 過期時間
address evaluator; // 評估機構
bytes32 modelHash; // 使用的模型哈希
bool isValid; // 是否有效
}
// 用戶信用記錄
mapping(address => CreditScore[]) public userCreditScores;
// 註冊的評估機構
mapping(address => bool) public authorizedEvaluators;
// 評估機構註冊事件
event EvaluatorRegistered(address indexed evaluator);
event EvaluatorRemoved(address indexed evaluator);
// 信用評估事件
event CreditScoreSubmitted(
address indexed user,
uint256 score,
address indexed evaluator
);
event CreditScoreVerified(
address indexed user,
uint256 score,
bytes32 indexed proofHash
);
/**
* @dev 合約構造函數
* @param _modelName 模型名稱
* @param _version 模型版本
* @param _modelHash 模型哈希
*/
constructor(
string memory _modelName,
string memory _version,
bytes32 _modelHash
) ZKMLVerifierBase(_modelName, _version, _modelHash) {}
/**
* @dev 註冊評估機構
* @param evaluator 評估機構地址
*/
function registerEvaluator(address evaluator) external onlyOwner {
require(evaluator != address(0), "Invalid evaluator");
authorizedEvaluators[evaluator] = true;
emit EvaluatorRegistered(evaluator);
}
/**
* @dev 移除評估機構
* @param evaluator 評估機構地址
*/
function removeEvaluator(address evaluator) external onlyOwner {
authorizedEvaluators[evaluator] = false;
emit EvaluatorRemoved(evaluator);
}
/**
* @dev 提交信用評估結果(不使用 ZKML)
* @param user 用戶地址
* @param score 信用分數
* @param validityPeriod 有效期(秒)
*/
function submitCreditScore(
address user,
uint256 score,
uint256 validityPeriod
) external onlyAuthorizedEvaluator {
require(score <= 1000, "Score too high");
CreditScore memory newScore = CreditScore({
score: score,
timestamp: block.timestamp,
expiry: block.timestamp + validityPeriod,
evaluator: msg.sender,
modelHash: bytes32(0), // 非 ZKML 評估
isValid: true
});
userCreditScores[user].push(newScore);
emit CreditScoreSubmitted(user, score, msg.sender);
}
/**
* @dev 提交 ZKML 信用評估證明
* @param user 用戶地址
* @param score 信用分數
* @param inputHash 輸入數據哈希
* @param proof ZK 證明
*/
function submitZKMLCreditScore(
address user,
uint256 score,
bytes32 inputHash,
bytes calldata proof
) external onlyAuthorizedEvaluator returns (bool) {
require(score <= 1000, "Score too high");
// 驗證 ZK 證明
bytes32 outputHash = bytes32(score);
require(
verifyProof(inputHash, outputHash, proof),
"ZKML proof verification failed"
);
// 計算證明哈希
bytes32 proofHash = keccak256(proof);
// 記錄驗證結果
_recordVerification(inputHash, outputHash, proofHash);
// 存儲信用記錄
CreditScore memory newScore = CreditScore({
score: score,
timestamp: block.timestamp,
expiry: block.timestamp + 365 days, // 默認一年有效期
evaluator: msg.sender,
modelHash: modelHash,
isValid: true
});
userCreditScores[user].push(newScore);
emit CreditScoreVerified(user, score, proofHash);
return true;
}
/**
* @dev 獲取用戶的最新信用評分
* @param user 用戶地址
* @return score 信用分數,若無記錄則返回 0
* @return isValid 是否有效
*/
function getLatestCreditScore(address user)
external
view
returns (uint256 score, bool isValid)
{
CreditScore[] storage scores = userCreditScores[user];
if (scores.length == 0) {
return (0, false);
}
// 從最新開始查找有效的評分
for (uint256 i = scores.length; i > 0; i--) {
CreditScore storage latest = scores[i - 1];
if (latest.isValid && latest.expiry > block.timestamp) {
return (latest.score, true);
}
}
return (0, false);
}
/**
* @dev 批量獲取用戶的信用評分歷史
* @param user 用戶地址
* @param startIndex 起始索引
* @param count 獲取數量
* @return scores 信用評分數組
*/
function getCreditScoreHistory(
address user,
uint256 startIndex,
uint256 count
) external view returns (CreditScore[] memory scores) {
CreditScore[] storage allScores = userCreditScores[user];
uint256 endIndex = startIndex + count;
if (endIndex > allScores.length) {
endIndex = allScores.length;
}
uint256 resultLength = endIndex - startIndex;
scores = new CreditScore[](resultLength);
for (uint256 i = startIndex; i < endIndex; i++) {
scores[i - startIndex] = allScores[i];
}
}
/**
* @dev 驗證用戶信用是否達標
* @param user 用戶地址
* @param minScore 最低要求分數
* @return bool 是否達標
*/
function checkCreditScore(address user, uint256 minScore)
external
view
returns (bool)
{
(uint256 score, bool isValid) = getLatestCreditScore(user);
return isValid && score >= minScore;
}
/**
* @dev 撤銷信用評分
* @param user 用戶地址
* @param index 評分索引
*/
function revokeCreditScore(address user, uint256 index) external {
CreditScore[] storage scores = userCreditScores[user];
require(index < scores.length, "Invalid index");
require(
scores[index].evaluator == msg.sender || msg.sender == owner,
"Not authorized"
);
scores[index].isValid = false;
}
// 修飾符
modifier onlyAuthorizedEvaluator() {
require(
authorizedEvaluators[msg.sender],
"Not an authorized evaluator"
);
_;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not the owner");
_;
}
address public owner;
constructor() {
owner = msg.sender;
}
}
2.2 信用評估 ZKML 電路設計
要實現完整的 ZKML 信用評估,還需要在鏈下設計對應的 ZK 電路。以下是使用 Circom 的電路設計示例:
// credit_score.circom
// 信用評估 ZKML 電路
pragma circom 2.0.0;
include "circomlib/poseidon.circom";
include "circomlib/bitify.circom";
include "circomlib/switcher.circom";
/**
* CreditScoreVerification 電路
*
* 輸入:
* - inputData: 用戶的原始數據(信用歷史)
* - outputScore: 模型的輸出分數
* - modelHash: 模型哈希
*
* 輸出:
* - output: 承諾輸出(用於智慧合約驗證)
*/
template CreditScoreVerification() {
// 輸入信號
signal input inputData[10]; // 10 個輸入特徵
signal input outputScore; // 輸出分數
signal input modelHash; // 模型哈希
signal input userAddress; // 用戶地址
// 輸出信號
signal output commitment; // 輸出承諾
// 驗證輸出分數在有效範圍內
component scoreValid = LessEq(0);
scoreValid.in[0] <== outputScore;
scoreValid.in[1] <== 1000; // 最大信用分數
// 計算輸入數據的承諾
component poseidon = Poseidon(12);
poseidon.inputs[0] <== inputData[0];
poseidon.inputs[1] <== inputData[1];
poseidon.inputs[2] <== inputData[2];
poseidon.inputs[3] <== inputData[3];
poseidon.inputs[4] <== inputData[4];
poseidon.inputs[5] <== inputData[5];
poseidon.inputs[6] <== inputData[6];
poseidon.inputs[7] <== inputData[7];
poseidon.inputs[8] <== inputData[8];
poseidon.inputs[9] <== inputData[9];
poseidon.inputs[10] <== userAddress;
poseidon.inputs[11] <== modelHash;
// 生成最終承諾
component outputHash = Poseidon(3);
outputHash.inputs[0] <== poseidon.out;
outputHash.inputs[1] <== outputScore;
outputHash.inputs[2] <== modelHash;
commitment <== outputHash.out;
// 約束輸出分數非負
outputScore * (1 - outputScore) === 0;
}
/**
* 信用分數範圍驗證電路
*
* 驗證輸出分數是否在指定範圍內
*/
template ScoreRangeCheck() {
signal input score;
signal input minScore;
signal input maxScore;
signal output out;
// 確保 minScore <= maxScore
(maxScore - minScore) * (maxScore - minScore) >= 0;
// 確保 minScore <= score <= maxScore
component gteMin = GreaterEqThan(32);
gteMin.in[0] <== score;
gteMin.in[1] <== minScore;
component lteMax = LessEqThan(32);
lteMax.in[0] <== score;
lteMax.in[1] <== maxScore;
out <== gteMin.out * lteMax.out;
}
/**
* 主電路
*/
template Main() {
// 信用評估電路
component creditCheck = CreditScoreVerification();
// 範圍檢查
component rangeCheck = ScoreRangeCheck();
// 連接信號
// 這裡需要根據實際應用連接輸入輸出
}
component main {public [outputScore]} = Main();
2.3 信用評估合約的部署與使用
以下是部署和使用信用評估合約的完整指南:
// deploy_credit_score.js
// 信用評估 ZKML 合約部署腳本
const { ethers } = require('hardhat');
async function main() {
console.log('開始部署信用評估 ZKML 合約...');
// 1. 獲取部署帳戶
const [deployer, evaluator1, evaluator2, user1, user2] = await ethers.getSigners();
console.log('部署帳戶:', deployer.address);
// 2. 部署 ZKML 驗證合約(需要先有驗證金鑰)
// 這裡假設已經生成了驗證金鑰
const modelName = "CreditScoreModel_v1";
const modelVersion = "1.0.0";
const modelHash = ethers.keccak256(
ethers.toUtf8Bytes("CreditScoreModel_v1_pretrained")
);
console.log('模型哈希:', modelHash);
// 部署信用評估合約
const CreditScoreZKML = await ethers.getContractFactory('CreditScoreZKML');
const creditScoreContract = await CreditScoreZKML.deploy(
modelName,
modelVersion,
modelHash
);
await creditScoreContract.waitForDeployment();
const contractAddress = await creditScoreContract.getAddress();
console.log('信用評估合約已部署:', contractAddress);
// 3. 註冊評估機構
await creditScoreContract.registerEvaluator(evaluator1.address);
console.log('已註冊評估機構:', evaluator1.address);
await creditScoreContract.registerEvaluator(evaluator2.address);
console.log('已註冊評估機構:', evaluator2.address);
// 4. 模擬 ZKML 證明生成(在實際應用中需要使用 zkSNARK 庫)
// 這裡只是演示流程
const inputDataHash = ethers.keccak256(
ethers.toUtf8Bytes("user_credit_data_sample")
);
const score = 750; // 信用分數
const outputHash = ethers.keccak256(
ethers.solidityPacked(
['bytes32', 'uint256', 'bytes32'],
[inputDataHash, score, modelHash]
)
);
// 生成假的 ZK 證明(在實際應用中需要真實的證明)
const mockProof = ethers.zeroPadValue(
ethers.keccak256(ethers.toUtf8Bytes("mock_proof")),
64
);
// 5. 提交 ZKML 信用評估
console.log('\n提交 ZKML 信用評估...');
const tx = await creditScoreContract.connect(evaluator1).submitZKMLCreditScore(
user1.address,
score,
inputDataHash,
mockProof
);
await tx.wait();
console.log('ZKML 信用評估已提交');
// 6. 查詢用戶信用評分
const [userScore, isValid] = await creditScoreContract.getLatestCreditScore(
user1.address
);
console.log('\n用戶信用評分:', userScore.toString());
console.log('評分是否有效:', isValid);
// 7. 驗證用戶信用是否達標
const minRequiredScore = 700;
const meetsRequirement = await creditScoreContract.checkCreditScore(
user1.address,
minRequiredScore
);
console.log('用戶信用是否達標 (需 >= 700):', meetsRequirement);
// 8. 測試非 ZKML 信用評估提交
console.log('\n提交非 ZKML 信用評估...');
const tx2 = await creditScoreContract.connect(evaluator2).submitCreditScore(
user2.address,
650, // 信用分數
180 days // 180 天有效期
);
await tx2.wait();
console.log('非 ZKML 信用評估已提交');
// 查詢用戶2的信用評分
const [user2Score, user2Valid] = await creditScoreContract.getLatestCreditScore(
user2.address
);
console.log('用戶2信用評分:', user2Score.toString());
console.log('\n部署完成!');
console.log('合約地址:', contractAddress);
console.log('模型名稱:', modelName);
console.log('模型版本:', modelVersion);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
第三章:模型知識財產權保護 ZKML 合約
3.1 應用場景分析
在 AI 和機器學習領域,模型的知識財產權(IP)是非常重要的資產。傳統區塊鏈無法保護模型的具體內容,因為任何人都可以讀取部署的智慧合約程式碼。ZKML 透過允許模型擁有者證明模型執行了特定計算,而不需要透露模型的具體權重或結構,從而實現模型 IP 的保護。
以下是一個完整的模型 IP 保護智慧合約實現:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./ZKMLVerifierBase.sol";
/**
* @title ModelIPProtection
* @dev 基於 ZKML 的模型知識財產權保護合約
*
* 功能描述:
* - 模型擁有者可以註冊其模型
* - 使用 ZKML 證明模型已執行,但不透露模型內容
* - 支持模型使用授權管理
* - 支持模型收益分配
*
* 使用場景:
* - AI 模型版權保護
* - 模型即服務(MaaS)
* - 去中心化 AI 市場
*/
contract ModelIPProtection is ZKMLVerifierBase {
// 模型信息結構
struct ModelInfo {
string name; // 模型名稱
string description; // 模型描述
bytes32 modelContentHash; // 模型內容哈希(用於驗證)
address owner; // 擁有者
uint256 registrationTime; // 註冊時間
uint256 price; // 使用價格
bool isActive; // 是否啟用
uint256 totalUsage; // 總使用次數
}
// 模型使用授權
struct License {
address user; // 被授權用戶
uint256 expiry; // 過期時間
uint256 maxUsage; // 最大使用次數
uint256 usedCount; // 已使用次數
}
// 模型使用記錄
struct UsageRecord {
address user; // 使用者
bytes32 inputHash; // 輸入哈希
bytes32 outputHash; // 輸出哈希
uint256 timestamp; // 使用時間
}
// 模型 ID -> 模型信息
mapping(bytes32 => ModelInfo) public models;
// 模型 ID -> 授權列表
mapping(bytes32 => License[]) public licenses;
// 模型 ID -> 使用記錄
mapping(bytes32 => UsageRecord[]) public usageRecords;
// 地址 -> 註冊的模型數量
mapping(address => uint256) public modelCount;
// 事件
event ModelRegistered(
bytes32 indexed modelId,
address indexed owner,
string name
);
event ModelUpdated(
bytes32 indexed modelId,
address indexed owner
);
event ModelDeactivated(
bytes32 indexed modelId,
address indexed owner
);
event LicenseGranted(
bytes32 indexed modelId,
address indexed user,
uint256 expiry,
uint256 maxUsage
);
event ModelExecuted(
bytes32 indexed modelId,
address indexed user,
bytes32 inputHash,
bytes32 outputHash
);
/**
* @dev 合約構造函數
*/
constructor() ZKMLVerifierBase("ModelIP", "1.0.0", bytes32(0)) {
owner = msg.sender;
}
address public owner;
/**
* @dev 註冊新模型
* @param name 模型名稱
* @param description 模型描述
* @param modelContentHash 模型內容哈希
* @param price 使用價格
* @return modelId 模型 ID
*/
function registerModel(
string memory name,
string memory description,
bytes32 modelContentHash,
uint256 price
) external returns (bytes32 modelId) {
// 生成模型 ID
modelId = keccak256(
abi.encodePacked(
msg.sender,
name,
block.timestamp
)
);
// 確保模型 ID 唯一
require(
models[modelId].registrationTime == 0,
"Model already registered"
);
// 註冊模型
models[modelId] = ModelInfo({
name: name,
description: description,
modelContentHash: modelContentHash,
owner: msg.sender,
registrationTime: block.timestamp,
price: price,
isActive: true,
totalUsage: 0
});
modelCount[msg.sender]++;
emit ModelRegistered(modelId, msg.sender, name);
return modelId;
}
/**
* @dev 更新模型信息
* @param modelId 模型 ID
* @param newDescription 新描述
* @param newPrice 新價格
*/
function updateModel(
bytes32 modelId,
string memory newDescription,
uint256 newPrice
) external {
ModelInfo storage model = models[modelId];
require(model.owner == msg.sender, "Not the owner");
require(model.isActive, "Model is not active");
model.description = newDescription;
model.price = newPrice;
emit ModelUpdated(modelId, msg.sender);
}
/**
* @dev 停用模型
* @param modelId 模型 ID
*/
function deactivateModel(bytes32 modelId) external {
ModelInfo storage model = models[modelId];
require(model.owner == msg.sender, "Not the owner");
model.isActive = false;
emit ModelDeactivated(modelId, msg.sender);
}
/**
* @dev 購買模型使用授權
* @param modelId 模型 ID
* @param duration 授權時長(秒)
* @param maxUsage 最大使用次數
*/
function purchaseLicense(
bytes32 modelId,
uint256 duration,
uint256 maxUsage
) external payable {
ModelInfo storage model = models[modelId];
require(model.isActive, "Model is not active");
require(msg.value >= model.price, "Insufficient payment");
// 計算授權過期時間
uint256 expiry = block.timestamp + duration;
// 創建授權
License memory newLicense = License({
user: msg.sender,
expiry: expiry,
maxUsage: maxUsage,
usedCount: 0
});
licenses[modelId].push(newLicense);
// 將款項轉給模型擁有者
payable(model.owner).transfer(msg.value);
emit LicenseGranted(modelId, msg.sender, expiry, maxUsage);
}
/**
* @dev 使用模型(ZKML 驗證)
* @param modelId 模型 ID
* @param inputHash 輸入數據哈希
* @param outputHash 輸出數據哈希
* @param proof ZK 證明
*/
function executeModel(
bytes32 modelId,
bytes32 inputHash,
bytes32 outputHash,
bytes calldata proof
) external {
ModelInfo storage model = models[modelId];
require(model.isActive, "Model is not active");
// 驗證 ZK 證明
require(
verifyProof(inputHash, outputHash, proof),
"ZKML proof verification failed"
);
// 驗證用戶有有效授權
bool hasValidLicense = false;
License[] storage userLicenses = licenses[modelId];
for (uint256 i = 0; i < userLicenses.length; i++) {
License storage license = userLicenses[i];
if (
license.user == msg.sender &&
license.expiry > block.timestamp &&
license.usedCount < license.maxUsage
) {
hasValidLicense = true;
license.usedCount++;
break;
}
}
require(hasValidLicense, "No valid license");
// 記錄使用
model.totalUsage++;
usageRecords[modelId].push(UsageRecord({
user: msg.sender,
inputHash: inputHash,
outputHash: outputHash,
timestamp: block.timestamp
}));
emit ModelExecuted(modelId, msg.sender, inputHash, outputHash);
}
/**
* @dev 獲取模型的授權數量
* @param modelId 模型 ID
* @return 授權數量
*/
function getLicenseCount(bytes32 modelId)
external
view
returns (uint256)
{
return licenses[modelId].length;
}
/**
* @dev 獲取模型的總使用次數
* @param modelId 模型 ID
* @return 總使用次數
*/
function getTotalUsage(bytes32 modelId)
external
view
returns (uint256)
{
return models[modelId].totalUsage;
}
/**
* @dev 獲取模型信息
* @param modelId 模型 ID
* @return 模型信息
*/
function getModelInfo(bytes32 modelId)
external
view
returns (ModelInfo memory)
{
return models[modelId];
}
}
第四章:預言機 ZKML 合約實現
4.1 去中心化預言機與 ZKML
傳統的區塊鏈預言機面臨著數據來源單一和數據操縱的風險。ZKML 可以用於創建更加安全和私密的預言機系統,允許預言機在不透露數據來源和計算邏輯的情況下提供可信的數據。
以下是一個基於 ZKML 的預言機合約實現:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "./ZKMLVerifierBase.sol";
/**
* @title ZKMLOracle
* @dev 基於 ZKML 的去中心化預言機合約
*
* 功能描述:
* - 數據提供者可以註冊並提供數據
* - 使用 ZKML 證明數據的正確性
* - 支持多種數據類型(價格、比分、事件結果等)
* - 實現數據請求-響應模式
*
* 使用場景:
* - DeFi 價格預言機
* - 體育比分預言機
* - 事件結果預言機
* - 天氣數據預言機
*/
contract ZKMLOracle is ZKMLVerifierBase {
// 數據類型枚舉
enum DataType {
Price, // 價格數據
Score, // 比分數據
EventResult, // 事件結果
Weather, // 天氣數據
Custom // 自定義數據
}
// 數據源信息
struct DataSource {
string name; // 數據源名稱
address operator; // 運營商地址
bytes32 modelHash; // 使用的模型哈希
uint256 stakeAmount; // 質押金額
bool isActive; // 是否啟用
uint256 reliabilityScore; // 可靠性評分
uint256 dataCount; // 提供的數據總數
}
// 請求信息
struct Request {
bytes32 requestId; // 請求 ID
address requester; // 請求者
DataType dataType; // 數據類型
string dataKey; // 數據鍵(如 ETH/USD)
uint256 timestamp; // 請求時間
bool fulfilled; // 是否已響應
bytes32 dataHash; // 數據哈希
}
// 響應信息
struct Response {
bytes32 requestId; // 請求 ID
address dataSource; // 數據源
bytes32 dataHash; // 數據哈希
uint256 timestamp; // 響應時間
}
// 數據源映射
mapping(address => DataSource) public dataSources;
// 請求映射
mapping(bytes32 => Request) public requests;
// 響應映射
mapping(bytes32 => Response[]) public responses;
// 最新數據映射(常用數據的快速訪問)
mapping(bytes32 => bytes32) public latestData;
// 質押要求
uint256 public constant MIN_STAKE = 1 ether;
// 請求計數器
uint256 public requestCount;
// 事件
event DataSourceRegistered(
address indexed dataSource,
string name
);
event DataSourceSlashed(
address indexed dataSource,
uint256 amount
);
event DataRequested(
bytes32 indexed requestId,
address indexed requester,
DataType dataType,
string dataKey
);
event DataFulfilled(
bytes32 indexed requestId,
address indexed dataSource,
bytes32 dataHash
);
/**
* @dev 合約構造函數
*/
constructor() ZKMLVerifierBase("ZKMLOracle", "1.0.0", bytes32(0)) {
owner = msg.sender;
}
address public owner;
/**
* @dev 註冊為數據源
* @param name 數據源名稱
* @param modelHash 使用的模型哈希
*/
function registerDataSource(
string memory name,
bytes32 modelHash
) external payable {
require(msg.value >= MIN_STAKE, "Insufficient stake");
require(
dataSources[msg.sender].stakeAmount == 0,
"Already registered"
);
dataSources[msg.sender] = DataSource({
name: name,
operator: msg.sender,
modelHash: modelHash,
stakeAmount: msg.value,
isActive: true,
reliabilityScore: 100, // 初始評分
dataCount: 0
});
emit DataSourceRegistered(msg.sender, name);
}
/**
* @dev 創建數據請求
* @param dataType 數據類型
* @param dataKey 數據鍵
* @return requestId 請求 ID
*/
function createRequest(
DataType dataType,
string memory dataKey
) external returns (bytes32 requestId) {
requestId = keccak256(
abi.encodePacked(
msg.sender,
dataType,
dataKey,
block.timestamp,
requestCount++
)
);
requests[requestId] = Request({
requestId: requestId,
requester: msg.sender,
dataType: dataType,
dataKey: dataKey,
timestamp: block.timestamp,
fulfilled: false,
dataHash: bytes32(0)
});
emit DataRequested(requestId, msg.sender, dataType, dataKey);
return requestId;
}
/**
* @dev 響應數據請求(使用 ZKML)
* @param requestId 請求 ID
* @param dataValue 數據值
* @param inputHash 輸入哈希
* @param proof ZK 證明
*/
function fulfillRequest(
bytes32 requestId,
bytes32 dataValue,
bytes32 inputHash,
bytes calldata proof
) external {
Request storage request = requests[requestId];
require(!request.fulfilled, "Already fulfilled");
require(
dataSources[msg.sender].isActive,
"Data source not active"
);
// 驗證 ZK 證明
require(
verifyProof(inputHash, dataValue, proof),
"ZKML proof verification failed"
);
// 計算數據哈希
bytes32 dataHash = keccak256(
abi.encodePacked(
request.dataKey,
dataValue,
block.timestamp
)
);
// 更新請求狀態
request.fulfilled = true;
request.dataHash = dataHash;
// 記錄響應
responses[requestId].push(Response({
requestId: requestId,
dataSource: msg.sender,
dataHash: dataHash,
timestamp: block.timestamp
}));
// 更新最新數據
bytes32 dataKeyHash = keccak256(
bytes(request.dataKey)
);
latestData[dataKeyHash] = dataHash;
// 更新數據源統計
DataSource storage source = dataSources[msg.sender];
source.dataCount++;
// 獎勵數據源
payable(msg.sender).transfer(0.01 ether);
emit DataFulfilled(requestId, msg.sender, dataHash);
}
/**
* @dev 響應數據請求(不使用 ZKML,用於測試)
* @param requestId 請求 ID
* @param dataValue 數據值
*/
function fulfillRequestSimple(
bytes32 requestId,
bytes32 dataValue
) external {
Request storage request = requests[requestId];
require(!request.fulfilled, "Already fulfilled");
require(
dataSources[msg.sender].isActive,
"Data source not active"
);
// 計算數據哈希
bytes32 dataHash = keccak256(
abi.encodePacked(
request.dataKey,
dataValue,
block.timestamp
)
);
// 更新請求狀態
request.fulfilled = true;
request.dataHash = dataHash;
// 記錄響應
responses[requestId].push(Response({
requestId: requestId,
dataSource: msg.sender,
dataHash: dataHash,
timestamp: block.timestamp
}));
// 更新最新數據
bytes32 dataKeyHash = keccak256(
bytes(request.dataKey)
);
latestData[dataKeyHash] = dataHash;
// 更新數據源統計
dataSources[msg.sender].dataCount++;
emit DataFulfilled(requestId, msg.sender, dataHash);
}
/**
* @dev 獲取最新數據
* @param dataKey 數據鍵
* @return 數據哈希
*/
function getLatestData(string memory dataKey)
external
view
returns (bytes32)
{
bytes32 dataKeyHash = keccak256(bytes(dataKey));
return latestData[dataKeyHash];
}
/**
* @dev 獲取請求的響應數量
* @param requestId 請求 ID
* @return 響應數量
*/
function getResponseCount(bytes32 requestId)
external
view
returns (uint256)
{
return responses[requestId].length;
}
}
第五章:部署與測試指南
5.1 開發環境配置
在開始部署 ZKML 合約之前,需要配置正確的開發環境:
// hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("@openzeppelin/hardhat-upgrades");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
hardhat: {
chainId: 31337
},
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
chainId: 11155111
},
mainnet: {
url: process.env.MAINNET_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
chainId: 1
}
},
gasReporter: {
enabled: true,
currency: "USD"
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY
}
};
5.2 合約測試
以下是完整的測試用例:
// test/ZKMLCreditScore.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { time } = require("@nomicfoundation/hardhat-network-helpers");
describe("CreditScoreZKML", function () {
let creditScoreContract;
let owner, evaluator, user1, user2;
beforeEach(async function () {
[owner, evaluator, user1, user2] = await ethers.getSigners();
const CreditScoreZKML = await ethers.getContractFactory(
"CreditScoreZKML"
);
creditScoreContract = await CreditScoreZKML.deploy(
"CreditScoreModel",
"1.0.0",
ethers.keccak256(ethers.toUtf8Bytes("test_model"))
);
await creditScoreContract.waitForDeployment();
// 註冊評估機構
await creditScoreContract.registerEvaluator(evaluator.address);
});
describe("信用評估提交", function () {
it("應該能夠提交非 ZKML 信用評估", async function () {
const tx = await creditScoreContract
.connect(evaluator)
.submitCreditScore(user1.address, 750, 365 days);
await tx.wait();
const [score, isValid] = await creditScoreContract
.getLatestCreditScore(user1.address);
expect(score).to.equal(750);
expect(isValid).to.equal(true);
});
it("應該能夠檢查信用是否達標", async function () {
await creditScoreContract
.connect(evaluator)
.submitCreditScore(user1.address, 750, 365 days);
const meetsRequirement = await creditScoreContract
.checkCreditScore(user1.address, 700);
expect(meetsRequirement).to.equal(true);
const failsRequirement = await creditScoreContract
.checkCreditScore(user1.address, 800);
expect(failsRequirement).to.equal(false);
});
it("應該能夠獲取信用評分歷史", async function () {
// 提交多個評分
await creditScoreContract
.connect(evaluator)
.submitCreditScore(user1.address, 650, 365 days);
await creditScoreContract
.connect(evaluator)
.submitCreditScore(user1.address, 750, 365 days);
const history = await creditScoreContract
.getCreditScoreHistory(user1.address, 0, 10);
expect(history.length).to.equal(2);
});
it("應該能夠撤銷信用評分", async function () {
await creditScoreContract
.connect(evaluator)
.submitCreditScore(user1.address, 750, 365 days);
await creditScoreContract.revokeCreditScore(user1.address, 0);
const [score, isValid] = await creditScoreContract
.getLatestCreditScore(user1.address);
expect(isValid).to.equal(false);
});
});
describe("訪問控制", function () {
it("非授權評估機構不應該能夠提交評分", async function () {
await expect(
creditScoreContract
.connect(user2)
.submitCreditScore(user1.address, 750, 365 days)
).to.be.revertedWith("Not an authorized evaluator");
});
it("只有評估機構可以撤銷評分", async function () {
await creditScoreContract
.connect(evaluator)
.submitCreditScore(user1.address, 750, 365 days);
await expect(
creditScoreContract
.connect(user2)
.revokeCreditScore(user1.address, 0)
).to.be.revertedWith("Not authorized");
});
});
});
5.3 部署腳本
以下是完整的部署腳本:
// scripts/deploy_zkml_ecosystem.js
const { ethers } = require("hardhat");
async function main() {
console.log("========================================");
console.log("ZKML 生態系統部署");
console.log("========================================\n");
const [deployer] = await ethers.getSigners();
console.log("部署帳戶:", deployer.address);
console.log("帳戶餘額:", ethers.formatEther(
await ethers.provider.getBalance(deployer.address)
), "ETH\n");
// 1. 部署信用評估合約
console.log("1. 部署信用評估 ZKML 合約...");
const CreditScoreZKML = await ethers.getContractFactory("CreditScoreZKML");
const creditScoreContract = await CreditScoreZKML.deploy(
"CreditScoreModel_v1",
"1.0.0",
ethers.keccak256(ethers.toUtf8Bytes("CreditScoreModel_v1_pretrained"))
);
await creditScoreContract.waitForDeployment();
const creditScoreAddress = await creditScoreContract.getAddress();
console.log(" 信用評估合約:", creditScoreAddress);
// 2. 部署模型 IP 保護合約
console.log("\n2. 部署模型 IP 保護合約...");
const ModelIPProtection = await ethers.getContractFactory("ModelIPProtection");
const modelIPContract = await ModelIPProtection.deploy();
await modelIPContract.waitForDeployment();
const modelIPAddress = await modelIPContract.getAddress();
console.log(" 模型 IP 保護合約:", modelIPAddress);
// 3. 部署 ZKML 預言機合約
console.log("\n3. 部署 ZKML 預言機合約...");
const ZKMLOracle = await ethers.getContractFactory("ZKMLOracle");
const oracleContract = await ZKMLOracle.deploy();
await oracleContract.waitForDeployment();
const oracleAddress = await oracleContract.getAddress();
console.log(" ZKML 預言機合約:", oracleAddress);
// 4. 部署示例 ZKML 驗證器
console.log("\n4. 部署示例 ZKML 驗證器...");
const SampleVerifier = await ethers.getContractFactory("SampleZKMLVerifier");
const sampleVerifier = await SampleVerifier.deploy(
"SampleModel",
"1.0.0",
ethers.keccak256(ethers.toUtf8Bytes("sample_model"))
);
await sampleVerifier.waitForDeployment();
const sampleVerifierAddress = await sampleVerifier.getAddress();
console.log(" 示例驗證器:", sampleVerifierAddress);
console.log("\n========================================");
console.log("部署完成!");
console.log("========================================");
console.log("\n合約地址總結:");
console.log("- 信用評估合約:", creditScoreAddress);
console.log("- 模型 IP 保護合約:", modelIPAddress);
console.log("- ZKML 預言機合約:", oracleAddress);
console.log("- 示例驗證器:", sampleVerifierAddress);
// 保存部署信息
const deploymentInfo = {
network: (await ethers.provider.getNetwork()).name,
chainId: (await ethers.provider.getNetwork()).chainId,
timestamp: new Date().toISOString(),
deployer: deployer.address,
contracts: {
CreditScoreZKML: creditScoreAddress,
ModelIPProtection: modelIPAddress,
ZKMLOracle: oracleAddress,
SampleZKMLVerifier: sampleVerifierAddress
}
};
console.log("\n部署信息:", JSON.stringify(deploymentInfo, null, 2));
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
第六章:安全考量與最佳實踐
6.1 ZKML 合約安全要點
在部署 ZKML 智慧合約時,需要特別注意以下安全考量:
ZKML 合約安全檢查清單:
1. 驗證金鑰管理
□ 驗證金鑰必須安全存儲
□ 實現驗證金鑰輪換機制
□ 防止驗證金鑰泄露
2. 輸入驗證
□ 驗證所有輸入參數的有效性
□ 防止整數溢出和下溢
□ 驗證輸入數據範圍
3. 訪問控制
□ 正確實現權限控制
□ 防止未授權訪問
□ 實現緊急暫停機制
4. 經濟安全
□ 設置合理的質押要求
□ 實現罰沒機制
□ 防止女巫攻擊
5. 隱私保護
□ 不在鏈上存儲敏感數據
□ 使用承諾-挑戰機制
□ 實現選擇性披露
6.2 常見攻擊向量防護
以下是針對 ZKML 合約的常見攻擊防護措施:
/**
* @title SecurityUtils
* @dev ZKML 合約安全工具庫
*/
library SecurityUtils {
/**
* @dev 驗證地址非零
*/
function validateAddress(address addr) internal pure {
require(addr != address(0), "Invalid zero address");
}
/**
* @dev 驗證數據範圍
*/
function validateRange(
uint256 value,
uint256 min,
uint256 max
) internal pure {
require(value >= min && value <= max, "Value out of range");
}
/**
* @dev 驗證時間戳
*/
function validateTimestamp(uint256 timestamp) internal view {
require(timestamp <= block.timestamp, "Future timestamp");
require(
timestamp >= block.timestamp - 365 days,
"Timestamp too old"
);
}
/**
* @dev 安全轉帳
*/
function safeTransfer(
address token,
address to,
uint256 amount
) internal {
require(to != address(0), "Transfer to zero address");
if (token == address(0)) {
require(
address(this).balance >= amount,
"Insufficient balance"
);
(bool success, ) = to.call{value: amount}("");
require(success, "Transfer failed");
} else {
// 假設是 ERC-20 代幣
(bool success, bytes memory data) = token.call(
abi.encodeWithSignature(
"transfer(address,uint256)",
to,
amount
)
);
require(success && (data.length == 0 || abi.decode(data, (bool))),
"Transfer failed");
}
}
}
結論
本文提供了 ZKML 在以太坊上開發的完整實踐指南,涵蓋了從基礎設施設計到實際部署的全流程。我們提供了多個完整可運行的智慧合約範例,包括信用評估、模型知識財產權保護、和去中心化預言機等應用場景。
透過這些範例和指南,開發者可以快速掌握 ZKML 技術的核心概念,並開始構建自己的 ZKML 應用。隨著 ZKML 技術的不斷成熟,我們預計將看到更多創新的應用場景在以太坊生態系統中涌現。
建議開發者在實際項目中:
- 仔細評估 ZKML 框架的選擇
- 進行全面的安全審計
- 實施適當的升級機制
- 建立完善的監控和應急響應系統
參考資源
- ezkl 官方文檔
- Circom 電路設計指南
- SnarkJS 證明生成庫
- OpenZeppelin 智慧合約庫
- 以太坊官方 ZKML 研究
風險聲明
本文僅供教育目的,不構成投資建議。ZKML 技術仍處於早期發展階段,實際部署需進行完整的安全審計。
相關文章
- ZKML 與以太坊整合深度技術分析:零知識證明在機器學習領域的革命性應用 — 零知識證明與機器學習的結合(ZKML)正在區塊鏈領域引發深刻變革。本文全面分析 ZKML 的技術原理、在以太坊上的實現方式、主要應用場景和未來發展趨勢。從去中心化 AI 市場到隱私保護預測市場,從模型驗證到推理認證,我們提供詳實的技術細節和實踐建議。
- ZKML 零知識機器學習完整指南:從理論到以太坊應用實踐 — 零知識機器學習(Zero-Knowledge Machine Learning,ZKML)是密碼學與人工智慧交叉的新興領域,近年來在區塊鏈社區引起了廣泛關注。ZKML 的核心願景是允許證明者向驗證者證明「某個機器學習模型的推理結果是正確的」,同時驗證者無法從證明中獲悉模型的輸入、輸出或模型本身的詳細資訊。這種能力為區塊鏈應用帶來了革命性的可能性:從去中心化 AI 市場到鏈上身份驗證,從隱私保護的信
- ERC-4626 Tokenized Vault 完整實現指南:從標準規範到生產級合約 — 本文深入探討 ERC-4626 標準的技術細節,提供完整的生產級合約實現。內容涵蓋標準接口定義、資產與份額轉換的數學模型、收益策略整合、費用機制設計,並提供可直接部署的 Solidity 代碼範例。通過本指南,開發者可以構建安全可靠的代幣化 vault 系統。
- Solidity 智慧合約完整實作指南:從 ERC-20 到可升級合約的工程實踐 — 本文從工程實踐角度深入講解 Solidity 智慧合約的完整開發流程,涵蓋 ERC-20 代幣合約的完整實現、基於角色的存取控制系統、可升級代理模式、以及使用 Foundry 框架的全面測試策略。我們提供了可直接用於生產環境的程式碼範例,包括完整的 ERC20 實現、AccessControl 角色管理、透明代理合約、以及包含模糊測試的測試套件。透過本文,開發者將掌握編寫安全、高效、可升級智慧合約的核心技能。
- 以太坊智能合約開發實作教程:從環境搭建到部署上線完整指南 — 本教程帶領讀者從零開始,建立完整的以太坊開發環境,撰寫第一個智能合約,並將其部署到測試網絡和主網。我們使用 Solidity 作為合約語言,Hardhat 作為開發框架,提供一步一步的詳細操作指南。內容涵蓋 Hardhat 專案初始化、ERC-20 代幣合約撰寫、單元測試、Sepolia 測試網絡部署、以及主網部署等完整流程。
延伸閱讀與來源
- Ethereum.org Developers 官方開發者入口與技術文件
- EIPs 以太坊改進提案
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!