chainvue/verus-sapling
GitHub: chainvue/verus-sapling
Verus Sapling 屏蔽交易的离线签名库,通过 WASM 零知识证明器在无需全节点的前提下完成隐私交易的构建与签名。
Stars: 1 | Forks: 0
# @chainvue/verus-sapling
[](https://www.npmjs.com/package/@chainvue/verus-sapling)
[](https://github.com/chainvue/verus-sapling/actions/workflows/ci.yml)
[](./LICENSE)
[](https://nodejs.org)
[`@chainvue/verus-sdk`](https://www.npmjs.com/package/@chainvue/verus-sdk) 的配套包
(透明交易)。SDK 保持了纯 TypeScript 且体积微小,而本包
添加了屏蔽签名真正所需的一样东西 —— Sapling zk-prover ——
编译为 WASM 并保持可选。
- 🔐 **所有三种屏蔽流程:** shield (`t→z`)、private send (`z→z`)、deshield (`z→t`),每个都带有 ZIP-302 memo。
- 🧾 **WASM 中的真实 zk 证明**(`sapling-crypto` → Groth16/BLS12-381)、RedJubjub spend-auth 与 binding signatures 以及 note 加密。
- 🛰️ **客户端 note 检测** —— 试解密 compact blocks;无需 `z_listunspent`,无需 wallet daemon。
- 🌐 **浏览器就绪** —— 内置 gRPC-web transport 和 Web Worker prover;一个可运行的 MV3 扩展可以从 Chrome 广播私密的 `z→z` 交易。
- 💰 **端到端 `bigint` 聪** —— 唯一一次受控的 float64 交叉,绝不会产生隐蔽的 `number`。
- ✅ **经 Daemon 验证** —— 字节布局的检验标准是能否被真实的 Verus daemon 接受,而非自洽测试。
## 目录
- [安装](#install) · [快速开始](#quick-start) · [工作原理](#how-it-works)
- [证明参数](#proving-parameters) · [后端](#backend-the-one-unavoidable-dependency)
- [安全性](#security) · [示例](#examples) · [项目结构](#project-layout) · [贡献](#contributing)
## 安装
```
npm install @chainvue/verus-sapling
```
编译后的 WASM prover 随包发布在 (`crate/pkg/`)。在运行时,您只需提供
两个 Sapling [证明参数](#proving-parameters)(约 50 MB,仅获取一次)。
## 快速开始
加载 prover,然后使用由 lightwalletd 驱动的编排逻辑。链上数据通过
`LightwalletdTransport` 传递;具体的 Node gRPC 客户端位于
`./lightwalletd` 子路径下(包根目录**不**引入任何 gRPC)。
```
import { detectNotes, buildShieldedSpend, initSapling } from '@chainvue/verus-sapling';
import { LightwalletdClient } from '@chainvue/verus-sapling/lightwalletd';
await initSapling(wasmBytes); // load the wasm module once
const client = new LightwalletdClient('lightwalletd:9077');
// 1. Find the wallet's own notes — no z_listunspent, no full node.
const notes = await detectNotes(client, detectProver, {
key: { dfvkHex }, // a viewing key is enough to scan (recommended)
fromHeight: walletBirthday, // toHeight defaults to the chain tip
});
// 2. Spend one detected note (z→z / z→t) with a memo.
const { hex } = await buildShieldedSpend(client, spendProver, {
note: { txid: notes[0].txid, outputIndex: notes[0].outputIndex, extskHex },
shieldedOutputs: [{ address, valueSats, memo }], // valueSats is a bigint
});
await client.sendTransaction(hex);
```
`detectProver` / `spendProver` 是轻量级的回调函数,您可以将它们连接到 WASM 构建器
(主线程上的 `detectNotes`;用于耗时约 20 秒证明的 Web Worker 中的 `spendShielded`
—— 参见 [`examples/extension`](examples/extension))。Memo 为 `string`,
≤ 512 字节。对于 `t→z` 屏蔽,请直接调用 `shieldT2z` 构建器。
## 工作原理
Rust crate (`crate/`) 被编译为 WASM,并负责处理必须
精确到字节的环节:**ZIP-243 sighash**、**v4 交易序列化器**、Sapling
**证明**、签名以及 note 加密。TypeScript 层 (`src/`)
保持轻量:负责输入验证、`bigint` 资金不变量、lightwalletd
transport 以及地址/密钥的封装转换。
### Verus = 原生 Zcash Sapling
已通过 Verus 源码和实时主网节点确认:Verus shielded 即
**原生 Zcash Sapling** —— 未经修改的 `zcash/librustzcash` 电路、字节完全一致的
MPC 参数、原生共识 branch id `0x76b809bb`、version group id
`0x892f2085`、tx v4。共识在主网和
测试网上**均**冻结于 Sapling 阶段(Canopy —— 将会对 ZIP-212 进行限制 —— 并不在 Verus 的升级集合中),因此
ZIP-212 enforcement 为 `Off`。整条路径中唯一属于 Verus 特定数据的值是
sighash 中注入的 branch id。甚至连 lightwalletd 的通信协议也是
原生的 —— `VerusCoin/lightwalletd` 的 protos 与 Zcash 的完全一致。
### 助记词:一句话,两种不相关的密钥推导方案
Verus Mobile 钱包从单个恢复
短语中推导出它的**两种**密钥 —— 通过两种毫不相干的推导方式:
| | transparent (`R…`) | shielded (`zs…`) |
|---|---|---|
| 推导方式 | `sha256(utf8(phrase))` + Agama/Iguana clamp | BIP-39 → ZIP-32 |
| BIP-39 | 忽略 —— 短语作为纯文本进行哈希 | 需要,真实的 PBKDF2 |
| path | 无 —— 每个短语对应一个密钥,无 HD | `m/32'/coin'/account'` |
| 网络 | 主网 ≡ 测试网 | 在**两者**上均为 coin type 133(见下文) |
| 存在于 | [`@chainvue/verus-sdk`](https://www.npmjs.com/package/@chainvue/verus-sdk) `keys.seedToWif` | `deriveSaplingAccount`(此处) |
```
import { deriveSaplingAccount, initSapling } from '@chainvue/verus-sapling';
await initSapling(wasmBytes);
const account = await deriveSaplingAccount({ mnemonic: phrase }); // coinType 133, account 0
account.address; // zs… — compare against your wallet before relying on it
account.extskHex; // spending key, the form every builder here takes
account.dfvkHex; // viewing key — enough to scan, cannot spend
```
值得牢记的结论:要让 z-address 真正存在,该短语必须是**有效的 BIP-39 助记词**
(Verus Mobile 通过 `validateMnemonic()` + ≥12 个单词来控制其屏蔽账户,
而 transparent 端则接受任何字符串),因此
相同的单词会在两端生成不相关的密钥。
**两个网络上的 coin type 133 —— 包括 VRSCTEST。** Verus Mobile 的
`parseDlightSeed` 在调用 `Tools.deriveSaplingSpendingKey(seed)` 时不带网络
参数,因此 Kotlin bridge 的 `networks.getOrDefault(network,
ZcashNetwork.Mainnet)` 会回退到主网,使得测试网钱包持有一个
主网路径的密钥。已于 2026-07-28 对照实时 VRSCTEST 钱包验证:在五个
候选路径中,只有 `m/32'/133'/0'` 复现了应用显示的地址。
此处的默认设置与之匹配;`COIN_TYPE_VRSCTEST` (1) 是为原生 zcash 工具准备的,推导出的密钥不属于任何 Verus Mobile 钱包。无论如何,两个网络上的地址 HRP 均为 `zs`。
### 后端:唯一不可避免的依赖
- **`t→z` (shielding)** **不**需要 commitment-tree witness —— 仅需要
您已经获取的 transparent UTXO。这是真正的“在完全没有额外后端的情况下进行私密签名”的案例。
- **`z→z` / `z→t`** 花费 shielded notes,这需要每个 note 的 Merkle **witness
+ anchor** 以及 **note 检测**。这些数据来自于链扫描服务 ——
一个原生的 **Verus lightwalletd**(标准 gRPC `GetBlockRange` / `GetTreeState`)。
签名主机依然无需运行全节点,但必须要有此服务。
浏览器还需要在其前方部署一个 gRPC-web 代理。
## 证明参数
prover 需要两个参数文件 —— **标准的 Zcash Sapling MPC
参数**,在 Verus 上字节完全一致。它们**不**被打包在内(约 50 MB 的体积会使每次安装变得臃肿);只需获取一次,并将它们的字节流传给 `initSapling` 和
prover 即可。
| 文件 | 大小 | SHA-256 |
| --- | --- | --- |
| `sapling-spend.params` | ~47 MB | `8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13` |
| `sapling-output.params` | ~3.5 MB | `2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4` |
**务必验证 SHA-256。** 您可以从任何 Zcash 全节点
(`zcutil/fetch-params.sh`)、本地的 Verus 安装,或您自己的主机上获取它们 —— 然后进行缓存
(浏览器中使用 IndexedDB / Cache API,Node 中使用文件系统)。非标准的
参数会导致生成的证明被 daemon 拒绝。
## 安全性
本库用于对资金进行签名,且签名主机上存在 spending key。
[`SECURITY.md`](SECURITY.md) 完整记录了信任模型 —— 哪些是
有保证的(密钥绝不通过网络传输、绝不记录在日志中,且绝不包含在
已签名的交易中),哪些是不受保证的(WASM 内存未被归零化;主机/供应链
被破坏)。**请通过 GitHub 安全公告私下报告漏洞**,
切勿使用公开的 issue。
## 示例
- [`examples/extension`](examples/extension) — 一个可运行的 **MV3 浏览器
扩展**:检测 notes、读取收件箱,并从
Chrome 广播私密的 `z→z` 交易,在 Web Worker 中进行证明。
- [`examples/messenger`](examples/messenger) — 一个端到端的 **shielded-memo
通讯应用**:在零价值 note 中携带带格式的消息。
## 项目结构
```
src/ TypeScript: validation, money, wallet orchestration, transport
browser/ gRPC-web transport + Web Worker prover (no external gRPC dep)
crate/ Rust: ZIP-243 sighash, v4 serializer, Sapling proving
pkg/ committed WASM build (so a fresh clone works without Rust)
proto/ stock Zcash lightwalletd gRPC definitions
examples/ runnable extension + messenger demos
test/ vitest suite (money, hex, protobuf, gRPC-web, wallet, zaddr, …)
```
文档:[安全](SECURITY.md) · [贡献](CONTRIBUTING.md) ·
[发布](RELEASING.md) · [声明](NOTICE)
## 贡献
欢迎贡献 —— 参见 [CONTRIBUTING.md](CONTRIBUTING.md)。提交标准为
`npm run build` → `npm run typecheck` → `npm test`,对于 crate 还需通过 `cargo test`。提交信息请遵循 [Conventional Commits](https://www.conventionalcommits.org/)
(它们用于驱动自动化发布)。
## 许可证
Apache-2.0。参见 [LICENSE](LICENSE)。关于 WASM prover
内置的 Rust crates、vendored lightwalletd protos 以及证明参数的第三方出处,
详见 [NOTICE](NOTICE)。
已在测试网上通过 Daemon 验证 — txids
以下三种流程的交易均由此代码构建,并被 Verus 测试网 daemon 接受: - **t→z** `1edf8aa6…6623` (原生) 和 `d142edf8…a0ef` (在 WASM 中构建) - **z→z** `53ea99fc…89feb` — 完全私密,接收方收到 9.9999 - **z→t** `86951c8d…bd8a` — 透明接收方收到 0.05 - **z→z,完全来源于 lightwalletd** `07e3b38e…f996` — 每一个字节的链上数据均 来自 lightwalletd,在 WASM 中签名,无需全节点 Note 检测已与共识进行了交叉核对:它为 某个 note 预测的 nullifier,与随后消费该 note 时产生的链上 nullifier 完全一致(逐字节对应)。 ZIP-243 序列化器/sighash 已针对 由 daemon 生成的黄金测试向量进行了回归测试(`cargo test`)。标签:AI工具, Sapling, Verus, WASM, 加密货币, 区块链, 可视化界面, 离线签名, 自动化攻击, 零知识证明