|
| 1 | +# Zcash Integration Guide |
| 2 | + |
| 3 | +Zcash is the first privacy-preserving chain in OWS. It supports shielded transactions via the PCZT (Partially Created Zcash Transaction) format, giving agents and wallets a way to hold and spend ZEC without exposing transaction metadata. |
| 4 | + |
| 5 | +## How Zcash differs from other OWS chains |
| 6 | + |
| 7 | +Every other chain in OWS follows the same pattern: derive a key via BIP-44, hash and sign a transaction, broadcast via JSON-RPC. Zcash breaks this pattern in two ways: |
| 8 | + |
| 9 | +1. **Key derivation uses ZIP-32, not BIP-44.** Zcash unified addresses contain Orchard and Sapling receivers, which require the raw BIP-39 seed passed through the ZIP-32 derivation scheme — not a BIP-32 derived private key. |
| 10 | + |
| 11 | +2. **Signing operates on PCZTs, not raw transactions.** Zcash shielded transactions require zero-knowledge proofs and per-pool spend authorization signatures (RedPallas for Orchard, Jubjub for Sapling). OWS handles only the signing step. The ZK proof generation happens externally. |
| 12 | + |
| 13 | +## Wallet creation |
| 14 | + |
| 15 | +```bash |
| 16 | +ows wallet create --name my-wallet |
| 17 | +``` |
| 18 | + |
| 19 | +Zcash appears alongside all other chains. The derived address is a unified address (`u1...`) with Orchard and Sapling receivers — shielded by default. |
| 20 | + |
| 21 | +``` |
| 22 | +eip155:1 → 0xab16a96D359eC26a11e2C2b3d8f8B8942d5Bfcdb |
| 23 | +solana:... → 7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q |
| 24 | +zcash:mainnet → u1qxnwj8h... |
| 25 | +``` |
| 26 | + |
| 27 | +Under the hood, OWS detects that the Zcash signer requires the raw seed (`needs_raw_seed() → true`) and passes the full 64-byte BIP-39 seed through ZIP-32 derivation instead of BIP-44: |
| 28 | + |
| 29 | +``` |
| 30 | +BIP-39 Mnemonic |
| 31 | + │ |
| 32 | + ▼ |
| 33 | +Raw Seed (64 bytes) |
| 34 | + │ |
| 35 | + ├── BIP-44 path → EVM, Solana, Bitcoin, ... |
| 36 | + │ |
| 37 | + └── ZIP-32 → Unified Spending Key |
| 38 | + │ |
| 39 | + ├── Orchard receiver |
| 40 | + ├── Sapling receiver |
| 41 | + └── Unified Address (u1...) |
| 42 | +``` |
| 43 | + |
| 44 | +## Address derivation |
| 45 | + |
| 46 | +To derive a Zcash address from an existing mnemonic: |
| 47 | + |
| 48 | +```bash |
| 49 | +echo "your twelve word mnemonic ..." | ows derive --chain zcash |
| 50 | +``` |
| 51 | + |
| 52 | +This returns a unified address. To derive all chains at once: |
| 53 | + |
| 54 | +```bash |
| 55 | +echo "your twelve word mnemonic ..." | ows derive |
| 56 | +``` |
| 57 | + |
| 58 | +## Signing a PCZT |
| 59 | + |
| 60 | +### The PCZT pipeline |
| 61 | + |
| 62 | +Zcash transactions are built collaboratively using the PCZT format. The roles are: |
| 63 | + |
| 64 | +``` |
| 65 | +Creator → Builds the transaction structure (inputs, outputs, amounts) |
| 66 | + │ |
| 67 | +Prover → Generates zero-knowledge proofs (Sapling, Orchard) |
| 68 | + │ |
| 69 | +Signer (OWS) → Applies spend authorization signatures |
| 70 | + │ |
| 71 | +Finalizer → Extracts the signed transaction |
| 72 | + │ |
| 73 | +Broadcaster → Sends to the network via lightwalletd |
| 74 | +``` |
| 75 | + |
| 76 | +OWS fills the **Signer** role. It receives a PCZT that already has ZK proofs applied, decrypts the spending key from the vault, signs the relevant inputs, and returns the signed PCZT. |
| 77 | + |
| 78 | +### CLI usage |
| 79 | + |
| 80 | +```bash |
| 81 | +# Sign a PCZT (hex-encoded) |
| 82 | +ows sign tx --chain zcash --wallet my-wallet --tx <pczt_hex> |
| 83 | +``` |
| 84 | + |
| 85 | +The `<pczt_hex>` must be a serialized PCZT that has already been through the Creator and Prover stages. Tools that produce PCZTs include: |
| 86 | + |
| 87 | +- **zipher-cli** (`zipher create-pczt`) |
| 88 | +- **Zodl** (the Zcash reference wallet, via `zcash_client_backend`) |
| 89 | +- Any tool using `zcash_client_backend::data_api::wallet::create_pczt_from_proposal` |
| 90 | + |
| 91 | +The output is the signed PCZT in hex. The caller is responsible for finalization and broadcast. |
| 92 | + |
| 93 | +### Security: trusting the PCZT source |
| 94 | + |
| 95 | +OWS signs whatever PCZT is passed to it — the same trust model as every other chain. When you call `ows sign tx --chain evm`, OWS signs the unsigned EVM transaction without inspecting whether the recipient or amount is what you intended. The caller (your agent, your CLI, your wallet) is responsible for constructing a safe transaction. |
| 96 | + |
| 97 | +For Zcash, this means: if a malicious PCZT is constructed to send your funds to an attacker's address, OWS will sign it. The PCZT format does carry metadata (amounts, recipients, memos) that a higher-level application could inspect before requesting a signature, but OWS itself operates at the signing primitive level — it does not enforce spending policies at the PCZT layer. |
| 98 | + |
| 99 | +This is consistent with the OWS security model: the vault holds keys, the signer signs, and the policy engine (if configured) enforces spending limits. Transaction construction and validation are the caller's responsibility across all chains. |
| 100 | + |
| 101 | +### Sign and broadcast |
| 102 | + |
| 103 | +For end-to-end sending: |
| 104 | + |
| 105 | +```bash |
| 106 | +ows sign send-tx --chain zcash --wallet my-wallet --tx <pczt_hex> |
| 107 | +``` |
| 108 | + |
| 109 | +This signs the PCZT, extracts the finalized transaction, and broadcasts it to lightwalletd via gRPC. The default endpoint is `zec.rocks:443` for mainnet. |
| 110 | + |
| 111 | +Returns the transaction ID (txid) on success. |
| 112 | + |
| 113 | +## What OWS signs |
| 114 | + |
| 115 | +When `sign tx --chain zcash` is called, OWS: |
| 116 | + |
| 117 | +1. Decrypts the BIP-39 mnemonic from the vault |
| 118 | +2. Derives the Unified Spending Key via ZIP-32 |
| 119 | +3. Extracts per-pool signing keys: |
| 120 | + - **Orchard:** `SpendAuthorizingKey` (RedPallas) |
| 121 | + - **Sapling:** `ask` (Jubjub) |
| 122 | + - **Transparent:** secp256k1 secret key |
| 123 | +4. Iterates over PCZT inputs and signs those matching the derived keys |
| 124 | +5. Skips dummy/padding actions (standard Orchard behavior) |
| 125 | +6. Returns the signed PCZT |
| 126 | + |
| 127 | +The spending key is zeroized after use. |
| 128 | + |
| 129 | +## Configuration |
| 130 | + |
| 131 | +### Default RPC endpoints |
| 132 | + |
| 133 | +| Network | Endpoint | |
| 134 | +|---------|----------| |
| 135 | +| `zcash:mainnet` | `https://zec.rocks:443` | |
| 136 | +| `zcash:testnet` | `https://testnet.zec.rocks:443` | |
| 137 | + |
| 138 | +Override via `~/.ows/config.toml`: |
| 139 | + |
| 140 | +```toml |
| 141 | +[rpc] |
| 142 | +"zcash:mainnet" = "https://your-lightwalletd:443" |
| 143 | +``` |
| 144 | + |
| 145 | +Or pass `--rpc-url` on the CLI. |
| 146 | + |
| 147 | +### Feature flag |
| 148 | + |
| 149 | +Zcash shielded support requires the `zcash-shielded` feature flag, which is enabled by default in the CLI. The feature adds dependencies on `zcash_keys`, `pczt`, `orchard`, and `sapling-crypto` for ZIP-32 derivation and PCZT signing. |
| 150 | + |
| 151 | +Without `zcash-shielded`, Zcash falls back to transparent-only support (t-addresses, secp256k1 signing). |
| 152 | + |
| 153 | +## Dependencies |
| 154 | + |
| 155 | +| Crate | Purpose | |
| 156 | +|-------|---------| |
| 157 | +| `zcash_keys` 0.12 | ZIP-32 key derivation, unified address encoding | |
| 158 | +| `zcash_protocol` 0.7 | Network parameters (mainnet/testnet) | |
| 159 | +| `zip32` 0.2 | Account ID types | |
| 160 | +| `pczt` 0.5 | PCZT parsing, Signer role | |
| 161 | +| `orchard` 0.11 | Orchard spend authorization key types | |
| 162 | +| `sapling-crypto` 0.5 | Sapling spend auth key (`ask`) | |
| 163 | +| `zcash_transparent` 0.6 | Transparent key derivation | |
| 164 | +| `zcash_primitives` 0.26 | Transaction serialization (for broadcast) | |
| 165 | + |
| 166 | +All crates are from the official [librustzcash](https://github.com/zcash/librustzcash) ecosystem, originally built by Electric Coin Company and now maintained by [ZODL](https://zodl.com) (Zcash Open Development Lab). |
| 167 | + |
| 168 | +## End-to-end example |
| 169 | + |
| 170 | +A complete shielded send using OWS and zipher-cli: |
| 171 | + |
| 172 | +```bash |
| 173 | +# 1. Create an OWS wallet |
| 174 | +ows wallet create --name agent-wallet |
| 175 | + |
| 176 | +# 2. Fund the Zcash address (send ZEC to the u1... address) |
| 177 | + |
| 178 | +# 3. Build a PCZT with zipher-cli (Creator + Prover) |
| 179 | +PCZT=$(zipher create-pczt \ |
| 180 | + --to u1recipient... \ |
| 181 | + --amount 0.01 \ |
| 182 | + --data-dir ~/.zipher) |
| 183 | + |
| 184 | +# 4. Sign with OWS (Signer) |
| 185 | +ows sign send-tx --chain zcash --wallet agent-wallet --tx $PCZT |
| 186 | + |
| 187 | +# 5. Transaction is broadcast and visible on a block explorer |
| 188 | +``` |
| 189 | + |
| 190 | +## References |
| 191 | + |
| 192 | +- [ZIP-32: Shielded Hierarchical Deterministic Wallets](https://zips.z.cash/zip-0032) |
| 193 | +- [ZIP-316: Unified Addresses](https://zips.z.cash/zip-0316) |
| 194 | +- [ZIP-244: Transaction Identifier and Commitment](https://zips.z.cash/zip-0244) |
| 195 | +- [PCZT specification](https://github.com/zcash/zips/pull/766) |
| 196 | +- [OWS Signing Interface](02-signing-interface.md) |
| 197 | +- [OWS Supported Chains](07-supported-chains.md) |
0 commit comments