@ledgerhq/zcash-utils 1.1.1 → 2.0.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,92 @@
1
1
  # @ledgerhq/zcash-utils
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - b555b18: First stable release (1.0.0): end-to-end shielded transaction crafting.
8
+
9
+ Since 0.3.1 the addon grew from a scan-only library into a full Orchard send pipeline:
10
+
11
+ - V5 PCZT transaction builder supporting Orchard send flows and mixed
12
+ transparent + Orchard inputs, with bip32 derivation stamped on the change
13
+ output and every transparent input
14
+ - `buildTransaction`, `finalizeTransaction`, and `broadcastTransaction` to
15
+ build, finalize, and submit a shielded transaction
16
+ - `parsePczt(pcztHex)` to decode canonical PCZT bytes into a structured
17
+ `PcztTransaction` consumed by `@ledgerhq/device-signer-kit-zcash`
18
+ - On-demand Orchard ShardTree witness computation at craft time
19
+ - `findBlockHeight(grpcUrl, timestamp)` binary search over block timestamps
20
+ - NU6.2-aware crate versions for correct branch-id resolution and shielded
21
+ parsing at/above the NU6.2 activation height
22
+
23
+ ### Minor Changes
24
+
25
+ - 65c6a95: Report the transparent bundle of a scanned transaction on `ShieldedTransaction`, via two new fields: `transparentOut` (sum of the transparent outputs, in zatoshis) and `hasTransparentInputs`.
26
+
27
+ A shielded→transparent send leaves no decrypted output a wallet can attribute to a counterparty: the value exits through the transparent bundle and only the change comes back, marked `internal`. Consumers therefore saw such a send as a self-transfer moving nothing. The scanner already deserializes the full transaction to compute the fee, so it can report these two facts directly instead of having each consumer re-parse the raw hex. `hasTransparentInputs` is needed alongside the amount: once transparent inputs are present, the transparent outputs may be paid by those inputs rather than out of the shielded pools, and the bundle alone cannot tell the two apart.
28
+
29
+ - 3f2ea9c: Add `findBlockHeight(grpcUrl, timestamp)` — binary search over block timestamps via gRPC
30
+
31
+ - New Rust function `find_block_height` in `zcash-sync` using interpolation search + streaming `GetBlockRange` for fast convergence (~6 RPCs, under 1.5s on mainnet)
32
+ - Exposed via NAPI as `findBlockHeight(grpcUrl: string, timestamp: number): Promise<number>`
33
+ - New CLI subcommand `height-at --grpc-url <URL> --date <YYYY-MM-DD|timestamp>`
34
+ - Returns the height of the first block whose timestamp is ≥ the target
35
+
36
+ - 01420fd: Add an optional `ironwoodSignatures` field to `FinalizeTransactionParams` to support finalizing V6 Ironwood PCZTs, and widen `finalizeTransaction` to return a V5 (ZIP-225) or V6 (ZIP-230) transaction depending on the input PCZT's shielded bundle. `FinalizeInputs` now accepts per-spend Ironwood `spendAuthSig` bytes injected via `apply_ironwood_signature`.
37
+
38
+ The new field is optional, so existing callers that pass only `orchardSignatures` and `transparentSignatures` keep working unchanged — hence a minor rather than a major bump. Each signature list is length-checked against the PCZT's unsigned actions for its pool, so supplying signatures for a pool the PCZT does not spend fails closed.
39
+
40
+ Crate dependencies bumped to `pczt 0.9.2`, `zcash_primitives 0.30`, `zcash_transparent 0.10`, `zcash_keys 0.16`, and `zcash_client_backend 0.24.0-rc.7`.
41
+
42
+ - b7f7d36: Sync the Ironwood (NU6.3) shielded pool in parallel to Orchard. Detect, trial-decrypt, and fully decrypt Ironwood notes (ZIP 2005 `0x03` note plaintext) from the Ironwood bundle of V6 transactions, expose them via a new `ironwoodNotes` array on `ShieldedTransaction` and a per-note `pool` discriminator, derive their position from the Ironwood commitment-tree-size counter, track spends against the Ironwood nullifier set, and compute ShardTree witnesses for the Ironwood tree. Existing Orchard/Sapling sync and the Orchard send path are unchanged. Bumps the Zcash crates to NU6.3-aware versions (orchard 0.15, zcash_primitives 0.29, zcash_protocol 0.10, and the wallet-side crates from crates.io release candidates: `zcash_client_backend 0.24.0-rc.1`, `pczt 0.8.0-rc.1`).
43
+ - 485d063: Add spending fields to ShieldedNote (nullifier, rseed, cmx, position, recipient, is_spent) to support shielded transaction crafting via PCZT. Position is derived from CompactBlock chain_metadata, is_spent is computed by nullifier matching across the scanned range.
44
+ - b8635d1: Expose the default unified receiving address (ZIP-316, `u1...` / `utest1...`) from key derivation. `DerivedKeys` gains a `unified_address` field, and `ledger-zcash-cli derive` now prints it alongside the UFVK in both `human` and `json` output. Derived purely from the UFVK (watch-only, no spending key involved) and independent of `--no-sapling`, since that flag only controls what's bundled into the encoded UFVK string, not the address's receiver set.
45
+ - 0d19606: Add `orchardAddressFromUfvk` — derives the Orchard-only unified address from an encoded UFVK string. Returns the same address the Ledger device shows on the Receive screen (matches `GetShieldedAddress` INS 0x51). Also renames `DerivedKeys.unifiedAddress` to `multiReceiverUnifiedAddress` to distinguish it from the Orchard-only address.
46
+ - 41a83b8: Add `finalizeTransaction` and `broadcastTransaction` NAPI functions for the Orchard send flow.
47
+ - 400dbb5: Add `buildTransaction` NAPI function for Orchard send flows.
48
+ - c16921d: Add `parsePczt(pcztHex)` — decode canonical PCZT bytes into a structured `PcztTransaction`
49
+
50
+ - New Rust function `parse_pczt` in `zcash-crypto` that parses the canonical PCZT bytes emitted by `buildTransaction` (`PCZT` magic + u32 LE version + postcard payload) and re-shapes them into a fully structured form: the global header, every transparent input/output, and each Orchard action broken out field-by-field
51
+ - Exposed via NAPI as `parsePczt(pcztHex: string): PcztTransaction`, matching the object `@ledgerhq/device-signer-kit-zcash`'s `DmkSignerZcash.signPcztTransaction` consumes (`Uint8Array` byte fields, `bigint` zatoshi values, `signingPath` derivation strings)
52
+ - Bridges `buildTransaction` (returns `pcztHex`) to the device signer without a TypeScript postcard parser
53
+ - Fails with a clear error when the input is not a valid PCZT or is missing a field the device requires to sign (e.g. Orchard `alpha`/`rcv`, an input's single `bip32_derivation`)
54
+
55
+ - adb99fb: Add on-demand Orchard ShardTree witness computation.
56
+
57
+ Public surface:
58
+
59
+ - `zcash_crypto::tree::{build_witnesses, WitnessInputs, WitnessOutput, ShardLeaves}`
60
+ - `zcash_sync::witness::{compute_witnesses, WitnessRequest, NoteRef}`
61
+
62
+ Witness data is fetched and assembled on demand at craft time. No tree state
63
+ is persisted between calls.
64
+
65
+ - 68d013b: Add `buildIronwoodTransaction` for the Ironwood (NU6.3) shielded pool: builds, proves, and serializes an unsigned V6 PCZT carrying an Ironwood bundle (spends and/or outputs), reusing the existing Orchard V5 crafting lifecycle against the updated Action circuit. Ironwood outputs use the `0x03` quantum-recoverable note plaintext, the emitted PCZT is redacted and serialized in the v2 wire format (required for any V6 transaction), and a dedicated non-zero-anchor check rejects an all-zero Ironwood commitment-tree root before it can be silently embedded. Anchor/witness resolution reuses the existing Ironwood sync path (`fetchIronwoodAnchor` / Ironwood witness computation). The shipped Orchard V5 send flow (`buildTransaction`) is unchanged. Like the V5 builder, this is device-coupled (not exposed via the CLI) and depends on release-candidate wallet-side crates (`pczt`, `zcash_client_backend`) pending stable NU6.3 releases.
66
+ - 65c6a95: Add `transactionDetails`, which fetches transactions by txid and reads from each what only the raw bytes hold: the fee it actually paid, and the addresses its shielded outputs paid.
67
+
68
+ Both answers require the whole transaction, so one fetch serves both. A fee derived from the transparent bundle alone — all an explorer can do — counts value entering a shielded pool as fee and cannot account for value leaving one. Shielded payees are encrypted and recoverable only by the account that created the outputs, through the outgoing viewing key the transaction was built with; pass `ufvk` to recover them.
69
+
70
+ Requests are pipelined over a single gRPC channel and answered in order. A transaction that cannot be fetched, parsed, or fully priced yields a `null` fee and no payees rather than an approximation.
71
+
72
+ Orchard-family and Sapling notes now also carry the address they pay (`recipient`), which for a note we sent is the payee rather than one of our own addresses.
73
+
74
+ - e07bc98: Add transparent input support to the V5 PCZT builder.
75
+
76
+ ### Patch Changes
77
+
78
+ - 4776701: Bump version to deploy new release
79
+ - 0c6f624: Update Zcash crates to NU6.2-aware versions (orchard 0.14, zcash_primitives 0.28, zcash_protocol 0.9, and transitive deps). Restores correct branch-id resolution and shielded-transaction parsing for blocks at or above the NU6.2 activation height (mainnet 3,364,600).
80
+ - b949c30: Keep the transparent and Orchard send paths working past NU6.3. Three things change under `build_transaction` at that activation even though the code did not: the builder derives V6 from the consensus branch, which the PCZT v1 device contract cannot encode, so the V5 format is now pinned explicitly; an Orchard bundle becomes `orchard_v3` in that epoch and only proves against the NU6.3 circuit generation, so the proving and verifying keys are selected from the branch instead of being fixed to the NU6.2 generation; and the Orchard pool disables cross-address transfers, so retained value is added through the builder's change API, which is what makes z→t build again.
81
+
82
+ Also fetch an anchor from the commitment-tree frontier alone. A shielded bundle with outputs but no real spend needs an anchor and no witness, and the frontier determines the root, so shielding no longer streams every completed shard root — and no longer depends on the server serving that pool's `GetSubtreeRoots`, which Ironwood does not yet.
83
+
84
+ - 6e5e9b0: Bump the librustzcash crate set to NU6.3-aware versions (zcash_protocol 0.10, zcash_primitives 0.29, and the compatible zcash_keys/zcash_address/zcash_transparent/orchard set, plus zcash_client_backend and pczt release candidates). This corrects `BranchId::for_height`, which resolves `Nu6_3` at mainnet height 3,428,143, so transactions parse and build correctly after NU6.3 activation. No public API change.
85
+ - 9ccce21: Route surplus change to the pool that funds it: an Orchard change output only when the transaction has Orchard spends (z→z, z→t), a transparent change output when there are none (t→t, t→z). For t→z this keeps the change transparent instead of migrating the whole balance into the shielded pool — only the sent amount is shielded.
86
+ - b1766c6: Add new retryable error for "h2 protocol error"
87
+ - 936b971: Emission happens AFTER Phase 5 so that `is_spent` flags are correct.
88
+ - 50e74c7: Decrease bundle size
89
+
3
90
  ## 1.0.4
4
91
 
5
92
  ### Minor Changes
package/index.d.ts CHANGED
@@ -352,19 +352,32 @@ export interface BuildIronwoodTransactionResult {
352
352
  export declare function buildIronwoodTransaction(params: BuildIronwoodTransactionParams): Promise<BuildIronwoodTransactionResult>
353
353
  /** Parameters for finalizing a PCZT with device-provided signatures. */
354
354
  export interface FinalizeTransactionParams {
355
- /** Hex-encoded canonical PCZT bytes from `buildTransaction`. */
355
+ /**
356
+ * Hex-encoded canonical PCZT bytes from `buildTransaction` or
357
+ * `buildIronwoodTransaction`.
358
+ */
356
359
  pczt: string
357
360
  /**
358
361
  * One 64-byte (128-hex-char) RedPallas `spendAuthSig` per real Orchard spend,
359
- * in PCZT-action order over the unsigned actions.
362
+ * in PCZT-action order over the unsigned actions. Empty for V6/Ironwood PCZTs.
360
363
  */
361
364
  orchardSignatures: Array<string>
362
- /** One DER-hex secp256k1 signature per transparent input (empty for pure Orchard). */
365
+ /**
366
+ * One 64-byte (128-hex-char) RedPallas `spendAuthSig` per real Ironwood spend,
367
+ * in PCZT-action order over the unsigned actions. Optional: omit it (or pass
368
+ * an empty array) for V5/Orchard and transparent-only PCZTs, which is why
369
+ * adding it did not break existing callers.
370
+ */
371
+ ironwoodSignatures?: Array<string>
372
+ /** One DER-hex secp256k1 signature per transparent input (empty for pure shielded). */
363
373
  transparentSignatures: Array<string>
364
374
  }
365
375
  /** Result of a successful `finalizeTransaction` call. */
366
376
  export interface FinalizeTransactionResult {
367
- /** Hex-encoded signed V5 transaction bytes (ready for `broadcastTransaction`). */
377
+ /**
378
+ * Hex-encoded signed transaction bytes (ready for `broadcastTransaction`).
379
+ * V5 (ZIP-225) or V6 (ZIP-230), depending on the input PCZT's shielded bundle.
380
+ */
368
381
  txHex: string
369
382
  /**
370
383
  * 64-char hex transaction id, big-endian *display* order (matches the sync
@@ -373,11 +386,17 @@ export interface FinalizeTransactionResult {
373
386
  txid: string
374
387
  }
375
388
  /**
376
- * Inject device signatures into a PCZT and extract the final signed V5 transaction.
389
+ * Inject device signatures into a PCZT and extract the final signed transaction.
377
390
  *
378
- * Accepts the PCZT from `buildTransaction`, one 64-byte RedPallas signature per
379
- * real (unsigned) Orchard action, and one DER secp256k1 signature per transparent
380
- * input. The Orchard binding signature is computed host-side.
391
+ * Accepts the PCZT from `buildTransaction` or `buildIronwoodTransaction`, one
392
+ * 64-byte RedPallas signature per real (unsigned) Orchard or Ironwood action
393
+ * (supply the list matching the PCZT's shielded bundle; the other may be empty
394
+ * or omitted), and one DER secp256k1 signature per transparent input. The
395
+ * binding signature is computed host-side.
396
+ *
397
+ * Each list is length-checked against the PCZT's unsigned actions for that pool,
398
+ * so passing signatures for a pool the PCZT does not spend fails closed rather
399
+ * than being silently ignored.
381
400
  *
382
401
  * CPU-bound (Halo 2 proof verification runs here): the pure call is dispatched
383
402
  * to `tokio::task::spawn_blocking` so the async executor is not starved.
@@ -559,6 +578,17 @@ export interface PcztTransaction {
559
578
  * or if a field the device requires to sign is missing from it.
560
579
  */
561
580
  export declare function parsePczt(pcztHex: string): PcztTransaction
581
+ /**
582
+ * Derive the Orchard-only unified address from an encoded UFVK string.
583
+ *
584
+ * Replicates exactly what the device derives: Orchard FVK, external scope,
585
+ * diversifier index 0, single Orchard receiver. Use this for the Receive
586
+ * modal — the result passes on-device verification.
587
+ *
588
+ * Do not confuse with `deriveKeys().multiReceiverUnifiedAddress`, which bundles
589
+ * all available receivers and does NOT match the device address.
590
+ */
591
+ export declare function orchardAddressFromUfvk(ufvk: string): string
562
592
  /**
563
593
  * Async iterator over matched shielded transactions.
564
594
  *
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 } = nativeBinding
313
+ const { TransactionStream, startSync, getChainTip, findBlockHeight, buildTransaction, buildIronwoodTransaction, finalizeTransaction, broadcastTransaction, transactionDetails, parsePczt, orchardAddressFromUfvk } = nativeBinding
314
314
 
315
315
  module.exports.TransactionStream = TransactionStream
316
316
  module.exports.startSync = startSync
@@ -322,3 +322,4 @@ module.exports.finalizeTransaction = finalizeTransaction
322
322
  module.exports.broadcastTransaction = broadcastTransaction
323
323
  module.exports.transactionDetails = transactionDetails
324
324
  module.exports.parsePczt = parsePczt
325
+ module.exports.orchardAddressFromUfvk = orchardAddressFromUfvk
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": "1.1.1",
3
+ "version": "2.0.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",
@@ -51,6 +51,7 @@
51
51
  "cli:sync": "cargo run -q -p zcash-cli -- sync --help",
52
52
  "// ── Node.js validation & benchmarks ───────────────────────────────": "",
53
53
  "sync-tx": "tsx examples/sync-tx.node.ts --start-height 1130000 --end-height 1180000",
54
- "bench": "tsx examples/bench.ts --start-height 280000 --end-height 285000"
54
+ "bench": "tsx examples/bench.ts --start-height 280000 --end-height 285000",
55
+ "napi-smoke": "tsx examples/napi-smoke.ts"
55
56
  }
56
57
  }