@ledgerhq/zcash-utils 1.1.0 → 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 +87 -0
- package/index.d.ts +38 -8
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.js +2 -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 +3 -2
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
389
|
+
* Inject device signatures into a PCZT and extract the final signed transaction.
|
|
377
390
|
*
|
|
378
|
-
* Accepts the PCZT from `buildTransaction`, one
|
|
379
|
-
* real (unsigned) Orchard
|
|
380
|
-
*
|
|
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
|
*
|
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 } = 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
|
package/index.linux-x64-gnu.node
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ledgerhq/zcash-utils",
|
|
3
|
-
"version": "
|
|
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
|
}
|