@ledgerhq/zcash-utils 0.3.0 → 1.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/index.d.ts CHANGED
@@ -123,6 +123,271 @@ export declare function getChainTip(grpcUrl: string): Promise<number>
123
123
  * If the timestamp is after the chain tip, returns the tip height.
124
124
  */
125
125
  export declare function findBlockHeight(grpcUrl: string, timestamp: number): Promise<number>
126
+ export interface OrchardSpendInputJs {
127
+ /** 86-char hex (43 bytes: 11-byte diversifier + 32-byte pk_d). */
128
+ recipient: string
129
+ /** Decimal u64 (string to avoid f64 precision loss). */
130
+ valueZat: string
131
+ /** 64-char hex (32 bytes). */
132
+ rho: string
133
+ /** 64-char hex. */
134
+ rseed: string
135
+ /** 64-char hex. */
136
+ cmx: string
137
+ /** Decimal u64 leaf position in the Orchard commitment tree. */
138
+ position: string
139
+ }
140
+ export interface OutputRequestJs {
141
+ /**
142
+ * Destination address: t-addr (P2PKH/P2SH) or u-addr (Orchard receiver).
143
+ * Sapling z-addresses and TEX (ZIP-320) addresses are rejected.
144
+ */
145
+ address: string
146
+ valueZat: string
147
+ memo?: string
148
+ }
149
+ /** One transparent (P2PKH) UTXO to spend. */
150
+ export interface TransparentInputJs {
151
+ /**
152
+ * 64-char hex (32 bytes) prevout txid in internal (little-endian) byte order.
153
+ * Ledger Live surfaces txids in display (big-endian) order; callers must
154
+ * reverse before passing.
155
+ */
156
+ txid: string
157
+ /** Output index within the origin transaction. */
158
+ vout: number
159
+ /**
160
+ * Hex-encoded raw scriptPubKey bytes (no CompactSize length prefix).
161
+ * Exposed to JS as `scriptPubKey` (canonical Bitcoin/Zcash casing) rather
162
+ * than napi's default `scriptPubkey` camelCasing of the Rust field name.
163
+ */
164
+ scriptPubKey: string
165
+ /** UTXO value in zatoshis (decimal string to avoid f64 precision loss). */
166
+ valueZat: string
167
+ /** 66-char hex (33 bytes) compressed secp256k1 pubkey controlling the UTXO. */
168
+ pubkey: string
169
+ /**
170
+ * BIP-44 chain (scope) the controlling key lives on: `0` = external,
171
+ * `1` = internal (change). With `address_index` this identifies the UTXO's
172
+ * signing key under the account. It is verified against the UFVK (the
173
+ * derived pubkey must equal `pubkey`) and stamped into the PCZT as the
174
+ * input's `bip32_derivation`, which the Ledger device uses as the signing
175
+ * path (the PCZT sign APDU carries no path).
176
+ */
177
+ derivationScope: number
178
+ /**
179
+ * Non-hardened BIP-44 address index of the controlling key (see
180
+ * `derivation_scope`).
181
+ */
182
+ addressIndex: number
183
+ }
184
+ export interface BuildTransactionParams {
185
+ grpcUrl: string
186
+ ufvk: string
187
+ network?: string
188
+ /**
189
+ * 64-char hex (32 bytes): ZIP-32 seed fingerprint of the wallet seed,
190
+ * read from the device. Stamped onto each real spend so the device can
191
+ * confirm the PCZT belongs to its seed before signing.
192
+ */
193
+ seedFingerprint: string
194
+ /** ZIP-32 account index the UFVK was derived at. */
195
+ accountIndex: number
196
+ /**
197
+ * Caller-owned fee in zatoshis (decimal string to avoid f64 precision
198
+ * loss). Per FR-4 the fee is selected by ledger-live; this
199
+ * crate validates it against ZIP-317 and derives change from it rather
200
+ * than computing a fee itself.
201
+ */
202
+ feeZat: string
203
+ spends: Array<OrchardSpendInputJs>
204
+ /** Transparent (P2PKH) UTXOs to spend. Empty for Private→* flows. */
205
+ transparentInputs: Array<TransparentInputJs>
206
+ outputs: Array<OutputRequestJs>
207
+ anchorHeight?: number
208
+ }
209
+ export interface BuildTransactionResult {
210
+ /**
211
+ * Hex-encoded canonical PCZT bytes (`PCZT` magic + u32 LE version +
212
+ * postcard payload). Ready for the device APDU streaming layer. Carries
213
+ * each real spend's ZIP-32 derivation path for on-device signing.
214
+ */
215
+ pcztHex: string
216
+ /** Decimal fee in zatoshis. */
217
+ feeZat: string
218
+ /** Block height the Merkle paths were computed against. */
219
+ anchorHeight: number
220
+ /** Orchard action count after dummy padding. */
221
+ nActionsOrchard: number
222
+ /** Transparent input count. */
223
+ nTransparentInputs: number
224
+ /** Transparent output count (including change). */
225
+ nTransparentOutputs: number
226
+ }
227
+ /**
228
+ * Build, prove, and serialize a PCZT for a send transaction.
229
+ *
230
+ * Supports Orchard-source (Private→*) and transparent-source (Public→*)
231
+ * flows. Halo 2 proof generation happens here for Orchard-bundle transactions
232
+ * (~2-5 s first call, ~hundreds of ms thereafter thanks to the process-global
233
+ * ProvingKey cache). Transparent-only transactions skip the Orchard prover.
234
+ *
235
+ * Note: unlike `finalize_transaction` (purely CPU-bound, offloaded via
236
+ * `spawn_blocking`), this is an async orchestrator that interleaves gRPC
237
+ * witness fetches with the CPU-bound proving step, so it cannot be wrapped in a
238
+ * single `spawn_blocking` call — the proving cost is borne inline.
239
+ */
240
+ export declare function buildTransaction(params: BuildTransactionParams): Promise<BuildTransactionResult>
241
+ /** Parameters for finalizing a PCZT with device-provided signatures. */
242
+ export interface FinalizeTransactionParams {
243
+ /** Hex-encoded canonical PCZT bytes from `buildTransaction`. */
244
+ pczt: string
245
+ /**
246
+ * One 64-byte (128-hex-char) RedPallas `spendAuthSig` per real Orchard spend,
247
+ * in PCZT-action order over the unsigned actions.
248
+ */
249
+ orchardSignatures: Array<string>
250
+ /** One DER-hex secp256k1 signature per transparent input (empty for pure Orchard). */
251
+ transparentSignatures: Array<string>
252
+ }
253
+ /** Result of a successful `finalizeTransaction` call. */
254
+ export interface FinalizeTransactionResult {
255
+ /** Hex-encoded signed V5 transaction bytes (ready for `broadcastTransaction`). */
256
+ txHex: string
257
+ /**
258
+ * 64-char hex transaction id, big-endian *display* order (matches the sync
259
+ * path's `ShieldedTransaction.txid` and the Ledger Live operation hash).
260
+ */
261
+ txid: string
262
+ }
263
+ /**
264
+ * Inject device signatures into a PCZT and extract the final signed V5 transaction.
265
+ *
266
+ * Accepts the PCZT from `buildTransaction`, one 64-byte RedPallas signature per
267
+ * real (unsigned) Orchard action, and one DER secp256k1 signature per transparent
268
+ * input. The Orchard binding signature is computed host-side.
269
+ *
270
+ * CPU-bound (Halo 2 proof verification runs here): the pure call is dispatched
271
+ * to `tokio::task::spawn_blocking` so the async executor is not starved.
272
+ */
273
+ export declare function finalizeTransaction(params: FinalizeTransactionParams): Promise<FinalizeTransactionResult>
274
+ /**
275
+ * Submit a signed transaction to a lightwalletd / Zaino endpoint.
276
+ *
277
+ * Returns the txid (64-char hex, big-endian display order — matches the sync
278
+ * path and the Ledger Live operation hash) on success (`errorCode == 0`).
279
+ * Returns a descriptive error on a non-zero `errorCode` (carrying the server's
280
+ * `errorMessage`) or on a gRPC transport failure.
281
+ */
282
+ export declare function broadcastTransaction(grpcUrl: string, txHex: string): Promise<string>
283
+ /** PCZT header (`common::Global`) fields. */
284
+ export interface PcztGlobal {
285
+ /** Transaction version (V5 = 5). */
286
+ txVersion: number
287
+ versionGroupId: number
288
+ consensusBranchId: number
289
+ /** `null` encodes the absent optional lock time. */
290
+ fallbackLockTime?: number
291
+ expiryHeight: number
292
+ /** SLIP-44 coin type (133 mainnet, 1 testnet). */
293
+ coinType: number
294
+ txModifiable: number
295
+ }
296
+ /** A `bip32_derivation` entry for a transparent input/output. */
297
+ export interface PcztBip32Derivation {
298
+ /** Derivation path (no `m/` prefix, hardened indices suffixed with `'`). */
299
+ signingPath: string
300
+ /** Compressed secp256k1 public key, 33 bytes. */
301
+ pubkey: Uint8Array
302
+ /** ZIP-32 seed fingerprint, 32 bytes. */
303
+ seedFingerprint: Uint8Array
304
+ }
305
+ /** A single transparent input. */
306
+ export interface PcztTransparentInput {
307
+ /** Previous output txid, 32 bytes (internal byte order, as stored in the PCZT). */
308
+ prevoutTxid: Uint8Array
309
+ prevoutIndex: number
310
+ /** `null` encodes the absent optional sequence number (final `0xffffffff`). */
311
+ sequence?: number
312
+ /** Input value in zatoshis (decimal string to avoid f64 precision loss). */
313
+ value: string
314
+ scriptPubKey: Uint8Array
315
+ /** Sighash type (`SIGHASH_ALL` = `0x01`). */
316
+ sighashType: number
317
+ derivation: PcztBip32Derivation
318
+ }
319
+ /** A single transparent output. */
320
+ export interface PcztTransparentOutput {
321
+ /** Output value in zatoshis (decimal string to avoid f64 precision loss). */
322
+ value: string
323
+ scriptPubKey: Uint8Array
324
+ /** Present (change output) or `null` (external recipient). */
325
+ derivation?: PcztBip32Derivation
326
+ }
327
+ /** A single Orchard action (spend + output halves), flattened for the device. */
328
+ export interface PcztOrchardAction {
329
+ /** Value commitment, 32 bytes. */
330
+ cvNet: Uint8Array
331
+ /** Spend nullifier, 32 bytes. */
332
+ nullifier: Uint8Array
333
+ /** Randomized verification key, 32 bytes. */
334
+ rk: Uint8Array
335
+ /** Raw Orchard address of the spent note, 43 bytes. */
336
+ spendRecipient: Uint8Array
337
+ /** Spent-note value in zatoshis (decimal string to avoid f64 precision loss). */
338
+ spendValue: string
339
+ /** Spend rho, 32 bytes. */
340
+ spendRho: Uint8Array
341
+ /** Spend rseed, 32 bytes. */
342
+ spendRseed: Uint8Array
343
+ /** Spend-authorization randomizer, 32 bytes. */
344
+ alpha: Uint8Array
345
+ /** ZIP-32 derivation path of the signing key. */
346
+ signingPath: string
347
+ /** ZIP-32 seed fingerprint, 32 bytes. */
348
+ seedFingerprint: Uint8Array
349
+ /** Note commitment x-coordinate, 32 bytes. */
350
+ cmx: Uint8Array
351
+ /** Ephemeral key, 32 bytes. */
352
+ ephemeralKey: Uint8Array
353
+ encCiphertext: Uint8Array
354
+ outCiphertext: Uint8Array
355
+ /** Raw Orchard address of the output note, 43 bytes. */
356
+ recipient: Uint8Array
357
+ /** Output-note value in zatoshis (decimal string to avoid f64 precision loss). */
358
+ value: string
359
+ /** Output rseed, 32 bytes. */
360
+ rseed: Uint8Array
361
+ /** Value commitment randomness, 32 bytes. */
362
+ rcv: Uint8Array
363
+ }
364
+ /** The Orchard action bundle plus its trailer. */
365
+ export interface PcztOrchardBundle {
366
+ actions: Array<PcztOrchardAction>
367
+ flags: number
368
+ /** Net value balance in zatoshis (signed decimal string, lossless for i128). */
369
+ valueBalance: string
370
+ /** Orchard commitment-tree anchor, 32 bytes. */
371
+ anchor: Uint8Array
372
+ }
373
+ /** A fully structured PCZT ready for `DmkSignerZcash.signPcztTransaction`. */
374
+ export interface PcztTransaction {
375
+ global: PcztGlobal
376
+ transparentInputs: Array<PcztTransparentInput>
377
+ transparentOutputs: Array<PcztTransparentOutput>
378
+ /** `null` when the transaction has no Orchard actions. */
379
+ orchardBundle?: PcztOrchardBundle
380
+ }
381
+ /**
382
+ * Parse canonical PCZT bytes (hex, as returned by `buildTransaction`) into the
383
+ * structured `PcztTransaction` object the Ledger device signer consumes.
384
+ *
385
+ * The PCZT binary format (postcard) is not trivially parseable in TypeScript;
386
+ * this decodes it in Rust and breaks out the transparent inputs/outputs and
387
+ * each Orchard action field-by-field. Fails if the input is not a valid PCZT,
388
+ * or if a field the device requires to sign is missing from it.
389
+ */
390
+ export declare function parsePczt(pcztHex: string): PcztTransaction
126
391
  /**
127
392
  * Async iterator over matched shielded transactions.
128
393
  *
Binary file
Binary file
package/index.js CHANGED
@@ -310,9 +310,13 @@ if (!nativeBinding) {
310
310
  throw new Error(`Failed to load native binding`)
311
311
  }
312
312
 
313
- const { TransactionStream, startSync, getChainTip, findBlockHeight } = nativeBinding
313
+ const { TransactionStream, startSync, getChainTip, findBlockHeight, buildTransaction, finalizeTransaction, broadcastTransaction, parsePczt } = nativeBinding
314
314
 
315
315
  module.exports.TransactionStream = TransactionStream
316
316
  module.exports.startSync = startSync
317
317
  module.exports.getChainTip = getChainTip
318
318
  module.exports.findBlockHeight = findBlockHeight
319
+ module.exports.buildTransaction = buildTransaction
320
+ module.exports.finalizeTransaction = finalizeTransaction
321
+ module.exports.broadcastTransaction = broadcastTransaction
322
+ module.exports.parsePczt = parsePczt
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": "0.3.0",
3
+ "version": "1.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",