@bitcoinerlab/descriptors 0.2.0 → 0.2.2

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,277 @@
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
+ ### keyExpressions and scriptExpressions
135
+
136
+ 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:
137
+
138
+ ```javascript
139
+ import { scriptExpressions } from '@bitcoinerlab/descriptors';
140
+ ```
141
+
142
+ `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`.
143
+
144
+ When using BIP32-based descriptors, the following parameters are required for the `scriptExpressions` functions:
145
+
146
+ ```javascript
147
+ pkhBIP32(params: {
148
+ masterNode: BIP32Interface; //A bitcoinjs-lib instance of a BIP32 object.
149
+ network?: Network; //A bitcoinjs-lib network
150
+ account: number;
151
+ change?: number | undefined; //0 -> external (receive), 1 -> internal (change)
152
+ index?: number | undefined | '*';
153
+ keyPath?: string; //You can use change & index or a keyPath such as "/0/0"
154
+ isPublic?: boolean; //Whether to use xpub or xprv
155
+ })
156
+ ```
157
+
158
+ For Ledger, `ledgerClient` and `ledgerState` are used instead of `masterNode`. These will be explained later when we discuss Ledger integration.
159
+
160
+ The `keyExpressions` category includes functions that generate string representations of key expressions for public keys. This is useful when working with miniscript-based descriptors.
14
161
 
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.
162
+ This library includes the following `keyExpressions`: `keyExpressionBIP32` and `keyExpressionLedger`. They can be imported as follows:
163
+
164
+ ```javascript
165
+ import {
166
+ keyExpressionBIP32,
167
+ keyExpressionLedger
168
+ } from '@bitcoinerlab/descriptors';
169
+ ```
170
+
171
+ The parameters required for these functions are:
172
+
173
+ ```javascript
174
+ function keyExpressionBIP32({
175
+ masterNode: BIP32Interface;
176
+ originPath: string;
177
+ change?: number | undefined; //0 -> external (receive), 1 -> internal (change)
178
+ index?: number | undefined | '*';
179
+ keyPath?: string | undefined; //In the case of the Ledger, keyPath must be /<1;0>/number
180
+ isPublic?: boolean;
181
+ });
182
+ ```
183
+
184
+ For Ledger, `ledgerClient` and `ledgerState` are used instead of `masterNode`.
185
+
186
+ 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.
187
+
188
+ ### Signers and Finalizers
189
+
190
+ 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).
191
+
192
+ To use them, import them as follows:
193
+
194
+ ```javascript
195
+ import { signers, finalizePsbt } from '@bitcoinerlab/descriptors';
196
+ ```
197
+
198
+ To sign with the signers:
199
+
200
+ ```javascript
201
+ await signers.signLedger({
202
+ ledgerClient,
203
+ ledgerState,
204
+ psbt,
205
+ descriptors: psbtInputDescriptors
206
+ });
207
+ //Here psbtInputDescriptors is an array of descriptors odered by their respective inputIndex in the psbt
208
+ signers.signBIP32({ psbt, masterNode });
209
+ signers.signECPair({ psbt, ecpair }); //Where ecpair is an instance of bitcoinjs-lib ECPairInterface
210
+ ```
211
+
212
+ 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`.
213
+
214
+ ### Hardware Wallet Integration
215
+
216
+ This library currently provides integration with Ledger wallets. Support for more devices is planned.
217
+
218
+ To use a Ledger device for signing, you can import the necessary functions as follows:
219
+
220
+ ```javascript
221
+ import { ledger } from '@bitcoinerlab/descriptors';
222
+ ```
223
+
224
+ 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:
225
+
226
+ ```javascript
227
+ //Throws if not running Bitcoin Test >= 2.1.0
228
+ await ledger.assertLedgerApp({
229
+ transport,
230
+ name: 'Bitcoin Test',
231
+ minVersion: '2.1.0'
232
+ });
233
+
234
+ const ledgerClient = new ledger.AppClient(transport);
235
+ ```
236
+
237
+ 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).
238
+
239
+ To register the policies of non-standard descriptors on the Ledger device, you can use the following code:
240
+
241
+ ```javascript
242
+ await ledger.registerLedgerWallet({
243
+ ledgerClient,
244
+ ledgerState,
245
+ descriptor: wshDescriptor,
246
+ policyName: 'BitcoinerLab'
247
+ });
248
+ ```
249
+
250
+ 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.
251
+
252
+ 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.
253
+
254
+ <a name="documentation"></a>
255
+
256
+ ## Additional Resources
257
+
258
+ For more information, refer to the following resources:
259
+
260
+ - [Guides](https://bitcoinerlab.com/guides): Comprehensive explanations and playgrounds to help you learn how to use the module.
261
+ - [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`.
262
+ - [Integration tests](https://github.com/bitcoinerlab/descriptors/tree/main/test/integration): Well-commented code examples showcasing the usage of all functions in the module.
263
+ - 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:
264
+
265
+ ```bash
266
+ git clone https://github.com/bitcoinerlab/descriptors
267
+ cd descriptors/
268
+ npm install
269
+ npm run docs
270
+ ```
271
+
272
+ The generated documentation will be available in the `docs/` directory. Open the `index.html` file to view the documentation.
273
+
274
+ 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
275
 
17
276
  ## Authors and Contributors
18
277
 
@@ -26,19 +285,19 @@ To download the source code and build the project, follow these steps:
26
285
 
27
286
  1. Clone the repository:
28
287
 
29
- ```
288
+ ```bash
30
289
  git clone https://github.com/bitcoinerlab/descriptors.git
31
290
  ```
32
291
 
33
292
  2. Install the dependencies:
34
293
 
35
- ```
294
+ ```bash
36
295
  npm install
37
296
  ```
38
297
 
39
298
  3. Build the project:
40
299
 
41
- ```
300
+ ```bash
42
301
  npm run build
43
302
  ```
44
303
 
@@ -46,29 +305,27 @@ This will build the project and generate the necessary files in the `dist` direc
46
305
 
47
306
  ### Testing
48
307
 
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.
308
+ 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
309
 
52
310
  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
311
 
54
312
  ```bash
55
- docker pull junderw/bitcoinjs-regtest-server
56
- docker run -d -p 127.0.0.1:8080:8080 junderw/bitcoinjs-regtest-server
313
+ docker pull bitcoinerlab/tester
314
+ docker run -d -p 8080:8080 -p 60401:60401 -p 3002:3002 bitcoinerlab/tester
57
315
  ```
58
316
 
59
317
  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
318
 
61
- ```
319
+ ```bash
62
320
  npm run test
63
321
  ```
64
322
 
65
323
  And, in case you have a Ledger device:
66
324
 
67
- ```
325
+ ```bash
68
326
  npm run test:integration:ledger
69
327
  ```
70
328
 
71
329
  ### License
72
330
 
73
331
  This project is licensed under the MIT License.
74
-
@@ -4,7 +4,6 @@ import type { TinySecp256k1Interface, ParseKeyExpression, DescriptorInterfaceCon
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): {
@@ -95,7 +95,6 @@ function evaluate({ expression, checksumRequired, index }) {
95
95
  /**
96
96
  * Builds the functions needed to operate with descriptors using an external elliptic curve (ecc) library.
97
97
  * @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).
98
- * @returns {Object} an object containing functions, `parse` and `checksum`.
99
98
  * @namespace
100
99
  */
101
100
  function DescriptorsFactory(ecc) {
@@ -133,13 +132,15 @@ function DescriptorsFactory(ecc) {
133
132
  }
134
133
  class Descriptor {
135
134
  /**
136
- * @param {Object} params
135
+ * @param {DescriptorInfo} params
136
+ * @param {string} params.expression - The descriptor string in ASCII format. It may include a "*" to denote an arbitrary index.
137
137
  * @param {number} params.index - The descriptor's index in the case of a range descriptor (must be an interger >=0).
138
- * @param {string} params.descriptor - The descriptor.
139
138
  * @param {boolean} [params.checksumRequired=false] - A flag indicating whether the descriptor is required to include a checksum.
139
+ * @param {boolean} [params.allowMiniscriptInP2SH=false] - A flag indicating whether this instance can parse and generate script satisfactions for sh(miniscript) top-level expressions of miniscripts. This is not recommended.
140
140
  * @param {object} [params.network=networks.bitcoin] One of bitcoinjs-lib [`networks`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/networks.js) (or another one following the same interface).
141
+ * @param {Preimage[]} [params.preimages=[]] An array of preimages. This info is necessary to finalize Psbts.
142
+ * @param {Buffer[]} [params.signersPubKeys] (Optional): An array of the public keys used for signing the transaction when spending the output associated with this descriptor. This parameter is only used if the descriptor object is being used to finalize a transaction. It is necessary to specify the spending path when working with miniscript-based expressions that have multiple spending paths. Set this parameter to an array containing the public keys involved in the desired spending path. Leave it `undefined` if you only need to generate the `scriptPubKey` or `address` for a descriptor, or if all the public keys involved in the descriptor will sign the transaction. In the latter case, the satisfier will automatically choose the most optimal spending path (if more than one is available).
141
143
  *
142
- * @see {@link https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/payments/index.d.ts}
143
144
  * @throws {Error} - when descriptor is invalid
144
145
  */
145
146
  constructor({ expression, index, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys }) {
@@ -418,6 +419,9 @@ function DescriptorsFactory(ecc) {
418
419
  getPayment() {
419
420
  return __classPrivateFieldGet(this, _Descriptor_payment, "f");
420
421
  }
422
+ /**
423
+ * Returns the Bitcoin Address
424
+ */
421
425
  getAddress() {
422
426
  if (!__classPrivateFieldGet(this, _Descriptor_payment, "f").address)
423
427
  throw new Error(`Error: could extract an address from the payment`);
@@ -428,6 +432,11 @@ function DescriptorsFactory(ecc) {
428
432
  throw new Error(`Error: could extract output.script from the payment`);
429
433
  return __classPrivateFieldGet(this, _Descriptor_payment, "f").output;
430
434
  }
435
+ /**
436
+ * Returns the compiled script satisfaction
437
+ * @param {PartialSig[]} signatures An array of signatures using this format: `interface PartialSig { pubkey: Buffer; signature: Buffer; }`
438
+ * @returns {Buffer}
439
+ */
431
440
  getScriptSatisfaction(signatures) {
432
441
  const miniscript = __classPrivateFieldGet(this, _Descriptor_miniscript, "f");
433
442
  const expandedMiniscript = __classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f");
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export declare function finalizePsbt({ psbt, descriptors, validate }: {
13
13
  export { keyExpressionBIP32, keyExpressionLedger } from './keyExpressions';
14
14
  import * as scriptExpressions from './scriptExpressions';
15
15
  export { scriptExpressions };
16
- import { AppClient } from '@bitcoinerlab/ledger';
16
+ import { AppClient } from 'ledger-bitcoin';
17
17
  import { LedgerState, getLedgerMasterFingerPrint, getLedgerXpub, registerLedgerWallet, assertLedgerApp } from './ledger';
18
18
  export declare const ledger: {
19
19
  getLedgerMasterFingerPrint: typeof getLedgerMasterFingerPrint;
package/dist/index.js CHANGED
@@ -42,12 +42,12 @@ Object.defineProperty(exports, "keyExpressionBIP32", { enumerable: true, get: fu
42
42
  Object.defineProperty(exports, "keyExpressionLedger", { enumerable: true, get: function () { return keyExpressions_1.keyExpressionLedger; } });
43
43
  const scriptExpressions = __importStar(require("./scriptExpressions"));
44
44
  exports.scriptExpressions = scriptExpressions;
45
- const ledger_1 = require("@bitcoinerlab/ledger");
46
- const ledger_2 = require("./ledger");
45
+ const ledger_bitcoin_1 = require("ledger-bitcoin");
46
+ const ledger_1 = require("./ledger");
47
47
  exports.ledger = {
48
- getLedgerMasterFingerPrint: ledger_2.getLedgerMasterFingerPrint,
49
- getLedgerXpub: ledger_2.getLedgerXpub,
50
- registerLedgerWallet: ledger_2.registerLedgerWallet,
51
- assertLedgerApp: ledger_2.assertLedgerApp,
52
- AppClient: ledger_1.AppClient
48
+ getLedgerMasterFingerPrint: ledger_1.getLedgerMasterFingerPrint,
49
+ getLedgerXpub: ledger_1.getLedgerXpub,
50
+ registerLedgerWallet: ledger_1.registerLedgerWallet,
51
+ assertLedgerApp: ledger_1.assertLedgerApp,
52
+ AppClient: ledger_bitcoin_1.AppClient
53
53
  };
@@ -3,7 +3,7 @@ import type { ECPairAPI } from 'ecpair';
3
3
  import type { BIP32API, BIP32Interface } from 'bip32';
4
4
  import type { KeyInfo } from './types';
5
5
  import { LedgerState } from './ledger';
6
- import type { AppClient } from '@bitcoinerlab/ledger';
6
+ import type { AppClient } from 'ledger-bitcoin';
7
7
  export declare function parseKeyExpression({ keyExpression, isSegwit, ECPair, BIP32, network }: {
8
8
  keyExpression: string;
9
9
  network?: Network;
@@ -161,7 +161,7 @@ exports.parseKeyExpression = parseKeyExpression;
161
161
  function assertChangeIndexKeyPath({ change, index, keyPath }) {
162
162
  if (!((change === undefined && index === undefined) ||
163
163
  (change !== undefined && index !== undefined)))
164
- throw new Error(`Error: Pass change, index and network or neither`);
164
+ throw new Error(`Error: Pass change and index or neither`);
165
165
  if ((change !== undefined) === (keyPath !== undefined))
166
166
  throw new Error(`Error: Pass either change and index or a keyPath`);
167
167
  }
package/dist/ledger.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import type { DescriptorInterface } from './types';
3
- import { AppClient } from '@bitcoinerlab/ledger';
3
+ import { AppClient } from 'ledger-bitcoin';
4
4
  export declare function assertLedgerApp({ transport, name, minVersion }: {
5
5
  transport: any;
6
6
  name: string;
package/dist/ledger.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // Distributed under the MIT software license
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.ledgerPolicyFromState = exports.comparePolicies = exports.ledgerPolicyFromStandard = exports.registerLedgerWallet = exports.descriptorToLedgerFormat = exports.getLedgerXpub = exports.getLedgerMasterFingerPrint = exports.assertLedgerApp = void 0;
6
- const ledger_1 = require("@bitcoinerlab/ledger");
6
+ const ledger_bitcoin_1 = require("ledger-bitcoin");
7
7
  const bitcoinjs_lib_1 = require("bitcoinjs-lib");
8
8
  const re_1 = require("./re");
9
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -204,7 +204,7 @@ async function registerLedgerWallet({ descriptor, ledgerClient, ledgerState, pol
204
204
  //It already existed. No need to register it again.
205
205
  }
206
206
  else {
207
- walletPolicy = new ledger_1.WalletPolicy(policyName, ledgerTemplate, keyRoots);
207
+ walletPolicy = new ledger_bitcoin_1.WalletPolicy(policyName, ledgerTemplate, keyRoots);
208
208
  let policyId;
209
209
  [policyId, policyHmac] = await ledgerClient.registerWallet(walletPolicy);
210
210
  const policy = {
@@ -91,7 +91,7 @@ function substituteAsm({ expandedAsm, expansionMap }) {
91
91
  //enclosed in <>, since <> represents hex code already encoded.
92
92
  //The regex below will match one or more digits within a string,
93
93
  //except if the sequence is surrounded by "<" and ">"
94
- .replace(/(?<![<])\b\d+\b(?![>])/g, (num) => numberEncodeAsm(Number(num)))
94
+ .replace(/(<\d+>)|\b\d+\b/g, match => match.startsWith('<') ? match : numberEncodeAsm(Number(match)))
95
95
  //we don't have numbers anymore, now it's safe to remove < and > since we
96
96
  //know that every remaining is either an op_code or a hex encoded number
97
97
  .replace(/[<>]/g, '');
package/dist/psbt.js CHANGED
@@ -144,26 +144,20 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
144
144
  }
145
145
  if (txId === undefined || !value)
146
146
  throw new Error(`Error: txHex+vout required. Alternatively, but ONLY for Segwit inputs, txId+value can also be passed.`);
147
- if (locktime !== undefined) {
148
- if (psbt.locktime !== 0 && psbt.locktime !== undefined)
149
- throw new Error(`Error: transaction locktime has already been set: ${psbt.locktime}`);
150
- psbt.setLocktime(locktime);
151
- }
152
- let inputSequence;
153
- if (locktime !== undefined) {
147
+ if (locktime) {
148
+ if (psbt.locktime && psbt.locktime !== locktime)
149
+ throw new Error(`Error: transaction locktime was already set with a different value: ${locktime} != ${psbt.locktime}`);
150
+ // nLockTime only works if at least one of the transaction inputs has an
151
+ // nSequence value that is below 0xffffffff. Let's make sure that at least
152
+ // this input's sequence < 0xffffffff
154
153
  if (sequence === undefined) {
155
- // for CTV nSequence MUST be <= 0xfffffffe otherwise OP_CHECKLOCKTIMEVERIFY will fail.
156
- inputSequence = 0xfffffffe;
154
+ //NOTE: if sequence is undefined, bitcoinjs-lib uses 0xffffffff as default
155
+ sequence = 0xfffffffe;
157
156
  }
158
157
  else if (sequence > 0xfffffffe) {
159
- throw new Error(`Error: incompatible sequence: ${inputSequence} and locktime: ${locktime}`);
160
- }
161
- else {
162
- inputSequence = sequence;
158
+ throw new Error(`Error: incompatible sequence: ${sequence} and locktime: ${locktime}`);
163
159
  }
164
- }
165
- else {
166
- inputSequence = sequence;
160
+ psbt.setLocktime(locktime);
167
161
  }
168
162
  const input = {
169
163
  hash: reverseBuffer(Buffer.from(txId, 'hex')),
@@ -185,8 +179,8 @@ function updatePsbt({ psbt, vout, txHex, txId, value, sequence, locktime, keysIn
185
179
  //There's no need to put both witnessUtxo and nonWitnessUtxo
186
180
  input.witnessUtxo = { script: scriptPubKey, value };
187
181
  }
188
- if (inputSequence !== undefined)
189
- input.sequence = inputSequence;
182
+ if (sequence !== undefined)
183
+ input.sequence = sequence;
190
184
  if (witnessScript)
191
185
  input.witnessScript = witnessScript;
192
186
  if (redeemScript)
@@ -1,4 +1,4 @@
1
- import type { AppClient } from '@bitcoinerlab/ledger';
1
+ import type { AppClient } from 'ledger-bitcoin';
2
2
  import { Network } from 'bitcoinjs-lib';
3
3
  import type { LedgerState } from './ledger';
4
4
  import type { BIP32Interface } from 'bip32';
package/dist/signers.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Psbt } from 'bitcoinjs-lib';
2
2
  import type { ECPairInterface } from 'ecpair';
3
3
  import type { BIP32Interface } from 'bip32';
4
- import { AppClient } from '@bitcoinerlab/ledger';
4
+ import { AppClient } from 'ledger-bitcoin';
5
5
  import type { DescriptorInterface } from './types';
6
6
  import { LedgerState } from './ledger';
7
7
  export declare function signInputECPair({ psbt, index, ecpair }: {
package/dist/signers.js CHANGED
@@ -3,8 +3,8 @@
3
3
  // Distributed under the MIT software license
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.signLedger = exports.signInputLedger = exports.signBIP32 = exports.signInputBIP32 = exports.signECPair = exports.signInputECPair = void 0;
6
- const ledger_1 = require("@bitcoinerlab/ledger");
7
- const ledger_2 = require("./ledger");
6
+ const ledger_bitcoin_1 = require("ledger-bitcoin");
7
+ const ledger_1 = require("./ledger");
8
8
  function signInputECPair({ psbt, index, ecpair }) {
9
9
  psbt.signInput(index, ecpair);
10
10
  }
@@ -23,12 +23,12 @@ function signBIP32({ psbt, masterNode }) {
23
23
  exports.signBIP32 = signBIP32;
24
24
  const ledgerSignaturesForInputIndex = (index, ledgerSignatures) => ledgerSignatures
25
25
  .filter(([i]) => i === index)
26
- .map(([_i, pubkey, signature]) => ({
27
- pubkey,
28
- signature
26
+ .map(([_i, partialSignature]) => ({
27
+ pubkey: partialSignature.pubkey,
28
+ signature: partialSignature.signature
29
29
  }));
30
30
  async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState }) {
31
- const result = await (0, ledger_2.descriptorToLedgerFormat)({
31
+ const result = await (0, ledger_1.descriptorToLedgerFormat)({
32
32
  descriptor,
33
33
  ledgerClient,
34
34
  ledgerState
@@ -37,24 +37,24 @@ async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerSt
37
37
  throw new Error(`Error: descriptor does not have a ledger input`);
38
38
  const { ledgerTemplate, keyRoots } = result;
39
39
  let ledgerSignatures;
40
- const standardPolicy = await (0, ledger_2.ledgerPolicyFromStandard)({
40
+ const standardPolicy = await (0, ledger_1.ledgerPolicyFromStandard)({
41
41
  descriptor,
42
42
  ledgerClient,
43
43
  ledgerState
44
44
  });
45
45
  if (standardPolicy) {
46
- ledgerSignatures = await ledgerClient.signPsbt(new ledger_1.PsbtV2().fromBitcoinJS(psbt), new ledger_1.DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
46
+ ledgerSignatures = await ledgerClient.signPsbt(new ledger_bitcoin_1.PsbtV2().fromBitcoinJS(psbt), new ledger_bitcoin_1.DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
47
47
  }
48
48
  else {
49
- const policy = await (0, ledger_2.ledgerPolicyFromState)({
49
+ const policy = await (0, ledger_1.ledgerPolicyFromState)({
50
50
  descriptor,
51
51
  ledgerClient,
52
52
  ledgerState
53
53
  });
54
54
  if (!policy || !policy.policyName || !policy.policyHmac)
55
55
  throw new Error(`Error: the descriptor's policy is not registered`);
56
- const walletPolicy = new ledger_1.WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
57
- ledgerSignatures = await ledgerClient.signPsbt(new ledger_1.PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
56
+ const walletPolicy = new ledger_bitcoin_1.WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
57
+ ledgerSignatures = await ledgerClient.signPsbt(new ledger_bitcoin_1.PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
58
58
  }
59
59
  //Add the signatures to the Psbt object using PartialSig format:
60
60
  psbt.updateInput(index, {
@@ -68,12 +68,12 @@ exports.signInputLedger = signInputLedger;
68
68
  async function signLedger({ psbt, descriptors, ledgerClient, ledgerState }) {
69
69
  const ledgerPolicies = [];
70
70
  for (const descriptor of descriptors) {
71
- const policy = (await (0, ledger_2.ledgerPolicyFromState)({
71
+ const policy = (await (0, ledger_1.ledgerPolicyFromState)({
72
72
  descriptor,
73
73
  ledgerClient,
74
74
  ledgerState
75
75
  })) ||
76
- (await (0, ledger_2.ledgerPolicyFromStandard)({
76
+ (await (0, ledger_1.ledgerPolicyFromStandard)({
77
77
  descriptor,
78
78
  ledgerClient,
79
79
  ledgerState
@@ -86,7 +86,7 @@ async function signLedger({ psbt, descriptors, ledgerClient, ledgerState }) {
86
86
  //cluster unique LedgerPolicies
87
87
  const uniquePolicies = [];
88
88
  for (const policy of ledgerPolicies) {
89
- if (!uniquePolicies.find((uniquePolicy) => (0, ledger_2.comparePolicies)(uniquePolicy, policy)))
89
+ if (!uniquePolicies.find((uniquePolicy) => (0, ledger_1.comparePolicies)(uniquePolicy, policy)))
90
90
  uniquePolicies.push(policy);
91
91
  }
92
92
  for (const uniquePolicy of uniquePolicies) {
@@ -95,12 +95,12 @@ async function signLedger({ psbt, descriptors, ledgerClient, ledgerState }) {
95
95
  uniquePolicy.policyHmac &&
96
96
  uniquePolicy.policyId) {
97
97
  //non-standard policy
98
- const walletPolicy = new ledger_1.WalletPolicy(uniquePolicy.policyName, uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots);
99
- ledgerSignatures = await ledgerClient.signPsbt(new ledger_1.PsbtV2().fromBitcoinJS(psbt), walletPolicy, uniquePolicy.policyHmac);
98
+ const walletPolicy = new ledger_bitcoin_1.WalletPolicy(uniquePolicy.policyName, uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots);
99
+ ledgerSignatures = await ledgerClient.signPsbt(new ledger_bitcoin_1.PsbtV2().fromBitcoinJS(psbt), walletPolicy, uniquePolicy.policyHmac);
100
100
  }
101
101
  else {
102
102
  //standard policy
103
- ledgerSignatures = await ledgerClient.signPsbt(new ledger_1.PsbtV2().fromBitcoinJS(psbt), new ledger_1.DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
103
+ ledgerSignatures = await ledgerClient.signPsbt(new ledger_bitcoin_1.PsbtV2().fromBitcoinJS(psbt), new ledger_bitcoin_1.DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
104
104
  }
105
105
  for (const [index, ,] of ledgerSignatures) {
106
106
  psbt.updateInput(index, {
package/dist/types.d.ts CHANGED
@@ -3,10 +3,23 @@ import type { ECPairInterface } from 'ecpair';
3
3
  import type { BIP32Interface } from 'bip32';
4
4
  import type { Network, Payment, Psbt } from 'bitcoinjs-lib';
5
5
  import type { PartialSig } from 'bip174/src/lib/interfaces';
6
- export interface Preimage {
6
+ /**
7
+ * Preimage
8
+ * @alias Preimage
9
+ * @memberof Descriptor
10
+ */
11
+ export type Preimage = {
12
+ /**
13
+ * Use same expressions as in miniscript. For example: "sha256(cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204)" or "ripemd160(095ff41131e5946f3c85f79e44adbcf8e27e080e)"
14
+ * Accepted functions: sha256, hash256, ripemd160, hash160
15
+ * Digests must be: 64-character HEX for sha256, hash160 or 30-character HEX for ripemd160 or hash160.
16
+ */
7
17
  digest: string;
18
+ /**
19
+ * Hex encoded preimate. Preimages are always 32 bytes (so, 64 character in hex).
20
+ */
8
21
  preimage: string;
9
- }
22
+ };
10
23
  export type TimeConstraints = {
11
24
  nLockTime: number | undefined;
12
25
  nSequence: number | undefined;
@@ -49,6 +62,12 @@ export interface TinySecp256k1Interface {
49
62
  xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
50
63
  privateNegate(d: Uint8Array): Uint8Array;
51
64
  }
65
+ /**
66
+ * DescriptorInfo
67
+ * What defines a Descriptor. This is the type needed in the constructor.
68
+ * @alias DescriptorInfo
69
+ * @memberof Descriptor
70
+ */
52
71
  export type DescriptorInfo = {
53
72
  expression: string;
54
73
  index?: number;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitcoinerlab/descriptors",
3
3
  "homepage": "https://github.com/bitcoinerlab/descriptors",
4
- "version": "0.2.0",
4
+ "version": "0.2.2",
5
5
  "description": "This library parses and creates Bitcoin Miniscript Descriptors and generates Partially Signed Bitcoin Transactions (PSBTs). It provides PSBT finalizers and signers for single-signature, BIP32 and Hardware Wallets.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  "build": "npx tsc",
10
10
  "prepublishOnly": "npm run build && npm test && echo \"\\n\\n\" && npm run test:integration:ledger",
11
11
  "docs": "jsdoc -c jsdoc.json",
12
- "regtest-docker": "docker ps | grep bitcoinjs-regtest-server > /dev/null || (docker pull junderw/bitcoinjs-regtest-server && docker run -d -p 8080:8080 junderw/bitcoinjs-regtest-server && sleep 5)",
12
+ "regtest-docker": "docker ps | grep bitcoinerlab/tester > /dev/null || (docker pull bitcoinerlab/tester && docker run -d -p 8080:8080 -p 60401:60401 -p 3002:3002 bitcoinerlab/tester && sleep 5)",
13
13
  "test:integration:soft": "npm run regtest-docker && npx ts-node test/integration/standardOutputs.ts && echo \"\\n\\n\" && npx ts-node test/integration/miniscript.ts",
14
14
  "test:integration:ledger": "npm run regtest-docker && npx ts-node test/integration/ledger.ts",
15
15
  "test:unit": "npm run build && node test/tools/generateBitcoinCoreFixtures.js && jest",
@@ -52,13 +52,14 @@
52
52
  "files": [
53
53
  "dist"
54
54
  ],
55
+ "COMMENT wrt ledger-bitcoin below": "ledger-bitcoin is installed using an alias. This package can be replaced with the official `ledger-bitcoin` npm package once [this PR](https://github.com/LedgerHQ/app-bitcoin-new/pull/147) is published to npm (likely in version > 0.2.1)",
55
56
  "dependencies": {
56
- "@bitcoinerlab/ledger": "^0.1.6",
57
57
  "@bitcoinerlab/miniscript": "^1.2.1",
58
58
  "@bitcoinerlab/secp256k1": "^1.0.2",
59
59
  "bip32": "^3.1.0",
60
60
  "bitcoinjs-lib": "^6.1.0",
61
- "ecpair": "^2.1.0"
61
+ "ecpair": "^2.1.0",
62
+ "ledger-bitcoin": "npm:@bitcoinerlab/ledger@^0.2.0"
62
63
  },
63
64
  "devDependencies": {
64
65
  "@babel/plugin-transform-modules-commonjs": "^7.20.11",
@@ -66,6 +67,7 @@
66
67
  "@typescript-eslint/eslint-plugin": "^5.53.0",
67
68
  "@typescript-eslint/parser": "^5.53.0",
68
69
  "babel-plugin-transform-import-meta": "^2.2.0",
70
+ "better-docs": "^2.7.2",
69
71
  "bip39": "^3.0.4",
70
72
  "bip65": "^1.0.3",
71
73
  "bip68": "^1.0.4",
@@ -74,7 +76,7 @@
74
76
  "eslint-plugin-prettier": "^4.2.1",
75
77
  "fs": "^0.0.1-security",
76
78
  "jest": "^29.4.3",
77
- "jsdoc": "^4.0.0",
79
+ "jsdoc": "^3.6.11",
78
80
  "path": "^0.12.7",
79
81
  "prettier": "^2.8.4",
80
82
  "regtest-client": "^0.2.0",