跳到主要内容

Direct Mode Quick Start

这个教程对应的是仓库里的 direct 提交流程。 如果你想边看文档边对照代码,直接打开 JetHalo/zk-Escrow 的 direct 分支 会更顺手一些。 如果你想先看实际效果,可以直接打开 zkEscrow direct 模式演示地址。这个地址对应的就是这篇教程讲的 direct 路线。

浏览器本地生成 Groth16 proof,服务端把 proofpublicSignalsvk 直接提交给 Kurier。Kurier 状态进入 finalized 之后,前端再请求授权签名,最后调用链上 finalize

这是一套 Base Sepolia 测试链 方案,不是 Base 主网方案。文档里的钱包、RPC、合约地址、测试 ETH 和链上交互,都默认发生在 Base Sepolia。

仓库里已经带上了浏览器 proving 需要的 wasm.zkeyvkey.json。如果你的目标只是先把 direct 模式跑通,可以直接使用现成 artifacts。如果你想从电路源码重新生成它们,下面也有完整步骤。

Prerequisite

开始之前,先准备好下面这些内容:

  • Node.js 22.x 或更新版本
  • npm
  • Foundry
  • 一个 Base Sepolia 钱包
  • 钱包里的测试 ETH
  • 一个可用的 Kurier API Key
  • 已部署好的 ZKEscrowRelease 合约地址
  • 这份合约对应的部署区块
  • 和合约 finalizeAuthority 一致的私钥

What you'll learn

完成这篇教程之后,你会得到:

  • 一个可以本地启动的 direct 模式环境
  • 一份最小可用的 apps/web/.env.local
  • 一条从 depositprove 再到 finalize 的完整调试路径
  • 一套可选的电路重编译和 The Graph 索引流程

1. Understand The Two Modes

先把 directaggregation 的差别说清楚。

Direct mode

direct 的重点是“证明直接提交,结果直接消费”。

  • 提交 proof 时就把完整 proof、公开输入和 verification key 一起交给 Kurier。
  • 前端只轮询 proof 状态。
  • 状态到 finalized 之后,直接进入授权和链上 finalize
  • 当前仓库的主流程就是这条路径,/api/submit-proof 固定写的是 submissionMode: direct

Aggregation mode

aggregation 会比 direct 多一层聚合批次。

  • proof 提交之后,不是立刻按单条结果消费。
  • 还要等 proof 被收进 aggregation batch。
  • 这时候除了 proof 状态,还要拿 aggregation tuple,例如 domainIdaggregationIdleafCountindexmerklePath
  • 仓库里保留 /api/proof-aggregation,就是为了处理这条路径。

如果你的目标是先把这套流程跑通,先用 direct。这个仓库当前默认也是 direct

2. Install The Project

Install the web app dependencies

先安装前端依赖:

cd apps/web
npm install

Optional: install the subgraph dependencies

如果你准备接 The Graph,再安装 subgraph 依赖:

cd indexer/subgraph
npm install

Optional: run the contract tests

如果你想先确认合约逻辑是通的,可以执行:

cd contracts
forge test

3. Generate The Proving Artifacts

如果你不想直接使用仓库里已经带好的 proving artifacts,而是想从电路源码重新生成 wasm.zkeyvkey.json,走这一步。

这一节是手工流程。当前仓库没有把 circuit build 和 trusted setup 封装成脚本。

Install the circuit dependencies

circuits/escrow/circom/escrowRelease.circom 依赖 circomlib,所以先在 circuits/escrow 目录补上本地依赖:

cd circuits/escrow
npm install circomlib snarkjs

另外还需要你本机已经装好 circom 二进制。

Compile escrowRelease.circom

把电路编译成 r1cswasmsym

cd circuits/escrow
mkdir -p build
circom circom/escrowRelease.circom --r1cs --wasm --sym -o build

执行完之后,关键输出会在这些位置:

  • build/escrowRelease.r1cs
  • build/escrowRelease.sym
  • build/escrowRelease_js/escrowRelease.wasm

Run the local trusted setup

这个电路走的是 groth16。本地开发可以直接做一套本地 ceremony。

下面这套流程只适合本地开发和教程复现,不适合拿来当生产 setup。

cd circuits/escrow
snarkjs powersoftau new bn128 16 build/pot16_0000.ptau
snarkjs powersoftau contribute build/pot16_0000.ptau build/pot16_0001.ptau --name="zkescrow dev ptau" -e="replace-with-random-text"
snarkjs powersoftau prepare phase2 build/pot16_0001.ptau build/pot16_final.ptau
snarkjs groth16 setup build/escrowRelease.r1cs build/pot16_final.ptau build/escrowRelease_0000.zkey
snarkjs zkey contribute build/escrowRelease_0000.zkey build/escrowRelease_final.zkey --name="zkescrow dev zkey" -e="replace-with-random-text"
snarkjs zkey export verificationkey build/escrowRelease_final.zkey build/vkey.json

到这里你会得到:

  • build/escrowRelease_final.zkey
  • build/vkey.json

Copy the artifacts into the web app

前端 proving 读取的是 apps/web/public/zk/escrow 目录,所以最后还要把新生成的 artifacts 复制过去:

cp circuits/escrow/build/escrowRelease_js/escrowRelease.wasm apps/web/public/zk/escrow/escrowRelease.wasm
cp circuits/escrow/build/escrowRelease_final.zkey apps/web/public/zk/escrow/escrowRelease_final.zkey
cp circuits/escrow/build/vkey.json apps/web/public/zk/escrow/vkey.json

如果你重新生成了 artifacts,建议再重启一次 apps/web 的开发服务器,避免继续读到旧缓存。

4. Configure The Direct Mode Environment

Create apps/web/.env.local

apps/web 目录下新建 .env.local,填入下面这些值:

NEXT_PUBLIC_ESCROW_ADDRESS=0xYourEscrowAddress
NEXT_PUBLIC_DEPLOY_BLOCK=12345678
NEXT_PUBLIC_DOMAIN=111
NEXT_PUBLIC_APP_ID=222
NEXT_PUBLIC_BASE_SEPOLIA_RPC_URL=https://your-base-sepolia-rpc
INDEXER_RPC_URL=https://your-base-sepolia-rpc
INDEXER_STRATEGY=sqlite
KURIER_API_KEY=your-kurier-api-key
KURIER_API_URL=https://api-testnet.kurier.xyz/api/v1
FINALIZE_AUTH_PRIVATE_KEY=0xyour_finalize_authority_private_key
NEXT_PUBLIC_KURIER_POLL_ATTEMPTS=150
NEXT_PUBLIC_KURIER_POLL_INTERVAL_MS=4000
NEXT_PUBLIC_INDEXER_POLL_INTERVAL_MS=10000

Understand the required values

  • NEXT_PUBLIC_ESCROW_ADDRESS 是已经部署好的 ZKEscrowRelease 合约地址。
  • NEXT_PUBLIC_DEPLOY_BLOCK 是合约部署区块。索引器会从这个区块开始扫描。
  • NEXT_PUBLIC_DOMAINNEXT_PUBLIC_APP_ID 必须和合约部署时的配置一致。
  • FINALIZE_AUTH_PRIVATE_KEY 必须和合约里的 finalizeAuthority 对上,否则 /api/authorize-finalize 产出的签名不能用。
  • 第一次跑教程时,先保留 INDEXER_STRATEGY=sqlite。这样前端会直接通过 RPC 扫链,把结果缓存到本地 SQLite,不需要先部署 subgraph。

5. Start The Web App

Run the development server

apps/web 目录启动开发服务器:

cd apps/web
npm run dev

Open the escrow page

然后打开:

http://localhost:3000

应用会自动跳到 /escrow

如果页面正常加载,你应该能看到:

  • Deposit / Withdraw 两个标签页
  • Base Sepolia 网络切换器
  • Proof 状态面板

6. Run The Direct Flow

Step 1: create a deposit

  1. 连接钱包。
  2. 切到 Base Sepolia。
  3. 输入存款金额和收款地址。
  4. 点击 Deposit & Lock

交易成功后,页面会生成一条 credential。这条值后面会直接用来生成 withdrawal proof,先保存好。

Step 2: generate and submit the proof

  1. 把刚才保存的 credential 填到 Withdraw 页签。
  2. 点击 Unlock
  3. 浏览器会本地生成 proof。
  4. 服务端会把 proof 按 direct 模式提交给 Kurier。
  5. 前端开始轮询 /api/proof-status
  6. 状态进入 finalized 之后,服务端通过 /api/authorize-finalize 生成授权签名。
  7. 钱包发起链上 finalize 交易。
  8. 收款地址收到资金。

这条路径里不会调用 /api/proof-aggregation。如果你看到 aggregation tuple 相关报错,说明当前环境和这篇教程假设的 direct 流程不一致。

7. Build And Deploy The Graph Subgraph

如果你不想依赖本地 SQLite 扫链,可以切到 The Graph。

Install the subgraph dependencies

先进入 indexer/subgraph

cd indexer/subgraph
npm install

Provide the subgraph inputs

这个 subgraph 会读取 DepositedFinalized 事件,所以至少要准备好下面这些值:

  • NEXT_PUBLIC_ESCROW_ADDRESS,或者 SUBGRAPH_ESCROW_ADDRESS
  • NEXT_PUBLIC_DEPLOY_BLOCK,或者 SUBGRAPH_START_BLOCK

可选值:

  • SUBGRAPH_NETWORK=base-sepolia

如果你已经完成了第 4 步,通常不需要重复填写这两个核心值,npm run render 会直接读取现有环境变量。

Generate subgraph.yaml

indexer/subgraph/subgraph.template.yaml 不是直接部署文件,先渲染成实际的 subgraph.yaml

cd indexer/subgraph
npm run render

Generate types and build the subgraph

cd indexer/subgraph
npm run codegen
npm run build

跑完之后,就完成了本地 subgraph 生成和构建。

Deploy to The Graph Studio

先准备 Graph Studio 的部署密钥和 slug:

export GRAPH_DEPLOY_KEY=<your_studio_deploy_key>
export SUBGRAPH_SLUG=<your_studio_subgraph_slug>

然后部署:

cd indexer/subgraph
npm run auth
npm run deploy

如果你不想走 npm script,也可以直接执行:

graph auth "$GRAPH_DEPLOY_KEY"
graph deploy "$SUBGRAPH_SLUG" subgraph.yaml \
--node https://api.studio.thegraph.com/deploy/ \
--deploy-key "$GRAPH_DEPLOY_KEY"

Connect the query URL to the web app

部署成功后,在 Graph Studio 里拿到 query URL,再把它写回 apps/web/.env.local

INDEXER_STRATEGY=thegraph
THEGRAPH_SUBGRAPH_URL=https://your-subgraph-query-url

如果你想优先走 The Graph,失败时再回退到本地扫描,也可以这样配:

INDEXER_STRATEGY=hybrid

Troubleshooting

Missing NEXT_PUBLIC_ESCROW_ADDRESS

说明 apps/web/.env.local 里还没配好合约地址,或者改完环境变量后没有重启开发服务器。

Local scan missing deposits

通常是 NEXT_PUBLIC_DEPLOY_BLOCK 配得太晚,导致本地索引从错误区块开始扫描。把它改成真实部署区块,然后点击页面上的 Rescan

Proof not finalized yet

说明 Kurier 还没有把这条 direct 提交处理到 finalized。先检查 KURIER_API_KEY、RPC 和 proof 参数,再看 Kurier 返回的原始状态。

bad authorization

先确认 FINALIZE_AUTH_PRIVATE_KEY 对应的地址,和合约里配置的 finalizeAuthority 是同一个地址。