zync-core
Trust-minimized Zcash light client primitives. Verification, scanning, proving. Everything a light client needs to sync the Orchard shielded pool without trusting a server.
Trust model
zync minimizes trust in the server. Every claim is verified cryptographically:
hardcoded activation hash
│
▼
┌─────────────┐ ligerito polynomial commitment
│ header chain │────────────────────────────────────── proven NOMT roots
│ proof │ O(log n) verification, (tree_root, nullifier_root,
└─────────────┘ no headers transmitted actions_commitment)
│ │
├────────────────────────┬────────────────────────────┤
▼ ▼ ▼
┌───────────┐ ┌────────────┐ ┌─────────────┐
│ commitment│ │ nullifier │ │ actions │
│ proofs │ │ proofs │ │ commitment │
└───────────┘ └────────────┘ │ chain │
NOMT sparse NOMT sparse └─────────────┘
merkle proof: merkle proof: SHA256 merkle root
note exists spent/unspent per block, Blake2b
in global tree status running chain
│ │ │
▼ ▼ ▼
received notes balance is server didn't omit,
are real correct insert, or reorder
actions in any block
What's verified:
-
Header chain. Block headers encoded into a trace polynomial, proven with ligerito. The verifier checks the proof in O(log n) without seeing any headers. Chain continuity (prev_hash linkage), difficulty, and height progression are enforced by trace constraints.
-
State proofs. NOMT sparse merkle proofs for note commitments and nullifiers. Each proof's root is bound to the header-proven tree root. Commitment proofs verify received notes exist. Nullifier proofs verify spent/unspent status.
-
Actions integrity. Running Blake2b chain over per-block orchard action merkle roots. Checked against the header-proven value. Detects any tampering with the compact block action data the server sends during sync.
-
Cross-verification. BFT majority (>2/3) consensus against independent lightwalletd nodes. Tip and activation block hashes compared across providers. Prevents single-server eclipse attacks.
-
Trial decryption. After decrypting a note, the commitment is recomputed from the decrypted fields and compared to the server-provided cmx. A malicious server cannot forge ciphertexts that decrypt to notes with arbitrary values.
What's NOT verified (scope boundaries):
- Proof-of-work. Headers are committed, not validated for PoW. The header proof proves the server's chain is internally consistent and matches the activation anchor. It doesn't re-validate every block.
- Sapling pool. Only Orchard is supported.
Modules
| Module | Purpose |
|---|---|
verifier |
Ligerito header chain proof verification (epoch + tip, parallel) |
nomt |
NOMT sparse merkle proof verification for commitments and nullifiers |
actions |
Per-block actions merkle root and running commitment chain |
scanner |
Orchard trial decryption with cmx verification (native + WASM parallel) |
sync |
Sync verification primitives: header proof validation, commitment/nullifier batch verification, cross-verify consensus, memo ciphertext extraction |
prover |
Ligerito proof generation from header chain traces |
trace |
Header chain trace encoding (headers → polynomial) |
client |
gRPC clients for zidecar and lightwalletd (feature-gated) |
Usage
Sync verification (light client)
use ;
// 1. verify header proof → extract proven NOMT roots
let proven = verify_header_proof?;
// 2. scan compact blocks
let scanner = from_fvk;
let notes = scanner.scan;
// 3. verify received notes exist (NOMT commitment proofs)
verify_commitment_proofs?;
// 4. verify nullifier status (NOMT nullifier proofs)
let spent = verify_nullifier_proofs?;
// 5. verify block actions weren't tampered with
verify_actions_commitment?;
Proof generation (server)
use ;
// encode headers into trace polynomial
let mut trace = encode_trace?;
// generate proof (auto-selects config based on trace size)
let proof = prove_auto?;
let bytes = proof.serialize_full?;
Note scanning
use ;
// from full viewing key (external scope, received notes)
let scanner = from_fvk;
// scan sequentially (WASM) or in parallel (native with rayon)
let found = scanner.scan;
// batch scanner tracks nullifiers across blocks
let mut batch = from_fvk;
for block in blocks
println!;
Cross-verification
use ;
// compare block hashes (handles LE/BE byte order)
assert!;
// BFT tally
let tally = CrossVerifyTally ;
assert!; // 4/5 > 2/3
Memo extraction
use extract_enc_ciphertext;
// extract 580-byte encrypted ciphertext from raw V5 transaction
if let Some = extract_enc_ciphertext
Features
| Feature | Default | Description |
|---|---|---|
client |
yes | gRPC clients for zidecar and lightwalletd |
parallel |
yes | Rayon-based parallel note scanning |
wasm |
no | WASM bindings (wasm-bindgen, console_error_panic_hook) |
wasm-parallel |
no | WASM + parallel (requires SharedArrayBuffer) |
WASM build
# single-threaded
# multi-threaded (requires COOP/COEP headers for SharedArrayBuffer)
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' \
Cross-verification endpoints
Default mainnet endpoints from two independent operators, geographically distributed:
| Endpoint | Provider | Region |
|---|---|---|
na.zec.rocks |
zec.rocks | North America |
eu.zec.rocks |
zec.rocks | Europe |
ap.zec.rocks |
zec.rocks | Asia Pacific |
us.zec.stardust.rest |
Chainsafe | US |
eu.zec.stardust.rest |
Chainsafe | Europe |
jp.zec.stardust.rest |
Chainsafe | Japan |
Available as zync_core::client::CROSSVERIFY_MAINNET.
Wire formats
Header proof
[epoch_full_size: u32 LE]
[epoch_full_proof]
[tip_full_proof]
Each full proof:
[public_outputs_len: u32 LE]
[public_outputs: bincode ProofPublicOutputs]
[log_size: u8]
[ligerito_proof: bincode FinalizedLigeritoProof]
Trace layout
32 fields per header (BinaryElem32 = 4 bytes each):
| Fields | Content |
|---|---|
| 0 | height |
| 1-8 | block_hash (32 bytes) |
| 9-16 | prev_hash (32 bytes) |
| 17 | nBits |
| 18 | cumulative_difficulty |
| 19 | running commitment |
| 20-23 | sapling_root (epoch boundaries) |
| 24-27 | orchard_root (epoch boundaries) |
| 28-29 | nullifier_root (epoch boundaries) |
| 30 | state_commitment |
| 31 | reserved |
Sentinel row (24 fields after last header):
| Fields | Content |
|---|---|
| 0-7 | tip_tree_root |
| 8-15 | tip_nullifier_root |
| 16-23 | final_actions_commitment |
License
MIT