@bitcoinerlab/descriptors 1.0.2 → 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/README.md +138 -145
- package/dist/checksum.d.ts +4 -0
- package/dist/checksum.js +4 -0
- package/dist/descriptors.d.ts +519 -7
- package/dist/descriptors.js +285 -162
- package/dist/index.d.ts +27 -7
- package/dist/index.js +12 -3
- package/dist/keyExpressions.d.ts +58 -1
- package/dist/keyExpressions.js +31 -3
- package/dist/ledger.d.ts +90 -13
- package/dist/ledger.js +253 -52
- package/dist/scriptExpressions.d.ts +73 -28
- package/dist/scriptExpressions.js +26 -4
- package/dist/signers.d.ts +37 -4
- package/dist/signers.js +102 -41
- package/dist/types.d.ts +120 -73
- package/package.json +32 -50
package/dist/ledger.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
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
|
-
};
|
|
27
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.ledgerPolicyFromState = exports.comparePolicies = exports.ledgerPolicyFromStandard = exports.registerLedgerWallet = exports.
|
|
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");
|
|
29
29
|
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
|
|
30
30
|
const re_1 = require("./re");
|
|
31
31
|
/**
|
|
@@ -49,7 +49,18 @@ const re_1 = require("./re");
|
|
|
49
49
|
async function importAndValidateLedgerBitcoin(ledgerClient) {
|
|
50
50
|
let ledgerBitcoinModule;
|
|
51
51
|
try {
|
|
52
|
-
|
|
52
|
+
// Originally, the code used dynamic imports:
|
|
53
|
+
// ledgerBitcoinModule = await import('ledger-bitcoin');
|
|
54
|
+
// However, in React Native with the Metro bundler, there's an issue with
|
|
55
|
+
// recognizing dynamic imports inside try-catch blocks. For details, refer to:
|
|
56
|
+
// https://github.com/react-native-community/discussions-and-proposals/issues/120
|
|
57
|
+
// The dynamic import gets transpiled to:
|
|
58
|
+
// ledgerBitcoinModule = Promise.resolve().then(() => __importStar(require('ledger-bitcoin')));
|
|
59
|
+
// Metro bundler fails to recognize the above as conditional. Hence, it tries
|
|
60
|
+
// to require 'ledger-bitcoin' unconditionally, leading to potential errors if
|
|
61
|
+
// 'ledger-bitcoin' is not installed (given it's an optional peerDependency).
|
|
62
|
+
// To bypass this, we directly use require:
|
|
63
|
+
ledgerBitcoinModule = require('ledger-bitcoin');
|
|
53
64
|
}
|
|
54
65
|
catch (error) {
|
|
55
66
|
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.');
|
|
@@ -74,6 +85,15 @@ async function ledgerAppInfo(transport) {
|
|
|
74
85
|
const flags = r.slice(i, (i += flagLength));
|
|
75
86
|
return { name, version, flags, format };
|
|
76
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
|
+
*/
|
|
77
97
|
async function assertLedgerApp({ transport, name, minVersion }) {
|
|
78
98
|
const { name: openName, version } = await ledgerAppInfo(transport);
|
|
79
99
|
if (openName !== name) {
|
|
@@ -114,7 +134,14 @@ function isLedgerStandard({ ledgerTemplate, keyRoots, network = bitcoinjs_lib_1.
|
|
|
114
134
|
return true;
|
|
115
135
|
return false;
|
|
116
136
|
}
|
|
117
|
-
|
|
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`);
|
|
118
145
|
const { AppClient } = (await importAndValidateLedgerBitcoin(ledgerClient));
|
|
119
146
|
if (!(ledgerClient instanceof AppClient))
|
|
120
147
|
throw new Error(`Error: pass a valid ledgerClient`);
|
|
@@ -126,7 +153,14 @@ async function getLedgerMasterFingerPrint({ ledgerClient, ledgerState }) {
|
|
|
126
153
|
return masterFingerprint;
|
|
127
154
|
}
|
|
128
155
|
exports.getLedgerMasterFingerPrint = getLedgerMasterFingerPrint;
|
|
129
|
-
|
|
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`);
|
|
130
164
|
const { AppClient } = (await importAndValidateLedgerBitcoin(ledgerClient));
|
|
131
165
|
if (!(ledgerClient instanceof AppClient))
|
|
132
166
|
throw new Error(`Error: pass a valid ledgerClient`);
|
|
@@ -149,7 +183,151 @@ async function getLedgerXpub({ originPath, ledgerClient, ledgerState }) {
|
|
|
149
183
|
}
|
|
150
184
|
exports.getLedgerXpub = getLedgerXpub;
|
|
151
185
|
/**
|
|
152
|
-
*
|
|
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
|
+
*
|
|
153
331
|
* keyRoots and template follow Ledger's specifications:
|
|
154
332
|
* https://github.com/LedgerHQ/app-bitcoin-new/blob/develop/doc/wallet.md
|
|
155
333
|
*
|
|
@@ -173,11 +351,11 @@ exports.getLedgerXpub = getLedgerXpub;
|
|
|
173
351
|
* This function takes into account all the considerations regarding Ledger
|
|
174
352
|
* policy implementation details expressed in the header of this file.
|
|
175
353
|
*/
|
|
176
|
-
async function
|
|
177
|
-
const expandedExpression =
|
|
178
|
-
const expansionMap =
|
|
354
|
+
async function ledgerPolicyFromOutput({ output, ledgerClient, ledgerState }) {
|
|
355
|
+
const expandedExpression = output.expand().expandedExpression;
|
|
356
|
+
const expansionMap = output.expand().expansionMap;
|
|
179
357
|
if (!expandedExpression || !expansionMap)
|
|
180
|
-
throw new Error(`Error: invalid
|
|
358
|
+
throw new Error(`Error: invalid output`);
|
|
181
359
|
const ledgerMasterFingerprint = await getLedgerMasterFingerPrint({
|
|
182
360
|
ledgerClient,
|
|
183
361
|
ledgerState
|
|
@@ -233,34 +411,57 @@ async function descriptorToLedgerFormat({ descriptor, ledgerClient, ledgerState
|
|
|
233
411
|
});
|
|
234
412
|
return { ledgerTemplate, keyRoots };
|
|
235
413
|
}
|
|
236
|
-
exports.
|
|
414
|
+
exports.ledgerPolicyFromOutput = ledgerPolicyFromOutput;
|
|
237
415
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
* If the policy is standard, it does not register it.
|
|
242
|
-
*
|
|
416
|
+
* To be removed in v3.0 and replaced by a version that does not accept
|
|
417
|
+
* descriptors
|
|
418
|
+
* @hidden
|
|
243
419
|
**/
|
|
244
|
-
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`);
|
|
245
431
|
const { WalletPolicy, AppClient } = (await importAndValidateLedgerBitcoin(ledgerClient));
|
|
246
432
|
if (!(ledgerClient instanceof AppClient))
|
|
247
433
|
throw new Error(`Error: pass a valid ledgerClient`);
|
|
248
|
-
|
|
249
|
-
|
|
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,
|
|
250
451
|
ledgerClient,
|
|
251
452
|
ledgerState
|
|
252
453
|
});
|
|
253
|
-
if (await ledgerPolicyFromStandard({
|
|
454
|
+
if (await ledgerPolicyFromStandard({ output, ledgerClient, ledgerState }))
|
|
254
455
|
return;
|
|
255
456
|
if (!result)
|
|
256
|
-
throw new Error(`Error:
|
|
457
|
+
throw new Error(`Error: output does not have a ledger input`);
|
|
257
458
|
const { ledgerTemplate, keyRoots } = result;
|
|
258
459
|
if (!ledgerState.policies)
|
|
259
460
|
ledgerState.policies = [];
|
|
260
461
|
let walletPolicy, policyHmac;
|
|
261
462
|
//Search in ledgerState first
|
|
262
463
|
const policy = await ledgerPolicyFromState({
|
|
263
|
-
|
|
464
|
+
output,
|
|
264
465
|
ledgerClient,
|
|
265
466
|
ledgerState
|
|
266
467
|
});
|
|
@@ -287,9 +488,9 @@ exports.registerLedgerWallet = registerLedgerWallet;
|
|
|
287
488
|
/**
|
|
288
489
|
* Retrieve a standard ledger policy or null if it does correspond.
|
|
289
490
|
**/
|
|
290
|
-
async function ledgerPolicyFromStandard({
|
|
291
|
-
const result = await
|
|
292
|
-
|
|
491
|
+
async function ledgerPolicyFromStandard({ output, ledgerClient, ledgerState }) {
|
|
492
|
+
const result = await ledgerPolicyFromOutput({
|
|
493
|
+
output,
|
|
293
494
|
ledgerClient,
|
|
294
495
|
ledgerState
|
|
295
496
|
});
|
|
@@ -299,7 +500,7 @@ async function ledgerPolicyFromStandard({ descriptor, ledgerClient, ledgerState
|
|
|
299
500
|
if (isLedgerStandard({
|
|
300
501
|
ledgerTemplate,
|
|
301
502
|
keyRoots,
|
|
302
|
-
network:
|
|
503
|
+
network: output.getNetwork()
|
|
303
504
|
}))
|
|
304
505
|
return { ledgerTemplate, keyRoots };
|
|
305
506
|
return null;
|
|
@@ -324,14 +525,14 @@ exports.comparePolicies = comparePolicies;
|
|
|
324
525
|
/**
|
|
325
526
|
* Retrieve a ledger policy from ledgerState or null if it does not exist yet.
|
|
326
527
|
**/
|
|
327
|
-
async function ledgerPolicyFromState({
|
|
328
|
-
const result = await
|
|
329
|
-
|
|
528
|
+
async function ledgerPolicyFromState({ output, ledgerClient, ledgerState }) {
|
|
529
|
+
const result = await ledgerPolicyFromOutput({
|
|
530
|
+
output,
|
|
330
531
|
ledgerClient,
|
|
331
532
|
ledgerState
|
|
332
533
|
});
|
|
333
534
|
if (!result)
|
|
334
|
-
throw new Error(`Error:
|
|
535
|
+
throw new Error(`Error: output does not have a ledger input`);
|
|
335
536
|
const { ledgerTemplate, keyRoots } = result;
|
|
336
537
|
if (!ledgerState.policies)
|
|
337
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:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
})
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Psbt } from 'bitcoinjs-lib';
|
|
2
2
|
import type { ECPairInterface } from 'ecpair';
|
|
3
3
|
import type { BIP32Interface } from 'bip32';
|
|
4
|
-
import type {
|
|
5
|
-
import { LedgerState } from './ledger';
|
|
4
|
+
import type { DescriptorInstance } from './descriptors';
|
|
5
|
+
import { LedgerState, LedgerManager } from './ledger';
|
|
6
6
|
export declare function signInputECPair({ psbt, index, ecpair }: {
|
|
7
7
|
psbt: Psbt;
|
|
8
8
|
index: number;
|
|
@@ -21,16 +21,49 @@ 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;
|
|
27
|
-
descriptor:
|
|
42
|
+
descriptor: DescriptorInstance;
|
|
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
|
-
descriptors:
|
|
66
|
+
descriptors: DescriptorInstance[];
|
|
34
67
|
ledgerClient: unknown;
|
|
35
68
|
ledgerState: LedgerState;
|
|
36
69
|
}): Promise<void>;
|