@bitcoinerlab/descriptors 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +322 -17
- package/dist/descriptors.d.ts +2 -2
- package/dist/descriptors.js +301 -237
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/keyExpressions.d.ts +1 -1
- package/dist/keyExpressions.js +11 -9
- package/dist/ledger.d.ts +1 -1
- package/dist/ledger.js +2 -2
- package/dist/miniscript.js +7 -4
- package/dist/psbt.js +22 -23
- package/dist/scriptExpressions.d.ts +1 -1
- package/dist/signers.d.ts +1 -1
- package/dist/signers.js +17 -17
- package/dist/types.d.ts +39 -3
- package/package.json +8 -6
package/dist/descriptors.js
CHANGED
|
@@ -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) {
|
|
@@ -115,6 +114,264 @@ function DescriptorsFactory(ecc) {
|
|
|
115
114
|
BIP32
|
|
116
115
|
});
|
|
117
116
|
};
|
|
117
|
+
/**
|
|
118
|
+
* Takes a descriptor (expression) and expands it to its corresponding Bitcoin script and other relevant details.
|
|
119
|
+
*
|
|
120
|
+
* @param {Object} params The parameters for the function.
|
|
121
|
+
* @param {string} params.expression The descriptor expression to be expanded.
|
|
122
|
+
* @param {string} [params.loggedExpression] The descriptor expression used for logging error messages. If not provided, defaults to the original expression.
|
|
123
|
+
* @param {Object} [params.network=networks.bitcoin] The Bitcoin network to use. If not provided, defaults to Bitcoin mainnet.
|
|
124
|
+
* @param {boolean} [params.allowMiniscriptInP2SH=false] Flag to allow miniscript in P2SH. If not provided, defaults to false.
|
|
125
|
+
*
|
|
126
|
+
* @returns {Object} An object containing various details about the expanded descriptor:
|
|
127
|
+
* - payment: The corresponding [bitcoinjs-lib Payment](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts) for the provided expression, if applicable.
|
|
128
|
+
* - expandedExpression: The expanded descriptor expression.
|
|
129
|
+
* - miniscript: The extracted miniscript from the expression, if any.
|
|
130
|
+
* - expansionMap: A map of key expressions in the descriptor to their corresponding expanded keys.
|
|
131
|
+
* - isSegwit: A boolean indicating whether the descriptor represents a SegWit script.
|
|
132
|
+
* - expandedMiniscript: The expanded miniscript, if any.
|
|
133
|
+
* - redeemScript: The redeem script for the descriptor, if applicable.
|
|
134
|
+
* - witnessScript: The witness script for the descriptor, if applicable.
|
|
135
|
+
*
|
|
136
|
+
* @throws {Error} Throws an error if the descriptor cannot be parsed or does not conform to the expected format.
|
|
137
|
+
*/
|
|
138
|
+
const expand = ({ expression, loggedExpression, //this is the expression that will be used for logging error messages
|
|
139
|
+
network = bitcoinjs_lib_1.networks.bitcoin, allowMiniscriptInP2SH = false }) => {
|
|
140
|
+
//remove the checksum before proceeding:
|
|
141
|
+
expression = expression.replace(new RegExp(RE.reChecksum + '$'), '');
|
|
142
|
+
let expandedExpression;
|
|
143
|
+
let miniscript;
|
|
144
|
+
let expansionMap;
|
|
145
|
+
let isSegwit;
|
|
146
|
+
let expandedMiniscript;
|
|
147
|
+
let payment;
|
|
148
|
+
let witnessScript;
|
|
149
|
+
let redeemScript;
|
|
150
|
+
const isRanged = expression.indexOf('*') !== -1;
|
|
151
|
+
if (!loggedExpression)
|
|
152
|
+
loggedExpression = expression;
|
|
153
|
+
//addr(ADDR)
|
|
154
|
+
if (expression.match(RE.reAddrAnchored)) {
|
|
155
|
+
if (isRanged)
|
|
156
|
+
throw new Error(`Error: addr() cannot be ranged`);
|
|
157
|
+
const matchedAddress = expression.match(RE.reAddrAnchored)?.[1]; //[1]-> whatever is found addr(->HERE<-)
|
|
158
|
+
if (!matchedAddress)
|
|
159
|
+
throw new Error(`Error: could not get an address in ${loggedExpression}`);
|
|
160
|
+
let output;
|
|
161
|
+
try {
|
|
162
|
+
output = bitcoinjs_lib_1.address.toOutputScript(matchedAddress, network);
|
|
163
|
+
}
|
|
164
|
+
catch (e) {
|
|
165
|
+
throw new Error(`Error: invalid address ${matchedAddress}`);
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
payment = p2pkh({ output, network });
|
|
169
|
+
}
|
|
170
|
+
catch (e) { }
|
|
171
|
+
try {
|
|
172
|
+
payment = p2sh({ output, network });
|
|
173
|
+
}
|
|
174
|
+
catch (e) { }
|
|
175
|
+
try {
|
|
176
|
+
payment = p2wpkh({ output, network });
|
|
177
|
+
}
|
|
178
|
+
catch (e) { }
|
|
179
|
+
try {
|
|
180
|
+
payment = p2wsh({ output, network });
|
|
181
|
+
}
|
|
182
|
+
catch (e) { }
|
|
183
|
+
try {
|
|
184
|
+
payment = p2tr({ output, network });
|
|
185
|
+
}
|
|
186
|
+
catch (e) { }
|
|
187
|
+
if (!payment) {
|
|
188
|
+
throw new Error(`Error: invalid address ${matchedAddress}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//pk(KEY)
|
|
192
|
+
else if (expression.match(RE.rePkAnchored)) {
|
|
193
|
+
isSegwit = false;
|
|
194
|
+
const keyExpression = expression.match(RE.reKeyExp)?.[0];
|
|
195
|
+
if (!keyExpression)
|
|
196
|
+
throw new Error(`Error: keyExpression could not me extracted`);
|
|
197
|
+
if (expression !== `pk(${keyExpression})`)
|
|
198
|
+
throw new Error(`Error: invalid expression ${loggedExpression}`);
|
|
199
|
+
expandedExpression = 'pk(@0)';
|
|
200
|
+
expansionMap = {
|
|
201
|
+
'@0': parseKeyExpression({ keyExpression, network, isSegwit })
|
|
202
|
+
};
|
|
203
|
+
if (!isRanged) {
|
|
204
|
+
const pubkey = expansionMap['@0'].pubkey;
|
|
205
|
+
//Note there exists no address for p2pk, but we can still use the script
|
|
206
|
+
if (!pubkey)
|
|
207
|
+
throw new Error(`Error: could not extract a pubkey from ${loggedExpression}`);
|
|
208
|
+
payment = p2pk({ pubkey, network });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//pkh(KEY) - legacy
|
|
212
|
+
else if (expression.match(RE.rePkhAnchored)) {
|
|
213
|
+
isSegwit = false;
|
|
214
|
+
const keyExpression = expression.match(RE.reKeyExp)?.[0];
|
|
215
|
+
if (!keyExpression)
|
|
216
|
+
throw new Error(`Error: keyExpression could not me extracted`);
|
|
217
|
+
if (expression !== `pkh(${keyExpression})`)
|
|
218
|
+
throw new Error(`Error: invalid expression ${loggedExpression}`);
|
|
219
|
+
expandedExpression = 'pkh(@0)';
|
|
220
|
+
expansionMap = {
|
|
221
|
+
'@0': parseKeyExpression({ keyExpression, network, isSegwit })
|
|
222
|
+
};
|
|
223
|
+
if (!isRanged) {
|
|
224
|
+
const pubkey = expansionMap['@0'].pubkey;
|
|
225
|
+
if (!pubkey)
|
|
226
|
+
throw new Error(`Error: could not extract a pubkey from ${loggedExpression}`);
|
|
227
|
+
payment = p2pkh({ pubkey, network });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//sh(wpkh(KEY)) - nested segwit
|
|
231
|
+
else if (expression.match(RE.reShWpkhAnchored)) {
|
|
232
|
+
isSegwit = true;
|
|
233
|
+
const keyExpression = expression.match(RE.reKeyExp)?.[0];
|
|
234
|
+
if (!keyExpression)
|
|
235
|
+
throw new Error(`Error: keyExpression could not me extracted`);
|
|
236
|
+
if (expression !== `sh(wpkh(${keyExpression}))`)
|
|
237
|
+
throw new Error(`Error: invalid expression ${loggedExpression}`);
|
|
238
|
+
expandedExpression = 'sh(wpkh(@0))';
|
|
239
|
+
expansionMap = {
|
|
240
|
+
'@0': parseKeyExpression({ keyExpression, network, isSegwit })
|
|
241
|
+
};
|
|
242
|
+
if (!isRanged) {
|
|
243
|
+
const pubkey = expansionMap['@0'].pubkey;
|
|
244
|
+
if (!pubkey)
|
|
245
|
+
throw new Error(`Error: could not extract a pubkey from ${loggedExpression}`);
|
|
246
|
+
payment = p2sh({ redeem: p2wpkh({ pubkey, network }), network });
|
|
247
|
+
redeemScript = payment.redeem?.output;
|
|
248
|
+
if (!redeemScript)
|
|
249
|
+
throw new Error(`Error: could not calculate redeemScript for ${loggedExpression}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//wpkh(KEY) - native segwit
|
|
253
|
+
else if (expression.match(RE.reWpkhAnchored)) {
|
|
254
|
+
isSegwit = true;
|
|
255
|
+
const keyExpression = expression.match(RE.reKeyExp)?.[0];
|
|
256
|
+
if (!keyExpression)
|
|
257
|
+
throw new Error(`Error: keyExpression could not me extracted`);
|
|
258
|
+
if (expression !== `wpkh(${keyExpression})`)
|
|
259
|
+
throw new Error(`Error: invalid expression ${loggedExpression}`);
|
|
260
|
+
expandedExpression = 'wpkh(@0)';
|
|
261
|
+
expansionMap = {
|
|
262
|
+
'@0': parseKeyExpression({ keyExpression, network, isSegwit })
|
|
263
|
+
};
|
|
264
|
+
if (!isRanged) {
|
|
265
|
+
const pubkey = expansionMap['@0'].pubkey;
|
|
266
|
+
if (!pubkey)
|
|
267
|
+
throw new Error(`Error: could not extract a pubkey from ${loggedExpression}`);
|
|
268
|
+
payment = p2wpkh({ pubkey, network });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//sh(wsh(miniscript))
|
|
272
|
+
else if (expression.match(RE.reShWshMiniscriptAnchored)) {
|
|
273
|
+
isSegwit = true;
|
|
274
|
+
miniscript = expression.match(RE.reShWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(wsh(->HERE<-))
|
|
275
|
+
if (!miniscript)
|
|
276
|
+
throw new Error(`Error: could not get miniscript in ${loggedExpression}`);
|
|
277
|
+
({ expandedMiniscript, expansionMap } = expandMiniscript({
|
|
278
|
+
miniscript,
|
|
279
|
+
isSegwit,
|
|
280
|
+
network
|
|
281
|
+
}));
|
|
282
|
+
expandedExpression = `sh(wsh(${expandedMiniscript}))`;
|
|
283
|
+
if (!isRanged) {
|
|
284
|
+
const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
|
|
285
|
+
witnessScript = script;
|
|
286
|
+
if (script.byteLength > MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
|
|
287
|
+
throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
|
|
288
|
+
}
|
|
289
|
+
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
290
|
+
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
|
|
291
|
+
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
292
|
+
}
|
|
293
|
+
payment = p2sh({
|
|
294
|
+
redeem: p2wsh({ redeem: { output: script, network }, network }),
|
|
295
|
+
network
|
|
296
|
+
});
|
|
297
|
+
redeemScript = payment.redeem?.output;
|
|
298
|
+
if (!redeemScript)
|
|
299
|
+
throw new Error(`Error: could not calculate redeemScript for ${loggedExpression}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
//sh(miniscript)
|
|
303
|
+
else if (expression.match(RE.reShMiniscriptAnchored)) {
|
|
304
|
+
//isSegwit false because we know it's a P2SH of a miniscript and not a
|
|
305
|
+
//P2SH that embeds a witness payment.
|
|
306
|
+
isSegwit = false;
|
|
307
|
+
miniscript = expression.match(RE.reShMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(->HERE<-)
|
|
308
|
+
if (!miniscript)
|
|
309
|
+
throw new Error(`Error: could not get miniscript in ${loggedExpression}`);
|
|
310
|
+
if (allowMiniscriptInP2SH === false &&
|
|
311
|
+
//These top-level expressions within sh are allowed within sh.
|
|
312
|
+
//They can be parsed with miniscript2Script, but first we must make sure
|
|
313
|
+
//that other expressions are not accepted (unless forced with allowMiniscriptInP2SH).
|
|
314
|
+
miniscript.search(/^(pk\(|pkh\(|wpkh\(|combo\(|multi\(|sortedmulti\(|multi_a\(|sortedmulti_a\()/) !== 0) {
|
|
315
|
+
throw new Error(`Error: Miniscript expressions can only be used in wsh`);
|
|
316
|
+
}
|
|
317
|
+
({ expandedMiniscript, expansionMap } = expandMiniscript({
|
|
318
|
+
miniscript,
|
|
319
|
+
isSegwit,
|
|
320
|
+
network
|
|
321
|
+
}));
|
|
322
|
+
expandedExpression = `sh(${expandedMiniscript})`;
|
|
323
|
+
if (!isRanged) {
|
|
324
|
+
const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
|
|
325
|
+
redeemScript = script;
|
|
326
|
+
if (script.byteLength > MAX_SCRIPT_ELEMENT_SIZE) {
|
|
327
|
+
throw new Error(`Error: P2SH script is too large, ${script.byteLength} bytes is larger than ${MAX_SCRIPT_ELEMENT_SIZE} bytes`);
|
|
328
|
+
}
|
|
329
|
+
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
330
|
+
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
|
|
331
|
+
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
332
|
+
}
|
|
333
|
+
payment = p2sh({ redeem: { output: script, network }, network });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
//wsh(miniscript)
|
|
337
|
+
else if (expression.match(RE.reWshMiniscriptAnchored)) {
|
|
338
|
+
isSegwit = true;
|
|
339
|
+
miniscript = expression.match(RE.reWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found wsh(->HERE<-)
|
|
340
|
+
if (!miniscript)
|
|
341
|
+
throw new Error(`Error: could not get miniscript in ${loggedExpression}`);
|
|
342
|
+
({ expandedMiniscript, expansionMap } = expandMiniscript({
|
|
343
|
+
miniscript,
|
|
344
|
+
isSegwit,
|
|
345
|
+
network
|
|
346
|
+
}));
|
|
347
|
+
expandedExpression = `wsh(${expandedMiniscript})`;
|
|
348
|
+
if (!isRanged) {
|
|
349
|
+
const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
|
|
350
|
+
witnessScript = script;
|
|
351
|
+
if (script.byteLength > MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
|
|
352
|
+
throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
|
|
353
|
+
}
|
|
354
|
+
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
355
|
+
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
|
|
356
|
+
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
357
|
+
}
|
|
358
|
+
payment = p2wsh({ redeem: { output: script, network }, network });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
throw new Error(`Error: Could not parse descriptor ${loggedExpression}`);
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
...(payment !== undefined ? { payment } : {}),
|
|
366
|
+
...(expandedExpression !== undefined ? { expandedExpression } : {}),
|
|
367
|
+
...(miniscript !== undefined ? { miniscript } : {}),
|
|
368
|
+
...(expansionMap !== undefined ? { expansionMap } : {}),
|
|
369
|
+
...(isSegwit !== undefined ? { isSegwit } : {}),
|
|
370
|
+
...(expandedMiniscript !== undefined ? { expandedMiniscript } : {}),
|
|
371
|
+
...(redeemScript !== undefined ? { redeemScript } : {}),
|
|
372
|
+
...(witnessScript !== undefined ? { witnessScript } : {})
|
|
373
|
+
};
|
|
374
|
+
};
|
|
118
375
|
/**
|
|
119
376
|
* Expand a miniscript to a generalized form using variables instead of key
|
|
120
377
|
* expressions. Variables will be of this form: @0, @1, ...
|
|
@@ -133,17 +390,18 @@ function DescriptorsFactory(ecc) {
|
|
|
133
390
|
}
|
|
134
391
|
class Descriptor {
|
|
135
392
|
/**
|
|
136
|
-
* @param {
|
|
393
|
+
* @param {DescriptorInfo} params
|
|
394
|
+
* @param {string} params.expression - The descriptor string in ASCII format. It may include a "*" to denote an arbitrary index.
|
|
137
395
|
* @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
396
|
* @param {boolean} [params.checksumRequired=false] - A flag indicating whether the descriptor is required to include a checksum.
|
|
397
|
+
* @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
398
|
* @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).
|
|
399
|
+
* @param {Preimage[]} [params.preimages=[]] An array of preimages. This info is necessary to finalize Psbts.
|
|
400
|
+
* @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
401
|
*
|
|
142
|
-
* @see {@link https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/src/payments/index.d.ts}
|
|
143
402
|
* @throws {Error} - when descriptor is invalid
|
|
144
403
|
*/
|
|
145
404
|
constructor({ expression, index, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys }) {
|
|
146
|
-
var _a, _b, _c, _d, _e, _f;
|
|
147
405
|
_Descriptor_instances.add(this);
|
|
148
406
|
_Descriptor_payment.set(this, void 0);
|
|
149
407
|
_Descriptor_preimages.set(this, []);
|
|
@@ -169,242 +427,40 @@ function DescriptorsFactory(ecc) {
|
|
|
169
427
|
...(index !== undefined ? { index } : {}),
|
|
170
428
|
checksumRequired
|
|
171
429
|
});
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
catch (e) { }
|
|
197
|
-
try {
|
|
198
|
-
payment = p2wsh({ output, network });
|
|
199
|
-
}
|
|
200
|
-
catch (e) { }
|
|
201
|
-
try {
|
|
202
|
-
payment = p2tr({ output, network });
|
|
203
|
-
}
|
|
204
|
-
catch (e) { }
|
|
205
|
-
if (!payment) {
|
|
206
|
-
throw new Error(`Error: invalid address ${matchedAddress}`);
|
|
207
|
-
}
|
|
208
|
-
__classPrivateFieldSet(this, _Descriptor_payment, payment, "f");
|
|
209
|
-
}
|
|
210
|
-
//pk(KEY)
|
|
211
|
-
else if (evaluatedExpression.match(RE.rePkAnchored)) {
|
|
212
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, false, "f");
|
|
213
|
-
const keyExpression = evaluatedExpression.match(RE.reKeyExp)?.[0];
|
|
214
|
-
if (!keyExpression)
|
|
215
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
216
|
-
if (evaluatedExpression !== `pk(${keyExpression})`)
|
|
217
|
-
throw new Error(`Error: invalid expression ${expression}`);
|
|
218
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, 'pk(@0)', "f");
|
|
219
|
-
__classPrivateFieldSet(this, _Descriptor_expansionMap, {
|
|
220
|
-
'@0': parseKeyExpression({
|
|
221
|
-
keyExpression,
|
|
222
|
-
network,
|
|
223
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f")
|
|
224
|
-
})
|
|
225
|
-
}, "f");
|
|
226
|
-
const pubkey = __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")['@0'].pubkey;
|
|
227
|
-
//Note there exists no address for p2pk, but we can still use the script
|
|
228
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2pk({ pubkey, network }), "f");
|
|
229
|
-
}
|
|
230
|
-
//pkh(KEY) - legacy
|
|
231
|
-
else if (evaluatedExpression.match(RE.rePkhAnchored)) {
|
|
232
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, false, "f");
|
|
233
|
-
const keyExpression = evaluatedExpression.match(RE.reKeyExp)?.[0];
|
|
234
|
-
if (!keyExpression)
|
|
235
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
236
|
-
if (evaluatedExpression !== `pkh(${keyExpression})`)
|
|
237
|
-
throw new Error(`Error: invalid expression ${expression}`);
|
|
238
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, 'pkh(@0)', "f");
|
|
239
|
-
__classPrivateFieldSet(this, _Descriptor_expansionMap, {
|
|
240
|
-
'@0': parseKeyExpression({
|
|
241
|
-
keyExpression,
|
|
242
|
-
network,
|
|
243
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f")
|
|
244
|
-
})
|
|
245
|
-
}, "f");
|
|
246
|
-
const pubkey = __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")['@0'].pubkey;
|
|
247
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2pkh({ pubkey, network }), "f");
|
|
248
|
-
}
|
|
249
|
-
//sh(wpkh(KEY)) - nested segwit
|
|
250
|
-
else if (evaluatedExpression.match(RE.reShWpkhAnchored)) {
|
|
251
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, true, "f");
|
|
252
|
-
const keyExpression = evaluatedExpression.match(RE.reKeyExp)?.[0];
|
|
253
|
-
if (!keyExpression)
|
|
254
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
255
|
-
if (evaluatedExpression !== `sh(wpkh(${keyExpression}))`)
|
|
256
|
-
throw new Error(`Error: invalid expression ${expression}`);
|
|
257
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, 'sh(wpkh(@0))', "f");
|
|
258
|
-
__classPrivateFieldSet(this, _Descriptor_expansionMap, {
|
|
259
|
-
'@0': parseKeyExpression({
|
|
260
|
-
keyExpression,
|
|
261
|
-
network,
|
|
262
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f")
|
|
263
|
-
})
|
|
264
|
-
}, "f");
|
|
265
|
-
const pubkey = __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")['@0'].pubkey;
|
|
266
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2sh({ redeem: p2wpkh({ pubkey, network }), network }), "f");
|
|
267
|
-
const redeemScript = __classPrivateFieldGet(this, _Descriptor_payment, "f").redeem?.output;
|
|
268
|
-
if (!redeemScript)
|
|
269
|
-
throw new Error(`Error: could not calculate redeemScript for ${expression}`);
|
|
270
|
-
__classPrivateFieldSet(this, _Descriptor_redeemScript, redeemScript, "f");
|
|
271
|
-
}
|
|
272
|
-
//wpkh(KEY) - native segwit
|
|
273
|
-
else if (evaluatedExpression.match(RE.reWpkhAnchored)) {
|
|
274
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, true, "f");
|
|
275
|
-
const keyExpression = evaluatedExpression.match(RE.reKeyExp)?.[0];
|
|
276
|
-
if (!keyExpression)
|
|
277
|
-
throw new Error(`Error: keyExpression could not me extracted`);
|
|
278
|
-
if (evaluatedExpression !== `wpkh(${keyExpression})`)
|
|
279
|
-
throw new Error(`Error: invalid expression ${expression}`);
|
|
280
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, 'wpkh(@0)', "f");
|
|
281
|
-
__classPrivateFieldSet(this, _Descriptor_expansionMap, {
|
|
282
|
-
'@0': parseKeyExpression({
|
|
283
|
-
keyExpression,
|
|
284
|
-
network,
|
|
285
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f")
|
|
286
|
-
})
|
|
287
|
-
}, "f");
|
|
288
|
-
const pubkey = __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")['@0'].pubkey;
|
|
289
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2wpkh({ pubkey, network }), "f");
|
|
290
|
-
}
|
|
291
|
-
//sh(wsh(miniscript))
|
|
292
|
-
else if (evaluatedExpression.match(RE.reShWshMiniscriptAnchored)) {
|
|
293
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, true, "f");
|
|
294
|
-
const miniscript = evaluatedExpression.match(RE.reShWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(wsh(->HERE<-))
|
|
295
|
-
if (!miniscript)
|
|
296
|
-
throw new Error(`Error: could not get miniscript in ${evaluatedExpression}`);
|
|
297
|
-
__classPrivateFieldSet(this, _Descriptor_miniscript, miniscript, "f");
|
|
298
|
-
(_a = this, _b = this, {
|
|
299
|
-
expandedMiniscript: ({ set value(_g) { __classPrivateFieldSet(_a, _Descriptor_expandedMiniscript, _g, "f"); } }).value,
|
|
300
|
-
expansionMap: ({ set value(_g) { __classPrivateFieldSet(_b, _Descriptor_expansionMap, _g, "f"); } }).value
|
|
301
|
-
} = expandMiniscript({
|
|
302
|
-
miniscript,
|
|
303
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f"),
|
|
304
|
-
network
|
|
305
|
-
}));
|
|
306
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, `sh(wsh(${__classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f")}))`, "f");
|
|
307
|
-
const script = (0, miniscript_1.miniscript2Script)({
|
|
308
|
-
expandedMiniscript: __classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f"),
|
|
309
|
-
expansionMap: __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")
|
|
310
|
-
});
|
|
311
|
-
__classPrivateFieldSet(this, _Descriptor_witnessScript, script, "f");
|
|
312
|
-
if (script.byteLength > MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
|
|
313
|
-
throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
|
|
314
|
-
}
|
|
315
|
-
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
316
|
-
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
|
|
317
|
-
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
318
|
-
}
|
|
319
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2sh({
|
|
320
|
-
redeem: p2wsh({ redeem: { output: script, network }, network }),
|
|
321
|
-
network
|
|
322
|
-
}), "f");
|
|
323
|
-
const redeemScript = __classPrivateFieldGet(this, _Descriptor_payment, "f").redeem?.output;
|
|
324
|
-
if (!redeemScript)
|
|
325
|
-
throw new Error(`Error: could not calculate redeemScript for ${expression}`);
|
|
326
|
-
__classPrivateFieldSet(this, _Descriptor_redeemScript, redeemScript, "f");
|
|
327
|
-
}
|
|
328
|
-
//sh(miniscript)
|
|
329
|
-
else if (evaluatedExpression.match(RE.reShMiniscriptAnchored)) {
|
|
330
|
-
//isSegwit false because we know it's a P2SH of a miniscript and not a
|
|
331
|
-
//P2SH that embeds a witness payment.
|
|
332
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, false, "f");
|
|
333
|
-
const miniscript = evaluatedExpression.match(RE.reShMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(->HERE<-)
|
|
334
|
-
if (!miniscript)
|
|
335
|
-
throw new Error(`Error: could not get miniscript in ${evaluatedExpression}`);
|
|
336
|
-
if (allowMiniscriptInP2SH === false &&
|
|
337
|
-
//These top-level expressions within sh are allowed within sh.
|
|
338
|
-
//They can be parsed with miniscript2Script, but first we must make sure
|
|
339
|
-
//that other expressions are not accepted (unless forced with allowMiniscriptInP2SH).
|
|
340
|
-
miniscript.search(/^(pk\(|pkh\(|wpkh\(|combo\(|multi\(|sortedmulti\(|multi_a\(|sortedmulti_a\()/) !== 0) {
|
|
341
|
-
throw new Error(`Error: Miniscript expressions can only be used in wsh`);
|
|
342
|
-
}
|
|
343
|
-
__classPrivateFieldSet(this, _Descriptor_miniscript, miniscript, "f");
|
|
344
|
-
(_c = this, _d = this, {
|
|
345
|
-
expandedMiniscript: ({ set value(_g) { __classPrivateFieldSet(_c, _Descriptor_expandedMiniscript, _g, "f"); } }).value,
|
|
346
|
-
expansionMap: ({ set value(_g) { __classPrivateFieldSet(_d, _Descriptor_expansionMap, _g, "f"); } }).value
|
|
347
|
-
} = expandMiniscript({
|
|
348
|
-
miniscript,
|
|
349
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f"),
|
|
350
|
-
network
|
|
351
|
-
}));
|
|
352
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, `sh(${__classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f")})`, "f");
|
|
353
|
-
const script = (0, miniscript_1.miniscript2Script)({
|
|
354
|
-
expandedMiniscript: __classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f"),
|
|
355
|
-
expansionMap: __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")
|
|
356
|
-
});
|
|
357
|
-
__classPrivateFieldSet(this, _Descriptor_redeemScript, script, "f");
|
|
358
|
-
if (script.byteLength > MAX_SCRIPT_ELEMENT_SIZE) {
|
|
359
|
-
throw new Error(`Error: P2SH script is too large, ${script.byteLength} bytes is larger than ${MAX_SCRIPT_ELEMENT_SIZE} bytes`);
|
|
360
|
-
}
|
|
361
|
-
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
362
|
-
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
|
|
363
|
-
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
364
|
-
}
|
|
365
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2sh({ redeem: { output: script, network }, network }), "f");
|
|
366
|
-
if (Buffer.compare(script, this.getRedeemScript()) !== 0)
|
|
367
|
-
throw new Error(`Error: redeemScript was not correctly set to the payment in expression ${expression}`);
|
|
368
|
-
}
|
|
369
|
-
//wsh(miniscript)
|
|
370
|
-
else if (evaluatedExpression.match(RE.reWshMiniscriptAnchored)) {
|
|
371
|
-
__classPrivateFieldSet(this, _Descriptor_isSegwit, true, "f");
|
|
372
|
-
const miniscript = evaluatedExpression.match(RE.reWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found wsh(->HERE<-)
|
|
373
|
-
if (!miniscript)
|
|
374
|
-
throw new Error(`Error: could not get miniscript in ${evaluatedExpression}`);
|
|
375
|
-
__classPrivateFieldSet(this, _Descriptor_miniscript, miniscript, "f");
|
|
376
|
-
(_e = this, _f = this, {
|
|
377
|
-
expandedMiniscript: ({ set value(_g) { __classPrivateFieldSet(_e, _Descriptor_expandedMiniscript, _g, "f"); } }).value,
|
|
378
|
-
expansionMap: ({ set value(_g) { __classPrivateFieldSet(_f, _Descriptor_expansionMap, _g, "f"); } }).value
|
|
379
|
-
} = expandMiniscript({
|
|
380
|
-
miniscript,
|
|
381
|
-
isSegwit: __classPrivateFieldGet(this, _Descriptor_isSegwit, "f"),
|
|
382
|
-
network
|
|
383
|
-
}));
|
|
384
|
-
__classPrivateFieldSet(this, _Descriptor_expandedExpression, `wsh(${__classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f")})`, "f");
|
|
385
|
-
const script = (0, miniscript_1.miniscript2Script)({
|
|
386
|
-
expandedMiniscript: __classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f"),
|
|
387
|
-
expansionMap: __classPrivateFieldGet(this, _Descriptor_expansionMap, "f")
|
|
388
|
-
});
|
|
389
|
-
__classPrivateFieldSet(this, _Descriptor_witnessScript, script, "f");
|
|
390
|
-
if (script.byteLength > MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
|
|
391
|
-
throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
|
|
392
|
-
}
|
|
393
|
-
const nonPushOnlyOps = countNonPushOnlyOPs(script);
|
|
394
|
-
if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
|
|
395
|
-
throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_SCRIPT}`);
|
|
396
|
-
}
|
|
397
|
-
__classPrivateFieldSet(this, _Descriptor_payment, p2wsh({ redeem: { output: script, network }, network }), "f");
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
throw new Error(`Error: Could not parse descriptor ${expression}`);
|
|
401
|
-
}
|
|
430
|
+
const expandedResult = expand({
|
|
431
|
+
expression: evaluatedExpression,
|
|
432
|
+
loggedExpression: expression,
|
|
433
|
+
network,
|
|
434
|
+
allowMiniscriptInP2SH
|
|
435
|
+
});
|
|
436
|
+
if (!expandedResult.payment)
|
|
437
|
+
throw new Error(`Error: could not extract a payment from ${expression}`);
|
|
438
|
+
__classPrivateFieldSet(this, _Descriptor_payment, expandedResult.payment, "f");
|
|
439
|
+
if (expandedResult.expandedExpression !== undefined)
|
|
440
|
+
__classPrivateFieldSet(this, _Descriptor_expandedExpression, expandedResult.expandedExpression, "f");
|
|
441
|
+
if (expandedResult.miniscript !== undefined)
|
|
442
|
+
__classPrivateFieldSet(this, _Descriptor_miniscript, expandedResult.miniscript, "f");
|
|
443
|
+
if (expandedResult.expansionMap !== undefined)
|
|
444
|
+
__classPrivateFieldSet(this, _Descriptor_expansionMap, expandedResult.expansionMap, "f");
|
|
445
|
+
if (expandedResult.isSegwit !== undefined)
|
|
446
|
+
__classPrivateFieldSet(this, _Descriptor_isSegwit, expandedResult.isSegwit, "f");
|
|
447
|
+
if (expandedResult.expandedMiniscript !== undefined)
|
|
448
|
+
__classPrivateFieldSet(this, _Descriptor_expandedMiniscript, expandedResult.expandedMiniscript, "f");
|
|
449
|
+
if (expandedResult.redeemScript !== undefined)
|
|
450
|
+
__classPrivateFieldSet(this, _Descriptor_redeemScript, expandedResult.redeemScript, "f");
|
|
451
|
+
if (expandedResult.witnessScript !== undefined)
|
|
452
|
+
__classPrivateFieldSet(this, _Descriptor_witnessScript, expandedResult.witnessScript, "f");
|
|
402
453
|
if (signersPubKeys) {
|
|
403
454
|
__classPrivateFieldSet(this, _Descriptor_signersPubKeys, signersPubKeys, "f");
|
|
404
455
|
}
|
|
405
456
|
else {
|
|
406
457
|
if (__classPrivateFieldGet(this, _Descriptor_expansionMap, "f")) {
|
|
407
|
-
__classPrivateFieldSet(this, _Descriptor_signersPubKeys, Object.values(__classPrivateFieldGet(this, _Descriptor_expansionMap, "f")).map(keyInfo =>
|
|
458
|
+
__classPrivateFieldSet(this, _Descriptor_signersPubKeys, Object.values(__classPrivateFieldGet(this, _Descriptor_expansionMap, "f")).map(keyInfo => {
|
|
459
|
+
const pubkey = keyInfo.pubkey;
|
|
460
|
+
if (!pubkey)
|
|
461
|
+
throw new Error(`Error: could not extract a pubkey from ${expression}`);
|
|
462
|
+
return pubkey;
|
|
463
|
+
}), "f");
|
|
408
464
|
}
|
|
409
465
|
else {
|
|
410
466
|
//We should only miss expansionMap in addr() expressions:
|
|
@@ -418,6 +474,9 @@ function DescriptorsFactory(ecc) {
|
|
|
418
474
|
getPayment() {
|
|
419
475
|
return __classPrivateFieldGet(this, _Descriptor_payment, "f");
|
|
420
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* Returns the Bitcoin Address
|
|
479
|
+
*/
|
|
421
480
|
getAddress() {
|
|
422
481
|
if (!__classPrivateFieldGet(this, _Descriptor_payment, "f").address)
|
|
423
482
|
throw new Error(`Error: could extract an address from the payment`);
|
|
@@ -428,6 +487,11 @@ function DescriptorsFactory(ecc) {
|
|
|
428
487
|
throw new Error(`Error: could extract output.script from the payment`);
|
|
429
488
|
return __classPrivateFieldGet(this, _Descriptor_payment, "f").output;
|
|
430
489
|
}
|
|
490
|
+
/**
|
|
491
|
+
* Returns the compiled script satisfaction
|
|
492
|
+
* @param {PartialSig[]} signatures An array of signatures using this format: `interface PartialSig { pubkey: Buffer; signature: Buffer; }`
|
|
493
|
+
* @returns {Buffer}
|
|
494
|
+
*/
|
|
431
495
|
getScriptSatisfaction(signatures) {
|
|
432
496
|
const miniscript = __classPrivateFieldGet(this, _Descriptor_miniscript, "f");
|
|
433
497
|
const expandedMiniscript = __classPrivateFieldGet(this, _Descriptor_expandedMiniscript, "f");
|
|
@@ -622,6 +686,6 @@ function DescriptorsFactory(ecc) {
|
|
|
622
686
|
throw new Error(`Error: cannot finalize psbt index ${index} since it does not correspond to this descriptor`);
|
|
623
687
|
}
|
|
624
688
|
};
|
|
625
|
-
return { Descriptor, parseKeyExpression, ECPair, BIP32 };
|
|
689
|
+
return { Descriptor, parseKeyExpression, expand, ECPair, BIP32 };
|
|
626
690
|
}
|
|
627
691
|
exports.DescriptorsFactory = DescriptorsFactory;
|
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 '
|
|
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
|
|
46
|
-
const
|
|
45
|
+
const ledger_bitcoin_1 = require("ledger-bitcoin");
|
|
46
|
+
const ledger_1 = require("./ledger");
|
|
47
47
|
exports.ledger = {
|
|
48
|
-
getLedgerMasterFingerPrint:
|
|
49
|
-
getLedgerXpub:
|
|
50
|
-
registerLedgerWallet:
|
|
51
|
-
assertLedgerApp:
|
|
52
|
-
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
|
};
|
package/dist/keyExpressions.d.ts
CHANGED
|
@@ -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 '
|
|
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;
|