@bitcoinerlab/descriptors 0.2.1 → 0.3.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/README.md CHANGED
@@ -1,18 +1,325 @@
1
- ## Bitcoin Descriptors Library *Pre Release*
1
+ # Bitcoin Descriptors Library
2
2
 
3
3
  This library is designed to parse and create Bitcoin Descriptors, including Miniscript, and generate Partially Signed Bitcoin Transactions (PSBTs). It also provides PSBT finalizers and signers for single-signature, BIP32, and Hardware Wallets.
4
4
 
5
- **NOTE: This is a pre-release version only.** The Usage documentation is still being finalized. However, you can take a look at the [integration tests](https://github.com/bitcoinerlab/descriptors/tree/main/test/integration) for some examples of how to use this library.
5
+ ## Features
6
6
 
7
- ### Features
8
-
9
- - Parses and creates Bitcoin Descriptors (including those based on the Miniscript language).
7
+ - 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)).
10
8
  - Generates Partially Signed Bitcoin Transactions (PSBTs).
11
9
  - Provides PSBT finalizers and signers for single-signature, BIP32, and Hardware Wallets (currently supports Ledger devices; more devices are planned).
12
10
 
13
- ### Usage
11
+ ## Concepts
12
+
13
+ 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 and spend unspent outputs described by those same descriptors. In order to do this, the descriptors must first be set into a PSBT.
14
+
15
+ 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.
16
+
17
+ <details>
18
+ <summary>Concepts</summary>
19
+
20
+ ### Descriptors
21
+
22
+ 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.
23
+
24
+ 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.
25
+
26
+ 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.
27
+
28
+ ### Partially Signed Bitcoin Transactions (PSBTs)
29
+
30
+ A PSBT (Partially Signed Bitcoin Transaction) is a format for sharing Bitcoin transactions between different parties.
31
+
32
+ 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.
33
+
34
+ </details>
35
+
36
+ ## Usage
37
+
38
+ 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).
39
+
40
+ To use this library (and accompanying libraries), you can install them using:
41
+
42
+ ```bash
43
+ npm install @bitcoinerlab/descriptors
44
+ npm install @bitcoinerlab/miniscript
45
+ npm install @bitcoinerlab/secp256k1
46
+ ```
47
+
48
+ The library can be split into four main parts:
49
+
50
+ - The `Descriptor` class, which is the core component that parses descriptors and can be used to finalize partially signed Bitcoin transactions (PSBTs).
51
+ - `keyExpressions` and `scriptExpressions`, which provide functions to create descriptor and key expressions (strings) from structured data, making it easier to work with complex descriptors.
52
+ - PSBT signers and finalizers, which are used to manage the signing and finalization of PSBTs.
53
+ - Hardware wallet integration, which provides support for interacting with hardware wallets such as Ledger devices.
54
+
55
+ ### Descriptor class
56
+
57
+ The Descriptor class is created dynamically by providing a cryptographic secp256k1 engine as shown below:
58
+
59
+ ```javascript
60
+ import * as secp256k1 from '@bitcoinerlab/secp256k1';
61
+ import * as descriptors from '@bitcoinerlab/descriptors';
62
+ const { Descriptor } = descriptors.DescriptorsFactory(secp256k1);
63
+ ```
64
+
65
+ After that, you can obtain an instance for a descriptor expression, such as a wpkh expression, like this:
66
+
67
+ ```javascript
68
+ const wpkhDescriptor = new Descriptor({
69
+ expression:
70
+ 'wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)'
71
+ });
72
+ ```
73
+
74
+ Here are the parameters that can be used to create a `new Descriptor`:
75
+
76
+ ```javascript
77
+ constructor({
78
+ expression, // The descriptor string in ASCII format. It may include a "*"
79
+ // to denote an arbitrary index.
80
+ index, // The descriptor's index in the case of a range descriptor
81
+ // (must be an integer >= 0).
82
+ checksumRequired = false, // Flag indicating if the descriptor is required
83
+ // to include a checksum.
84
+ allowMiniscriptInP2SH = false, // Flag indicating if this instance can parse
85
+ // and generate script satisfactions for
86
+ // sh(miniscript) top-level expressions of
87
+ // miniscripts. This is not recommended.
88
+ network = networks.bitcoin, // One of bitcoinjs-lib `networks`
89
+ // (https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/networks.js)
90
+ // or another one with the same interface.
91
+ preimages = [], // An array of preimages of type `Preimage`: `Preimage[]`.
92
+ // This info is necessary to finalize Psbts.
93
+ signersPubKeys // (Optional): An array of the public keys used for signing
94
+ // the transaction when spending the output associated with
95
+ // this descriptor. This parameter is only used if the
96
+ // descriptor object is being used to finalize a transaction.
97
+ // It is necessary to specify the spending path when working
98
+ // with miniscript-based expressions that have multiple
99
+ // spending paths. Set this parameter to an array containing
100
+ // the public keys involved in the desired spending path.
101
+ // Leave it `undefined` if you only need to generate the
102
+ // `scriptPubKey` or `address` for a descriptor, or if all
103
+ // the public keys involved in the descriptor will sign the
104
+ // transaction. In the latter case, the satisfier will
105
+ // automatically choose the most optimal spending path in terms
106
+ // of tx size (if more than one path is available).
107
+ // For more details on using this parameter, refer to this
108
+ // Stack Exchange answer: https://bitcoin.stackexchange.com/a/118036/89665
109
+ }: DescriptorInfo);
110
+ ```
111
+
112
+ The `Descriptor` class offers various helpful methods, 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, `updatePsbt()` and `finalizePsbt()`.
113
+
114
+ The `updatePsbt()` method is an essential part of the library, responsible for adding an input to the PSBT corresponding to the UTXO (unspent transaction output) 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," `updatePsbt()` adds timelock information to the PSBT.
115
+
116
+ To call `updatePsbt()`, use the following syntax:
117
+
118
+ ```javascript
119
+ const inputIndex = descriptor.updatePsbt({ psbt, txHex, vout });
120
+ ```
121
+
122
+ Here, `psbt` is an instance of a [bitconjs-lib Psbt class](https://github.com/bitcoinjs/bitcoinjs-lib), `txHex` is the hex string that serializes the previous transaction, and `vout` is an integer corresponding to the output index of the descriptor in the previous transaction. The method returns a number that corresponds to the input number that this descriptor will take in the `psbt`.
123
+
124
+ The `finalizePsbt()` method is the final step in adding the unlocking script (scriptWitness or scriptSig) that satisfies the spending condition to the transaction, effectively finalizing the Psbt. It should be called after all necessary signing operations have been completed. The syntax for calling this method is as follows:
125
+
126
+ ```javascript
127
+ descriptor.finalizePsbt({ index, psbt });
128
+ ```
129
+
130
+ Here, `index` is the `inputIndex` obtained from the `updatePsbt()` method and `psbt` is an instance of a bitcoinjs-lib `Psbt` object.
131
+
132
+ For further information on using the Descriptor class, refer to the [comprehensive guides](https://bitcoinerlab.com/guides) that offer explanations and playgrounds to help learn the module. Additionally, a [Stack Exchange answer](https://bitcoin.stackexchange.com/a/118036/89665) provides a focused explanation on the constructor, specifically the `signersPubKeys` parameter, and the usage of `updatePsbt`, `finalizePsbt`, `getAddress`, and `getScriptPubKey`.
133
+
134
+ #### Tip: Parsing descriptors without instantiating a class
135
+
136
+ `DescriptorsFactory` provides a convenient `expand()` function that allows you to parse a descriptor expression without the need to instantiate the `Descriptor` class. This function can be used as follows:
137
+
138
+ ```javascript
139
+ const { expand } = descriptors.DescriptorsFactory(secp256k1);
140
+ const result = expand({
141
+ expression: 'sh(wsh(andor(pk(0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2),older(8640),pk([d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*))))',
142
+ network: networks.testnet, // One of bitcoinjs-lib `networks`
143
+ // (https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/networks.js)
144
+ // or another one with the same interface.
145
+ allowMiniscriptInP2SH: true // Optional flag to allow miniscript in P2SH
146
+ });
147
+ ```
148
+
149
+ The `expand()` function returns an object with the following properties:
150
+
151
+ - `payment: Payment | undefined`: The corresponding [bitcoinjs-lib Payment](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts) for the provided expression, if applicable.
152
+ - `expandedExpression: string | undefined`: The expanded descriptor expression.
153
+ - `miniscript: string | undefined`: The extracted miniscript from the expression, if any.
154
+ - `expansionMap: ExpansionMap | undefined`: A map of key expressions in the descriptor to their corresponding expanded keys.
155
+ - `isSegwit: boolean | undefined`: A boolean indicating whether the descriptor represents a SegWit script.
156
+ - `expandedMiniscript: string | undefined`: The expanded miniscript, if any.
157
+ - `redeemScript: Buffer | undefined`: The redeem script for the descriptor, if applicable.
158
+ - `witnessScript: Buffer | undefined`: The witness script for the descriptor, if applicable.
159
+
160
+ For the example expression provided, the `expandedExpression` and a portion of the `expansionMap` would be as follows:
161
+
162
+ ```javascript
163
+ // expression: 'sh(wsh(andor(pk(0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2),older(8640),pk([d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*))))'
164
+
165
+ expandedExpression: 'sh(wsh(andor(pk(@0),older(8640),pk(@1))))',
166
+ expansionMap: {
167
+ '@0': {
168
+ keyExpression:
169
+ '0252972572d465d016d4c501887b8df303eee3ed602c056b1eb09260dfa0da0ab2'
170
+ },
171
+ '@1': {
172
+ keyExpression:
173
+ "[d34db33f/49'/0'/0']tpubDCdxmvzJ5QBjTN8oCjjyT2V58AyZvA1fkmCeZRC75QMoaHcVP2m45Bv3hmnR7ttAwkb2UNYyoXdHVt4gwBqRrJqLUU2JrM43HippxiWpHra/1/2/3/4/*",
174
+ keyPath: '/1/2/3/4/*',
175
+ originPath: "/49'/0'/0'",
176
+ path: "m/49'/0'/0'/1/2/3/4/*",
177
+ // Other relevant properties returned: `pubkey`, `ecpair` & `bip32` interfaces, `masterFingerprint`, etc.
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### keyExpressions and scriptExpressions
183
+
184
+ This library also includes a set of function helpers that facilitate the generation of the `expression` parameter in the constructor of the `Descriptor` class. These helpers are located under the `scriptExpressions` module, which can be imported using the following statement:
185
+
186
+ ```javascript
187
+ import { scriptExpressions } from '@bitcoinerlab/descriptors';
188
+ ```
189
+
190
+ `scriptExpressions` includes functions that generate script expressions for commonly used script expressions. Some of the available functions are `pkhBIP32()`, `shWpkhBIP32`, `wpkhBIP32`, `pkhLedger()`, `shWpkhLedger` and `wpkhLedger`.
14
191
 
15
- Usage instructions will be added to this README as soon as they are finalized. In the meantime, please refer to the [integration tests](https://github.com/bitcoinerlab/descriptors/tree/main/test/integration) for some examples of how to use this library.
192
+ When using BIP32-based descriptors, the following parameters are required for the `scriptExpressions` functions:
193
+
194
+ ```javascript
195
+ pkhBIP32(params: {
196
+ masterNode: BIP32Interface; //A bitcoinjs-lib instance of a BIP32 object.
197
+ network?: Network; //A bitcoinjs-lib network
198
+ account: number;
199
+ change?: number | undefined; //0 -> external (receive), 1 -> internal (change)
200
+ index?: number | undefined | '*';
201
+ keyPath?: string; //You can use change & index or a keyPath such as "/0/0"
202
+ isPublic?: boolean; //Whether to use xpub or xprv
203
+ })
204
+ ```
205
+
206
+ For Ledger, `ledgerClient` and `ledgerState` are used instead of `masterNode`. These will be explained later when we discuss Ledger integration.
207
+
208
+ The `keyExpressions` category includes functions that generate string representations of key expressions for public keys. This is useful when working with miniscript-based descriptors.
209
+
210
+ This library includes the following `keyExpressions`: `keyExpressionBIP32` and `keyExpressionLedger`. They can be imported as follows:
211
+
212
+ ```javascript
213
+ import {
214
+ keyExpressionBIP32,
215
+ keyExpressionLedger
216
+ } from '@bitcoinerlab/descriptors';
217
+ ```
218
+
219
+ The parameters required for these functions are:
220
+
221
+ ```javascript
222
+ function keyExpressionBIP32({
223
+ masterNode: BIP32Interface;
224
+ originPath: string;
225
+ change?: number | undefined; //0 -> external (receive), 1 -> internal (change)
226
+ index?: number | undefined | '*';
227
+ keyPath?: string | undefined; //In the case of the Ledger, keyPath must be /<1;0>/number
228
+ isPublic?: boolean;
229
+ });
230
+ ```
231
+
232
+ For Ledger, `ledgerClient` and `ledgerState` are used instead of `masterNode`.
233
+
234
+ Both functions will generate strings that fully define BIP32 keys. For example: `[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*`. Read [Bitcoin Core descriptors documentation](https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md) to learn more about Key Expressions.
235
+
236
+ ### Signers and Finalizers
237
+
238
+ This library provides a Psbt finalizer and three types of signers: ECPair for single-signature, BIP32, and Ledger (for Ledger Wallet devices, with plans for other devices).
239
+
240
+ To use them, import them as follows:
241
+
242
+ ```javascript
243
+ import { signers, finalizePsbt } from '@bitcoinerlab/descriptors';
244
+ ```
245
+
246
+ To sign with the signers:
247
+
248
+ ```javascript
249
+ await signers.signLedger({
250
+ ledgerClient,
251
+ ledgerState,
252
+ psbt,
253
+ descriptors: psbtInputDescriptors
254
+ });
255
+ //Here psbtInputDescriptors is an array of descriptors odered by their respective inputIndex in the psbt
256
+ signers.signBIP32({ psbt, masterNode });
257
+ signers.signECPair({ psbt, ecpair }); //Where ecpair is an instance of bitcoinjs-lib ECPairInterface
258
+ ```
259
+
260
+ To finalize the `psbt`, you can either call the method `finalizePsbtInput({ index, psbt })` on each descriptor, passing as arguments the `psbt` and its input `index`, or call the helper function: `finalizePsbt({psbt, descriptors })`. In the latter case, `descriptors` is an array of descriptors ordered by their respective input index in the `psbt`.
261
+
262
+ ### Hardware Wallet Integration
263
+
264
+ This library currently provides integration with Ledger wallets. Support for more devices is planned.
265
+
266
+ To use a Ledger device for signing, you can import the necessary functions as follows:
267
+
268
+ ```javascript
269
+ import { ledger } from '@bitcoinerlab/descriptors';
270
+ ```
271
+
272
+ You can 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:
273
+
274
+ ```javascript
275
+ //Throws if not running Bitcoin Test >= 2.1.0
276
+ await ledger.assertLedgerApp({
277
+ transport,
278
+ name: 'Bitcoin Test',
279
+ minVersion: '2.1.0'
280
+ });
281
+
282
+ const ledgerClient = new ledger.AppClient(transport);
283
+ ```
284
+
285
+ 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).
286
+
287
+ To register the policies of non-standard descriptors on the Ledger device, you can use the following code:
288
+
289
+ ```javascript
290
+ await ledger.registerLedgerWallet({
291
+ ledgerClient,
292
+ ledgerState,
293
+ descriptor: wshDescriptor,
294
+ policyName: 'BitcoinerLab'
295
+ });
296
+ ```
297
+
298
+ 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.
299
+
300
+ Finally, `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.
301
+
302
+ <a name="documentation"></a>
303
+
304
+ ## Additional Resources
305
+
306
+ For more information, refer to the following resources:
307
+
308
+ - [Guides](https://bitcoinerlab.com/guides): Comprehensive explanations and playgrounds to help you learn how to use the module.
309
+ - [Stack Exchange answer](https://bitcoin.stackexchange.com/a/118036/89665): Focused explanation on the constructor, specifically the `signersPubKeys` parameter, and the usage of `updatePsbt`, `finalizePsbt`, `getAddress`, and `getScriptPubKey`.
310
+ - [Integration tests](https://github.com/bitcoinerlab/descriptors/tree/main/test/integration): Well-commented code examples showcasing the usage of all functions in the module.
311
+ - API Documentation: Auto-generated documentation from the source code, providing detailed information about the library and its methods. To generate the API documentation locally, follow these commands:
312
+
313
+ ```bash
314
+ git clone https://github.com/bitcoinerlab/descriptors
315
+ cd descriptors/
316
+ npm install
317
+ npm run docs
318
+ ```
319
+
320
+ The generated documentation will be available in the `docs/` directory. Open the `index.html` file to view the documentation.
321
+
322
+ Please note that not all the functions have been fully documented yet. However, you can easily understand their usage by reading the source code or by checking the integration tests or playgrounds.
16
323
 
17
324
  ## Authors and Contributors
18
325
 
@@ -26,19 +333,19 @@ To download the source code and build the project, follow these steps:
26
333
 
27
334
  1. Clone the repository:
28
335
 
29
- ```
336
+ ```bash
30
337
  git clone https://github.com/bitcoinerlab/descriptors.git
31
338
  ```
32
339
 
33
340
  2. Install the dependencies:
34
341
 
35
- ```
342
+ ```bash
36
343
  npm install
37
344
  ```
38
345
 
39
346
  3. Build the project:
40
347
 
41
- ```
348
+ ```bash
42
349
  npm run build
43
350
  ```
44
351
 
@@ -46,29 +353,27 @@ This will build the project and generate the necessary files in the `dist` direc
46
353
 
47
354
  ### Testing
48
355
 
49
-
50
- Before committing any code, make sure it passes all tests. First, make sure that you have a Bitcoin regtest node running and that you have set up the Express-based bitcoind manager from this repository: https://github.com/bitcoinjs/regtest-server. The manager should be running on 127.0.0.1:8080.
356
+ Before committing any code, make sure it passes all tests. First, make sure that you have a Bitcoin regtest node running and that you have set up [this Express-based bitcoind manager](https://github.com/bitcoinjs/regtest-server) running on 127.0.0.1:8080.
51
357
 
52
358
  The easiest way to set up these services is to use a Docker image that comes preconfigured with them. You can use the following commands to download and run the Docker image:
53
359
 
54
360
  ```bash
55
- docker pull junderw/bitcoinjs-regtest-server
56
- docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server
361
+ docker pull bitcoinerlab/tester
362
+ docker run -d -p 8080:8080 -p 60401:60401 -p 3002:3002 bitcoinerlab/tester
57
363
  ```
58
364
 
59
365
  This will start a container running a Bitcoin regtest node and the bitcoind manager on your machine. Once you have your node and manager set up, you can run the tests using the following command:
60
366
 
61
- ```
367
+ ```bash
62
368
  npm run test
63
369
  ```
64
370
 
65
371
  And, in case you have a Ledger device:
66
372
 
67
- ```
373
+ ```bash
68
374
  npm run test:integration:ledger
69
375
  ```
70
376
 
71
377
  ### License
72
378
 
73
379
  This project is licensed under the MIT License.
74
-
@@ -1,15 +1,15 @@
1
1
  import { BIP32API } from 'bip32';
2
2
  import { ECPairAPI } from 'ecpair';
3
- import type { TinySecp256k1Interface, ParseKeyExpression, DescriptorInterfaceConstructor } from './types';
3
+ import type { TinySecp256k1Interface, ParseKeyExpression, Expand, DescriptorInterfaceConstructor } from './types';
4
4
  /**
5
5
  * Builds the functions needed to operate with descriptors using an external elliptic curve (ecc) library.
6
6
  * @param {Object} ecc - an object containing elliptic curve operations, such as [tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1) or [@bitcoinerlab/secp256k1](https://github.com/bitcoinerlab/secp256k1).
7
- * @returns {Object} an object containing functions, `parse` and `checksum`.
8
7
  * @namespace
9
8
  */
10
9
  export declare function DescriptorsFactory(ecc: TinySecp256k1Interface): {
11
10
  Descriptor: DescriptorInterfaceConstructor;
12
11
  ECPair: ECPairAPI;
13
12
  parseKeyExpression: ParseKeyExpression;
13
+ expand: Expand;
14
14
  BIP32: BIP32API;
15
15
  };