@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 +28 -0
- package/README.md +15 -14
- package/index.d.ts +57 -5
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.js +3 -1
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-arm64-msvc.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
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
|
-
|
|
15
|
-
@ledgerhq:registry=https://<artifactory-host>/
|
|
12
|
+
npm install @ledgerhq/zcash-utils
|
|
16
13
|
```
|
|
17
14
|
|
|
18
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
372
|
-
*
|
|
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
|
*
|
package/index.darwin-arm64.node
CHANGED
|
Binary file
|
package/index.darwin-x64.node
CHANGED
|
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
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|