实现原理: 模式二:离线生成证明 (
|
| 特性 | 说明 |
|---|---|
| 生成完整的 ZK 证明 | 跟广播模式一样的证明流程(~30 秒 Varuna),一样的手续费授权 |
| 不广播 | 返回 Transaction JSON 字符串而不是 tx_id,不出块、不扣费 |
| 需要网络 | 虽然不上链,但还是需要拉取最新的 state root(证明生成需要链上状态) |
实现原理:SDK 的 AleoClient::prove_execution() 复用了 execute_and_broadcast 的 90% 代码,核心差异只在最后一步:
pub async fn prove_execution(&self, ...) -> Result<String> {
// ... 前 7 步跟 execute_and_broadcast 完全一致 ...
// 第 8 步:返回 JSON 字符串,不广播
let tx_json = serde_json::to_string_pretty(&transaction)?;
Ok(tx_json)
// 对比广播版的第 8 步:
// let tx_id = self.network.broadcast_transaction(&tx_json).await?;
// self.network.wait_for_confirmation(&tx_id).await
}
verify / verify --deep)有朋友在群里说:你发上去的交易我凭什么相信它是真的?Aleo 给了你答案——verify_transaction_api 方法可以本地验证任何链上交易的 ZK 证明。
verify 命令有两个模式:
浅查(默认):只拉取交易基本信息
$ aleo-cli --node "https://api.explorer.provable.com/v2/testnet" \
verify at1z9elr8m25jcvlh4kdaaxcf62t9rcx38n7tus3tqsz0tsh6cz859spmu0y2
输出:
=== Aleo Verify ===
🔍 Transaction: at1z9elr8m25jcvlh4kdaaxcf62t9rcx38n7tus3tqsz0tsh6cz859spmu0y2
📋 Type: deploy
📋 Status: confirmed
👤 Owner: aleo1cu0xk4tt99pgxgl
深度验证(--deep):拉取交易后本地重算 ZK 证明
部署交易验证:
$ aleo-cli --node "https://api.explorer.provable.com/v2/testnet" \
verify at1z9elr8m25jcvlh4kdaaxcf62t9rcx38n7tus3tqsz0tsh6cz859spmu0y2 --deep
输出:
=== Aleo Verify ===
🔍 Transaction: at1z9elr8m25jcvlh4kdaaxcf62t9rcx38n7tus3tqsz0tsh6cz859spmu0y2
📋 Type: deploy
📋 Status: confirmed
👤 Owner: aleo1cu0xk4tt99pgxgl
🔬 Deep verification (proof) enabled...
✅ Deployment proof VERIFIED
Program: test_cli_deploy.aleo
执行交易验证:
$ aleo-cli --node "https://api.explorer.provable.com/v2/testnet" \
verify at1n3tjm6hhehp9zsc5j2ce9cdmh030hcznzsh26qsvkkgnkhykhyrsv5aq9a --deep
输出:
=== Aleo Verify ===
🔍 Transaction: at1n3tjm6hhehp9zsc5j2ce9cdmh030hcznzsh26qsvkkgnkhykhyrsv5aq9a
📋 Type: execute
📋 Status: confirmed
🔧 Transition #0: ?::?
Input: aleo1ss6e8mpfcxgvzd8urjan8c36mj5k0sllxse8y0hwvr80ufququys52qz2r
Input: 1000000u64
🔬 Deep verification (proof) enabled...
✅ Execution proof VERIFIED
Program: credits.aleo
Function: transfer_public
Transitions: 1
验证了什么:verify --deep 实际上做了两步:
GET /v2/testnet/transaction/{id},返回完整的交易 JSON(含 execution/deployment + 证明)Transaction 枚举,调用 Process::verify_execution() 或 Process::verify_deployment() 重新计算证明的哈希,跟链上提交的证明对比如果证明被篡改过(比如有人改动了交易参数),本地校验会失败:
❌ Execution proof verification FAILED
Error: 'transition proof is invalid'
这就是 Aleo "信任但验证" 的设计哲学——任何人拿到链上交易都可以本地验证其正确性,不依赖节点。
现象:执行非 credits.aleo 的程序时加 --local 报错:
Authorization failed — Program 'test_cli_deploy.aleo' does not exist
原因:AleoClient::execute_local() 只内置了 credits.aleo 系统合约。因为 execute_local 不走 set_program() 注册流程,自定义程序没有被加载到 Process 中。
解决:--local 模式下自动检测程序 ID,非 credits.aleo 时提示用户换用 --prove 或默认模式(从网络拉取程序后全流程证明)。
if program_id != "credits.aleo" {
anyhow::bail!(
"--local mode is only available for credits.aleo. \
Use --prove or omit the flag for custom programs."
);
}
现象:verify --deep 验证部署交易时报错:
Error: Process already contains program 'test_cli_deploy.aleo' with edition '0'
原因:Process::verify_deployment() 内部会调 add_program() 注册被验证的程序。如果 Process 已经加载了同名程序(从 SDK 初始化或之前的验证残留),就会冲突。
解决:在 client.rs 的 verify_execution 方法中,部署类型使用一个新的、空的 ExecutionEngine(不加载任何用户程序),避免程序名冲突:
Transaction::Deploy(_owner, deployment, _fee) => {
// 使用一个全新 Engine(不含用户程序),避免 program_id 冲突
let engine = ExecutionEngine::new_empty()?;
engine.verify_deployment_transaction(deployment, rng)
}
现象:Transaction::from_str(json) 反序列化失败。
原因:测试网返回的交易 JSON 中包含 confirmations 字段(已入块确认数),但 snarkVM 的 Transaction 结构体在未确认阶段没有这个字段。
解决:需要先从 JSON 中提取 transaction 字段(内层对象),再传给 from_str。网络层做了字段适配。
现象:初始实现调用 execution.id() 获取 execution ID,编译错误。
原因:snarkVM 4.10.0 中 Execution 结构体没有直接暴露 id() 方法(可能是跨版本差异)。真正的 ID 在 Transition::transition_id() 中。
解决:改为遍历 transitions:
// 错误方式
let exec_id = execution.id();
// 正确方式
for transition in execution.transitions() {
let tx_id = transition.transition_id();
}
| 模式 | 命令 | 耗时 | 是否上链 | 验证结果 |
|---|---|---|---|---|
| 本地无证明 | exec --local |
毫秒级 | ❌ 不广播 | ✅ 返回执行响应 |
| 离线证明 | exec --prove |
~30秒 | ❌ 不广播 | ✅ 完整 TX JSON(含 ZK proof ID au1...) |
| 浅查交易 | verify <tx_id> |
秒级 | ❌ 只拉取 | ✅ 显示类型/状态/所有者 |
| 深度验证 | verify <tx_id> --deep |
~1秒 + 网络 | ❌ 只拉取 | ✅ deploy + execute 证明均验证通过 |
| JS SDK | Rust CLI | 状态 |
|---|---|---|
run() |
exec --local |
✅ 已实现 + 真实测试 |
run(proveExecution=true) |
exec --prove |
✅ 已实现 + 真实测试 |
verifyExecution() |
verify --deep |
✅ 已实现 + 真实测试 |
buildExecutionTransaction() |
exec (默认) / deploy |
✅ 此前已实现 |
broadcastTransaction() |
集成在 exec / deploy 中 |
✅ 此前已实现 |
| 项目 | 详情 |
|---|---|
| aleo-cli | GitHub:https://github.com/qiaopengjun5162/aleo-cli |
| 9 个命令/子命令(generate/query/balance/transfer/deploy/exec + --local/--prove/verify + --deep) | |
| 最新 commit:ec80570(feat: add verify --deep) | |
| aleo-rust-sdk | v0.3.0:https://crates.io/crates/aleo-rust-sdk |
| 最新 commit:ae9cd70(feat: add verify_execution) | |
| docs.rs:https://docs.rs/aleo-rust-sdk |
1. 三模式对应开发三阶段:
--local(毫秒级反馈,先确保逻辑正确)--prove(确认 ZK 证明能生成,不浪费手续费)verify --deep(验证已上链交易的 ZK 证明)2. JS SDK 的 run() 在 Rust SDK 中需要三个独立方法:JS 通过一个 proveExecution 布尔开关切换,而 Rust 需要显式调用不同的管线路径(execute_local / prove_execution / execute_and_broadcast)。这是因为 Rust 的所有权模型让编译器需要在编译期确定调用路径。
3. ZK 证明验证不是玄学:verify --deep 实际做的是反序列化交易 → 提取 transitions → 调 Varuna 验证器重算。这个流程跟链上的验证节点做的完全一致,所以本地验证通过 = 链上确实接受了这笔交易。
4. Zero-knowledge, zero-trust:Aleo 的设计让任何人都可以在不信任节点的情况下验证链上交易。你不需要跑全节点,不需要信任 API 提供商,只需要一条 aleo-cli verify <tx_id> --deep 命令。
verify_execution API)到 crates.ioexec --prove 的输出保存为文件(--output 参数),方便离线传输--local:在 CLI 加载用户指定的 .aleo 文件到 Process