@bitcoinerlab/descriptors 0.3.1 → 1.0.1

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
@@ -273,15 +273,26 @@ To finalize the `psbt`, you can either call the method `finalizePsbtInput({ inde
273
273
 
274
274
  This library currently provides integration with Ledger wallets. Support for more devices is planned.
275
275
 
276
- To use a Ledger device for signing, you can import the necessary functions as follows:
276
+ 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.
277
+
278
+ To use this library with Ledger devices, you must first install Ledger support:
279
+
280
+ ```bash
281
+ npm install ledger-bitcoin
282
+ ```
283
+
284
+ For Ledger device signing, import the necessary functions as follows:
277
285
 
278
286
  ```javascript
287
+ import Transport from '@ledgerhq/hw-transport-node-hid'; //or hw-transport-web-hid, for web
288
+ import { AppClient } from 'ledger-bitcoin';
279
289
  import { ledger } from '@bitcoinerlab/descriptors';
280
290
  ```
281
291
 
282
- 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:
292
+ 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:
283
293
 
284
294
  ```javascript
295
+ const transport = await Transport.create();
285
296
  //Throws if not running Bitcoin Test >= 2.1.0
286
297
  await ledger.assertLedgerApp({
287
298
  transport,
@@ -289,12 +300,12 @@ await ledger.assertLedgerApp({
289
300
  minVersion: '2.1.0'
290
301
  });
291
302
 
292
- const ledgerClient = new ledger.AppClient(transport);
303
+ const ledgerClient = new AppClient(transport);
293
304
  ```
294
305
 
295
306
  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).
296
307
 
297
- To register the policies of non-standard descriptors on the Ledger device, you can use the following code:
308
+ To register the policies of non-standard descriptors on the Ledger device, use the following code:
298
309
 
299
310
  ```javascript
300
311
  await ledger.registerLedgerWallet({
@@ -307,7 +318,7 @@ await ledger.registerLedgerWallet({
307
318
 
308
319
  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.
309
320
 
310
- 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.
321
+ 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 for future use.
311
322
 
312
323
  <a name="documentation"></a>
313
324
 
package/dist/index.d.ts CHANGED
@@ -13,13 +13,11 @@ 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 'ledger-bitcoin';
17
16
  import { LedgerState, getLedgerMasterFingerPrint, getLedgerXpub, registerLedgerWallet, assertLedgerApp } from './ledger';
18
17
  export declare const ledger: {
19
18
  getLedgerMasterFingerPrint: typeof getLedgerMasterFingerPrint;
20
19
  getLedgerXpub: typeof getLedgerXpub;
21
20
  registerLedgerWallet: typeof registerLedgerWallet;
22
21
  assertLedgerApp: typeof assertLedgerApp;
23
- AppClient: typeof AppClient;
24
22
  };
25
23
  export type { LedgerState };
package/dist/index.js CHANGED
@@ -42,12 +42,10 @@ 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_bitcoin_1 = require("ledger-bitcoin");
46
45
  const ledger_1 = require("./ledger");
47
46
  exports.ledger = {
48
47
  getLedgerMasterFingerPrint: ledger_1.getLedgerMasterFingerPrint,
49
48
  getLedgerXpub: ledger_1.getLedgerXpub,
50
49
  registerLedgerWallet: ledger_1.registerLedgerWallet,
51
- assertLedgerApp: ledger_1.assertLedgerApp,
52
- AppClient: ledger_bitcoin_1.AppClient
50
+ assertLedgerApp: ledger_1.assertLedgerApp
53
51
  };
@@ -3,7 +3,6 @@ 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 'ledger-bitcoin';
7
6
  export declare function parseKeyExpression({ keyExpression, isSegwit, ECPair, BIP32, network }: {
8
7
  keyExpression: string;
9
8
  network?: Network;
@@ -12,7 +11,7 @@ export declare function parseKeyExpression({ keyExpression, isSegwit, ECPair, BI
12
11
  BIP32: BIP32API;
13
12
  }): KeyInfo;
14
13
  export declare function keyExpressionLedger({ ledgerClient, ledgerState, originPath, keyPath, change, index }: {
15
- ledgerClient: AppClient;
14
+ ledgerClient: unknown;
16
15
  ledgerState: LedgerState;
17
16
  originPath: string;
18
17
  change?: number | undefined;
package/dist/ledger.d.ts CHANGED
@@ -1,6 +1,24 @@
1
1
  /// <reference types="node" />
2
2
  import type { DescriptorInterface } from './types';
3
- import { AppClient } from 'ledger-bitcoin';
3
+ /**
4
+ * Dynamically imports the 'ledger-bitcoin' module and, if provided, checks if `ledgerClient` is an instance of `AppClient`.
5
+ *
6
+ * @async
7
+ * @param {unknown} ledgerClient - An optional parameter that, if provided, is checked to see if it's an instance of `AppClient`.
8
+ * @throws {Error} Throws an error if `ledgerClient` is provided but is not an instance of `AppClient`.
9
+ * @throws {Error} Throws an error if the 'ledger-bitcoin' module cannot be imported. This typically indicates that the 'ledger-bitcoin' peer dependency is not installed.
10
+ * @returns {Promise<any>} Returns a promise that resolves with the entire 'ledger-bitcoin' module if it can be successfully imported.
11
+ *
12
+ * @example
13
+ *
14
+ * importAndValidateLedgerBitcoin(ledgerClient)
15
+ * .then((module) => {
16
+ * const { AppClient, PsbtV2, DefaultWalletPolicy, WalletPolicy, DefaultDescriptorTemplate, PartialSignature } = module;
17
+ * // Use the imported objects...
18
+ * })
19
+ * .catch((error) => console.error(error));
20
+ */
21
+ export declare function importAndValidateLedgerBitcoin(ledgerClient?: unknown): Promise<typeof import("ledger-bitcoin")>;
4
22
  export declare function assertLedgerApp({ transport, name, minVersion }: {
5
23
  transport: any;
6
24
  name: string;
@@ -21,12 +39,12 @@ export type LedgerState = {
21
39
  };
22
40
  };
23
41
  export declare function getLedgerMasterFingerPrint({ ledgerClient, ledgerState }: {
24
- ledgerClient: AppClient;
42
+ ledgerClient: unknown;
25
43
  ledgerState: LedgerState;
26
44
  }): Promise<Buffer>;
27
45
  export declare function getLedgerXpub({ originPath, ledgerClient, ledgerState }: {
28
46
  originPath: string;
29
- ledgerClient: AppClient;
47
+ ledgerClient: unknown;
30
48
  ledgerState: LedgerState;
31
49
  }): Promise<string>;
32
50
  /**
@@ -56,7 +74,7 @@ export declare function getLedgerXpub({ originPath, ledgerClient, ledgerState }:
56
74
  */
57
75
  export declare function descriptorToLedgerFormat({ descriptor, ledgerClient, ledgerState }: {
58
76
  descriptor: DescriptorInterface;
59
- ledgerClient: AppClient;
77
+ ledgerClient: unknown;
60
78
  ledgerState: LedgerState;
61
79
  }): Promise<{
62
80
  ledgerTemplate: string;
@@ -71,7 +89,7 @@ export declare function descriptorToLedgerFormat({ descriptor, ledgerClient, led
71
89
  **/
72
90
  export declare function registerLedgerWallet({ descriptor, ledgerClient, ledgerState, policyName }: {
73
91
  descriptor: DescriptorInterface;
74
- ledgerClient: AppClient;
92
+ ledgerClient: unknown;
75
93
  ledgerState: LedgerState;
76
94
  policyName: string;
77
95
  }): Promise<void>;
@@ -80,7 +98,7 @@ export declare function registerLedgerWallet({ descriptor, ledgerClient, ledgerS
80
98
  **/
81
99
  export declare function ledgerPolicyFromStandard({ descriptor, ledgerClient, ledgerState }: {
82
100
  descriptor: DescriptorInterface;
83
- ledgerClient: AppClient;
101
+ ledgerClient: unknown;
84
102
  ledgerState: LedgerState;
85
103
  }): Promise<LedgerPolicy | null>;
86
104
  export declare function comparePolicies(policyA: LedgerPolicy, policyB: LedgerPolicy): boolean;
@@ -89,6 +107,6 @@ export declare function comparePolicies(policyA: LedgerPolicy, policyB: LedgerPo
89
107
  **/
90
108
  export declare function ledgerPolicyFromState({ descriptor, ledgerClient, ledgerState }: {
91
109
  descriptor: DescriptorInterface;
92
- ledgerClient: AppClient;
110
+ ledgerClient: unknown;
93
111
  ledgerState: LedgerState;
94
112
  }): Promise<LedgerPolicy | null>;
package/dist/ledger.js CHANGED
@@ -1,11 +1,66 @@
1
1
  "use strict";
2
2
  // Copyright (c) 2023 Jose-Luis Landabaso - https://bitcoinerlab.com
3
3
  // Distributed under the MIT software license
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || function (mod) {
21
+ if (mod && mod.__esModule) return mod;
22
+ var result = {};
23
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24
+ __setModuleDefault(result, mod);
25
+ return result;
26
+ };
4
27
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.ledgerPolicyFromState = exports.comparePolicies = exports.ledgerPolicyFromStandard = exports.registerLedgerWallet = exports.descriptorToLedgerFormat = exports.getLedgerXpub = exports.getLedgerMasterFingerPrint = exports.assertLedgerApp = void 0;
6
- const ledger_bitcoin_1 = require("ledger-bitcoin");
28
+ exports.ledgerPolicyFromState = exports.comparePolicies = exports.ledgerPolicyFromStandard = exports.registerLedgerWallet = exports.descriptorToLedgerFormat = exports.getLedgerXpub = exports.getLedgerMasterFingerPrint = exports.assertLedgerApp = exports.importAndValidateLedgerBitcoin = void 0;
7
29
  const bitcoinjs_lib_1 = require("bitcoinjs-lib");
8
30
  const re_1 = require("./re");
31
+ /**
32
+ * Dynamically imports the 'ledger-bitcoin' module and, if provided, checks if `ledgerClient` is an instance of `AppClient`.
33
+ *
34
+ * @async
35
+ * @param {unknown} ledgerClient - An optional parameter that, if provided, is checked to see if it's an instance of `AppClient`.
36
+ * @throws {Error} Throws an error if `ledgerClient` is provided but is not an instance of `AppClient`.
37
+ * @throws {Error} Throws an error if the 'ledger-bitcoin' module cannot be imported. This typically indicates that the 'ledger-bitcoin' peer dependency is not installed.
38
+ * @returns {Promise<any>} Returns a promise that resolves with the entire 'ledger-bitcoin' module if it can be successfully imported.
39
+ *
40
+ * @example
41
+ *
42
+ * importAndValidateLedgerBitcoin(ledgerClient)
43
+ * .then((module) => {
44
+ * const { AppClient, PsbtV2, DefaultWalletPolicy, WalletPolicy, DefaultDescriptorTemplate, PartialSignature } = module;
45
+ * // Use the imported objects...
46
+ * })
47
+ * .catch((error) => console.error(error));
48
+ */
49
+ async function importAndValidateLedgerBitcoin(ledgerClient) {
50
+ let ledgerBitcoinModule;
51
+ try {
52
+ ledgerBitcoinModule = await Promise.resolve().then(() => __importStar(require('ledger-bitcoin')));
53
+ }
54
+ catch (error) {
55
+ throw new Error('Could not import "ledger-bitcoin". This is a peer dependency and needs to be installed explicitly. Please run "npm install ledger-bitcoin" to use Ledger Hardware Wallet functionality.');
56
+ }
57
+ const { AppClient } = ledgerBitcoinModule;
58
+ if (ledgerClient !== undefined && !(ledgerClient instanceof AppClient)) {
59
+ throw new Error('Error: invalid AppClient instance');
60
+ }
61
+ return ledgerBitcoinModule;
62
+ }
63
+ exports.importAndValidateLedgerBitcoin = importAndValidateLedgerBitcoin;
9
64
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
65
  async function ledgerAppInfo(transport) {
11
66
  const r = await transport.send(0xb0, 0x01, 0x00, 0x00);
@@ -60,6 +115,9 @@ function isLedgerStandard({ ledgerTemplate, keyRoots, network = bitcoinjs_lib_1.
60
115
  return false;
61
116
  }
62
117
  async function getLedgerMasterFingerPrint({ ledgerClient, ledgerState }) {
118
+ const { AppClient } = await importAndValidateLedgerBitcoin(ledgerClient);
119
+ if (!(ledgerClient instanceof AppClient))
120
+ throw new Error(`Error: pass a valid ledgerClient`);
63
121
  let masterFingerprint = ledgerState.masterFingerprint;
64
122
  if (!masterFingerprint) {
65
123
  masterFingerprint = Buffer.from(await ledgerClient.getMasterFingerprint(), 'hex');
@@ -69,6 +127,9 @@ async function getLedgerMasterFingerPrint({ ledgerClient, ledgerState }) {
69
127
  }
70
128
  exports.getLedgerMasterFingerPrint = getLedgerMasterFingerPrint;
71
129
  async function getLedgerXpub({ originPath, ledgerClient, ledgerState }) {
130
+ const { AppClient } = await importAndValidateLedgerBitcoin(ledgerClient);
131
+ if (!(ledgerClient instanceof AppClient))
132
+ throw new Error(`Error: pass a valid ledgerClient`);
72
133
  if (!ledgerState.xpubs)
73
134
  ledgerState.xpubs = {};
74
135
  let xpub = ledgerState.xpubs[originPath];
@@ -80,6 +141,8 @@ async function getLedgerXpub({ originPath, ledgerClient, ledgerState }) {
80
141
  catch (err) {
81
142
  xpub = await ledgerClient.getExtendedPubkey(`m${originPath}`, true);
82
143
  }
144
+ if (typeof xpub !== 'string')
145
+ throw new Error(`Error: ledgerClient did not return a valid xpub`);
83
146
  ledgerState.xpubs[originPath] = xpub;
84
147
  }
85
148
  return xpub;
@@ -179,6 +242,9 @@ exports.descriptorToLedgerFormat = descriptorToLedgerFormat;
179
242
  *
180
243
  **/
181
244
  async function registerLedgerWallet({ descriptor, ledgerClient, ledgerState, policyName }) {
245
+ const { WalletPolicy, AppClient } = await importAndValidateLedgerBitcoin(ledgerClient);
246
+ if (!(ledgerClient instanceof AppClient))
247
+ throw new Error(`Error: pass a valid ledgerClient`);
182
248
  const result = await descriptorToLedgerFormat({
183
249
  descriptor,
184
250
  ledgerClient,
@@ -204,7 +270,7 @@ async function registerLedgerWallet({ descriptor, ledgerClient, ledgerState, pol
204
270
  //It already existed. No need to register it again.
205
271
  }
206
272
  else {
207
- walletPolicy = new ledger_bitcoin_1.WalletPolicy(policyName, ledgerTemplate, keyRoots);
273
+ walletPolicy = new WalletPolicy(policyName, ledgerTemplate, keyRoots);
208
274
  let policyId;
209
275
  [policyId, policyHmac] = await ledgerClient.registerWallet(walletPolicy);
210
276
  const policy = {
@@ -1,4 +1,3 @@
1
- import type { AppClient } from 'ledger-bitcoin';
2
1
  import { Network } from 'bitcoinjs-lib';
3
2
  import type { LedgerState } from './ledger';
4
3
  import type { BIP32Interface } from 'bip32';
@@ -30,7 +29,7 @@ export declare const wpkhBIP32: ({ masterNode, network, keyPath, account, change
30
29
  isPublic?: boolean;
31
30
  }) => string;
32
31
  export declare const pkhLedger: ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
33
- ledgerClient: AppClient;
32
+ ledgerClient: unknown;
34
33
  ledgerState: LedgerState;
35
34
  network?: Network;
36
35
  account: number;
@@ -39,7 +38,7 @@ export declare const pkhLedger: ({ ledgerClient, ledgerState, network, account,
39
38
  index?: number | undefined | '*';
40
39
  }) => Promise<string>;
41
40
  export declare const shWpkhLedger: ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
42
- ledgerClient: AppClient;
41
+ ledgerClient: unknown;
43
42
  ledgerState: LedgerState;
44
43
  network?: Network;
45
44
  account: number;
@@ -48,7 +47,7 @@ export declare const shWpkhLedger: ({ ledgerClient, ledgerState, network, accoun
48
47
  index?: number | undefined | '*';
49
48
  }) => Promise<string>;
50
49
  export declare const wpkhLedger: ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
51
- ledgerClient: AppClient;
50
+ ledgerClient: unknown;
52
51
  ledgerState: LedgerState;
53
52
  network?: Network;
54
53
  account: number;
package/dist/signers.d.ts CHANGED
@@ -1,7 +1,6 @@
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 'ledger-bitcoin';
5
4
  import type { DescriptorInterface } from './types';
6
5
  import { LedgerState } from './ledger';
7
6
  export declare function signInputECPair({ psbt, index, ecpair }: {
@@ -26,12 +25,12 @@ export declare function signInputLedger({ psbt, index, descriptor, ledgerClient,
26
25
  psbt: Psbt;
27
26
  index: number;
28
27
  descriptor: DescriptorInterface;
29
- ledgerClient: AppClient;
28
+ ledgerClient: unknown;
30
29
  ledgerState: LedgerState;
31
30
  }): Promise<void>;
32
31
  export declare function signLedger({ psbt, descriptors, ledgerClient, ledgerState }: {
33
32
  psbt: Psbt;
34
33
  descriptors: DescriptorInterface[];
35
- ledgerClient: AppClient;
34
+ ledgerClient: unknown;
36
35
  ledgerState: LedgerState;
37
36
  }): Promise<void>;
package/dist/signers.js CHANGED
@@ -3,7 +3,6 @@
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_bitcoin_1 = require("ledger-bitcoin");
7
6
  const ledger_1 = require("./ledger");
8
7
  function signInputECPair({ psbt, index, ecpair }) {
9
8
  psbt.signInput(index, ecpair);
@@ -28,6 +27,9 @@ const ledgerSignaturesForInputIndex = (index, ledgerSignatures) => ledgerSignatu
28
27
  signature: partialSignature.signature
29
28
  }));
30
29
  async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState }) {
30
+ const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient);
31
+ if (!(ledgerClient instanceof AppClient))
32
+ throw new Error(`Error: pass a valid ledgerClient`);
31
33
  const result = await (0, ledger_1.descriptorToLedgerFormat)({
32
34
  descriptor,
33
35
  ledgerClient,
@@ -43,7 +45,7 @@ async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerSt
43
45
  ledgerState
44
46
  });
45
47
  if (standardPolicy) {
46
- ledgerSignatures = await ledgerClient.signPsbt(new ledger_bitcoin_1.PsbtV2().fromBitcoinJS(psbt), new ledger_bitcoin_1.DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
48
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(ledgerTemplate, keyRoots[0]), null);
47
49
  }
48
50
  else {
49
51
  const policy = await (0, ledger_1.ledgerPolicyFromState)({
@@ -53,8 +55,8 @@ async function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerSt
53
55
  });
54
56
  if (!policy || !policy.policyName || !policy.policyHmac)
55
57
  throw new Error(`Error: the descriptor's policy is not registered`);
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
+ const walletPolicy = new WalletPolicy(policy.policyName, ledgerTemplate, keyRoots);
59
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, policy.policyHmac);
58
60
  }
59
61
  //Add the signatures to the Psbt object using PartialSig format:
60
62
  psbt.updateInput(index, {
@@ -66,6 +68,9 @@ exports.signInputLedger = signInputLedger;
66
68
  //it clusters together wallet policy types before signing
67
69
  //it throws if it cannot sign any input.
68
70
  async function signLedger({ psbt, descriptors, ledgerClient, ledgerState }) {
71
+ const { PsbtV2, DefaultWalletPolicy, WalletPolicy, AppClient } = await (0, ledger_1.importAndValidateLedgerBitcoin)(ledgerClient);
72
+ if (!(ledgerClient instanceof AppClient))
73
+ throw new Error(`Error: pass a valid ledgerClient`);
69
74
  const ledgerPolicies = [];
70
75
  for (const descriptor of descriptors) {
71
76
  const policy = (await (0, ledger_1.ledgerPolicyFromState)({
@@ -95,12 +100,12 @@ async function signLedger({ psbt, descriptors, ledgerClient, ledgerState }) {
95
100
  uniquePolicy.policyHmac &&
96
101
  uniquePolicy.policyId) {
97
102
  //non-standard policy
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);
103
+ const walletPolicy = new WalletPolicy(uniquePolicy.policyName, uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots);
104
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), walletPolicy, uniquePolicy.policyHmac);
100
105
  }
101
106
  else {
102
107
  //standard policy
103
- ledgerSignatures = await ledgerClient.signPsbt(new ledger_bitcoin_1.PsbtV2().fromBitcoinJS(psbt), new ledger_bitcoin_1.DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
108
+ ledgerSignatures = await ledgerClient.signPsbt(new PsbtV2().fromBitcoinJS(psbt), new DefaultWalletPolicy(uniquePolicy.ledgerTemplate, uniquePolicy.keyRoots[0]), null);
104
109
  }
105
110
  for (const [index, ,] of ledgerSignatures) {
106
111
  psbt.updateInput(index, {
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.3.1",
4
+ "version": "1.0.1",
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",
@@ -52,14 +52,13 @@
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)",
56
- "dependencies": {
57
- "@bitcoinerlab/miniscript": "^1.2.1",
58
- "@bitcoinerlab/secp256k1": "^1.0.2",
59
- "bip32": "^3.1.0",
60
- "bitcoinjs-lib": "^6.1.0",
61
- "ecpair": "^2.1.0",
62
- "ledger-bitcoin": "npm:@bitcoinerlab/ledger@^0.2.0"
55
+ "peerDependencies": {
56
+ "ledger-bitcoin": "^0.2.2"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "ledger-bitcoin": {
60
+ "optional": true
61
+ }
63
62
  },
64
63
  "devDependencies": {
65
64
  "@babel/plugin-transform-modules-commonjs": "^7.20.11",
@@ -77,10 +76,18 @@
77
76
  "fs": "^0.0.1-security",
78
77
  "jest": "^29.4.3",
79
78
  "jsdoc": "^3.6.11",
79
+ "ledger-bitcoin": "^0.2.2",
80
80
  "path": "^0.12.7",
81
81
  "prettier": "^2.8.8",
82
82
  "regtest-client": "^0.2.0",
83
83
  "ts-node-dev": "^2.0.0",
84
84
  "typescript": "5.0"
85
+ },
86
+ "dependencies": {
87
+ "@bitcoinerlab/miniscript": "^1.2.1",
88
+ "@bitcoinerlab/secp256k1": "^1.0.5",
89
+ "bip32": "^4.0.0",
90
+ "bitcoinjs-lib": "^6.1.3",
91
+ "ecpair": "^2.1.0"
85
92
  }
86
93
  }
package/README.draft.md DELETED
@@ -1,84 +0,0 @@
1
- A JavaScript library for parsing and signing Bitcoin descriptors, including those based on the Miniscript language.
2
-
3
- Key features:
4
- It allows signing using Hierarchical Deterministic keeps (those that use word mnemonics) and also Hardware Wallets (the Ledger wallet so far; more are comming).
5
- It integrates miniscript, making it super easy to program advanced transactions. For example, timelock-based vaults, multisigs vaults or inheritance are now a breeze.
6
- Dessigned to be used with psbt (Partially Signed Bitcion transactions) so that it can be used in mkuti-party scenarios.
7
- Can be used with Typescript and Javascript.
8
- Integrates with the bitcoinjs-lib family of modules.
9
-
10
- Usage:
11
-
12
- Installation:
13
- npm install @bitcoinerlab/descriptors
14
-
15
- Usage:
16
-
17
- This section describes how to use this library, starting from a very easy transaction and ends up using a smart contract that only allows spending a transaction after a certain time, providing a certain secret (a preimage) and can only be unlocked by co-signing with a Ledger wallet and a Software Wallet.
18
-
19
- We will show examples that have been run on the testnet network. We share the mnemonic we used so that you can replicate the transactions: . You won't be able to spend the transactions again but you will be able to replicate everything.
20
-
21
- ```
22
- MNEMONIC: "drum turtle globe inherit autumn flavor slice illness sniff distance carbon elder"
23
- ```
24
-
25
- TIP: you can use https://iancoleman.io/bip39/ to verify some of the steps below.
26
-
27
- Simple case using standard transactions:
28
-
29
- Let's start with an easy case. Let's send some sats from a Bitcoin Legacy address to a Segwit one.
30
-
31
- We are ready to use the library. Here we skip all initialization. You will be able to see the complete code on a shared repository with the tests.
32
-
33
- Examples are written using Javascript. If you want to use Typescript (recommended), please take a look to the test/integration folder for some advanced use use cases.
34
- Please, note that the code below is just for explanation purposes. When writting code for production you must assert all intermediate steps.
35
-
36
- ```javascript
37
- //NETWORK is set to testnet
38
- const masterNode = BIP32.fromSeed(mnemonicToSeedSync(MNEMONIC), NETWORK);
39
- const descriptorLegacy = pkhBIP32({ masterNode, network: NETWORK, account: 0, change: 0, index: 1 });
40
-
41
- console.log(descriptorLegacy.getAddress()); //moovc1JqGrz4v6FA2U8ks8ZqjSwjv3yRKQ
42
- ```
43
-
44
- So we sent some sats (`1679037`) to that address above. Those sats were sent in `TX_ID = "ee02b5a12c2f22e892bed376781fc9ed435f0d192a1b67ca47a7190804d8e868"`. [See it here](https://blockstream.info/testnet/tx/ee02b5a12c2f22e892bed376781fc9ed435f0d192a1b67ca47a7190804d8e868).
45
-
46
- Now let's spend them. We will send all the funds, except for a fee to our first internal address (change 1) in account 0 of the P2WPKH (Segwit) wallet:
47
-
48
- <!--https://coinfaucet.eu/en/btc-testnet/-->
49
-
50
- ```javascript
51
- const TX_ID = "ee02b5a12c2f22e892bed376781fc9ed435f0d192a1b67ca47a7190804d8e868";
52
- const FEE = 100;
53
-
54
- //Define the Segwit descriptor where we will receive the sats:
55
- const descriptorSegwit = wpkhBIP32({ masterNode, network: NETWORK, account: 0, change: 1, index: 0 });
56
-
57
- //Get some info from the network about the Legacy utxo we will spend from:
58
- const txHex = await (await fetch(`https://blockstream.info/testnet/api/tx/${TX_ID}/hex`)).text();
59
- const txOuts = (await (await fetch(`https://blockstream.info/testnet/api/tx/${TX_ID}`)).json()).vout;
60
- const vout = txOuts.findIndex(txOut => txOut.scriptpubkey === descriptorLegacy.getScriptPubKey());
61
- const initialValue = txOut[vout].value; //1679037, as mentioned above
62
-
63
- //Create a transaction (Partially Signed Bitcoin Transaction) now:
64
- const psbt = new Psbt({network: NETWORK});
65
- //Update the transaction with the Legacy descriptor input:
66
- const legacyInputNumber = descriptorLegacy.updatePsbt({ psbt, vout, txHex });
67
- //Now add the Segwit address as the new output. Let's give some FEE to the miners
68
- psbt.addOutput({ address: descriptorSegwit.getAddress(), value: initialValue - FEE});
69
-
70
- //Sign the transaction, finalize it and submit it to the miners:
71
- signBIP32({ psbt, masterNode });
72
- descriptorLegacy.finalizePsbtInput({psbt, index: legacyInputNumber});
73
- const spendTx = psbt.extractTransaction();
74
- console.log(spendTx.getId()); //
75
- await fetch('https://blockstream.info/testnet/api/tx', {
76
- method: 'POST',
77
- body: spendTx.toHex()
78
- });
79
- ```
80
- We have sent the 1679037 - 100 to ourselves in just a few lines.
81
- Easy! See the tx here: ....
82
- This has only started. Let's do really cool stuff now.
83
-
84
-