chainvue/verus-sapling

GitHub: chainvue/verus-sapling

Verus Sapling 屏蔽交易的离线签名库,通过 WASM 零知识证明器在无需全节点的前提下完成隐私交易的构建与签名。

Stars: 1 | Forks: 0

# @chainvue/verus-sapling [![npm](https://img.shields.io/npm/v/%40chainvue%2Fverus-sapling)](https://www.npmjs.com/package/@chainvue/verus-sapling) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/chainvue/verus-sapling/actions/workflows/ci.yml) [![license](https://img.shields.io/npm/l/%40chainvue%2Fverus-sapling)](./LICENSE) [![node](https://img.shields.io/node/v/%40chainvue%2Fverus-sapling)](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。
已在测试网上通过 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`)。
## 示例 - [`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)。
标签:AI工具, Sapling, Verus, WASM, 加密货币, 区块链, 可视化界面, 离线签名, 自动化攻击, 零知识证明