@ledgerhq/zcash-utils 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @ledgerhq/zcash-utils
2
2
 
3
+ ## 2.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6350264: Add `"regtest"` as a valid `network` value for `buildTransaction`/`buildIronwoodTransaction`. A fresh regtest chain's tip height never reaches the real mainnet/testnet NU5/NU6.3 activation heights, so both builders previously rejected every send against a local node. `network: "regtest"` now resolves to a `LocalNetwork` whose Overwinter-through-Canopy upgrades activate at height 1 and NU5/NU6/NU6.1/NU6.2/NU6.3 at height 2, matching the canonical zebra/zaino/librustzcash regtest defaults.
8
+
9
+ A regtest build now stamps derivation paths with mainnet's coin type (133), not regtest's own SLIP-44 value (1, shared with testnet) — this crate's key-derivation surface always derives regtest keys under the mainnet convention, so stamping the SLIP-44 value produced a path no caller actually derives from.
10
+
11
+ Known limitation: the PCZT's own `global.coin_type` header field (set independently by the `pczt` crate's `Creator` role from the network's type, with no accessor this crate can override afterwards) still carries regtest's SLIP-44 value (1), inconsistent with the mainnet coin type stamped into the derivation paths above. This has no effect on this crate's own device-free signing surface, which never reads that field; it would matter only to a real device, which is out of scope for regtest support.
12
+
13
+ ### Patch Changes
14
+
15
+ - ab5930f: Compute Ironwood witnesses from the server's `GetSubtreeRoots` instead of deriving shard roots locally, and fix a shard-0 scan that could hang on either pool.
16
+
17
+ Ironwood sends previously rescanned the pool's whole history on every spend and recomputed each completed shard root from scratch, because the deployed indexer rejected `GetSubtreeRoots` for Ironwood. It now serves it, so both pools take the same route: the cost is the spend's own shard footprint rather than the pool's lifetime, and the leaf ceiling that would have failed every shielded send as the pool grew is gone. Measured on mainnet against real shard-0 notes, witness computation drops from roughly 26s to 11s, and no longer degrades as the pool fills.
18
+
19
+ Also fixes a latent hang affecting Orchard as well as Ironwood: the scan for a note in commitment-tree shard 0 started at block 1, and since both pools begin millions of blocks after genesis, `GetBlockRange` — which has no per-request timeout — streamed most of the chain instead of failing. The scan now starts at the pool's first leaf.
20
+
21
+ - 7b40d6d: Move `zcash_client_backend` to its `0.24.0` stable release. The exact pin on `0.24.0-rc.7` carried a comment instructing exactly this once the stable release existed; it was published on 2026-08-19 and the pin had not followed. Nothing in the dependency tree is a release candidate any more, and the full workspace suite passes on the stable release.
22
+
23
+ Retire the claims that pin had left behind, three of which were false rather than merely stale. `buildIronwoodTransaction` was documented — in the shipped `index.d.ts`, so every consumer read it — as "dry-run pending the NU6.3 wallet-side crates stabilizing (`pczt`, `zcash_client_backend` are release candidates)". `pczt` was never a release candidate: it is pinned exactly at `0.9.3`, a stable release, because it defines the byte stream the firmware parses. And the crafting path is not a dry run: it is implemented, and its witness computation is tested offline against a real Ironwood anchor captured from a public testnet node. The README now says what the support is actually tested against instead.
24
+
25
+ Fix the two things the README got wrong about the package itself. It told readers to route the `@ledgerhq` scope to an internal registry, when the package is published on the public npm registry and `npm install` works unconfigured with no `.npmrc` entry at all. It also declared its license with a trailing editorial flourish rather than naming it plainly.
26
+
27
+ Replace the Ledger staging gRPC endpoint used as the example value throughout the public surface — `index.d.ts`, the CLI's `--grpc-url` help, `docs/ffi-node.md`, the runnable examples — with the public `https://testnet.zec.rocks:443`. The former host does not resolve outside Ledger's network, so every published example was unusable by anyone outside it. Ledger's own nodes remain the defaults in the integration tests, which are Ledger's to run.
28
+
29
+ - 013e2cf: Add a test-only NAPI signing surface (`testDeriveKeys`, `testSignPczt`) so the `ledger-live` coin-tester can derive keys and sign a PCZT's Orchard actions, Ironwood (V6) actions, and transparent inputs from a seed, acting as a device stand-in in CI. Never call this from production wallet code.
30
+
3
31
  ## 2.3.0
4
32
 
5
33
  ### Minor Changes
package/README.md CHANGED
@@ -8,25 +8,24 @@ Spending key material never enters this layer. It reads a Unified Full Viewing K
8
8
 
9
9
  ## Installation
10
10
 
11
- The package is published to Ledger's internal JFrog Artifactory, not to public npm, so the `@ledgerhq` scope must be routed there:
12
-
13
11
  ```sh
14
- # ~/.npmrc
15
- @ledgerhq:registry=https://<artifactory-host>/
12
+ npm install @ledgerhq/zcash-utils
16
13
  ```
17
14
 
18
- ```sh
19
- pnpm add @ledgerhq/zcash-utils
20
- ```
15
+ Published on the public npm registry under the `@ledgerhq` scope; no registry configuration is needed.
21
16
 
22
- Prebuilt `.node` binaries are bundled in the package for six targets: `darwin-arm64`, `darwin-x64`, `linux-x64-gnu`, `linux-arm64-gnu`, `win32-x64-msvc`, `win32-arm64-msvc`. On any other platform, build from source — see [`CONTRIBUTING.md`](CONTRIBUTING.md).
17
+ Prebuilt `.node` binaries are bundled for six targets — `darwin-arm64`, `darwin-x64`, `linux-x64-gnu`, `linux-arm64-gnu`, `win32-x64-msvc`, `win32-arm64-msvc` — so there is no compilation step and no Rust toolchain to install. On any other platform, build from source: see [`CONTRIBUTING.md`](CONTRIBUTING.md).
18
+
19
+ Node.js 20 or later. The addon is a native module, so it runs in Node and in the Electron main process, not in a browser or a renderer without `nodeIntegration`.
23
20
 
24
21
  ## Quick start — scanning
25
22
 
26
23
  ```typescript
27
24
  import { startSync, getChainTip } from "@ledgerhq/zcash-utils";
28
25
 
29
- const grpcUrl = "https://zaino-zec-testnet.nodes.stg.ledger-test.com/";
26
+ // Any lightwalletd or Zaino endpoint. This one is public; Ledger Live points
27
+ // at its own infrastructure.
28
+ const grpcUrl = "https://testnet.zec.rocks:443";
30
29
  const tip = await getChainTip(grpcUrl);
31
30
 
32
31
  const stream = await startSync({
@@ -157,7 +156,7 @@ Full signatures, every field, and its constraints are in [`index.d.ts`](index.d.
157
156
  | Export | Purpose |
158
157
  | --- | --- |
159
158
  | `buildTransaction(params)` | Builds, proves, and serializes a V5 PCZT from Orchard notes, transparent UTXOs, or both. Bears the Halo 2 proving cost inline (~2–5 s cold, ~hundreds of ms after, via a process-global proving-key cache). |
160
- | `buildIronwoodTransaction(params)` | Same, for an Ironwood (NU6.3) source — a redacted V6 PCZT. See the status note below. |
159
+ | `buildIronwoodTransaction(params)` | Same, for an Ironwood (NU6.3) source — a redacted V6 PCZT. See [Ironwood (NU6.3)](#ironwood-nu63) below. |
161
160
  | `parsePczt(pcztHex)` | Decodes canonical PCZT bytes into the `PcztTransaction` the device signer consumes. Fails if a field the device needs to sign is missing. |
162
161
  | `finalizeTransaction(params)` | Injects device signatures, computes the binding signature, extracts the signed transaction and its txid. CPU-bound (proof verification), dispatched to a blocking thread. |
163
162
  | `broadcastTransaction(grpcUrl, txHex)` | Submits a signed transaction to a lightwalletd / Zaino endpoint; returns the txid. |
@@ -186,11 +185,13 @@ The same quantity is not spelled the same way everywhere in this API. Read this
186
185
 
187
186
  **`scriptPubKey`** keeps its canonical Bitcoin/Zcash casing, rather than napi's default camelCasing of the Rust field name (`scriptPubkey`).
188
187
 
189
- ## Ironwood (NU6.3) status
188
+ ## Ironwood (NU6.3)
189
+
190
+ Ironwood is supported on both halves of the wallet, and the crates it rests on are on their stable releases.
190
191
 
191
- `buildIronwoodTransaction` is exposed so the JS side can be wired up in parallel, but Rust-side Ironwood crafting is still a dry run: the wallet-side crates it depends on (`pczt`, `zcash_client_backend`) are release candidates for NU6.3. Those pins are re-confirmed and bumped to the stable releases before the mainnet build cut. See [`docs/architecture.md`](docs/architecture.md).
192
+ Scanning is not gated by `orchardOnly`: `ShieldedTransaction.ironwoodNotes` is populated alongside `orchardNotes`, and `ShieldedNote.pool` tells the two apart. Crafting goes through `buildIronwoodTransaction`, which emits a V6 (ZIP-230) PCZT; `finalizeTransaction` and `broadcastTransaction` accept V6 as they do V5.
192
193
 
193
- Ironwood notes on the scanning path are complete and not gated by `orchardOnly`: `ShieldedTransaction.ironwoodNotes` is populated alongside `orchardNotes`, and `ShieldedNote.pool` distinguishes them.
194
+ What that support is tested against is worth knowing, since the pool is young. Witness computation is checked offline against a real Ironwood anchor captured from a public testnet node, scanning from the NU6.3 testnet activation height. Trial decryption is exercised against a synthetic Ironwood action built from the `orchard` note-encryption API — genuine cryptography, but not a real on-chain transaction, because no Ironwood transaction addressed to a key we hold has been available to capture as a fixture.
194
195
 
195
196
  ## Documentation
196
197
 
@@ -203,4 +204,4 @@ Ironwood notes on the scanning path are complete and not gated by `orchardOnly`:
203
204
 
204
205
  ## License
205
206
 
206
- [Apache-2.0](LICENSE.md), as the rest of the Ledger device stack.
207
+ [Apache-2.0](LICENSE.md)
package/index.d.ts CHANGED
@@ -10,7 +10,10 @@
10
10
  * a seed phrase directly in this layer.
11
11
  */
12
12
  export interface SyncParams {
13
- /** gRPC endpoint URL (e.g. `"https://zaino-zec-testnet.nodes.stg.ledger-test.com/"`). */
13
+ /**
14
+ * gRPC endpoint URL of a lightwalletd or Zaino server (e.g. the public
15
+ * `"https://testnet.zec.rocks:443"`).
16
+ */
14
17
  grpcUrl: string
15
18
  /** Unified Full Viewing Key (UFVK) for the account to scan. */
16
19
  viewingKey: string
@@ -368,10 +371,8 @@ export interface BuildIronwoodTransactionResult {
368
371
  /**
369
372
  * Build, prove, and serialize a redacted V6 PCZT for an Ironwood send.
370
373
  *
371
- * Rust-only crafting is dry-run pending the NU6.3 wallet-side crates
372
- * stabilizing (`pczt`, `zcash_client_backend` are release candidates); this
373
- * NAPI wrapper is exposed now so the JS side can be wired up in parallel and
374
- * re-pinned when those crates stabilize (see `docs/architecture.md`).
374
+ * The Ironwood counterpart of `buildTransaction`, taking Ironwood notes in
375
+ * place of Orchard ones and emitting a V6 (ZIP-230) PCZT.
375
376
  *
376
377
  * Same proving-cost profile as `buildTransaction`: Halo 2 proof generation
377
378
  * happens here for the Ironwood bundle (~2-5 s first call against the
@@ -679,6 +680,57 @@ export declare function parsePczt(pcztHex: string): PcztTransaction
679
680
  * all available receivers and does NOT match the device address.
680
681
  */
681
682
  export declare function orchardAddressFromUfvk(ufvk: string): string
683
+ /** TEST-ONLY. Account UFVK + transparent xpub, as derived from a mnemonic. */
684
+ export interface TestDerivedKeys {
685
+ ufvk: string
686
+ xpub: string
687
+ }
688
+ /**
689
+ * TEST-ONLY. Derives the account UFVK and transparent xpub from a mnemonic.
690
+ * Not named `deriveKeys` — that name is reserved for an anticipated future
691
+ * production export.
692
+ *
693
+ * Exists so the `ledger-live` coin-tester can obtain the same UFVK/xpub a
694
+ * device would report, without a physical Ledger. Never call this from
695
+ * production wallet code: it holds the mnemonic (spending-key material) in
696
+ * process memory, which the production host must never do.
697
+ */
698
+ export declare function testDeriveKeys(mnemonic: string, account: number, network: string): TestDerivedKeys
699
+ /** TEST-ONLY. Result of [`test_sign_pczt`]. */
700
+ export interface TestSignPcztResult {
701
+ /**
702
+ * 128-hex-char RedPallas `spendAuthSig`, one per unsigned Orchard action,
703
+ * in PCZT-action order. Empty when the PCZT carries no Orchard bundle.
704
+ */
705
+ orchardSignatures: Array<string>
706
+ /**
707
+ * 128-hex-char RedPallas `spendAuthSig`, one per unsigned Ironwood
708
+ * action, in PCZT-action order. Empty when the PCZT carries no Ironwood
709
+ * bundle (e.g. a V5/Orchard or transparent-only PCZT).
710
+ */
711
+ ironwoodSignatures: Array<string>
712
+ /**
713
+ * Hex DER secp256k1 signature, one per transparent input, in input order.
714
+ * Empty when the PCZT carries no transparent inputs.
715
+ */
716
+ transparentSignatures: Array<string>
717
+ }
718
+ /**
719
+ * TEST-ONLY. Signs whichever bundles the PCZT carries (Orchard actions,
720
+ * Ironwood actions, transparent inputs) using spending-key material derived
721
+ * from `mnemonic`. An absent bundle yields an empty list for that leg, not
722
+ * an error.
723
+ *
724
+ * Exists so the `ledger-live` coin-tester can act as a device stand-in in
725
+ * CI: `coin-zcash` routes z→z, z→t and t→z through the V6/Ironwood builder
726
+ * (only t→t stays V5), so both spend-auth legs are covered here. Never call
727
+ * this from production wallet code: it derives and holds spending-key
728
+ * material from a seed, which the production host must never do.
729
+ *
730
+ * CPU-bound (Orchard/Ironwood proving-key-adjacent signing work): dispatched
731
+ * to `tokio::task::spawn_blocking`, mirroring `finalize_transaction`.
732
+ */
733
+ export declare function testSignPczt(mnemonic: string, account: number, network: string, pcztHex: string): Promise<TestSignPcztResult>
682
734
  /**
683
735
  * Async iterator over matched shielded transactions.
684
736
  *
Binary file
Binary file
package/index.js CHANGED
@@ -310,7 +310,7 @@ if (!nativeBinding) {
310
310
  throw new Error(`Failed to load native binding`)
311
311
  }
312
312
 
313
- const { TransactionStream, startSync, getChainTip, findBlockHeight, buildTransaction, buildIronwoodTransaction, finalizeTransaction, broadcastTransaction, transactionDetails, parsePczt, orchardAddressFromUfvk } = nativeBinding
313
+ const { TransactionStream, startSync, getChainTip, findBlockHeight, buildTransaction, buildIronwoodTransaction, finalizeTransaction, broadcastTransaction, transactionDetails, parsePczt, orchardAddressFromUfvk, testDeriveKeys, testSignPczt } = nativeBinding
314
314
 
315
315
  module.exports.TransactionStream = TransactionStream
316
316
  module.exports.startSync = startSync
@@ -323,3 +323,5 @@ module.exports.broadcastTransaction = broadcastTransaction
323
323
  module.exports.transactionDetails = transactionDetails
324
324
  module.exports.parsePczt = parsePczt
325
325
  module.exports.orchardAddressFromUfvk = orchardAddressFromUfvk
326
+ module.exports.testDeriveKeys = testDeriveKeys
327
+ module.exports.testSignPczt = testSignPczt
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/zcash-utils",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "Native Zcash key derivation and shielded sync — Node.js (NAPI)",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",