@bitcoinerlab/descriptors 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ledger.js CHANGED
@@ -2,7 +2,30 @@
2
2
  // Copyright (c) 2023 Jose-Luis Landabaso - https://bitcoinerlab.com
3
3
  // Distributed under the MIT software license
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.ledgerPolicyFromState = exports.comparePolicies = exports.ledgerPolicyFromStandard = exports.registerLedgerWallet = exports.descriptorToLedgerFormat = exports.getLedgerXpub = exports.getLedgerMasterFingerPrint = exports.assertLedgerApp = exports.importAndValidateLedgerBitcoin = void 0;
5
+ exports.ledgerPolicyFromState = exports.comparePolicies = exports.ledgerPolicyFromStandard = exports.registerLedgerWallet = exports.ledgerPolicyFromOutput = exports.ledgerPolicyFromPsbtInput = exports.getLedgerXpub = exports.getLedgerMasterFingerPrint = exports.assertLedgerApp = exports.importAndValidateLedgerBitcoin = void 0;
6
+ /*
7
+ * Notes on Ledger implementation:
8
+ *
9
+ * Ledger assumes as external all keyRoots that do not have origin information.
10
+ *
11
+ * Some known Ledger Limitations (based on my tests as of Febr 2023):
12
+ *
13
+ * 1) All keyExpressions must be expanded into @i. In other words,
14
+ * this template is not valid:
15
+ * wsh(and_v(v:pk(03ed0b41d808b012b3a77dd7f6a30c4180dfbcab604133d90ce7593ec7f3e4037b),and_v(v:sha256(6c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd5333),and_v(and_v(v:pk(@0/**),v:pk(@1/**)),older(5)))))
16
+ * (note the fixed 03ed0b41d808b012b3a77dd7f6a30c4180dfbcab604133d90ce7593ec7f3e4037b pubkey)
17
+ *
18
+ * 2) All elements in the keyRoot vector must be xpub-type (no xprv-type, no pubkey-type, ...)
19
+ *
20
+ * 3) All originPaths of the expressions in the keyRoot vector must be the same.
21
+ * On the other hand, an empty originPath is permitted for external keys.
22
+ *
23
+ * 4) Since all originPaths must be the same and originPaths for the Ledger are
24
+ * necessary, a Ledger device can only sign at most 1 key per policy and input.
25
+ *
26
+ * All the conditions above are checked in function ledgerPolicyFromOutput.
27
+ */
28
+ const descriptors_1 = require("./descriptors");
6
29
  const bitcoinjs_lib_1 = require("bitcoinjs-lib");
7
30
  const re_1 = require("./re");
8
31
  /**
@@ -62,6 +85,15 @@ async function ledgerAppInfo(transport) {
62
85
  const flags = r.slice(i, (i += flagLength));
63
86
  return { name, version, flags, format };
64
87
  }
88
+ /**
89
+ * Verifies if the Ledger device is connected, if the required Bitcoin App is opened,
90
+ * and if the version of the app meets the minimum requirements.
91
+ *
92
+ * @throws Will throw an error if the Ledger device is not connected, the required
93
+ * Bitcoin App is not opened, or if the version is below the required number.
94
+ *
95
+ * @returns Promise<void> - A promise that resolves if all assertions pass, or throws otherwise.
96
+ */
65
97
  async function assertLedgerApp({ transport, name, minVersion }) {
66
98
  const { name: openName, version } = await ledgerAppInfo(transport);
67
99
  if (openName !== name) {
@@ -102,7 +134,14 @@ function isLedgerStandard({ ledgerTemplate, keyRoots, network = bitcoinjs_lib_1.
102
134
  return true;
103
135
  return false;
104
136
  }
105
- async function getLedgerMasterFingerPrint({ ledgerClient, ledgerState }) {
137
+ /** @hidden */
138
+ async function getLedgerMasterFingerPrint({ ledgerClient, ledgerState, ledgerManager }) {
139
+ if (ledgerManager && (ledgerClient || ledgerState))
140
+ throw new Error(`ledgerClient and ledgerState have been deprecated`);
141
+ if (ledgerManager)
142
+ ({ ledgerClient, ledgerState } = ledgerManager);
143
+ if (!ledgerClient || !ledgerState)
144
+ throw new Error(`Could not retrieve ledgerClient or ledgerState`);
106
145
  const { AppClient } = (await importAndValidateLedgerBitcoin(ledgerClient));
107
146
  if (!(ledgerClient instanceof AppClient))
108
147
  throw new Error(`Error: pass a valid ledgerClient`);
@@ -114,7 +153,14 @@ async function getLedgerMasterFingerPrint({ ledgerClient, ledgerState }) {
114
153
  return masterFingerprint;
115
154
  }
116
155
  exports.getLedgerMasterFingerPrint = getLedgerMasterFingerPrint;
117
- async function getLedgerXpub({ originPath, ledgerClient, ledgerState }) {
156
+ /** @hidden */
157
+ async function getLedgerXpub({ originPath, ledgerClient, ledgerState, ledgerManager }) {
158
+ if (ledgerManager && (ledgerClient || ledgerState))
159
+ throw new Error(`ledgerClient and ledgerState have been deprecated`);
160
+ if (ledgerManager)
161
+ ({ ledgerClient, ledgerState } = ledgerManager);
162
+ if (!ledgerClient || !ledgerState)
163
+ throw new Error(`Could not retrieve ledgerClient or ledgerState`);
118
164
  const { AppClient } = (await importAndValidateLedgerBitcoin(ledgerClient));
119
165
  if (!(ledgerClient instanceof AppClient))
120
166
  throw new Error(`Error: pass a valid ledgerClient`);
@@ -137,7 +183,151 @@ async function getLedgerXpub({ originPath, ledgerClient, ledgerState }) {
137
183
  }
138
184
  exports.getLedgerXpub = getLedgerXpub;
139
185
  /**
140
- * Takes a descriptor and gets its Ledger Wallet Policy, that is, its keyRoots and template.
186
+ * Checks whether there is a policy in ledgerState that the ledger
187
+ * could use to sign this psbt input.
188
+ *
189
+ * It found return the policy, otherwise, return undefined
190
+ *
191
+ * All considerations in the header of this file are applied
192
+ */
193
+ async function ledgerPolicyFromPsbtInput({ ledgerManager, psbt, index }) {
194
+ const { ledgerState, ledgerClient, ecc, network } = ledgerManager;
195
+ const { Output } = (0, descriptors_1.DescriptorsFactory)(ecc);
196
+ const input = psbt.data.inputs[index];
197
+ if (!input)
198
+ throw new Error(`Input numer ${index} not set.`);
199
+ let scriptPubKey;
200
+ if (input.nonWitnessUtxo) {
201
+ const vout = psbt.txInputs[index]?.index;
202
+ if (vout === undefined)
203
+ throw new Error(`Could not extract vout from nonWitnessUtxo for input ${index}.`);
204
+ scriptPubKey = bitcoinjs_lib_1.Transaction.fromBuffer(input.nonWitnessUtxo).outs[vout]
205
+ ?.script;
206
+ }
207
+ else if (input.witnessUtxo) {
208
+ scriptPubKey = input.witnessUtxo.script;
209
+ }
210
+ if (!scriptPubKey)
211
+ throw new Error(`Could not retrieve scriptPubKey for input ${index}.`);
212
+ const bip32Derivations = input.bip32Derivation;
213
+ if (!bip32Derivations || !bip32Derivations.length)
214
+ throw new Error(`Input ${index} does not contain bip32 derivations.`);
215
+ const ledgerMasterFingerprint = await getLedgerMasterFingerPrint({
216
+ ledgerManager
217
+ });
218
+ for (const bip32Derivation of bip32Derivations) {
219
+ //get the keyRoot and keyPath. If it matches one of our policies then
220
+ //we are still not sure this is the policy that must be used yet
221
+ //So we must use the template and the keyRoot of each policy and compute the
222
+ //scriptPubKey:
223
+ if (Buffer.compare(bip32Derivation.masterFingerprint, ledgerMasterFingerprint) === 0) {
224
+ // Match /m followed by n consecutive hardened levels and then 2 consecutive unhardened levels:
225
+ const match = bip32Derivation.path.match(/m((\/\d+['hH])*)(\/\d+\/\d+)?/);
226
+ const originPath = match ? match[1] : undefined; //n consecutive hardened levels
227
+ const keyPath = match ? match[3] : undefined; //2 unhardened levels or undefined
228
+ if (originPath && keyPath) {
229
+ const [, strChange, strIndex] = keyPath.split('/');
230
+ if (!strChange || !strIndex)
231
+ throw new Error(`keyPath ${keyPath} incorrectly extracted`);
232
+ const change = parseInt(strChange, 10);
233
+ const index = parseInt(strIndex, 10);
234
+ const coinType = network === bitcoinjs_lib_1.networks.bitcoin ? 0 : 1;
235
+ //standard policy candidate. This policy will be added to the pool
236
+ //of policies below and check if it produces the correct scriptPubKey
237
+ let standardPolicy;
238
+ if (change === 0 || change === 1) {
239
+ const standardTemplate = originPath.match(new RegExp(`^/44'/${coinType}'/(\\d+)'$`))
240
+ ? 'pkh(@0/**)'
241
+ : originPath.match(new RegExp(`^/84'/${coinType}'/(\\d+)'$`))
242
+ ? 'wpkh(@0/**)'
243
+ : originPath.match(new RegExp(`^/49'/${coinType}'/(\\d+)'$`))
244
+ ? 'sh(wpkh(@0/**))'
245
+ : originPath.match(new RegExp(`^/86'/${coinType}'/(\\d+)'$`))
246
+ ? 'tr(@0/**)'
247
+ : undefined;
248
+ if (standardTemplate) {
249
+ const xpub = await getLedgerXpub({
250
+ originPath,
251
+ ledgerClient,
252
+ ledgerState
253
+ });
254
+ standardPolicy = {
255
+ ledgerTemplate: standardTemplate,
256
+ keyRoots: [
257
+ `[${ledgerMasterFingerprint.toString('hex')}${originPath}]${xpub}`
258
+ ]
259
+ };
260
+ }
261
+ }
262
+ const policies = [...(ledgerState.policies || [])];
263
+ if (standardPolicy)
264
+ policies.push(standardPolicy);
265
+ for (const policy of policies) {
266
+ //Build the descriptor from the ledgerTemplate + keyRoots
267
+ //then get the scriptPubKey
268
+ let descriptor = policy.ledgerTemplate;
269
+ // Replace change (making sure the value in the change level for the
270
+ // template of the policy meets the change in bip32Derivation):
271
+ descriptor = descriptor.replace(/\/\*\*/g, `/<0;1>/*`);
272
+ const regExpMN = new RegExp(`/<(\\d+);(\\d+)>`, 'g');
273
+ let matchMN;
274
+ while (descriptor && (matchMN = regExpMN.exec(descriptor)) !== null) {
275
+ const [M, N] = [
276
+ parseInt(matchMN[1], 10),
277
+ parseInt(matchMN[2], 10)
278
+ ];
279
+ if (M === change || N === change)
280
+ descriptor = descriptor.replace(`/<${M};${N}>`, `/${change}`);
281
+ else
282
+ descriptor = undefined;
283
+ }
284
+ if (descriptor) {
285
+ // Replace index:
286
+ descriptor = descriptor.replace(/\/\*/g, `/${index}`);
287
+ // Replace origin in reverse order to prevent
288
+ // misreplacements, e.g., @10 being mistaken for @1 and leaving a 0.
289
+ for (let i = policy.keyRoots.length - 1; i >= 0; i--) {
290
+ const keyRoot = policy.keyRoots[i];
291
+ if (!keyRoot)
292
+ throw new Error(`keyRoot ${keyRoot} invalidly extracted.`);
293
+ const match = keyRoot.match(/\[([^]+)\]/);
294
+ const keyRootOrigin = match && match[1];
295
+ if (keyRootOrigin) {
296
+ const [, ...arrKeyRootOriginPath] = keyRootOrigin.split('/');
297
+ const keyRootOriginPath = '/' + arrKeyRootOriginPath.join('/');
298
+ //We check all origins to be the same even if they do not
299
+ //belong to the ledger (read the header in this file)
300
+ if (descriptor && keyRootOriginPath === originPath)
301
+ descriptor = descriptor.replace(new RegExp(`@${i}`, 'g'), keyRoot);
302
+ else
303
+ descriptor = undefined;
304
+ }
305
+ else
306
+ descriptor = undefined;
307
+ }
308
+ //verify the scriptPubKey from the input vs. the one obtained from
309
+ //the policy after having filled in the keyPath in the template
310
+ if (descriptor) {
311
+ const policyScriptPubKey = new Output({
312
+ descriptor,
313
+ network
314
+ }).getScriptPubKey();
315
+ if (Buffer.compare(policyScriptPubKey, scriptPubKey) === 0) {
316
+ return policy;
317
+ }
318
+ }
319
+ }
320
+ }
321
+ }
322
+ }
323
+ }
324
+ return;
325
+ }
326
+ exports.ledgerPolicyFromPsbtInput = ledgerPolicyFromPsbtInput;
327
+ /**
328
+ * Given an output, it extracts its descriptor and converts it to a Ledger
329
+ * Wallet Policy, that is, its keyRoots and template.
330
+ *
141
331
  * keyRoots and template follow Ledger's specifications:
142
332
  * https://github.com/LedgerHQ/app-bitcoin-new/blob/develop/doc/wallet.md
143
333
  *
@@ -161,11 +351,11 @@ exports.getLedgerXpub = getLedgerXpub;
161
351
  * This function takes into account all the considerations regarding Ledger
162
352
  * policy implementation details expressed in the header of this file.
163
353
  */
164
- async function descriptorToLedgerFormat({ descriptor, ledgerClient, ledgerState }) {
165
- const expandedExpression = descriptor.expand().expandedExpression;
166
- const expansionMap = descriptor.expand().expansionMap;
354
+ async function ledgerPolicyFromOutput({ output, ledgerClient, ledgerState }) {
355
+ const expandedExpression = output.expand().expandedExpression;
356
+ const expansionMap = output.expand().expansionMap;
167
357
  if (!expandedExpression || !expansionMap)
168
- throw new Error(`Error: invalid descriptor`);
358
+ throw new Error(`Error: invalid output`);
169
359
  const ledgerMasterFingerprint = await getLedgerMasterFingerPrint({
170
360
  ledgerClient,
171
361
  ledgerState
@@ -221,34 +411,57 @@ async function descriptorToLedgerFormat({ descriptor, ledgerClient, ledgerState
221
411
  });
222
412
  return { ledgerTemplate, keyRoots };
223
413
  }
224
- exports.descriptorToLedgerFormat = descriptorToLedgerFormat;
414
+ exports.ledgerPolicyFromOutput = ledgerPolicyFromOutput;
225
415
  /**
226
- * It registers a policy based on a descriptor. It stores it in ledgerState.
227
- *
228
- * If the policy was already registered, it does not register it.
229
- * If the policy is standard, it does not register it.
230
- *
416
+ * To be removed in v3.0 and replaced by a version that does not accept
417
+ * descriptors
418
+ * @hidden
231
419
  **/
232
- async function registerLedgerWallet({ descriptor, ledgerClient, ledgerState, policyName }) {
420
+ async function registerLedgerWallet({ descriptor, ledgerClient, ledgerState, ledgerManager, policyName }) {
421
+ if (typeof descriptor !== 'string' && ledgerManager)
422
+ throw new Error(`Invalid usage: descriptor must be a string`);
423
+ if (ledgerManager && (ledgerClient || ledgerState))
424
+ throw new Error(`Invalid usage: either ledgerManager or ledgerClient + ledgerState`);
425
+ if (ledgerManager)
426
+ ({ ledgerClient, ledgerState } = ledgerManager);
427
+ if (!ledgerClient)
428
+ throw new Error(`ledgerManager not provided`);
429
+ if (!ledgerState)
430
+ throw new Error(`ledgerManager not provided`);
233
431
  const { WalletPolicy, AppClient } = (await importAndValidateLedgerBitcoin(ledgerClient));
234
432
  if (!(ledgerClient instanceof AppClient))
235
433
  throw new Error(`Error: pass a valid ledgerClient`);
236
- const result = await descriptorToLedgerFormat({
237
- descriptor,
434
+ let output;
435
+ if (typeof descriptor === 'string') {
436
+ if (!ledgerManager)
437
+ throw new Error(`ledgerManager not provided`);
438
+ const { Output } = (0, descriptors_1.DescriptorsFactory)(ledgerManager.ecc);
439
+ output = new Output({
440
+ descriptor,
441
+ ...(descriptor.includes('*') ? { index: 0 } : {}),
442
+ network: ledgerManager.network
443
+ });
444
+ }
445
+ else
446
+ output = descriptor;
447
+ if (await ledgerPolicyFromStandard({ output, ledgerClient, ledgerState }))
448
+ return;
449
+ const result = await ledgerPolicyFromOutput({
450
+ output,
238
451
  ledgerClient,
239
452
  ledgerState
240
453
  });
241
- if (await ledgerPolicyFromStandard({ descriptor, ledgerClient, ledgerState }))
454
+ if (await ledgerPolicyFromStandard({ output, ledgerClient, ledgerState }))
242
455
  return;
243
456
  if (!result)
244
- throw new Error(`Error: descriptor does not have a ledger input`);
457
+ throw new Error(`Error: output does not have a ledger input`);
245
458
  const { ledgerTemplate, keyRoots } = result;
246
459
  if (!ledgerState.policies)
247
460
  ledgerState.policies = [];
248
461
  let walletPolicy, policyHmac;
249
462
  //Search in ledgerState first
250
463
  const policy = await ledgerPolicyFromState({
251
- descriptor,
464
+ output,
252
465
  ledgerClient,
253
466
  ledgerState
254
467
  });
@@ -275,9 +488,9 @@ exports.registerLedgerWallet = registerLedgerWallet;
275
488
  /**
276
489
  * Retrieve a standard ledger policy or null if it does correspond.
277
490
  **/
278
- async function ledgerPolicyFromStandard({ descriptor, ledgerClient, ledgerState }) {
279
- const result = await descriptorToLedgerFormat({
280
- descriptor,
491
+ async function ledgerPolicyFromStandard({ output, ledgerClient, ledgerState }) {
492
+ const result = await ledgerPolicyFromOutput({
493
+ output,
281
494
  ledgerClient,
282
495
  ledgerState
283
496
  });
@@ -287,7 +500,7 @@ async function ledgerPolicyFromStandard({ descriptor, ledgerClient, ledgerState
287
500
  if (isLedgerStandard({
288
501
  ledgerTemplate,
289
502
  keyRoots,
290
- network: descriptor.getNetwork()
503
+ network: output.getNetwork()
291
504
  }))
292
505
  return { ledgerTemplate, keyRoots };
293
506
  return null;
@@ -312,14 +525,14 @@ exports.comparePolicies = comparePolicies;
312
525
  /**
313
526
  * Retrieve a ledger policy from ledgerState or null if it does not exist yet.
314
527
  **/
315
- async function ledgerPolicyFromState({ descriptor, ledgerClient, ledgerState }) {
316
- const result = await descriptorToLedgerFormat({
317
- descriptor,
528
+ async function ledgerPolicyFromState({ output, ledgerClient, ledgerState }) {
529
+ const result = await ledgerPolicyFromOutput({
530
+ output,
318
531
  ledgerClient,
319
532
  ledgerState
320
533
  });
321
534
  if (!result)
322
- throw new Error(`Error: descriptor does not have a ledger input`);
535
+ throw new Error(`Error: output does not have a ledger input`);
323
536
  const { ledgerTemplate, keyRoots } = result;
324
537
  if (!ledgerState.policies)
325
538
  ledgerState.policies = [];
@@ -1,57 +1,102 @@
1
1
  import { Network } from 'bitcoinjs-lib';
2
- import type { LedgerState } from './ledger';
2
+ import type { LedgerState, LedgerManager } from './ledger';
3
3
  import type { BIP32Interface } from 'bip32';
4
4
  export declare const pkhBIP32: ({ masterNode, network, keyPath, account, change, index, isPublic }: {
5
5
  masterNode: BIP32Interface;
6
+ /** @default networks.bitcoin */
6
7
  network?: Network;
7
8
  account: number;
8
9
  change?: number | undefined;
9
10
  index?: number | undefined | '*';
10
11
  keyPath?: string;
12
+ /**
13
+ * Compute an xpub or xprv
14
+ * @default true
15
+ */
11
16
  isPublic?: boolean;
12
17
  }) => string;
13
18
  export declare const shWpkhBIP32: ({ masterNode, network, keyPath, account, change, index, isPublic }: {
14
19
  masterNode: BIP32Interface;
20
+ /** @default networks.bitcoin */
15
21
  network?: Network;
16
22
  account: number;
17
23
  change?: number | undefined;
18
24
  index?: number | undefined | '*';
19
25
  keyPath?: string;
26
+ /**
27
+ * Compute an xpub or xprv
28
+ * @default true
29
+ */
20
30
  isPublic?: boolean;
21
31
  }) => string;
22
32
  export declare const wpkhBIP32: ({ masterNode, network, keyPath, account, change, index, isPublic }: {
23
33
  masterNode: BIP32Interface;
34
+ /** @default networks.bitcoin */
24
35
  network?: Network;
25
36
  account: number;
26
37
  change?: number | undefined;
27
38
  index?: number | undefined | '*';
28
39
  keyPath?: string;
40
+ /**
41
+ * Compute an xpub or xprv
42
+ * @default true
43
+ */
29
44
  isPublic?: boolean;
30
45
  }) => string;
31
- export declare const pkhLedger: ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
32
- ledgerClient: unknown;
33
- ledgerState: LedgerState;
34
- network?: Network;
35
- account: number;
36
- keyPath?: string;
37
- change?: number | undefined;
38
- index?: number | undefined | '*';
39
- }) => Promise<string>;
40
- export declare const shWpkhLedger: ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
41
- ledgerClient: unknown;
42
- ledgerState: LedgerState;
43
- network?: Network;
44
- account: number;
45
- keyPath?: string;
46
- change?: number | undefined;
47
- index?: number | undefined | '*';
48
- }) => Promise<string>;
49
- export declare const wpkhLedger: ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
50
- ledgerClient: unknown;
51
- ledgerState: LedgerState;
52
- network?: Network;
53
- account: number;
54
- keyPath?: string;
55
- change?: number | undefined;
56
- index?: number | undefined | '*';
57
- }) => Promise<string>;
46
+ export declare const pkhLedger: {
47
+ ({ ledgerManager, account, keyPath, change, index }: {
48
+ ledgerManager: LedgerManager;
49
+ account: number;
50
+ keyPath?: string;
51
+ change?: number | undefined;
52
+ index?: number | undefined | '*';
53
+ }): Promise<string>;
54
+ ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
55
+ ledgerClient: unknown;
56
+ ledgerState: LedgerState;
57
+ /** @default networks.bitcoin */
58
+ network?: Network;
59
+ account: number;
60
+ keyPath?: string;
61
+ change?: number | undefined;
62
+ index?: number | undefined | '*';
63
+ }): Promise<string>;
64
+ };
65
+ export declare const shWpkhLedger: {
66
+ ({ ledgerManager, account, keyPath, change, index }: {
67
+ ledgerManager: LedgerManager;
68
+ account: number;
69
+ keyPath?: string;
70
+ change?: number | undefined;
71
+ index?: number | undefined | '*';
72
+ }): Promise<string>;
73
+ ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
74
+ ledgerClient: unknown;
75
+ ledgerState: LedgerState;
76
+ /** @default networks.bitcoin */
77
+ network?: Network;
78
+ account: number;
79
+ keyPath?: string;
80
+ change?: number | undefined;
81
+ index?: number | undefined | '*';
82
+ }): Promise<string>;
83
+ };
84
+ export declare const wpkhLedger: {
85
+ ({ ledgerManager, account, keyPath, change, index }: {
86
+ ledgerManager: LedgerManager;
87
+ account: number;
88
+ keyPath?: string;
89
+ change?: number | undefined;
90
+ index?: number | undefined | '*';
91
+ }): Promise<string>;
92
+ ({ ledgerClient, ledgerState, network, account, keyPath, change, index }: {
93
+ ledgerClient: unknown;
94
+ ledgerState: LedgerState;
95
+ /** @default networks.bitcoin */
96
+ network?: Network;
97
+ account: number;
98
+ keyPath?: string;
99
+ change?: number | undefined;
100
+ index?: number | undefined | '*';
101
+ }): Promise<string>;
102
+ };
@@ -11,7 +11,20 @@ function assertStandardKeyPath(keyPath) {
11
11
  }
12
12
  }
13
13
  function standardExpressionsBIP32Maker(purpose, scriptTemplate) {
14
- function standardKeyExpressionBIP32({ masterNode, network = bitcoinjs_lib_1.networks.bitcoin, keyPath, account, change, index, isPublic = true }) {
14
+ /**
15
+ * Computes the standard descriptor based on given parameters.
16
+ *
17
+ * You can define the output location either by:
18
+ * - Providing the full `keyPath` (e.g., "/0/2").
19
+ * OR
20
+ * - Specifying the `change` and `index` values separately (e.g., `{change:0, index:2}`).
21
+ *
22
+ * For ranged indexing, the `index` can be set as a wildcard '*'. For example:
23
+ * - `keyPath="/0/*"`
24
+ * OR
25
+ * - `{change:0, index:'*'}`.
26
+ */
27
+ function standardScriptExpressionBIP32({ masterNode, network = bitcoinjs_lib_1.networks.bitcoin, keyPath, account, change, index, isPublic = true }) {
15
28
  const originPath = `/${purpose}'/${network === bitcoinjs_lib_1.networks.bitcoin ? 0 : 1}'/${account}'`;
16
29
  if (keyPath !== undefined)
17
30
  assertStandardKeyPath(keyPath);
@@ -25,13 +38,22 @@ function standardExpressionsBIP32Maker(purpose, scriptTemplate) {
25
38
  });
26
39
  return scriptTemplate.replace('KEYEXPRESSION', keyExpression);
27
40
  }
28
- return standardKeyExpressionBIP32;
41
+ return standardScriptExpressionBIP32;
29
42
  }
30
43
  exports.pkhBIP32 = standardExpressionsBIP32Maker(44, 'pkh(KEYEXPRESSION)');
31
44
  exports.shWpkhBIP32 = standardExpressionsBIP32Maker(49, 'sh(wpkh(KEYEXPRESSION))');
32
45
  exports.wpkhBIP32 = standardExpressionsBIP32Maker(84, 'wpkh(KEYEXPRESSION)');
33
46
  function standardExpressionsLedgerMaker(purpose, scriptTemplate) {
34
- async function standardKeyExpressionLedger({ ledgerClient, ledgerState, network = bitcoinjs_lib_1.networks.bitcoin, account, keyPath, change, index }) {
47
+ /** @hidden */
48
+ async function standardScriptExpressionLedger({ ledgerClient, ledgerState, ledgerManager, network = bitcoinjs_lib_1.networks.bitcoin, account, keyPath, change, index }) {
49
+ if (ledgerManager && (ledgerClient || ledgerState))
50
+ throw new Error(`ledgerClient and ledgerState have been deprecated`);
51
+ if (ledgerManager && network)
52
+ throw new Error(`ledgerManager already includes the network object`);
53
+ if (ledgerManager)
54
+ ({ ledgerClient, ledgerState, network } = ledgerManager);
55
+ if (!ledgerClient || !ledgerState)
56
+ throw new Error(`Could not retrieve ledgerClient or ledgerState`);
35
57
  const originPath = `/${purpose}'/${network === bitcoinjs_lib_1.networks.bitcoin ? 0 : 1}'/${account}'`;
36
58
  if (keyPath !== undefined)
37
59
  assertStandardKeyPath(keyPath);
@@ -45,7 +67,7 @@ function standardExpressionsLedgerMaker(purpose, scriptTemplate) {
45
67
  });
46
68
  return scriptTemplate.replace('KEYEXPRESSION', keyExpression);
47
69
  }
48
- return standardKeyExpressionLedger;
70
+ return standardScriptExpressionLedger;
49
71
  }
50
72
  exports.pkhLedger = standardExpressionsLedgerMaker(44, 'pkh(KEYEXPRESSION)');
51
73
  exports.shWpkhLedger = standardExpressionsLedgerMaker(49, 'sh(wpkh(KEYEXPRESSION))');
package/dist/signers.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { Psbt } from 'bitcoinjs-lib';
2
2
  import type { ECPairInterface } from 'ecpair';
3
3
  import type { BIP32Interface } from 'bip32';
4
4
  import type { DescriptorInstance } from './descriptors';
5
- import { LedgerState } from './ledger';
5
+ import { LedgerState, LedgerManager } from './ledger';
6
6
  export declare function signInputECPair({ psbt, index, ecpair }: {
7
7
  psbt: Psbt;
8
8
  index: number;
@@ -21,6 +21,21 @@ export declare function signBIP32({ psbt, masterNode }: {
21
21
  psbt: Psbt;
22
22
  masterNode: BIP32Interface;
23
23
  }): void;
24
+ /**
25
+ * Signs an input of the `psbt` where the keys are controlled by a Ledger
26
+ * device.
27
+ *
28
+ * The function will throw an error if it's unable to sign the input.
29
+ */
30
+ export declare function signInputLedger({ psbt, index, ledgerManager }: {
31
+ psbt: Psbt;
32
+ index: number;
33
+ ledgerManager: LedgerManager;
34
+ }): Promise<void>;
35
+ /**
36
+ * @deprecated
37
+ * @hidden
38
+ */
24
39
  export declare function signInputLedger({ psbt, index, descriptor, ledgerClient, ledgerState }: {
25
40
  psbt: Psbt;
26
41
  index: number;
@@ -28,6 +43,24 @@ export declare function signInputLedger({ psbt, index, descriptor, ledgerClient,
28
43
  ledgerClient: unknown;
29
44
  ledgerState: LedgerState;
30
45
  }): Promise<void>;
46
+ /**
47
+ * Signs the inputs of the `psbt` where the keys are controlled by a Ledger
48
+ * device.
49
+ *
50
+ * `signLedger` can sign multiple inputs of the same wallet policy in a single
51
+ * pass by grouping inputs by their wallet policy type before the signing
52
+ * process.
53
+ *
54
+ * The function will throw an error if it's unable to sign any input.
55
+ */
56
+ export declare function signLedger({ psbt, ledgerManager }: {
57
+ psbt: Psbt;
58
+ ledgerManager: LedgerManager;
59
+ }): Promise<void>;
60
+ /**
61
+ * @deprecated
62
+ * @hidden
63
+ */
31
64
  export declare function signLedger({ psbt, descriptors, ledgerClient, ledgerState }: {
32
65
  psbt: Psbt;
33
66
  descriptors: DescriptorInstance[];