@bitcoinerlab/descriptors-core 3.1.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.
Files changed (76) hide show
  1. package/README.md +710 -0
  2. package/dist/adapters/applyPR2137.d.ts +2 -0
  3. package/dist/adapters/applyPR2137.js +150 -0
  4. package/dist/adapters/bitcoinjs.d.ts +8 -0
  5. package/dist/adapters/bitcoinjs.js +36 -0
  6. package/dist/adapters/scure/address.d.ts +2 -0
  7. package/dist/adapters/scure/address.js +50 -0
  8. package/dist/adapters/scure/bip32.d.ts +2 -0
  9. package/dist/adapters/scure/bip32.js +16 -0
  10. package/dist/adapters/scure/common.d.ts +14 -0
  11. package/dist/adapters/scure/common.js +36 -0
  12. package/dist/adapters/scure/ecpair.d.ts +2 -0
  13. package/dist/adapters/scure/ecpair.js +58 -0
  14. package/dist/adapters/scure/payments.d.ts +2 -0
  15. package/dist/adapters/scure/payments.js +216 -0
  16. package/dist/adapters/scure/psbt.d.ts +43 -0
  17. package/dist/adapters/scure/psbt.js +382 -0
  18. package/dist/adapters/scure/script.d.ts +20 -0
  19. package/dist/adapters/scure/script.js +163 -0
  20. package/dist/adapters/scure/transaction.d.ts +2 -0
  21. package/dist/adapters/scure/transaction.js +32 -0
  22. package/dist/adapters/scure.d.ts +6 -0
  23. package/dist/adapters/scure.js +37 -0
  24. package/dist/adapters/scureKeys.d.ts +4 -0
  25. package/dist/adapters/scureKeys.js +135 -0
  26. package/dist/bip174.d.ts +87 -0
  27. package/dist/bip174.js +12 -0
  28. package/dist/bitcoinLib.d.ts +385 -0
  29. package/dist/bitcoinLib.js +19 -0
  30. package/dist/bitcoinjs-lib-internals.d.ts +6 -0
  31. package/dist/bitcoinjs-lib-internals.js +60 -0
  32. package/dist/bitcoinjs.d.ts +12 -0
  33. package/dist/bitcoinjs.js +18 -0
  34. package/dist/checksum.d.ts +6 -0
  35. package/dist/checksum.js +58 -0
  36. package/dist/crypto.d.ts +3 -0
  37. package/dist/crypto.js +79 -0
  38. package/dist/descriptors.d.ts +481 -0
  39. package/dist/descriptors.js +1888 -0
  40. package/dist/index.d.ts +23 -0
  41. package/dist/index.js +87 -0
  42. package/dist/keyExpressions.d.ts +124 -0
  43. package/dist/keyExpressions.js +310 -0
  44. package/dist/keyInterfaces.d.ts +5 -0
  45. package/dist/keyInterfaces.js +50 -0
  46. package/dist/ledger.d.ts +183 -0
  47. package/dist/ledger.js +618 -0
  48. package/dist/miniscript.d.ts +125 -0
  49. package/dist/miniscript.js +310 -0
  50. package/dist/multipath.d.ts +13 -0
  51. package/dist/multipath.js +76 -0
  52. package/dist/networkUtils.d.ts +3 -0
  53. package/dist/networkUtils.js +16 -0
  54. package/dist/networks.d.ts +16 -0
  55. package/dist/networks.js +31 -0
  56. package/dist/parseUtils.d.ts +7 -0
  57. package/dist/parseUtils.js +46 -0
  58. package/dist/psbt.d.ts +40 -0
  59. package/dist/psbt.js +228 -0
  60. package/dist/re.d.ts +31 -0
  61. package/dist/re.js +79 -0
  62. package/dist/resourceLimits.d.ts +28 -0
  63. package/dist/resourceLimits.js +84 -0
  64. package/dist/scriptExpressions.d.ts +95 -0
  65. package/dist/scriptExpressions.js +98 -0
  66. package/dist/scure.d.ts +4 -0
  67. package/dist/scure.js +10 -0
  68. package/dist/signers.d.ts +161 -0
  69. package/dist/signers.js +324 -0
  70. package/dist/tapMiniscript.d.ts +231 -0
  71. package/dist/tapMiniscript.js +524 -0
  72. package/dist/tapTree.d.ts +91 -0
  73. package/dist/tapTree.js +166 -0
  74. package/dist/types.d.ts +296 -0
  75. package/dist/types.js +4 -0
  76. package/package.json +148 -0
package/README.md ADDED
@@ -0,0 +1,710 @@
1
+ # Bitcoin Descriptors Library
2
+
3
+ This library is designed to parse and create Bitcoin Descriptors, including Miniscript and Taproot script trees and generate Partially Signed Bitcoin Transactions (PSBTs). It also provides PSBT signers and finalizers for single-key, BIP32 and Hardware Wallets.
4
+
5
+ This library uses an underlying Bitcoin library for creating, signing & decoding transactions. Users can pick:
6
+
7
+ - `@bitcoinerlab/descriptors` for the [bitcoinjs-lib](https://github.com/bitcoinjs/bitcoinjs-lib) and [bitcoinjs](https://github.com/bitcoinjs) family of libraries: battle-tested and broadly used.
8
+ - `@bitcoinerlab/descriptors-scure` for [@scure/btc-signer](https://github.com/paulmillr/scure-btc-signer) and the [noble](https://github.com/paulmillr/noble-curves)/[scure](https://github.com/paulmillr/scure-btc-signer) family of libraries: audited, fast and minimal.
9
+
10
+ Or pick `@bitcoinerlab/descriptors-core` for advanced/manual initialization,
11
+ when you want to wire the underlying Bitcoin library yourself.
12
+
13
+ ## TL;DR (quick start)
14
+
15
+ ```bash
16
+ npm install @bitcoinerlab/descriptors @bitcoinerlab/miniscript-policies
17
+ ```
18
+
19
+ This quick example compiles a timelocked Miniscript policy, creates a descriptor address to fund, then builds, signs, and finalizes a PSBT that spends that funded UTXO and prints the final transaction hex.
20
+
21
+ ```javascript
22
+ import { ECPair, Output, Psbt, signers } from '@bitcoinerlab/descriptors'; // bitcoinjs-ready package
23
+
24
+ const ecpair = ECPair.makeRandom(); // Creates a signer for a single-key wallet
25
+
26
+ // Timelocked policy: signature + relative timelock (older)
27
+ const { miniscript } = compilePolicy('and(pk(@bob),older(10))');
28
+
29
+ const descriptor = `wsh(${miniscript.replace('@bob', toHex(ecpair.publicKey))})`;
30
+
31
+ // 1) Build the output description
32
+ const fundedOutput = new Output({ descriptor });
33
+ const address = fundedOutput.getAddress(); // Fund this address
34
+
35
+ // 2) Prepare PSBT input/output
36
+ const psbt = new Psbt();
37
+
38
+ const txHex = 'FUNDING_TX_HEX'; // hex of the tx that funded the address above
39
+ const vout = 0; // Output index (vout) of that UTXO within FUNDING_TX_HEX
40
+ const finalizeInput = fundedOutput.updatePsbtAsInput({ psbt, txHex, vout });
41
+
42
+ const recipient = new Output({
43
+ descriptor: 'addr(bc1qgw6xanldsz959z45y4dszehx4xkuzf7nfhya8x)'
44
+ }); // Final address where we'll send the funds after timelock
45
+ recipient.updatePsbtAsOutput({ psbt, value: 10000n }); // input covers this+fees
46
+
47
+ // 3) Sign and finalize
48
+ signers.signECPair({ psbt, ecpair });
49
+ finalizeInput({ psbt });
50
+
51
+ console.log('Push this: ' + psbt.extractTransaction().toHex());
52
+ ```
53
+
54
+ <details>
55
+ <summary>Click to see the scure variant</summary>
56
+
57
+ ```bash
58
+ npm install @bitcoinerlab/descriptors-scure @bitcoinerlab/miniscript-policies
59
+ ```
60
+
61
+ ```typescript
62
+ import { Output, btc, secp256k1, signers } from '@bitcoinerlab/descriptors-scure';
63
+
64
+ const privKey = btc.utils.randomPrivateKeyBytes();
65
+ const pubkey = secp256k1.getPublicKey(privKey, true);
66
+
67
+ // Timelocked policy: signature + relative timelock (older)
68
+ const { miniscript } = compilePolicy('and(pk(@bob),older(10))');
69
+
70
+ const descriptor = `wsh(${miniscript.replace('@bob', toHex(pubkey))})`;
71
+
72
+ // 1) Build the output description
73
+ const fundedOutput = new Output({ descriptor });
74
+ const address = fundedOutput.getAddress(); // Fund this address
75
+
76
+ // 2) Prepare transaction input/output
77
+ const psbt = new btc.Transaction();
78
+
79
+ const txHex = 'FUNDING_TX_HEX'; // hex of the tx that funded the address above
80
+ const vout = 0; // Output index (vout) of that UTXO within FUNDING_TX_HEX
81
+ const finalizeInput = fundedOutput.updatePsbtAsInput({ psbt, txHex, vout });
82
+
83
+ const recipient = new Output({
84
+ descriptor: 'addr(bc1qgw6xanldsz959z45y4dszehx4xkuzf7nfhya8x)'
85
+ });
86
+ recipient.updatePsbtAsOutput({ psbt, value: 10000n }); // input covers this+fees
87
+
88
+ // 3) Sign and finalize
89
+ signers.signPrivKey({ psbt, privKey });
90
+ finalizeInput({ psbt });
91
+
92
+ console.log('Push this: ' + psbt.hex);
93
+ ```
94
+
95
+ </details>
96
+
97
+ ## Features
98
+
99
+ - Parses and creates [Bitcoin Descriptors](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) (including those based on the [Miniscript language](https://bitcoinerlab.com/modules/miniscript)).
100
+ - Supports Taproot descriptors with trees: `tr(KEY,TREE)` (tapscript).
101
+ - Generates Partially Signed Bitcoin Transactions (PSBTs).
102
+ - Provides PSBT finalizers and signers for single-signature, BIP32 and Hardware Wallets (currently supports Ledger devices; more devices are planned).
103
+
104
+ ### Version Compatibility
105
+
106
+ Starting in `3.x`, `@bitcoinerlab/descriptors` is aligned with the modern bitcoinjs stack (`bitcoinjs-lib 7.x`).
107
+
108
+ In practical terms, this means:
109
+
110
+ - byte arrays are represented as `Uint8Array`;
111
+ - satoshi values are represented as `bigint`.
112
+
113
+ If you need older bitcoinjs versions, keep using `@bitcoinerlab/descriptors 2.x`.
114
+ If you want Taproot trees (`tr(KEY,TREE)`), use `3.x+`.
115
+
116
+ Starting in `3.1.x`, scure users can install
117
+ `@bitcoinerlab/descriptors-scure`. For advanced/custom backend wiring, use
118
+ `@bitcoinerlab/descriptors-core`.
119
+
120
+ ## Concepts
121
+
122
+ This library has two main capabilities related to Bitcoin descriptors. Firstly, it can generate `addresses` and `scriptPubKeys` from descriptors. These `addresses` and `scriptPubKeys` can be used to receive funds from other parties. Secondly, the library is able to sign transactions and spend unspent outputs described by those same descriptors. In order to do this, the descriptors must first be set into a PSBT.
123
+
124
+ If you are not familiar with _Bitcoin descriptors_ and _partially signed Bitcoin transactions (PSBTs)_, click on the section below to expand and read more about these concepts.
125
+
126
+ <details>
127
+ <summary>Concepts</summary>
128
+
129
+ ### Descriptors
130
+
131
+ In Bitcoin, a transaction consists of a set of inputs that are spent into a different set of outputs. Each input spends an output in a previous transaction. A Bitcoin descriptor is a string of text that describes the rules and conditions required to spend an output in a transaction.
132
+
133
+ For example, `wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)` is a descriptor that describes a pay-to-witness-public-key-hash (P2WPKH) type of output with the specified public key. If you know the corresponding private key for the transaction for which this descriptor is an output, you can spend it.
134
+
135
+ Descriptors can express much more complex conditions, such as multi-party cooperation, time-locked outputs and more. These conditions can be expressed using the Bitcoin Miniscript language, which is a way of writing Bitcoin Scripts in a structured and more easily understandable way.
136
+
137
+ ### Partially Signed Bitcoin Transactions (PSBTs)
138
+
139
+ A PSBT (Partially Signed Bitcoin Transaction) is a format for sharing Bitcoin transactions between different parties.
140
+
141
+ PSBTs come in handy when working with descriptors, especially when using scripts, because they allow multiple parties to collaborate in the signing process. This is especially useful when using hardware wallets or other devices that require separate signatures or authorizations.
142
+
143
+ </details>
144
+
145
+ ## Usage
146
+
147
+ Before we dive in, it's worth mentioning that we have several comprehensive guides available covering different aspects of the library. These guides provide explanations and code examples in interactive playgrounds, allowing you to see the changes in the output as you modify the code. This hands-on learning experience, combined with clear explanations, helps you better understand how to use the library effectively. [Check out the available guides here](https://bitcoinerlab.com/guides).
148
+
149
+ Furthermore, we've meticulously documented our API. For an in-depth look into Classes, functions and types, head over [here](https://bitcoinerlab.com/modules/descriptors/api).
150
+
151
+ For most users, install the package that matches your preferred Bitcoin library.
152
+ Use `@bitcoinerlab/descriptors-core` only if you want to wire the backend manually.
153
+
154
+ Bitcoinjs backend:
155
+
156
+ ```bash
157
+ npm install @bitcoinerlab/descriptors
158
+ ```
159
+
160
+ Scure/noble backend:
161
+
162
+ ```bash
163
+ npm install @bitcoinerlab/descriptors-scure
164
+ ```
165
+
166
+ If you plan to compile policy strings into Miniscript in your app, also install:
167
+
168
+ ```bash
169
+ npm install @bitcoinerlab/miniscript-policies
170
+ ```
171
+
172
+ The examples below keep the bitcoinjs stack visible by default. When
173
+ backend-specific code differs, a collapsible scure variant is shown right
174
+ below.
175
+
176
+ If you want to manage backend dependencies yourself, use
177
+ `@bitcoinerlab/descriptors-core`.
178
+
179
+ For a minimal end-to-end scure example, see [`test/integration/scure.ts`](test/integration/scure.ts).
180
+
181
+ The library can be split into four main parts:
182
+
183
+ - The `Output` class is the central component for managing descriptors. It facilitates the creation of outputs to receive funds and enables the signing and finalization of PSBTs (Partially Signed Bitcoin Transactions) for spending UTXOs (Unspent Transaction Outputs).
184
+ - PSBT signers and finalizers, which are used to manage the signing and finalization of PSBTs.
185
+ - `keyExpressions` and `scriptExpressions`, which provide functions to create key and standard descriptor expressions (strings) from structured data.
186
+ - Hardware wallet integration, which provides support for interacting with hardware wallets such as Ledger devices.
187
+
188
+ ### Output class
189
+
190
+ For normal usage, import `Output` directly from the preset package you chose.
191
+
192
+ ```javascript
193
+ import { Output } from '@bitcoinerlab/descriptors'; // bitcoinjs-ready package
194
+ ```
195
+
196
+ ```javascript
197
+ import { Output } from '@bitcoinerlab/descriptors-scure'; // scure-ready package
198
+ ```
199
+
200
+ <details>
201
+ <summary>Advanced initialization using @bitcoinerlab/descriptors-core</summary>
202
+
203
+ Use `@bitcoinerlab/descriptors-core` only if you want explicit backend binding.
204
+
205
+ Bitcoinjs-style core setup:
206
+
207
+ ```bash
208
+ npm install @bitcoinerlab/descriptors-core @bitcoinerlab/secp256k1 bitcoinjs-lib@7 bip32@5 ecpair@3
209
+ ```
210
+
211
+ ```javascript
212
+ import * as ecc from '@bitcoinerlab/secp256k1';
213
+ import { DescriptorsFactory } from '@bitcoinerlab/descriptors-core';
214
+
215
+ const { Output } = DescriptorsFactory(ecc);
216
+ ```
217
+
218
+ Scure-style core setup:
219
+
220
+ ```bash
221
+ npm install @bitcoinerlab/descriptors-core @scure/btc-signer@2 @scure/bip32@2 @noble/curves@2 @scure/base@2
222
+ ```
223
+
224
+ ```javascript
225
+ import { DescriptorsFactory } from '@bitcoinerlab/descriptors-core';
226
+ import { createScureLib } from '@bitcoinerlab/descriptors-core/scure';
227
+
228
+ const { Output } = DescriptorsFactory(createScureLib());
229
+ ```
230
+
231
+ </details>
232
+
233
+ Once set up, you can obtain an instance for an output, described by a descriptor such as a `wpkh`, as follows:
234
+
235
+ ```javascript
236
+ const wpkhOutput = new Output({
237
+ descriptor:
238
+ 'wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)'
239
+ });
240
+ ```
241
+
242
+ For advanced spend-path control (for example `signersPubKeys`, `taprootSpendPath`, and `tapLeaf`), see [Spending-path selection: from WSH Miniscript to Taproot](#spending-path-selection-from-wsh-miniscript-to-taproot).
243
+
244
+ Detailed information about constructor parameters can be found in [the API documentation](https://bitcoinerlab.com/modules/descriptors/api/classes/Output.html#constructor) and in [this Stack Exchange answer](https://bitcoin.stackexchange.com/a/118036/89665).
245
+
246
+ Some commonly used constructor parameters are:
247
+
248
+ - `index`: for ranged descriptors ending in `*`.
249
+ - `change`: for multipath key expressions such as `/**` or `/<0;1>/*`. For example, in `/<0;1>/*`, use `change: 0` or `change: 1`.
250
+
251
+ The `Output` class [offers various helpful methods](https://bitcoinerlab.com/modules/descriptors/api/classes/Output.html), including `getAddress()`, which returns the address associated with the descriptor, `getScriptPubKey()`, which returns the `scriptPubKey` for the descriptor, `expand()`, which decomposes a descriptor into its elemental parts, `updatePsbtAsInput()` and `updatePsbtAsOutput()`.
252
+
253
+ The library supports a wide range of descriptor types, including:
254
+
255
+ - Pay-to-Public-Key-Hash (P2PKH): `pkh(KEY)`
256
+ - Pay-to-Witness-Public-Key-Hash (P2WPKH): `wpkh(KEY)`
257
+ - Pay-to-Script-Hash (P2SH): `sh(SCRIPT)`
258
+ - Pay-to-Witness-Script-Hash (P2WSH): `wsh(SCRIPT)`
259
+ - Pay-to-Taproot (P2TR) with key-only or script tree: `tr(KEY)` and `tr(KEY,TREE)`
260
+ - Address-based descriptors: `addr(ADDRESS)`
261
+
262
+ These descriptors can be used with various key expressions, including raw public keys, BIP32 derivation paths and more.
263
+
264
+ For example, a Taproot descriptor with script leaves can look like:
265
+
266
+ ```
267
+ tr(INTERNAL_KEY,{pk(KEY_A),{pk(KEY_B),and_v(v:pk(KEY_C),older(144))}})
268
+ ```
269
+
270
+ This means the output has an internal-key spend path and additional script-path options inside the tree.
271
+
272
+ The `updatePsbtAsInput()` method is an essential part of the library, responsible for adding an input to the PSBT corresponding to the UTXO described by the descriptor. Additionally, when the descriptor expresses an absolute time-spending condition, such as "This UTXO can only be spent after block N", `updatePsbtAsInput()` adds timelock information to the PSBT.
273
+
274
+ To call `updatePsbtAsInput()`, use the following syntax:
275
+
276
+ ```javascript
277
+ import { Psbt } from '@bitcoinerlab/descriptors';
278
+ const psbt = new Psbt();
279
+ const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout, rbf });
280
+ ```
281
+
282
+ <details>
283
+ <summary>Click to see the scure variant</summary>
284
+
285
+ ```javascript
286
+ import { btc } from '@bitcoinerlab/descriptors-scure';
287
+
288
+ const psbt = new btc.Transaction();
289
+ const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout, rbf });
290
+ ```
291
+
292
+ </details>
293
+
294
+ Here, `psbt` refers to either a [bitcoinjs-lib `Psbt` class](https://github.com/bitcoinjs/bitcoinjs-lib) instance or an `@scure/btc-signer` `Transaction`, depending on the backend you chose. The parameter `txHex` denotes a hex string that serializes the previous transaction containing this output. Meanwhile, `vout` is an integer that marks the position of the output within that transaction. Finally, `rbf` is an optional parameter (defaulting to `true`) used to indicate whether the transaction uses Replace-By-Fee (RBF). When RBF is enabled, transactions can be replaced while they are in the mempool with others that have higher fees. Note that RBF is enabled for the entire transaction if at least one input signals it. Also, note that transactions using relative time locks inherently opt into RBF due to the `nSequence` range used.
295
+
296
+ The method returns the `inputFinalizer()` function. This finalizer function completes a PSBT input by adding the unlocking script (`scriptWitness` or `scriptSig`) that satisfies the previous output's spending conditions. Bear in mind that both `scriptSig` and `scriptWitness` incorporate signatures. As such, you should complete all necessary signing operations before calling `inputFinalizer()`. Detailed [explanations on the `inputFinalizer` method](#signers-and-finalizers-finalize-psbt-input) can be found in the Signers and Finalizers section.
297
+
298
+ Similarly, `updatePsbtAsOutput` allows you to add an output to a PSBT. For instance, to configure a `psbt` that sends `10,000` sats to the SegWit address `bc1qgw6xanldsz959z45y4dszehx4xkuzf7nfhya8x`:
299
+
300
+ ```javascript
301
+ const recipientOutput = new Output({
302
+ descriptor: `addr(bc1qgw6xanldsz959z45y4dszehx4xkuzf7nfhya8x)`
303
+ });
304
+ recipientOutput.updatePsbtAsOutput({ psbt, value: 10000n });
305
+ ```
306
+
307
+ For further information on using the `Output` class, refer to the [comprehensive guides](https://bitcoinerlab.com/guides) that offer explanations and playgrounds to help learn the module. For specific details on the methods, refer directly to [the API](https://bitcoinerlab.com/modules/descriptors/api/classes/Output.html).
308
+
309
+ #### Parsing Descriptors with `expand()`
310
+
311
+ Most applications do not need `expand()` for normal receive/spend flows. It is mainly useful for debugging and introspection (for example, checking expanded expressions, key mappings, scripts, and parsed metadata).
312
+
313
+ ```javascript
314
+ import { expand } from '@bitcoinerlab/descriptors'; // or '@bitcoinerlab/descriptors-scure' for scure
315
+
316
+ const info = expand({ descriptor });
317
+ ```
318
+
319
+ For full details on returned fields, refer to [the API](https://bitcoinerlab.com/modules/descriptors/api/types/Expansion.html).
320
+
321
+ #### Spending-path selection: from WSH Miniscript to Taproot
322
+
323
+ When a descriptor has more than one valid way to spend, the library needs to know which path you intend to use.
324
+
325
+ For `wsh(miniscript)` and `sh(wsh(miniscript))`, this is usually done with `signersPubKeys`: you pass the public keys expected to sign and the library selects the most optimal satisfiable branch.
326
+
327
+ `signersPubKeys` is passed as an array of public keys (`Uint8Array[]`).
328
+ If omitted, the library assumes all descriptor keys may sign.
329
+
330
+ Example with two BIP32-derived keys and one preferred signer:
331
+
332
+ ```javascript
333
+ import { randomBytes } from 'crypto';
334
+ import { BIP32, Output, keyExpressionBIP32 } from '@bitcoinerlab/descriptors';
335
+
336
+ const masterNode = BIP32.fromSeed(randomBytes(32));
337
+
338
+ const originPath = "/84'/0'/0'";
339
+
340
+ const keyPathA = '/0/0';
341
+ const keyPathB = '/0/1';
342
+
343
+ const keyExprA = keyExpressionBIP32({ masterNode, originPath, keyPath: keyPathA });
344
+ const keyExprB = keyExpressionBIP32({ masterNode, originPath, keyPath: keyPathB });
345
+
346
+ const signerPubKeyA = masterNode.derivePath(`m${originPath}${keyPathA}`).publicKey;
347
+
348
+ // Two possible branches:
349
+ // - branch 1: signature by keyA + older(10)
350
+ // - branch 2: signature by keyB (no timelock)
351
+ const output = new Output({
352
+ descriptor: `wsh(andor(pk(${keyExprA}),older(10),pk(${keyExprB})))`,
353
+ signersPubKeys: [signerPubKeyA] // choose the keyA (timelock branch)
354
+ });
355
+ ```
356
+
357
+ <details>
358
+ <summary>Click to see the scure variant</summary>
359
+
360
+ ```javascript
361
+ import { randomBytes } from '@noble/hashes/utils.js';
362
+ import { HDKey, Output, keyExpressionBIP32 } from '@bitcoinerlab/descriptors-scure';
363
+
364
+ const masterNode = HDKey.fromMasterSeed(randomBytes(32));
365
+
366
+ const originPath = "/84'/0'/0'";
367
+
368
+ const keyPathA = '/0/0';
369
+ const keyPathB = '/0/1';
370
+
371
+ const keyExprA = keyExpressionBIP32({ masterNode, originPath, keyPath: keyPathA });
372
+ const keyExprB = keyExpressionBIP32({ masterNode, originPath, keyPath: keyPathB });
373
+
374
+ const signerPubKeyA = masterNode.derive(`m${originPath}${keyPathA}`).publicKey;
375
+
376
+ const output = new Output({
377
+ descriptor: `wsh(andor(pk(${keyExprA}),older(10),pk(${keyExprB})))`,
378
+ signersPubKeys: [signerPubKeyA]
379
+ });
380
+ ```
381
+
382
+ </details>
383
+
384
+ Taproot uses the same idea. For `tr(KEY,TREE)`, `signersPubKeys` helps determine which leaves are satisfiable and which satisfiable path is more optimal. In addition, Taproot provides two optional controls:
385
+
386
+ - `taprootSpendPath` (`'key' | 'script'`) to force key-path or script-path spending.
387
+ - `tapLeaf` to force a specific script leaf when using script path.
388
+
389
+ If `taprootSpendPath` is omitted for `tr(KEY,TREE)`, the library uses script path and auto-selects the most optimal satisfiable leaf from available spending data (including `signersPubKeys` and preimages when relevant).
390
+
391
+ If you specifically plan to spend from the internal key, set:
392
+
393
+ ```javascript
394
+ new Output({
395
+ descriptor: 'tr(INTERNAL_KEY,{pk(KEY_A),pk(KEY_B)})',
396
+ taprootSpendPath: 'key'
397
+ });
398
+ ```
399
+
400
+ If you want to force a specific script leaf:
401
+
402
+ ```javascript
403
+ new Output({
404
+ descriptor: 'tr(INTERNAL_KEY,{pk(KEY_A),pk(KEY_B)})',
405
+ taprootSpendPath: 'script',
406
+ tapLeaf: 'pk(KEY_A)'
407
+ });
408
+ ```
409
+
410
+ These spending-path parameters (`signersPubKeys`, `taprootSpendPath`, `tapLeaf`) are only needed when spending/finalizing UTXOs with multiple candidate paths. They are not needed just to derive addresses or `scriptPubKeys`.
411
+
412
+ For a focused walkthrough of constructor choices (including `signersPubKeys`) and practical usage of `updatePsbtAsInput`, `getAddress` and `getScriptPubKey`, see this [Stack Exchange answer](https://bitcoin.stackexchange.com/a/118036/89665).
413
+
414
+ ### Signers and Finalizers
415
+
416
+ This library encompasses a PSBT finalizer as well as signer helpers for single-key, BIP32 and Ledger flows.
417
+
418
+ To incorporate these functionalities, use the following import statement:
419
+
420
+ ```javascript
421
+ import { signers } from '@bitcoinerlab/descriptors' // or '@bitcoinerlab/descriptors-scure' for scure
422
+ ```
423
+
424
+ For signing operations, utilize the methods provided by the [`signers`](https://bitcoinerlab.com/modules/descriptors/api/modules/signers.html):
425
+
426
+ ```javascript
427
+ // `psbt` here is a bitcoinjs-lib `Psbt` (for example: `const psbt = new Psbt()`)
428
+
429
+ // For Ledger
430
+ await signers.signLedger({ psbt, ledgerManager });
431
+
432
+ // For BIP32 - https://github.com/bitcoinjs/bip32
433
+ signers.signBIP32({ psbt, masterNode }); // Here, `masterNode` is a bitcoinjs `BIP32Interface` (see examples above)
434
+
435
+ // For ECPair - https://github.com/bitcoinjs/ecpair
436
+ signers.signECPair({ psbt, ecpair }); // Here, `ecpair` is a bitcoinjs `ECPairInterface`
437
+ ```
438
+
439
+ <details>
440
+ <summary>Click to see the scure variant</summary>
441
+
442
+ ```javascript
443
+ // `psbt` here is an `@scure/btc-signer` `Transaction`
444
+ // (for example: `const psbt = new btc.Transaction()`)
445
+
446
+ // For BIP32 with @scure/bip32
447
+ signers.signBIP32({ psbt, masterNode }); // Here, `masterNode` is an `HDKey` (see examples above)
448
+
449
+ // For raw private keys
450
+ signers.signPrivKey({ psbt, privKey }); // Here, `privKey` is a 32-byte `Uint8Array`
451
+ signers.signInputPrivKey({ psbt, index: 0, privKey }); // Same `privKey` type as above
452
+
453
+ // For Ledger
454
+ await signers.signLedger({ psbt, ledgerManager }); // Again, `masterNode` is an `HDKey`
455
+ ```
456
+
457
+ </details>
458
+
459
+ Detailed information on Ledger integration will be provided in subsequent sections.
460
+
461
+ <a name="signers-and-finalizers-finalize-psbt-input"></a>
462
+
463
+ #### Finalizing the `psbt`
464
+
465
+ When finalizing the `psbt`, the [`updatePsbtAsInput` method](https://bitcoinerlab.com/modules/descriptors/api/classes/Output.html#updatePsbtAsInput) plays a key role. When invoked, the `output.updatePsbtAsInput()` sets up the `psbt` by designating the output as an input and, if required, adjusts the transaction locktime. In addition, it returns a `inputFinalizer` function tailored for this specific `psbt` input.
466
+
467
+ ##### Procedure
468
+
469
+ 1. For each unspent output from a previous transaction that you're referencing in a `psbt` as an input to be spent, call the `updatePsbtAsInput` method:
470
+
471
+ ```javascript
472
+ const inputFinalizer = output.updatePsbtAsInput({ psbt, txHex, vout });
473
+ ```
474
+
475
+ 2. Once you've completed the necessary signing operations on the `psbt`, use the returned finalizer function on each input:
476
+
477
+ ```javascript
478
+ inputFinalizer({ psbt });
479
+ ```
480
+
481
+ ##### Important Notes
482
+
483
+ - The finalizer function returned from `updatePsbtAsInput` adds the necessary unlocking script (`scriptWitness` or `scriptSig`) that satisfies the `Output`'s spending conditions. Remember, both `scriptSig` and `scriptWitness` contain signatures. Ensure that all necessary signing operations are completed before finalizing.
484
+
485
+ - When using `updatePsbtAsInput`, the `txHex` parameter is crucial. For Segwit inputs, you can choose to pass `txId` and `value` instead of `txHex`. However, ensure the accuracy of the `value` to avoid potential fee attacks. When unsure, use `txHex` and skip `txId` and `value`.
486
+
487
+ - Hardware wallets require the [full `txHex` for Segwit](https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd).
488
+
489
+ ### Key Expressions and Script Expressions
490
+
491
+ This library also provides a series of function helpers designed to streamline the generation of `descriptor` strings. These strings can serve as input parameters in the `Output` class constructor. These helpers are nested within the `scriptExpressions` module. You can import them as illustrated below:
492
+
493
+ ```javascript
494
+ import { scriptExpressions } from '@bitcoinerlab/descriptors'; // or '@bitcoinerlab/descriptors-scure' for scure
495
+ ```
496
+
497
+ Within the `scriptExpressions` module, there are functions designed to generate descriptors for commonly used scripts. Some examples include `pkhBIP32()`, `shWpkhBIP32()`, `wpkhBIP32()`, `pkhLedger()`, `shWpkhLedger()` and `wpkhLedger()`. Refer to [the API](https://bitcoinerlab.com/modules/descriptors/api/modules/scriptExpressions.html) for a detailed list and further information.
498
+
499
+ When using BIP32-based descriptors, the following parameters are required for the `scriptExpressions` functions:
500
+
501
+ ```javascript
502
+ pkhBIP32(params: {
503
+ masterNode: BIP32Interface; //bitcoinjs-lib BIP32 - https://github.com/bitcoinjs/bip32
504
+ network?: Network; //A bitcoinjs-lib network
505
+ account: number;
506
+ change?: number | undefined; //0 -> external (receive), 1 -> internal (change)
507
+ index?: number | undefined | '*';
508
+ keyPath?: string; //You can use change & index or a keyPath such as "/0/0"
509
+ isPublic?: boolean; //Whether to use xpub or xprv
510
+ })
511
+ ```
512
+
513
+ <details>
514
+ <summary>Click to see the scure variant</summary>
515
+
516
+ ```javascript
517
+ pkhBIP32(params: {
518
+ masterNode: HDKey; // @scure/bip32 - https://github.com/paulmillr/scure-bip32
519
+ network?: Network;
520
+ account: number;
521
+ change?: number | undefined;
522
+ index?: number | undefined | '*';
523
+ keyPath?: string;
524
+ isPublic?: boolean;
525
+ })
526
+ ```
527
+
528
+ </details>
529
+
530
+ For functions suffixed with _Ledger_ (designed to generate descriptors for Ledger Hardware devices), replace `masterNode` with `ledgerManager`. Detailed information on Ledger integration will be provided in the following section.
531
+
532
+ The `keyExpressions` category includes functions that generate string representations of key expressions for public keys.
533
+
534
+ This library includes the following `keyExpressions`: [`keyExpressionBIP32`](https://bitcoinerlab.com/modules/descriptors/api/functions/keyExpressionBIP32.html) and [`keyExpressionLedger`](https://bitcoinerlab.com/modules/descriptors/api/functions/keyExpressionLedger.html). They can be imported as follows:
535
+
536
+ ```javascript
537
+ import { keyExpressionBIP32, keyExpressionLedger } from '@bitcoinerlab/descriptors'; // or '@bitcoinerlab/descriptors-scure' for scure
538
+ ```
539
+
540
+ The parameters required for these functions are:
541
+
542
+ ```javascript
543
+ function keyExpressionBIP32({
544
+ masterNode: BIP32Interface; //bitcoinjs-lib BIP32 - https://github.com/bitcoinjs/bip32
545
+ originPath: string;
546
+ change?: number | undefined; //0 -> external (receive), 1 -> internal (change)
547
+ index?: number | undefined | '*';
548
+ keyPath?: string | undefined; //In the case of the Ledger, keyPath can also use multipath (e.g. /<0;1>/number)
549
+ isPublic?: boolean;
550
+ });
551
+ ```
552
+
553
+ <details>
554
+ <summary>Click to see the scure variant</summary>
555
+
556
+ ```javascript
557
+ function keyExpressionBIP32({
558
+ masterNode: HDKey; // @scure/bip32 - https://github.com/paulmillr/scure-bip32
559
+ originPath: string;
560
+ change?: number | undefined;
561
+ index?: number | undefined | '*';
562
+ keyPath?: string | undefined;
563
+ isPublic?: boolean;
564
+ });
565
+ ```
566
+
567
+ </details>
568
+
569
+ For the `keyExpressionLedger` function, use `ledgerManager` instead of `masterNode`.
570
+
571
+ Both functions will generate strings that fully define BIP32 keys. For example:
572
+
573
+ ```text
574
+ [d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*
575
+ ```
576
+
577
+ Read [Bitcoin Core descriptors documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) to learn more about Key Expressions.
578
+
579
+ ### Hardware Wallet Integration
580
+
581
+ This library currently provides integration with Ledger wallets. Support for more devices is planned.
582
+
583
+ Before we dive in, note that, in addition to the documentation below, it is highly recommended to visit the [Ledger Playground](https://bitcoinerlab.com/guides/ledger-programming) with an interactive code sandbox of this lib interacting with a Ledger device.
584
+
585
+ To use this library with Ledger devices, you must first install Ledger support:
586
+
587
+ ```bash
588
+ npm install @ledgerhq/ledger-bitcoin @ledgerhq/hw-transport-node-hid
589
+ ```
590
+
591
+ For Ledger device signing, import the necessary functions as follows:
592
+
593
+ ```javascript
594
+ import Transport from '@ledgerhq/hw-transport-node-hid'; //or hw-transport-web-hid, for web
595
+ import { AppClient } from '@ledgerhq/ledger-bitcoin';
596
+ import { Output, ledger, networks } from '@bitcoinerlab/descriptors'; // or '@bitcoinerlab/descriptors-scure' for scure
597
+ ```
598
+
599
+ Then, use the following code to assert that the Ledger app is running Bitcoin Test version 2.1.0 or higher and to create a new Ledger client:
600
+
601
+ ```javascript
602
+ const transport = await Transport.create();
603
+ //Throws if not running Bitcoin Test >= 2.1.0
604
+ await ledger.assertLedgerApp({ transport, name: 'Bitcoin Test', minVersion: '2.1.0' });
605
+
606
+ const ledgerClient = new AppClient(transport);
607
+ const ledgerManager = {
608
+ ledgerClient,
609
+ ledgerState: {},
610
+ Output,
611
+ network: networks.testnet
612
+ };
613
+ ```
614
+
615
+ Here, `transport` is an instance of a Transport object that allows communication with Ledger devices. You can use any of the transports [provided by Ledger](https://github.com/LedgerHQ/ledger-live#libs---libraries).
616
+
617
+ To register the policies of non-standard descriptors on the Ledger device, use the following code:
618
+
619
+ ```javascript
620
+ await ledger.registerLedgerWallet({
621
+ ledgerManager,
622
+ descriptor: wshDescriptor,
623
+ policyName: 'BitcoinerLab'
624
+ });
625
+ ```
626
+
627
+ This code will auto-skip the policy registration process if it already exists. Please refer to [Ledger documentation](https://github.com/LedgerHQ/app-bitcoin-new/blob/develop/doc/wallet.md) to learn more about their Wallet Policies registration procedures.
628
+
629
+ Finally, `ledgerManager.ledgerState` is an object used to store information related to Ledger devices. Although Ledger devices themselves are stateless, this object can be used to store information such as xpubs, master fingerprints and wallet policies. You can pass an initially empty object that will be updated with more information as it is used. The object can be serialized and stored for future use.
630
+
631
+ The [API reference for the ledger module](https://bitcoinerlab.com/modules/descriptors/api/variables/ledger.html) provides a comprehensive list of functions related to the Ledger Hardware Wallet, along with detailed explanations of their parameters and behavior.
632
+
633
+ <a name="documentation"></a>
634
+
635
+ ## Additional Resources
636
+
637
+ For more information, refer to the following resources:
638
+
639
+ - **[Guides](https://bitcoinerlab.com/guides)**: Comprehensive explanations and playgrounds to help you learn how to use the module.
640
+ - **[API](https://bitcoinerlab.com/modules/descriptors/api)**: Dive into the details of the Classes, functions and types.
641
+ - **[Stack Exchange answer](https://bitcoin.stackexchange.com/a/118036/89665)**: Focused explanation on the constructor, specifically the `signersPubKeys` parameter and the usage of `updatePsbtAsInput`, `getAddress` and `getScriptPubKey`.
642
+ - **[Integration tests](https://github.com/bitcoinerlab/descriptors/tree/main/test/integration)**: Well-commented code examples showcasing the usage of all functions in the module.
643
+ - **Local Documentation**: Generate comprehensive API documentation from the source code:
644
+
645
+ ```bash
646
+ git clone https://github.com/bitcoinerlab/descriptors
647
+ cd descriptors/
648
+ npm install
649
+ npm run docs
650
+ ```
651
+
652
+ The generated documentation will be available in the `docs/` directory. Open the `index.html` file to view the documentation.
653
+
654
+ ## Authors and Contributors
655
+
656
+ The project was initially developed and is currently maintained by [Jose-Luis Landabaso](https://github.com/landabaso). Contributions and help from other developers are welcome.
657
+
658
+ Here are some resources to help you get started with contributing:
659
+
660
+ ### Building from source
661
+
662
+ To download the source code and build the project, follow these steps:
663
+
664
+ 1. Clone the repository:
665
+
666
+ ```bash
667
+ git clone https://github.com/bitcoinerlab/descriptors.git
668
+ ```
669
+
670
+ 2. Install the dependencies:
671
+
672
+ ```bash
673
+ npm install
674
+ ```
675
+
676
+ 3. Build the project:
677
+
678
+ ```bash
679
+ npm run build
680
+ ```
681
+
682
+ This will build the project and generate the necessary files in the `dist` directory.
683
+
684
+ ### Testing
685
+
686
+ Before committing any code, make sure it passes all tests.
687
+
688
+ Run unit tests:
689
+
690
+ ```bash
691
+ npm run test:unit
692
+ ```
693
+
694
+ Run the full test pipeline (lint + build + unit + integration):
695
+
696
+ ```bash
697
+ npm run test
698
+ ```
699
+
700
+ Integration tests require Docker. Make sure the `docker` command is installed and available in your PATH. When integration tests run, they automatically start or reuse a local container with the regtest services needed by this repository.
701
+
702
+ And, in case you have a Ledger device:
703
+
704
+ ```bash
705
+ npm run test:integration:ledger
706
+ ```
707
+
708
+ ### License
709
+
710
+ This project is licensed under the MIT License.