testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseScope
This skill covers the two error layers in Compact (static vs dynamic), how to read compiler messages (especially undeclared disclosure traces), the fast development loop with , version management across 6 components, and the most common debugging patterns. Official testing docs are "coming soon" — this is battle-tested from real development.
--skip-zk适用范围
本技能涵盖Compact中的两层错误(静态与动态)、如何解读编译器消息(尤其是未声明披露追踪信息)、使用的快速开发循环、6个组件的版本管理,以及最常见的调试模式。官方测试文档“即将推出”——本内容来自实际开发中的实战经验。
--skip-zk1) Two Error Layers
1) 两层错误类型
Compact has two error types, not one:
Static errors — Caught by the compiler before generating any output. You see these in your terminal while developing. Static errors are your friend — the compiler tells you exactly what's wrong and where.
Dynamic errors — Caught at runtime by the generated JavaScript. You see these when a circuit executes. These are harder to debug because they happen inside generated code you didn't write.
Compact包含两种错误类型,而非一种:
静态错误 —— 在生成任何输出前由编译器捕获。开发时会在终端中看到这些错误。静态错误是你的帮手——编译器会准确告诉你问题所在及位置。
动态错误 —— 由生成的JavaScript在运行时捕获。电路执行时会看到这些错误。这类错误更难调试,因为它们发生在你未编写的生成代码内部。
Static Errors (Compile Time)
静态错误(编译阶段)
| Error type | What it is | When caught |
|---|---|---|
| Syntax | Malformed code | Parser |
| Type mismatch | Wrong type used | Type checker |
| Undeclared disclosure | | Witness protection |
| Undefined reference | Unknown identifier | Name resolver |
| Generic not specialized | Generic entity used at top level | Scope checker |
| Recursive struct | Struct refers to itself | Declaration checker |
| Recursive circuit | Circuit calls itself | Declaration checker |
| | Statement checker |
| Sealed ledger write | Write to sealed field in circuit | Declaration checker |
If the compiler produces no output, there's at least one static error. Check the messages.
| 错误类型 | 说明 | 捕获时机 |
|---|---|---|
| 语法错误 | 代码格式错误 | 解析阶段 |
| 类型不匹配 | 使用了错误的类型 | 类型检查阶段 |
| 未声明披露 | 缺少 | 见证保护阶段 |
| 未定义引用 | 未知标识符 | 名称解析阶段 |
| 泛型未特化 | 顶层使用了泛型实体 | 作用域检查阶段 |
| 递归结构体 | 结构体引用自身 | 声明检查阶段 |
| 递归电路 | 电路调用自身 | 声明检查阶段 |
| 循环内部包含 | 语句检查阶段 |
| 密封账本写入 | 向电路中的密封字段写入数据 | 声明检查阶段 |
如果编译器未生成任何输出,则至少存在一个静态错误。请检查提示消息。
Dynamic Errors (Runtime)
动态错误(运行阶段)
| Error type | What it is | Example |
|---|---|---|
| Type mismatch | Wrong argument type/number | Calling with wrong args |
| Overflow | Cast value too large for target | |
| Underflow | Counter decremented below zero | |
| Uninitialized nested value | Nested ledger state not initialized | |
| Merkle tree full | Insert into full tree | |
Dynamic errors are harder to debug. Read the error message for the line number in your source file.
| 错误类型 | 说明 | 示例 |
|---|---|---|
| 类型不匹配 | 参数类型/数量错误 | 使用错误参数调用函数 |
| 溢出 | 转换值超出目标类型范围 | |
| 下溢 | 计数器递减至零以下 | 计数器为0时执行 |
| 未初始化嵌套值 | 嵌套账本状态未初始化 | 插入前执行 |
| 默克尔树已满 | 向已满的树中插入数据 | 树已满时执行 |
动态错误更难调试。请查看错误消息中指向的源文件行号。
2) Reading Compiler Error Messages
2) 解读编译器错误消息
Type Error
类型错误
/path/contract.compact line 12 char 5:
type error: expected Uint<64>, got FieldRead: line:character, expected type, got type. The caret () points to the problem token.
^/path/contract.compact line 12 char 5:
type error: expected Uint<64>, got Field解读方式:行号:字符位置,预期类型,实际类型。脱字符()指向问题标记。
^Undeclared Disclosure
未声明披露
Exception: /path/contract.compact line 6 char 11:
potential witness-value disclosure must be declared but is not:
witness value potentially disclosed:
the return value of witness getBalance at line 2 char 1
nature of the disclosure:
ledger operation might disclose the witness value
via this path through the program:
the right-hand side of = at line 6 char 11Read this bottom to top. The path traces how witness data traveled:
- Origin: at line 2
getBalance() - Path: flows through the right-hand side of the assignment
- Destination: the ledger operation at line 6
Fix: Add somewhere along that path, as close to the disclosure point as possible.
disclose()Exception: /path/contract.compact line 6 char 11:
potential witness-value disclosure must be declared but is not:
witness value potentially disclosed:
the return value of witness getBalance at line 2 char 1
nature of the disclosure:
ledger operation might disclose the witness value
via this path through the program:
the right-hand side of = at line 6 char 11从下往上阅读。路径追踪了见证数据的传播流程:
- 源头: 第2行的
getBalance() - 路径: 流经赋值语句的右侧
- 终点: 第6行的账本操作
修复方法: 在该路径上添加,尽可能靠近披露点。
disclose()Missing disclose()
on Return
disclose()返回值缺少disclose()
disclose()Exception: line 5 char 3:
potential witness-value disclosure must be declared but is not:
witness value potentially disclosed:
the return value of witness getBalance at line 2 char 1
nature of the disclosure:
the value returned from exported circuit check might disclose
the result of a comparison involving the witness valueEven a comparison result counts as disclosure. Wrap the witness call or the return value with .
Booleandisclose()Exception: line 5 char 3:
potential witness-value disclosure must be declared but is not:
witness value potentially disclosed:
the return value of witness getBalance at line 2 char 1
nature of the disclosure:
the value returned from exported circuit check might disclose
the result of a comparison involving the witness value即使是比较结果也属于披露范畴。将见证调用或返回值用包裹。
Booleandisclose()Version Mismatch
版本不匹配
Error: runtime version mismatch: expected 0.15.0, got 0.14.2The compiled contract expects a different runtime version. See version management below.
Error: runtime version mismatch: expected 0.15.0, got 0.14.2编译后的合约需要不同的运行时版本。请查看下方的版本管理部分。
3) The --skip-zk
Development Loop
--skip-zk3) 使用--skip-zk
的开发循环
--skip-zkGenerating proving keys is slow. During iterative development, skip it:
bash
compact compile --skip-zk contracts/contract.compact contracts/managed/contractThis produces and , enough to test logic. Re-enable for final builds.
contract/index.jscompiler/contract-info.json生成证明密钥的过程很慢。在迭代开发期间,可以跳过此步骤:
bash
compact compile --skip-zk contracts/contract.compact contracts/managed/contract此命令会生成和,足以测试逻辑。最终构建时需重新启用该步骤。
contract/index.jscompiler/contract-info.json4) Version Management
4) 版本管理
Midnight has 6 components that must stay in sync. When they're not, you get opaque runtime errors about version mismatches.
| Component | What it is | How to check |
|---|---|---|
| CLI tool | | |
| Compiler | | |
| Runtime | | |
| Ledger | | |
| JS libraries | | |
| Proof server | Docker image | image tag |
Midnight有6个组件必须保持版本同步。如果版本不一致,会出现关于版本不匹配的模糊运行时错误。
| 组件 | 说明 | 检查方式 |
|---|---|---|
| CLI工具 | | |
| 编译器 | | |
| 运行时 | | |
| 账本 | | |
| JS库 | | |
| 证明服务器 | Docker镜像 | 镜像标签 |
Check Current Versions
检查当前版本
bash
compact --version
compact compile --version
npm list @midnight-ntwrk/compact-runtime
npm list @midnight-ntwrk/ledger-v8bash
compact --version
compact compile --version
npm list @midnight-ntwrk/compact-runtime
npm list @midnight-ntwrk/ledger-v8Consult the Compatibility Matrix
参考兼容性矩阵
The official release compatibility matrix is the source of truth. Never mix versions without checking it.
官方发布兼容性矩阵是权威参考。未经检查切勿混合使用不同版本。
Lock Exact Versions in package.json
package.json在package.json
中锁定精确版本
package.jsonjson
{
"dependencies": {
"@midnight-ntwrk/compact-runtime": "0.15.0",
"@midnight-ntwrk/ledger-v8": "8.0.3"
}
}Do not use or — these allow automatic updates that silently break compatibility.
^~json
{
"dependencies": {
"@midnight-ntwrk/compact-runtime": "0.15.0",
"@midnight-ntwrk/ledger-v8": "8.0.3"
}
}请勿使用或——这些符号会允许自动更新,从而悄无声息地破坏兼容性。
^~Use npm ci
for Reproducible Installs
npm ci使用npm ci
进行可复现安装
npm cibash
rm -rf node_modules
npm cinpm cipackage-lock.jsonnpm installbash
rm -rf node_modules
npm cinpm cipackage-lock.jsonnpm installAfter Updating Any Component
更新任意组件后
- Update all related components together
- Recompile contracts
- Restart the proof server with the new Docker image
- Run your test suite
- 同步更新所有相关组件
- 重新编译合约
- 使用新的Docker镜像重启证明服务器
- 运行测试套件
5) Common Mistakes and Fixes
5) 常见错误及修复方法
Forgot disclose()
on Ledger Write
disclose()账本写入时忘记添加disclose()
disclose()compact
// wrong — compiler error
balance = getBalance();
// correct
balance = disclose(getBalance());compact
// 错误 —— 编译器报错
balance = getBalance();
// 正确
balance = disclose(getBalance());Forgot disclose()
on Return Value
disclose()返回值忘记添加disclose()
disclose()compact
// wrong — compiler error (comparison of witness data)
export circuit check(n: Uint<64>): Boolean {
return getSecret() > n;
}
// correct — declare the disclosure
export circuit check(n: Uint<64>): Boolean {
return disclose(getSecret()) > n;
}compact
// 错误 —— 编译器报错(见证数据比较)
export circuit check(n: Uint<64>): Boolean {
return getSecret() > n;
}
// 正确 —— 声明披露
export circuit check(n: Uint<64>): Boolean {
return disclose(getSecret()) > n;
}return
Inside for
Loop
returnforfor
循环内使用return
forreturncompact
// wrong — static error
circuit findFirst(v: Vector<4, Field>, target: Field): Boolean {
for (const x of v) {
if (x == target) return true;
}
return false;
}
// correct — use fold
circuit findFirst(v: Vector<4, Field>, target: Field): Boolean {
return fold((found, x) => found || x == target, false, v);
}compact
// 错误 —— 静态错误
circuit findFirst(v: Vector<4, Field>, target: Field): Boolean {
for (const x of v) {
if (x == target) return true;
}
return false;
}
// 正确 —— 使用fold
circuit findFirst(v: Vector<4, Field>, target: Field): Boolean {
return fold((found, x) => found || x == target, false, v);
}Recursive Circuit
递归电路
compact
// wrong — static error: recursion not allowed
circuit factorial(n: Uint<64>): Uint<64> {
return n == 0 ? 1 : n * factorial(n - 1);
}Rewrite using or explicit unrolling. Compact requires finite circuits.
foldcompact
// 错误 —— 静态错误:不允许递归
circuit factorial(n: Uint<64>): Uint<64> {
return n == 0 ? 1 : n * factorial(n - 1);
}使用或显式展开重写。Compact要求电路必须是有限的。
foldNarrowing Cast Overflows at Runtime
运行时窄化转换溢出
compact
const x: Uint<64> = 1000;
const y = x as Uint<8>; // dynamic error: 1000 doesn't fitAlways verify the value fits before casting. Use or bounded types.
assertcompact
const x: Uint<64> = 1000;
const y = x as Uint<8>; // 动态错误:1000超出范围转换前务必验证值是否符合范围。使用或有界类型。
assertUninitialized Nested Ledger State
未初始化的嵌套账本状态
compact
ledger fld: Map<Boolean, Map<Field, Counter>>;
// wrong — dynamic error (inner map not initialized)
export circuit increment(b: Boolean, n: Field): [] {
fld.lookup(b).lookup(n) += 1;
}
// correct — initialize first
export circuit init(b: Boolean): [] {
fld.insert(disclose(b), default<Map<Field, Counter>>);
}compact
ledger fld: Map<Boolean, Map<Field, Counter>>;
// 错误 —— 动态错误(内部映射未初始化)
export circuit increment(b: Boolean, n: Field): [] {
fld.lookup(b).lookup(n) += 1;
}
// 正确 —— 先初始化
export circuit init(b: Boolean): [] {
fld.insert(disclose(b), default<Map<Field, Counter>>);
}transientHash
Result Used Without disclose()
transientHashdisclose()使用transientHash
结果时未添加disclose()
transientHashdisclose()compact
// wrong — compiler error (witness-tainted)
ledger h: Field;
export circuit store(v: Field): [] {
h = transientHash<Field>(v);
}
// correct — declare disclosure
h = disclose(transientHash<Field>(v));
// or use transientCommit (nonce provides hiding, no disclose needed)
h = transientCommit<Field>(v, nonce);compact
// 错误 —— 编译器报错(见证数据污染)
ledger h: Field;
export circuit store(v: Field): [] {
h = transientHash<Field>(v);
}
// 正确 —— 声明披露
h = disclose(transientHash<Field>(v));
// 或使用transientCommit(随机数提供隐藏,无需disclose)
h = transientCommit<Field>(v, nonce);6) Common Environment Issues
6) 常见环境问题
| Error | Cause | Fix |
|---|---|---|
| Binary not on PATH | |
| Node.js tried to import directory | Open new terminal, clear caches |
| Docker connection errors | Docker Desktop not running | Start Docker Desktop |
| Port 6300 in use | Another container on same port | |
| Version mismatch at runtime | Outdated runtime package | Check compatibility matrix, update |
| 错误 | 原因 | 修复方法 |
|---|---|---|
| 二进制文件不在PATH中 | |
| Node.js尝试导入目录 | 打开新终端,清除缓存 |
| Docker连接错误 | Docker Desktop未运行 | 启动Docker Desktop |
| 端口6300被占用 | 其他容器使用同一端口 | 使用 |
| 运行时版本不匹配 | 运行时包过时 | 检查兼容性矩阵,更新版本 |
7) Version Check Script
7) 版本检查脚本
Run this before filing a bug report:
bash
#!/bin/bash
echo "=== Midnight Version Check ==="
echo "CLI:"; compact --version || echo "not found"
echo "Compiler:"; compact compile --version || echo "not found"
echo "Runtime:"
npm list --depth=0 | grep @midnight-ntwrk || echo "none found"
echo "Node.js:"; node --version
echo "Compare with: docs.midnight.network/relnotes/support-matrix"提交bug报告前运行此脚本:
bash
#!/bin/bash
echo "=== Midnight Version Check ==="
echo "CLI:"; compact --version || echo "not found"
echo "Compiler:"; compact compile --version || echo "not found"
echo "Runtime:"
npm list --depth=0 | grep @midnight-ntwrk || echo "none found"
echo "Node.js:"; node --version
echo "Compare with: docs.midnight.network/relnotes/support-matrix"8) Getting Help
8) 获取帮助
If stuck after checking these notes:
- Discord — post your error message and version details
#dev-chat - FAQ — docs.midnight.network/troubleshoot/faq
- Forum — forum.midnight.network
Always include:
- Output of the version check script
- The full error message
- The file (or relevant excerpt)
.compact - What you expected vs what happened
如果查看这些说明后仍无法解决问题:
- Discord —— 发布你的错误消息和版本详情
#dev-chat - FAQ —— docs.midnight.network/troubleshoot/faq
- 论坛 —— forum.midnight.network
请务必提供:
- 版本检查脚本的输出
- 完整的错误消息
- 文件(或相关片段)
.compact - 预期结果与实际结果的差异
9) Quick Recap
9) 快速回顾
- Static errors — compiler catches them before output. Check the messages.
- Dynamic errors — happen at runtime inside generated code. Read the line numbers.
- Undeclared disclosure trace — read bottom to top, it traces the path from origin to disclosure.
- Use during development. Enable for final builds.
--skip-zk - Lock exact versions in . Use
package.json.npm ci - Check the compatibility matrix before updating any component.
- After any update: recompile, restart proof server, run tests.
- 静态错误 —— 编译器在生成输出前捕获。请检查提示消息。
- 动态错误 —— 发生在运行时的生成代码内部。请查看行号。
- 未声明披露追踪 —— 从下往上阅读,它追踪了从源头到披露点的路径。
- 开发时使用。最终构建时启用该步骤。
--skip-zk - 在中锁定精确版本。使用
package.json。npm ci - 更新任何组件前检查兼容性矩阵。
- 更新后: 重新编译、重启证明服务器、运行测试。