@bitcoinerlab/descriptors 3.0.6 → 3.1.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.
Files changed (44) hide show
  1. package/README.md +9 -487
  2. package/index.d.ts +13 -0
  3. package/index.js +16 -0
  4. package/package.json +22 -53
  5. package/dist/applyPR2137.d.ts +0 -2
  6. package/dist/applyPR2137.js +0 -153
  7. package/dist/bitcoinjs-lib-internals.d.ts +0 -10
  8. package/dist/bitcoinjs-lib-internals.js +0 -60
  9. package/dist/checksum.d.ts +0 -6
  10. package/dist/checksum.js +0 -58
  11. package/dist/descriptors.d.ts +0 -433
  12. package/dist/descriptors.js +0 -1743
  13. package/dist/index.d.ts +0 -21
  14. package/dist/index.js +0 -85
  15. package/dist/keyExpressions.d.ts +0 -83
  16. package/dist/keyExpressions.js +0 -247
  17. package/dist/ledger.d.ts +0 -167
  18. package/dist/ledger.js +0 -580
  19. package/dist/miniscript.d.ts +0 -123
  20. package/dist/miniscript.js +0 -305
  21. package/dist/multipath.d.ts +0 -13
  22. package/dist/multipath.js +0 -76
  23. package/dist/networkUtils.d.ts +0 -3
  24. package/dist/networkUtils.js +0 -16
  25. package/dist/parseUtils.d.ts +0 -7
  26. package/dist/parseUtils.js +0 -46
  27. package/dist/psbt.d.ts +0 -44
  28. package/dist/psbt.js +0 -193
  29. package/dist/re.d.ts +0 -31
  30. package/dist/re.js +0 -79
  31. package/dist/resourceLimits.d.ts +0 -25
  32. package/dist/resourceLimits.js +0 -89
  33. package/dist/scriptExpressions.d.ts +0 -95
  34. package/dist/scriptExpressions.js +0 -89
  35. package/dist/signers.d.ts +0 -84
  36. package/dist/signers.js +0 -215
  37. package/dist/stackResourceLimits.d.ts +0 -17
  38. package/dist/stackResourceLimits.js +0 -35
  39. package/dist/tapMiniscript.d.ts +0 -220
  40. package/dist/tapMiniscript.js +0 -510
  41. package/dist/tapTree.d.ts +0 -86
  42. package/dist/tapTree.js +0 -166
  43. package/dist/types.d.ts +0 -238
  44. package/dist/types.js +0 -4
@@ -1,1743 +0,0 @@
1
- "use strict";
2
- // Copyright (c) 2026 Jose-Luis Landabaso - https://bitcoinerlab.com
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 () {
21
- var ownKeys = function(o) {
22
- ownKeys = Object.getOwnPropertyNames || function (o) {
23
- var ar = [];
24
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
- return ar;
26
- };
27
- return ownKeys(o);
28
- };
29
- return function (mod) {
30
- if (mod && mod.__esModule) return mod;
31
- var result = {};
32
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
- __setModuleDefault(result, mod);
34
- return result;
35
- };
36
- })();
37
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
38
- if (kind === "m") throw new TypeError("Private method is not writable");
39
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
40
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
41
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
42
- };
43
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
44
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
45
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
46
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
47
- };
48
- var __importDefault = (this && this.__importDefault) || function (mod) {
49
- return (mod && mod.__esModule) ? mod : { "default": mod };
50
- };
51
- Object.defineProperty(exports, "__esModule", { value: true });
52
- exports.DescriptorsFactory = DescriptorsFactory;
53
- const lodash_memoize_1 = __importDefault(require("lodash.memoize")); //TODO: make sure this is propoely used
54
- const bitcoinjs_lib_1 = require("bitcoinjs-lib");
55
- const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
56
- const varuint_bitcoin_1 = require("varuint-bitcoin");
57
- const uint8array_tools_1 = require("uint8array-tools");
58
- const { p2sh, p2wpkh, p2pkh, p2pk, p2wsh, p2tr } = bitcoinjs_lib_1.payments;
59
- const bip32_1 = require("bip32");
60
- const ecpair_1 = require("ecpair");
61
- const psbt_1 = require("./psbt");
62
- const checksum_1 = require("./checksum");
63
- const keyExpressions_1 = require("./keyExpressions");
64
- const RE = __importStar(require("./re"));
65
- const miniscript_1 = require("./miniscript");
66
- const tapTree_1 = require("./tapTree");
67
- const tapMiniscript_1 = require("./tapMiniscript");
68
- const parseUtils_1 = require("./parseUtils");
69
- const multipath_1 = require("./multipath");
70
- const resourceLimits_1 = require("./resourceLimits");
71
- const ECDSA_FAKE_SIGNATURE_SIZE = 72;
72
- const TAPROOT_FAKE_SIGNATURE_SIZE = 64;
73
- function vectorSize(someVector) {
74
- const length = someVector.length;
75
- return ((0, varuint_bitcoin_1.encodingLength)(length) +
76
- someVector.reduce((sum, witness) => {
77
- return sum + varSliceSize(witness);
78
- }, 0));
79
- }
80
- function varSliceSize(someScript) {
81
- const length = someScript.length;
82
- return (0, varuint_bitcoin_1.encodingLength)(length) + length;
83
- }
84
- /*
85
- * Returns a bare descriptor without checksum and particularized for a certain
86
- * index (if desc was a range descriptor)
87
- * @hidden
88
- */
89
- function evaluate({ descriptor, checksumRequired, index, change }) {
90
- if (!descriptor)
91
- throw new Error('You must provide a descriptor.');
92
- const mChecksum = descriptor.match(String.raw `(${RE.reChecksum})$`);
93
- if (mChecksum === null && checksumRequired === true)
94
- throw new Error(`Error: descriptor ${descriptor} has not checksum`);
95
- //evaluatedDescriptor: a bare desc without checksum and particularized for a certain
96
- //index (if desc was a range descriptor)
97
- let evaluatedDescriptor = descriptor;
98
- if (mChecksum !== null) {
99
- const checksum = mChecksum[0].substring(1); //remove the leading #
100
- evaluatedDescriptor = descriptor.substring(0, descriptor.length - mChecksum[0].length);
101
- if (checksum !== (0, checksum_1.DescriptorChecksum)(evaluatedDescriptor)) {
102
- throw new Error(`Error: invalid descriptor checksum for ${descriptor}`);
103
- }
104
- }
105
- evaluatedDescriptor = (0, multipath_1.resolveMultipathDescriptor)({
106
- descriptor: evaluatedDescriptor,
107
- ...(change !== undefined ? { change } : {})
108
- });
109
- if (index !== undefined) {
110
- const mWildcard = evaluatedDescriptor.match(/\*/g);
111
- if (mWildcard && mWildcard.length > 0) {
112
- //From https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
113
- //To prevent a combinatorial explosion of the search space, if more than
114
- //one of the multi() key arguments is a BIP32 wildcard path ending in /* or
115
- //*', the multi() descriptor only matches multisig scripts with the ith
116
- //child key from each wildcard path in lockstep, rather than scripts with
117
- //any combination of child keys from each wildcard path.
118
- //We extend this reasoning for musig for all cases
119
- evaluatedDescriptor = evaluatedDescriptor.replaceAll('*', index.toString());
120
- }
121
- else
122
- throw new Error(`Error: index passed for non-ranged descriptor: ${descriptor}`);
123
- }
124
- return evaluatedDescriptor;
125
- }
126
- // Helper: parse sortedmulti(M, k1, k2,...)
127
- function parseSortedMulti(inner) {
128
- // inner: "2,key1,key2,key3"
129
- const parts = inner.split(',').map(p => p.trim());
130
- if (parts.length < 2)
131
- throw new Error(`sortedmulti(): must contain M and at least one key: ${inner}`);
132
- const m = Number(parts[0]);
133
- if (!Number.isInteger(m) || m < 1 || m > 20)
134
- throw new Error(`sortedmulti(): invalid M=${parts[0]}`);
135
- const keyExpressions = parts.slice(1);
136
- if (keyExpressions.length < m)
137
- throw new Error(`sortedmulti(): M cannot exceed number of keys: ${inner}`);
138
- if (keyExpressions.length > 20)
139
- throw new Error(`sortedmulti(): descriptors support up to 20 keys (per BIP 380/383).`);
140
- return { m, keyExpressions };
141
- }
142
- const MAX_PUBKEYS_PER_MULTI_A = 999;
143
- // Helper: parse sortedmulti_a(M, k1, k2,...)
144
- function parseSortedMultiA(inner) {
145
- const parts = inner.split(',').map(p => p.trim());
146
- if (parts.length < 2)
147
- throw new Error(`sortedmulti_a(): must contain M and at least one key: ${inner}`);
148
- const m = Number(parts[0]);
149
- if (!Number.isInteger(m) || m < 1)
150
- throw new Error(`sortedmulti_a(): invalid M=${parts[0]}`);
151
- const keyExpressions = parts.slice(1);
152
- if (keyExpressions.length < m)
153
- throw new Error(`sortedmulti_a(): M cannot exceed number of keys: ${inner}`);
154
- if (keyExpressions.length > MAX_PUBKEYS_PER_MULTI_A)
155
- throw new Error(`sortedmulti_a(): descriptors support up to ${MAX_PUBKEYS_PER_MULTI_A} keys.`);
156
- return { m, keyExpressions };
157
- }
158
- function parseTrExpression(expression) {
159
- if (!expression.startsWith('tr(') || !expression.endsWith(')'))
160
- throw new Error(`Error: invalid descriptor ${expression}`);
161
- const innerExpression = expression.slice(3, -1).trim();
162
- if (!innerExpression)
163
- throw new Error(`Error: invalid descriptor ${expression}`);
164
- const splitResult = (0, parseUtils_1.splitTopLevelComma)({
165
- expression: innerExpression,
166
- onError: () => new Error(`Error: invalid descriptor ${expression}`)
167
- });
168
- //if no commas: innerExpression === keyExpression
169
- if (!splitResult)
170
- return { keyExpression: innerExpression };
171
- return { keyExpression: splitResult.left, treeExpression: splitResult.right };
172
- }
173
- /**
174
- * Constructs the necessary functions and classes for working with descriptors
175
- * using an external elliptic curve (ecc) library.
176
- *
177
- * Notably, it returns the {@link _Internal_.Output | `Output`} class, which
178
- * provides methods to create, sign, and finalize PSBTs based on descriptor
179
- * expressions.
180
- *
181
- * The Factory also returns utility methods like `expand` (detailed below)
182
- * and `parseKeyExpression` (see {@link KeyExpressionParser}).
183
- *
184
- * Additionally, for convenience, the function returns `BIP32` and `ECPair`.
185
- * These are {@link https://github.com/bitcoinjs bitcoinjs-lib} classes designed
186
- * for managing {@link https://github.com/bitcoinjs/bip32 | `BIP32`} keys and
187
- * public/private key pairs:
188
- * {@link https://github.com/bitcoinjs/ecpair | `ECPair`}, respectively.
189
- *
190
- * @param {Object} ecc - An object with elliptic curve operations, such as
191
- * [tiny-secp256k1](https://github.com/bitcoinjs/tiny-secp256k1) or
192
- * [@bitcoinerlab/secp256k1](https://github.com/bitcoinerlab/secp256k1).
193
- */
194
- function DescriptorsFactory(ecc) {
195
- var _Output_instances, _Output_payment, _Output_preimages, _Output_signersPubKeys, _Output_miniscript, _Output_witnessScript, _Output_redeemScript, _Output_isSegwit, _Output_isTaproot, _Output_expandedExpression, _Output_expandedMiniscript, _Output_tapTreeExpression, _Output_tapTree, _Output_tapTreeInfo, _Output_taprootSpendPath, _Output_tapLeaf, _Output_expansionMap, _Output_network, _Output_resolveMiniscriptSignersPubKeys, _Output_assertMiniscriptSatisfactionResourceLimits, _Output_resolveTapTreeSignersPubKeys, _Output_getConstraints, _Output_assertPsbtInput;
196
- (0, bitcoinjs_lib_1.initEccLib)(ecc); //Taproot requires initEccLib
197
- const BIP32 = (0, bip32_1.BIP32Factory)(ecc);
198
- const ECPair = (0, ecpair_1.ECPairFactory)(ecc);
199
- const signatureValidator = (pubkey, msghash, signature) => {
200
- if (pubkey.length === 32) {
201
- //x-only
202
- if (!ecc.verifySchnorr) {
203
- throw new Error('TinySecp256k1Interface is not initialized properly: verifySchnorr is missing.');
204
- }
205
- return ecc.verifySchnorr(msghash, pubkey, signature);
206
- }
207
- else {
208
- return ECPair.fromPublicKey(pubkey).verify(msghash, signature);
209
- }
210
- };
211
- /**
212
- * Takes a string key expression (xpub, xprv, pubkey or wif) and parses it
213
- */
214
- const parseKeyExpression = ({ keyExpression, isSegwit, isTaproot, network = bitcoinjs_lib_1.networks.bitcoin }) => {
215
- return (0, keyExpressions_1.parseKeyExpression)({
216
- keyExpression,
217
- network,
218
- ...(typeof isSegwit === 'boolean' ? { isSegwit } : {}),
219
- ...(typeof isTaproot === 'boolean' ? { isTaproot } : {}),
220
- ECPair,
221
- BIP32
222
- });
223
- };
224
- /**
225
- * Builds a taproot leaf expansion override for descriptor-level
226
- * `sortedmulti_a(...)`.
227
- *
228
- * Why this exists:
229
- * - `sortedmulti_a` is a descriptor script expression (not a Miniscript
230
- * fragment).
231
- *
232
- * What this does:
233
- * - Resolves each key expression to a concrete pubkey and builds a leaf-local
234
- * placeholder map (`@0`, `@1`, ... in input order).
235
- * - Derives the internal compilation form by sorting placeholders by pubkey
236
- * bytes and lowering to `multi_a(...)`.
237
- * - Compiles tapscript from that lowered form and returns it as override
238
- * data.
239
- *
240
- * Returns `undefined` for non-`sortedmulti_a` leaves so normal taproot miniscript
241
- * expansion/compilation is used.
242
- */
243
- function buildTapLeafSortedMultiAOverride({ expression, network = bitcoinjs_lib_1.networks.bitcoin }) {
244
- if (!/\bsortedmulti_a\(/.test(expression))
245
- return undefined;
246
- const trimmed = expression.trim();
247
- const match = trimmed.match(/^sortedmulti_a\((.*)\)$/);
248
- if (!match)
249
- throw new Error(`Error: sortedmulti_a() must be a standalone taproot leaf expression`);
250
- const inner = match[1];
251
- if (!inner)
252
- throw new Error(`Error: invalid sortedmulti_a() expression: ${expression}`);
253
- const { m, keyExpressions } = parseSortedMultiA(inner);
254
- const keyInfos = keyExpressions.map(keyExpression => {
255
- const keyInfo = parseKeyExpression({
256
- keyExpression,
257
- isSegwit: true,
258
- isTaproot: true,
259
- network
260
- });
261
- if (!keyInfo.pubkey)
262
- throw new Error(`Error: sortedmulti_a() key must resolve to a concrete pubkey: ${keyExpression}`);
263
- return keyInfo;
264
- });
265
- const expansionMap = {};
266
- keyInfos.forEach((keyInfo, index) => {
267
- expansionMap[`@${index}`] = keyInfo;
268
- });
269
- // sortedmulti_a is descriptor-level sugar. We preserve it in
270
- // expandedExpression, but compile tapscript from its internal multi_a
271
- // lowering with sorted placeholders.
272
- const expandedExpression = `sortedmulti_a(${[
273
- m,
274
- ...Object.keys(expansionMap)
275
- ].join(',')})`;
276
- const compileExpandedMiniscript = (0, tapMiniscript_1.compileSortedMultiAExpandedExpression)({
277
- expandedExpression,
278
- expansionMap
279
- });
280
- const tapScript = (0, miniscript_1.miniscript2Script)({
281
- expandedMiniscript: compileExpandedMiniscript,
282
- expansionMap,
283
- tapscript: true
284
- });
285
- return { expandedExpression, expansionMap, tapScript };
286
- }
287
- function expand({ descriptor, index, change, checksumRequired = false, network = bitcoinjs_lib_1.networks.bitcoin, allowMiniscriptInP2SH = false }) {
288
- if (!descriptor)
289
- throw new Error(`descriptor not provided`);
290
- let expandedExpression;
291
- let miniscript;
292
- let expansionMap;
293
- let isSegwit;
294
- let isTaproot;
295
- let expandedMiniscript;
296
- let tapTreeExpression;
297
- let tapTree;
298
- let tapTreeInfo;
299
- let payment;
300
- let witnessScript;
301
- let redeemScript;
302
- const isRanged = descriptor.indexOf('*') !== -1;
303
- if (index !== undefined)
304
- if (!Number.isInteger(index) || index < 0)
305
- throw new Error(`Error: invalid index ${index}`);
306
- //Verify and remove checksum (if exists) and
307
- //particularize range descriptor for index (if desc is range descriptor)
308
- const canonicalExpression = evaluate({
309
- descriptor,
310
- ...(index !== undefined ? { index } : {}),
311
- ...(change !== undefined ? { change } : {}),
312
- checksumRequired
313
- });
314
- const isCanonicalRanged = canonicalExpression.indexOf('*') !== -1;
315
- //addr(ADDR)
316
- if (canonicalExpression.match(RE.reAddrAnchored)) {
317
- if (isRanged)
318
- throw new Error(`Error: addr() cannot be ranged`);
319
- const matchedAddress = canonicalExpression.match(RE.reAddrAnchored)?.[1]; //[1]-> whatever is found addr(->HERE<-)
320
- if (!matchedAddress)
321
- throw new Error(`Error: could not get an address in ${descriptor}`);
322
- let output;
323
- try {
324
- output = bitcoinjs_lib_1.address.toOutputScript(matchedAddress, network);
325
- }
326
- catch (e) {
327
- void e;
328
- throw new Error(`Error: invalid address ${matchedAddress}`);
329
- }
330
- try {
331
- payment = p2pkh({ output, network });
332
- isSegwit = false;
333
- isTaproot = false;
334
- }
335
- catch (e) {
336
- void e;
337
- }
338
- try {
339
- payment = p2sh({ output, network });
340
- // It assumes that an addr(SH_ADDRESS) is always a add(SH_WPKH) address
341
- isSegwit = true;
342
- isTaproot = false;
343
- }
344
- catch (e) {
345
- void e;
346
- }
347
- try {
348
- payment = p2wpkh({ output, network });
349
- isSegwit = true;
350
- isTaproot = false;
351
- }
352
- catch (e) {
353
- void e;
354
- }
355
- try {
356
- payment = p2wsh({ output, network });
357
- isSegwit = true;
358
- isTaproot = false;
359
- }
360
- catch (e) {
361
- void e;
362
- }
363
- try {
364
- payment = p2tr({ output, network });
365
- isSegwit = true;
366
- isTaproot = true;
367
- }
368
- catch (e) {
369
- void e;
370
- }
371
- if (!payment) {
372
- throw new Error(`Error: invalid address ${matchedAddress}`);
373
- }
374
- }
375
- //pk(KEY)
376
- else if (canonicalExpression.match(RE.rePkAnchored)) {
377
- isSegwit = false;
378
- isTaproot = false;
379
- const keyExpression = canonicalExpression.match(RE.reNonSegwitKeyExp)?.[0];
380
- if (!keyExpression)
381
- throw new Error(`Error: keyExpression could not me extracted`);
382
- if (canonicalExpression !== `pk(${keyExpression})`)
383
- throw new Error(`Error: invalid expression ${descriptor}`);
384
- expandedExpression = 'pk(@0)';
385
- const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
386
- expansionMap = { '@0': pKE };
387
- if (!isCanonicalRanged) {
388
- const pubkey = pKE.pubkey;
389
- //Note there exists no address for p2pk, but we can still use the script
390
- if (!pubkey)
391
- throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
392
- payment = p2pk({ pubkey, network });
393
- }
394
- }
395
- //pkh(KEY) - legacy
396
- else if (canonicalExpression.match(RE.rePkhAnchored)) {
397
- isSegwit = false;
398
- isTaproot = false;
399
- const keyExpression = canonicalExpression.match(RE.reNonSegwitKeyExp)?.[0];
400
- if (!keyExpression)
401
- throw new Error(`Error: keyExpression could not me extracted`);
402
- if (canonicalExpression !== `pkh(${keyExpression})`)
403
- throw new Error(`Error: invalid expression ${descriptor}`);
404
- expandedExpression = 'pkh(@0)';
405
- const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
406
- expansionMap = { '@0': pKE };
407
- if (!isCanonicalRanged) {
408
- const pubkey = pKE.pubkey;
409
- if (!pubkey)
410
- throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
411
- payment = p2pkh({ pubkey, network });
412
- }
413
- }
414
- //sh(wpkh(KEY)) - nested segwit
415
- else if (canonicalExpression.match(RE.reShWpkhAnchored)) {
416
- isSegwit = true;
417
- isTaproot = false;
418
- const keyExpression = canonicalExpression.match(RE.reSegwitKeyExp)?.[0];
419
- if (!keyExpression)
420
- throw new Error(`Error: keyExpression could not me extracted`);
421
- if (canonicalExpression !== `sh(wpkh(${keyExpression}))`)
422
- throw new Error(`Error: invalid expression ${descriptor}`);
423
- expandedExpression = 'sh(wpkh(@0))';
424
- const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
425
- expansionMap = { '@0': pKE };
426
- if (!isCanonicalRanged) {
427
- const pubkey = pKE.pubkey;
428
- if (!pubkey)
429
- throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
430
- payment = p2sh({ redeem: p2wpkh({ pubkey, network }), network });
431
- redeemScript = payment.redeem?.output;
432
- if (!redeemScript)
433
- throw new Error(`Error: could not calculate redeemScript for ${descriptor}`);
434
- }
435
- }
436
- //wpkh(KEY) - native segwit
437
- else if (canonicalExpression.match(RE.reWpkhAnchored)) {
438
- isSegwit = true;
439
- isTaproot = false;
440
- const keyExpression = canonicalExpression.match(RE.reSegwitKeyExp)?.[0];
441
- if (!keyExpression)
442
- throw new Error(`Error: keyExpression could not me extracted`);
443
- if (canonicalExpression !== `wpkh(${keyExpression})`)
444
- throw new Error(`Error: invalid expression ${descriptor}`);
445
- expandedExpression = 'wpkh(@0)';
446
- const pKE = parseKeyExpression({ keyExpression, network, isSegwit });
447
- expansionMap = { '@0': pKE };
448
- if (!isCanonicalRanged) {
449
- const pubkey = pKE.pubkey;
450
- if (!pubkey)
451
- throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
452
- payment = p2wpkh({ pubkey, network });
453
- }
454
- }
455
- // sortedmulti script expressions
456
- // sh(sortedmulti())
457
- else if (canonicalExpression.match(RE.reShSortedMultiAnchored)) {
458
- isSegwit = false;
459
- isTaproot = false;
460
- const inner = canonicalExpression.match(RE.reShSortedMultiAnchored)?.[1];
461
- if (!inner)
462
- throw new Error(`Error extracting sortedmulti() in ${descriptor}`);
463
- const { m, keyExpressions } = parseSortedMulti(inner);
464
- const pKEs = keyExpressions.map(k => parseKeyExpression({ keyExpression: k, network, isSegwit: false }));
465
- const map = {};
466
- pKEs.forEach((pke, i) => (map['@' + i] = pke));
467
- expansionMap = map;
468
- expandedExpression =
469
- 'sh(sortedmulti(' +
470
- [m, ...Object.keys(expansionMap).map(k => k)].join(',') +
471
- '))';
472
- if (!isCanonicalRanged) {
473
- const pubkeys = pKEs.map(p => {
474
- if (!p.pubkey)
475
- throw new Error(`Error: key has no pubkey`);
476
- return p.pubkey;
477
- });
478
- pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
479
- const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
480
- redeemScript = redeem.output;
481
- if (!redeemScript)
482
- throw new Error(`Error creating redeemScript`);
483
- payment = bitcoinjs_lib_1.payments.p2sh({ redeem, network });
484
- }
485
- }
486
- // wsh(sortedmulti())
487
- else if (canonicalExpression.match(RE.reWshSortedMultiAnchored)) {
488
- isSegwit = true;
489
- isTaproot = false;
490
- const inner = canonicalExpression.match(RE.reWshSortedMultiAnchored)?.[1];
491
- if (!inner)
492
- throw new Error(`Error extracting sortedmulti() in ${descriptor}`);
493
- const { m, keyExpressions } = parseSortedMulti(inner);
494
- const pKEs = keyExpressions.map(k => parseKeyExpression({ keyExpression: k, network, isSegwit: true }));
495
- const map = {};
496
- pKEs.forEach((pke, i) => (map['@' + i] = pke));
497
- expansionMap = map;
498
- expandedExpression =
499
- 'wsh(sortedmulti(' +
500
- [m, ...Object.keys(expansionMap).map(k => k)].join(',') +
501
- '))';
502
- if (!isCanonicalRanged) {
503
- const pubkeys = pKEs.map(p => {
504
- if (!p.pubkey)
505
- throw new Error(`Error: key has no pubkey`);
506
- return p.pubkey;
507
- });
508
- pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
509
- const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
510
- witnessScript = redeem.output;
511
- if (!witnessScript)
512
- throw new Error(`Error computing witnessScript`);
513
- payment = bitcoinjs_lib_1.payments.p2wsh({ redeem, network });
514
- }
515
- }
516
- // sh(wsh(sortedmulti()))
517
- else if (canonicalExpression.match(RE.reShWshSortedMultiAnchored)) {
518
- isSegwit = true;
519
- isTaproot = false;
520
- const inner = canonicalExpression.match(RE.reShWshSortedMultiAnchored)?.[1];
521
- if (!inner)
522
- throw new Error(`Error extracting sortedmulti() in ${descriptor}`);
523
- const { m, keyExpressions } = parseSortedMulti(inner);
524
- const pKEs = keyExpressions.map(k => parseKeyExpression({ keyExpression: k, network, isSegwit: true }));
525
- const map = {};
526
- pKEs.forEach((pke, i) => (map['@' + i] = pke));
527
- expansionMap = map;
528
- expandedExpression =
529
- 'sh(wsh(sortedmulti(' +
530
- [m, ...Object.keys(expansionMap).map(k => k)].join(',') +
531
- ')))';
532
- if (!isCanonicalRanged) {
533
- const pubkeys = pKEs.map(p => {
534
- if (!p.pubkey)
535
- throw new Error(`Error: key has no pubkey`);
536
- return p.pubkey;
537
- });
538
- pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
539
- const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
540
- const wsh = bitcoinjs_lib_1.payments.p2wsh({ redeem, network });
541
- witnessScript = redeem.output;
542
- redeemScript = wsh.output;
543
- payment = bitcoinjs_lib_1.payments.p2sh({ redeem: wsh, network });
544
- }
545
- }
546
- //sh(wsh(miniscript))
547
- else if (canonicalExpression.match(RE.reShWshMiniscriptAnchored)) {
548
- isSegwit = true;
549
- isTaproot = false;
550
- miniscript = canonicalExpression.match(RE.reShWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(wsh(->HERE<-))
551
- if (!miniscript)
552
- throw new Error(`Error: could not get miniscript in ${descriptor}`);
553
- ({ expandedMiniscript, expansionMap } = expandMiniscript({
554
- miniscript,
555
- isSegwit,
556
- network
557
- }));
558
- expandedExpression = `sh(wsh(${expandedMiniscript}))`;
559
- if (!isCanonicalRanged) {
560
- const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
561
- witnessScript = script;
562
- if (script.byteLength > resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
563
- throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
564
- }
565
- (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
566
- payment = p2sh({
567
- redeem: p2wsh({ redeem: { output: script, network }, network }),
568
- network
569
- });
570
- redeemScript = payment.redeem?.output;
571
- if (!redeemScript)
572
- throw new Error(`Error: could not calculate redeemScript for ${descriptor}`);
573
- }
574
- }
575
- //sh(miniscript)
576
- else if (canonicalExpression.match(RE.reShMiniscriptAnchored)) {
577
- //isSegwit false because we know it's a P2SH of a miniscript and not a
578
- //P2SH that embeds a witness payment.
579
- isSegwit = false;
580
- isTaproot = false;
581
- miniscript = canonicalExpression.match(RE.reShMiniscriptAnchored)?.[1]; //[1]-> whatever is found sh(->HERE<-)
582
- if (!miniscript)
583
- throw new Error(`Error: could not get miniscript in ${descriptor}`);
584
- if (allowMiniscriptInP2SH === false &&
585
- // These top-level script expressions are allowed inside sh(...).
586
- // The sorted script expressions (`sortedmulti`, `sortedmulti_a`) are
587
- // handled in dedicated descriptor/taproot branches and are intentionally
588
- // not part of this miniscript gate.
589
- // Here we only keep legacy top-level forms to avoid accepting arbitrary
590
- // miniscript in P2SH unless explicitly enabled.
591
- miniscript.search(/^(pk\(|pkh\(|wpkh\(|combo\(|multi\(|multi_a\()/) !==
592
- 0) {
593
- throw new Error(`Error: Miniscript expressions can only be used in wsh`);
594
- }
595
- ({ expandedMiniscript, expansionMap } = expandMiniscript({
596
- miniscript,
597
- isSegwit,
598
- network
599
- }));
600
- expandedExpression = `sh(${expandedMiniscript})`;
601
- if (!isCanonicalRanged) {
602
- const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
603
- redeemScript = script;
604
- if (script.byteLength > resourceLimits_1.MAX_SCRIPT_ELEMENT_SIZE) {
605
- throw new Error(`Error: P2SH script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_SCRIPT_ELEMENT_SIZE} bytes`);
606
- }
607
- (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
608
- payment = p2sh({ redeem: { output: script, network }, network });
609
- }
610
- }
611
- //wsh(miniscript)
612
- else if (canonicalExpression.match(RE.reWshMiniscriptAnchored)) {
613
- isSegwit = true;
614
- isTaproot = false;
615
- miniscript = canonicalExpression.match(RE.reWshMiniscriptAnchored)?.[1]; //[1]-> whatever is found wsh(->HERE<-)
616
- if (!miniscript)
617
- throw new Error(`Error: could not get miniscript in ${descriptor}`);
618
- ({ expandedMiniscript, expansionMap } = expandMiniscript({
619
- miniscript,
620
- isSegwit,
621
- network
622
- }));
623
- expandedExpression = `wsh(${expandedMiniscript})`;
624
- if (!isCanonicalRanged) {
625
- const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
626
- witnessScript = script;
627
- if (script.byteLength > resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
628
- throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
629
- }
630
- (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
631
- payment = p2wsh({ redeem: { output: script, network }, network });
632
- }
633
- }
634
- //tr(KEY) or tr(KEY,TREE) - taproot
635
- else if (canonicalExpression.startsWith('tr(')) {
636
- isSegwit = true;
637
- isTaproot = true;
638
- const { keyExpression, treeExpression } = parseTrExpression(canonicalExpression);
639
- expandedExpression = treeExpression
640
- ? `tr(@0,${treeExpression})`
641
- : 'tr(@0)';
642
- const pKE = parseKeyExpression({
643
- keyExpression,
644
- network,
645
- isSegwit,
646
- isTaproot
647
- });
648
- expansionMap = { '@0': pKE };
649
- if (treeExpression) {
650
- tapTreeExpression = treeExpression;
651
- tapTree = (0, tapTree_1.parseTapTreeExpression)(treeExpression);
652
- if (!isCanonicalRanged) {
653
- tapTreeInfo = (0, tapMiniscript_1.buildTapTreeInfo)({
654
- tapTree,
655
- network,
656
- BIP32,
657
- ECPair,
658
- // `leafExpansionOverride` runs per leaf expression.
659
- // For non-matching leaves it returns undefined and
660
- // normal miniscript expansion is used;
661
- // for sortedmulti_a leaves it returns descriptor-level
662
- // metadata plus precompiled tapscript bytes.
663
- leafExpansionOverride: (expression) => buildTapLeafSortedMultiAOverride({ expression, network })
664
- });
665
- }
666
- }
667
- if (!isCanonicalRanged) {
668
- const pubkey = pKE.pubkey;
669
- if (!pubkey)
670
- throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
671
- const internalPubkey = (0, tapMiniscript_1.normalizeTaprootPubkey)(pubkey);
672
- if (treeExpression) {
673
- if (!tapTreeInfo)
674
- throw new Error(`Error: taproot tree info not available`);
675
- payment = p2tr({
676
- internalPubkey,
677
- scriptTree: (0, tapMiniscript_1.tapTreeInfoToScriptTree)(tapTreeInfo),
678
- network
679
- });
680
- }
681
- else {
682
- payment = p2tr({ internalPubkey, network });
683
- }
684
- }
685
- }
686
- else {
687
- throw new Error(`Error: Could not parse descriptor ${descriptor}`);
688
- }
689
- return {
690
- ...(payment !== undefined ? { payment } : {}),
691
- ...(expandedExpression !== undefined ? { expandedExpression } : {}),
692
- ...(miniscript !== undefined ? { miniscript } : {}),
693
- ...(expansionMap !== undefined ? { expansionMap } : {}),
694
- ...(isSegwit !== undefined ? { isSegwit } : {}),
695
- ...(isTaproot !== undefined ? { isTaproot } : {}),
696
- ...(expandedMiniscript !== undefined ? { expandedMiniscript } : {}),
697
- ...(tapTreeExpression !== undefined ? { tapTreeExpression } : {}),
698
- ...(tapTree !== undefined ? { tapTree } : {}),
699
- ...(tapTreeInfo !== undefined ? { tapTreeInfo } : {}),
700
- ...(redeemScript !== undefined ? { redeemScript } : {}),
701
- ...(witnessScript !== undefined ? { witnessScript } : {}),
702
- isRanged,
703
- canonicalExpression
704
- };
705
- }
706
- /**
707
- * Expand a miniscript to a generalized form using variables instead of key
708
- * expressions. Variables will be of this form: @0, @1, ...
709
- * This is done so that it can be compiled with compileMiniscript and
710
- * satisfied with satisfier.
711
- * Also compute pubkeys from descriptors to use them later.
712
- */
713
- function expandMiniscript({ miniscript, isSegwit, network = bitcoinjs_lib_1.networks.bitcoin }) {
714
- return (0, miniscript_1.expandMiniscript)({
715
- miniscript,
716
- isSegwit,
717
- isTaproot: false, //TODO:
718
- network,
719
- BIP32,
720
- ECPair
721
- });
722
- }
723
- /**
724
- * The `Output` class is the central component for managing descriptors.
725
- * It facilitates the creation of outputs to receive funds and enables the
726
- * signing and finalization of PSBTs (Partially Signed Bitcoin Transactions)
727
- * for spending UTXOs (Unspent Transaction Outputs).
728
- */
729
- class Output {
730
- /**
731
- * @param options
732
- * @throws {Error} - when descriptor is invalid
733
- */
734
- constructor({ descriptor, index, change, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys, taprootSpendPath, tapLeaf }) {
735
- _Output_instances.add(this);
736
- _Output_payment.set(this, void 0);
737
- _Output_preimages.set(this, []);
738
- _Output_signersPubKeys.set(this, void 0);
739
- _Output_miniscript.set(this, void 0);
740
- _Output_witnessScript.set(this, void 0);
741
- _Output_redeemScript.set(this, void 0);
742
- //isSegwit true if witnesses are needed to the spend coins sent to this descriptor.
743
- //may be unset because we may get addr(P2SH) which we don't know if they have segwit.
744
- _Output_isSegwit.set(this, void 0);
745
- _Output_isTaproot.set(this, void 0);
746
- _Output_expandedExpression.set(this, void 0);
747
- _Output_expandedMiniscript.set(this, void 0);
748
- _Output_tapTreeExpression.set(this, void 0);
749
- _Output_tapTree.set(this, void 0);
750
- _Output_tapTreeInfo.set(this, void 0);
751
- _Output_taprootSpendPath.set(this, void 0);
752
- _Output_tapLeaf.set(this, void 0);
753
- _Output_expansionMap.set(this, void 0);
754
- _Output_network.set(this, void 0);
755
- __classPrivateFieldSet(this, _Output_network, network, "f");
756
- __classPrivateFieldSet(this, _Output_preimages, preimages, "f");
757
- if (typeof descriptor !== 'string')
758
- throw new Error(`Error: invalid descriptor type`);
759
- const expandedResult = expand({
760
- descriptor,
761
- ...(index !== undefined ? { index } : {}),
762
- ...(change !== undefined ? { change } : {}),
763
- checksumRequired,
764
- network,
765
- allowMiniscriptInP2SH
766
- });
767
- const isTaprootDescriptor = expandedResult.isTaproot === true;
768
- const hasTapTree = expandedResult.expandedExpression?.startsWith('tr(@0,') ?? false;
769
- const resolvedTaprootSpendPath = taprootSpendPath ?? (hasTapTree ? 'script' : 'key');
770
- if (!isTaprootDescriptor) {
771
- if (taprootSpendPath !== undefined || tapLeaf !== undefined)
772
- throw new Error(`Error: taprootSpendPath/tapLeaf require a taproot descriptor`);
773
- }
774
- else {
775
- if (taprootSpendPath === 'script' && !hasTapTree)
776
- throw new Error(`Error: taprootSpendPath=script requires a tr(KEY,TREE) descriptor`);
777
- if (resolvedTaprootSpendPath === 'key' && tapLeaf !== undefined)
778
- throw new Error(`Error: tapLeaf cannot be used when taprootSpendPath is key`);
779
- if (tapLeaf !== undefined && !hasTapTree)
780
- throw new Error(`Error: tapLeaf can only be used with tr(KEY,TREE) descriptors`);
781
- }
782
- if (expandedResult.isRanged && index === undefined)
783
- throw new Error(`Error: index was not provided for ranged descriptor`);
784
- if (!expandedResult.payment)
785
- throw new Error(`Error: could not extract a payment from ${descriptor}`);
786
- __classPrivateFieldSet(this, _Output_payment, expandedResult.payment, "f");
787
- if (expandedResult.expandedExpression !== undefined)
788
- __classPrivateFieldSet(this, _Output_expandedExpression, expandedResult.expandedExpression, "f");
789
- if (expandedResult.miniscript !== undefined)
790
- __classPrivateFieldSet(this, _Output_miniscript, expandedResult.miniscript, "f");
791
- if (expandedResult.expansionMap !== undefined)
792
- __classPrivateFieldSet(this, _Output_expansionMap, expandedResult.expansionMap, "f");
793
- if (expandedResult.isSegwit !== undefined)
794
- __classPrivateFieldSet(this, _Output_isSegwit, expandedResult.isSegwit, "f");
795
- if (expandedResult.isTaproot !== undefined)
796
- __classPrivateFieldSet(this, _Output_isTaproot, expandedResult.isTaproot, "f");
797
- if (expandedResult.expandedMiniscript !== undefined)
798
- __classPrivateFieldSet(this, _Output_expandedMiniscript, expandedResult.expandedMiniscript, "f");
799
- if (expandedResult.tapTreeExpression !== undefined)
800
- __classPrivateFieldSet(this, _Output_tapTreeExpression, expandedResult.tapTreeExpression, "f");
801
- if (expandedResult.tapTree !== undefined)
802
- __classPrivateFieldSet(this, _Output_tapTree, expandedResult.tapTree, "f");
803
- if (expandedResult.tapTreeInfo !== undefined)
804
- __classPrivateFieldSet(this, _Output_tapTreeInfo, expandedResult.tapTreeInfo, "f");
805
- if (expandedResult.redeemScript !== undefined)
806
- __classPrivateFieldSet(this, _Output_redeemScript, expandedResult.redeemScript, "f");
807
- if (expandedResult.witnessScript !== undefined)
808
- __classPrivateFieldSet(this, _Output_witnessScript, expandedResult.witnessScript, "f");
809
- if (signersPubKeys)
810
- __classPrivateFieldSet(this, _Output_signersPubKeys, signersPubKeys, "f");
811
- __classPrivateFieldSet(this, _Output_taprootSpendPath, resolvedTaprootSpendPath, "f");
812
- if (tapLeaf !== undefined)
813
- __classPrivateFieldSet(this, _Output_tapLeaf, tapLeaf, "f");
814
- this.getSequence = (0, lodash_memoize_1.default)(this.getSequence);
815
- this.getLockTime = (0, lodash_memoize_1.default)(this.getLockTime);
816
- const getSignaturesKey = (signatures) => signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
817
- ? signatures
818
- : signatures
819
- .map(s => `${(0, uint8array_tools_1.toHex)(s.pubkey)}-${(0, uint8array_tools_1.toHex)(s.signature)}`)
820
- .join('|');
821
- this.guessOutput = (0, lodash_memoize_1.default)(this.guessOutput);
822
- this.inputWeight = (0, lodash_memoize_1.default)(this.inputWeight,
823
- // resolver function:
824
- (isSegwitTx, signatures, options) => {
825
- const segwitKey = isSegwitTx ? 'segwit' : 'non-segwit';
826
- const signaturesKey = getSignaturesKey(signatures);
827
- const taprootSighashKey = options?.taprootSighash ?? 'SIGHASH_DEFAULT';
828
- return `${segwitKey}-${signaturesKey}-taprootSighash:${taprootSighashKey}`;
829
- });
830
- this.outputWeight = (0, lodash_memoize_1.default)(this.outputWeight);
831
- }
832
- /**
833
- * Returns the compiled Script Satisfaction for a miniscript-based Output.
834
- * The satisfaction is the unlocking script, derived by the Satisfier
835
- * algorithm (https://bitcoin.sipa.be/miniscript/).
836
- *
837
- * This method uses a two-pass flow:
838
- * 1) Planning: constraints (nLockTime/nSequence) are computed using fake
839
- * signatures. This is done since the final solution may not need all the
840
- * signatures in signersPubKeys. And we may avoid the user do extra
841
- * signing (tedious op with HWW).
842
- * 2) Signing: the provided signatures are used to build the final
843
- * satisfaction, while enforcing the planned constraints so the same
844
- * solution is selected. Not all the signatures of signersPubKeys may
845
- * be required.
846
- *
847
- * The return value includes the satisfaction script and the constraints.
848
- */
849
- getScriptSatisfaction(
850
- /**
851
- * An array with all the signatures needed to
852
- * build the Satisfaction of this miniscript-based `Output`.
853
- *
854
- * `signatures` must be passed using this format (pairs of `pubKey/signature`):
855
- * `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
856
- */
857
- signatures) {
858
- const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
859
- const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
860
- const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
861
- if (miniscript === undefined ||
862
- expandedMiniscript === undefined ||
863
- expansionMap === undefined)
864
- throw new Error(`Error: cannot get satisfaction from not expanded miniscript ${miniscript}`);
865
- //This crates the plans using fake signatures
866
- const constraints = __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this);
867
- const satisfaction = (0, miniscript_1.satisfyMiniscript)({
868
- expandedMiniscript,
869
- expansionMap,
870
- signatures,
871
- preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
872
- //Here we pass the TimeConstraints obtained using signersPubKeys to
873
- //verify that the solutions found using the final signatures have not
874
- //changed
875
- timeConstraints: {
876
- nLockTime: constraints?.nLockTime,
877
- nSequence: constraints?.nSequence
878
- }
879
- });
880
- __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
881
- return satisfaction;
882
- }
883
- /**
884
- * Returns the taproot script‑path satisfaction for a tap miniscript
885
- * descriptor. This mirrors {@link getScriptSatisfaction} and uses the same
886
- * two‑pass plan/sign flow.
887
- *
888
- * In addition to nLockTime/nSequence, it returns the selected tapLeafHash
889
- * (the leaf chosen during planning) and the leaf’s tapscript.
890
- */
891
- getTapScriptSatisfaction(
892
- /**
893
- * An array with all the signatures needed to
894
- * build the Satisfaction of this miniscript-based `Output`.
895
- *
896
- * `signatures` must be passed using this format (pairs of `pubKey/signature`):
897
- * `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
898
- */
899
- signatures) {
900
- if (__classPrivateFieldGet(this, _Output_taprootSpendPath, "f") !== 'script')
901
- throw new Error(`Error: taprootSpendPath is key; script-path satisfaction is not allowed`);
902
- const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
903
- if (!tapTreeInfo)
904
- throw new Error(`Error: taproot tree info not available`);
905
- const constraints = __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this);
906
- return (0, tapMiniscript_1.satisfyTapTree)({
907
- tapTreeInfo,
908
- preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
909
- signatures,
910
- ...(constraints?.tapLeafHash
911
- ? { tapLeaf: constraints.tapLeafHash }
912
- : {}),
913
- ...(constraints
914
- ? {
915
- timeConstraints: {
916
- nLockTime: constraints.nLockTime,
917
- nSequence: constraints.nSequence
918
- }
919
- }
920
- : {})
921
- });
922
- }
923
- /**
924
- * Creates and returns an instance of bitcoinjs-lib
925
- * [`Payment`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts)'s interface with the `scriptPubKey` of this `Output`.
926
- */
927
- getPayment() {
928
- return __classPrivateFieldGet(this, _Output_payment, "f");
929
- }
930
- /**
931
- * Returns the Bitcoin Address of this `Output`.
932
- */
933
- getAddress() {
934
- if (!__classPrivateFieldGet(this, _Output_payment, "f").address)
935
- throw new Error(`Error: could extract an address from the payment`);
936
- return __classPrivateFieldGet(this, _Output_payment, "f").address;
937
- }
938
- /**
939
- * Returns this `Output`'s scriptPubKey.
940
- */
941
- getScriptPubKey() {
942
- if (!__classPrivateFieldGet(this, _Output_payment, "f").output)
943
- throw new Error(`Error: could extract output.script from the payment`);
944
- return __classPrivateFieldGet(this, _Output_payment, "f").output;
945
- }
946
- /**
947
- * Gets the nSequence required to fulfill this `Output`.
948
- */
949
- getSequence() {
950
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nSequence;
951
- }
952
- /**
953
- * Gets the nLockTime required to fulfill this `Output`.
954
- */
955
- getLockTime() {
956
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nLockTime;
957
- }
958
- /**
959
- * Returns the tapleaf hash selected during planning for taproot script-path
960
- * spends. If signersPubKeys are provided, selection is optimized for those
961
- * pubkeys. If a specific tapLeaf selector is used in spending calls, this
962
- * reflects that selection.
963
- */
964
- getTapLeafHash() {
965
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.tapLeafHash;
966
- }
967
- /**
968
- * Gets the witnessScript required to fulfill this `Output`. Only applies to
969
- * Segwit outputs.
970
- */
971
- getWitnessScript() {
972
- return __classPrivateFieldGet(this, _Output_witnessScript, "f");
973
- }
974
- /**
975
- * Gets the redeemScript required to fullfill this `Output`. Only applies to
976
- * SH outputs: sh(wpkh), sh(wsh), sh(lockingScript).
977
- */
978
- getRedeemScript() {
979
- return __classPrivateFieldGet(this, _Output_redeemScript, "f");
980
- }
981
- /**
982
- * Gets the bitcoinjs-lib [`network`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/networks.ts) used to create this `Output`.
983
- */
984
- getNetwork() {
985
- return __classPrivateFieldGet(this, _Output_network, "f");
986
- }
987
- /**
988
- * Whether this `Output` is Segwit.
989
- *
990
- * *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
991
- * that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
992
- * (Script Hash-Witness Public Key Hash).
993
- * For inputs using arbitrary scripts (not standard addresses),
994
- * use a descriptor in the format `sh(MINISCRIPT)`.
995
- *
996
- */
997
- isSegwit() {
998
- return __classPrivateFieldGet(this, _Output_isSegwit, "f");
999
- }
1000
- /**
1001
- * Whether this `Output` is Taproot.
1002
- */
1003
- isTaproot() {
1004
- return __classPrivateFieldGet(this, _Output_isTaproot, "f");
1005
- }
1006
- /**
1007
- * Attempts to determine the type of output script by testing it against
1008
- * various payment types.
1009
- *
1010
- * This method tries to identify if the output is one of the following types:
1011
- * - P2SH (Pay to Script Hash)
1012
- * - P2WSH (Pay to Witness Script Hash)
1013
- * - P2WPKH (Pay to Witness Public Key Hash)
1014
- * - P2PKH (Pay to Public Key Hash)
1015
- * - P2TR (Pay to Taproot)
1016
- *
1017
- * @returns An object { isPKH: boolean; isWPKH: boolean; isSH: boolean; isWSH: boolean; isTR: boolean;}
1018
- * with boolean properties indicating the detected output type
1019
- */
1020
- guessOutput() {
1021
- function guessSH(output) {
1022
- try {
1023
- bitcoinjs_lib_1.payments.p2sh({ output });
1024
- return true;
1025
- }
1026
- catch (err) {
1027
- void err;
1028
- return false;
1029
- }
1030
- }
1031
- function guessWSH(output) {
1032
- try {
1033
- bitcoinjs_lib_1.payments.p2wsh({ output });
1034
- return true;
1035
- }
1036
- catch (err) {
1037
- void err;
1038
- return false;
1039
- }
1040
- }
1041
- function guessWPKH(output) {
1042
- try {
1043
- bitcoinjs_lib_1.payments.p2wpkh({ output });
1044
- return true;
1045
- }
1046
- catch (err) {
1047
- void err;
1048
- return false;
1049
- }
1050
- }
1051
- function guessPKH(output) {
1052
- try {
1053
- bitcoinjs_lib_1.payments.p2pkh({ output });
1054
- return true;
1055
- }
1056
- catch (err) {
1057
- void err;
1058
- return false;
1059
- }
1060
- }
1061
- function guessTR(output) {
1062
- try {
1063
- bitcoinjs_lib_1.payments.p2tr({ output });
1064
- return true;
1065
- }
1066
- catch (err) {
1067
- void err;
1068
- return false;
1069
- }
1070
- }
1071
- const isPKH = guessPKH(this.getScriptPubKey());
1072
- const isWPKH = guessWPKH(this.getScriptPubKey());
1073
- const isSH = guessSH(this.getScriptPubKey());
1074
- const isWSH = guessWSH(this.getScriptPubKey());
1075
- const isTR = guessTR(this.getScriptPubKey());
1076
- if ([isPKH, isWPKH, isSH, isWSH, isTR].filter(Boolean).length > 1)
1077
- throw new Error('Cannot have multiple output types.');
1078
- return { isPKH, isWPKH, isSH, isWSH, isTR };
1079
- }
1080
- // References for inputWeight & outputWeight:
1081
- // https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c
1082
- // https://bitcoinops.org/en/tools/calc-size/
1083
- // Look for byteLength: https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/transaction.ts
1084
- // https://github.com/bitcoinjs/coinselect/blob/master/utils.js
1085
- // https://bitcoin.stackexchange.com/questions/111395/what-is-the-weight-of-a-p2tr-input
1086
- /**
1087
- * Computes the Weight Unit contributions of this Output as if it were the
1088
- * input in a tx.
1089
- *
1090
- * *NOTE:* When the descriptor in an input is `addr(address)`, it is assumed
1091
- * that any `addr(SH_TYPE_ADDRESS)` is in fact a Segwit `SH_WPKH`
1092
- * (Script Hash-Witness Public Key Hash).
1093
- *, Also any `addr(SINGLE_KEY_ADDRESS)` * is assumed to be a single key Taproot
1094
- * address (like those defined in BIP86).
1095
- * For inputs using arbitrary scripts (not standard addresses),
1096
- * use a descriptor in the format `sh(MINISCRIPT)`, `wsh(MINISCRIPT)` or
1097
- * `tr(KEY,TREE)` for taproot script-path expressions.
1098
- */
1099
- // NOTE(taproot-weight): Output instances are concrete. If descriptor has
1100
- // wildcards, constructor requires `index`. No ranged-without-index
1101
- // estimation is attempted here.
1102
- // TODO(taproot-weight): Remaining items:
1103
- // - Annex: not modeled; if annex is used, add witness item sizing.
1104
- // - Taproot sighash defaults: options.taprootSighash currently drives fake
1105
- // signature sizing; ensure coinselector passes the intended mode.
1106
- // - After PSBT taproot script-path fields are fully populated, add regtest
1107
- // integration fixtures comparing real tx vsize with inputWeight/outputWeight
1108
- // estimates for taproot key-path and script-path spends.
1109
- inputWeight(
1110
- /**
1111
- * Indicates if the transaction is a Segwit transaction.
1112
- * If a transaction isSegwitTx, a single byte is then also required for
1113
- * non-witness inputs to encode the length of the empty witness stack:
1114
- * encodeLength(0) + 0 = 1
1115
- * Read more:
1116
- * https://gist.github.com/junderw/b43af3253ea5865ed52cb51c200ac19c?permalink_comment_id=4760512#gistcomment-4760512
1117
- */
1118
- isSegwitTx,
1119
- /*
1120
- * Array of `PartialSig`. Each `PartialSig` includes
1121
- * a public key and its corresponding signature. This parameter
1122
- * enables the accurate calculation of signature sizes for ECDSA.
1123
- * Pass 'DANGEROUSLY_USE_FAKE_SIGNATURES' to assume
1124
- * ECDSA_FAKE_SIGNATURE_SIZE bytes for ECDSA.
1125
- * For taproot, the fake signature size is controlled by
1126
- * options.taprootSighash (64 for 'SIGHASH_DEFAULT', 65
1127
- * for 'non-SIGHASH_DEFAULT'). default value is SIGHASH_DEFAULT
1128
- * Mainly used for testing.
1129
- */
1130
- signatures,
1131
- /*
1132
- * Options that affect taproot fake signature sizing.
1133
- * taprootSighash: 'SIGHASH_DEFAULT' | 'non-SIGHASH_DEFAULT' (default: 'SIGHASH_DEFAULT').
1134
- * This is only used when signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'.
1135
- */
1136
- options = {
1137
- taprootSighash: 'SIGHASH_DEFAULT'
1138
- }) {
1139
- const taprootSighash = options.taprootSighash ?? 'SIGHASH_DEFAULT';
1140
- if (this.isSegwit() && !isSegwitTx)
1141
- throw new Error(`a tx is segwit if at least one input is segwit`);
1142
- //expand any miniscript-based descriptor. If not miniscript-based, then it's
1143
- //an addr() descriptor. For those, we can only guess their type.
1144
- const expansion = this.expand().expandedExpression;
1145
- const { isPKH, isWPKH, isSH, isTR } = this.guessOutput();
1146
- const errorMsg = `Input type not implemented. Currently supported: pkh(KEY), wpkh(KEY), tr(KEY), \
1147
- sh(wpkh(KEY)), sh(wsh(MINISCRIPT)), sh(MINISCRIPT), wsh(MINISCRIPT), \
1148
- addr(PKH_ADDRESS), addr(WPKH_ADDRESS), addr(SH_WPKH_ADDRESS), addr(SINGLE_KEY_ADDRESS). \
1149
- expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${isTR}.`;
1150
- if (!expansion && !isPKH && !isWPKH && !isSH && !isTR)
1151
- throw new Error(errorMsg);
1152
- const resolveEcdsaSignatureSize = () => {
1153
- if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES')
1154
- return ((0, varuint_bitcoin_1.encodingLength)(ECDSA_FAKE_SIGNATURE_SIZE) +
1155
- ECDSA_FAKE_SIGNATURE_SIZE);
1156
- if (signatures.length !== 1)
1157
- throw new Error('More than one signture was not expected');
1158
- const singleSignature = signatures[0];
1159
- if (!singleSignature)
1160
- throw new Error('Signatures not present');
1161
- const length = singleSignature.signature.length;
1162
- return (0, varuint_bitcoin_1.encodingLength)(length) + length;
1163
- };
1164
- const resolveMiniscriptSignatures = () => {
1165
- const signerPubKeys = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this);
1166
- if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
1167
- return signerPubKeys.map(pubkey => ({
1168
- pubkey,
1169
- // https://transactionfee.info/charts/bitcoin-script-ecdsa-length/
1170
- signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
1171
- }));
1172
- }
1173
- const providedSignerPubKeysSet = new Set(signatures.map(sig => (0, uint8array_tools_1.toHex)(sig.pubkey)));
1174
- const missingSignerPubKeys = signerPubKeys.filter(pubkey => !providedSignerPubKeysSet.has((0, uint8array_tools_1.toHex)(pubkey)));
1175
- if (missingSignerPubKeys.length > 0)
1176
- throw new Error(`Error: inputWeight expected signatures for all planned miniscript signers. Missing ${missingSignerPubKeys.length} signer(s)`);
1177
- const signerPubKeysSet = new Set(signerPubKeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)));
1178
- return signatures.filter(sig => signerPubKeysSet.has((0, uint8array_tools_1.toHex)(sig.pubkey)));
1179
- };
1180
- const taprootFakeSignatureSize = taprootSighash === 'SIGHASH_DEFAULT'
1181
- ? TAPROOT_FAKE_SIGNATURE_SIZE
1182
- : TAPROOT_FAKE_SIGNATURE_SIZE + 1;
1183
- const resolvePlannedTaprootRequiredPubKeys = () => {
1184
- const tapTreeSignerPubKeys = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(tapMiniscript_1.normalizeTaprootPubkey);
1185
- const taggedFakeSignatures = tapTreeSignerPubKeys.map((pubkey, index) => ({
1186
- pubkey,
1187
- signature: new Uint8Array(taprootFakeSignatureSize).fill(index + 1)
1188
- }));
1189
- const plannedTaprootSatisfaction = this.getTapScriptSatisfaction(taggedFakeSignatures);
1190
- const requiredPubkeys = taggedFakeSignatures
1191
- .filter(fakeSignature => plannedTaprootSatisfaction.stackItems.some(stackItem => (0, uint8array_tools_1.compare)(stackItem, fakeSignature.signature) === 0))
1192
- .map(fakeSignature => fakeSignature.pubkey);
1193
- return Array.from(new Set(requiredPubkeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey))))
1194
- .map(hex => (0, uint8array_tools_1.fromHex)(hex))
1195
- .map(tapMiniscript_1.normalizeTaprootPubkey);
1196
- };
1197
- const resolveTaprootSignatures = () => {
1198
- if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
1199
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
1200
- pubkey,
1201
- signature: new Uint8Array(taprootFakeSignatureSize)
1202
- }));
1203
- }
1204
- const normalizedSignatures = signatures.map(sig => ({
1205
- pubkey: (0, tapMiniscript_1.normalizeTaprootPubkey)(sig.pubkey),
1206
- signature: sig.signature
1207
- }));
1208
- const plannedRequiredPubKeys = resolvePlannedTaprootRequiredPubKeys();
1209
- const providedTapTreeSignerPubKeysSet = new Set(normalizedSignatures.map(sig => (0, uint8array_tools_1.toHex)(sig.pubkey)));
1210
- const missingTapTreeSignerPubKeys = plannedRequiredPubKeys.filter(pubkey => !providedTapTreeSignerPubKeysSet.has((0, uint8array_tools_1.toHex)(pubkey)));
1211
- if (missingTapTreeSignerPubKeys.length > 0)
1212
- throw new Error(`Error: inputWeight expected signatures for the planned taproot script-path satisfaction. Missing ${missingTapTreeSignerPubKeys.length} signer(s)`);
1213
- const plannedRequiredPubKeysSet = new Set(plannedRequiredPubKeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)));
1214
- return normalizedSignatures.filter(sig => plannedRequiredPubKeysSet.has((0, uint8array_tools_1.toHex)(sig.pubkey)));
1215
- };
1216
- const resolveTaprootSignatureSize = () => {
1217
- let length;
1218
- if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
1219
- length = taprootFakeSignatureSize;
1220
- }
1221
- else {
1222
- const normalizedSignatures = signatures.map(sig => ({
1223
- pubkey: (0, tapMiniscript_1.normalizeTaprootPubkey)(sig.pubkey),
1224
- signature: sig.signature
1225
- }));
1226
- const internalPubkey = this.getPayment().internalPubkey;
1227
- if (!internalPubkey) {
1228
- //addr() of tr addresses may not have internalPubkey
1229
- if (normalizedSignatures.length !== 1)
1230
- throw new Error('Error: inputWeight for addr(TR_ADDRESS) requires exactly one signature. Internal taproot pubkey is unavailable in addr() descriptors; use tr(KEY) for strict key matching.');
1231
- const singleSignature = normalizedSignatures[0];
1232
- if (!singleSignature)
1233
- throw new Error('Signatures not present');
1234
- length = singleSignature.signature.length;
1235
- return (0, varuint_bitcoin_1.encodingLength)(length) + length;
1236
- }
1237
- const normalizedInternalPubkey = (0, tapMiniscript_1.normalizeTaprootPubkey)(internalPubkey);
1238
- const keyPathSignatures = normalizedSignatures.filter(sig => (0, uint8array_tools_1.compare)(sig.pubkey, normalizedInternalPubkey) === 0);
1239
- if (keyPathSignatures.length !== 1)
1240
- throw new Error('More than one signture was not expected');
1241
- const singleSignature = keyPathSignatures[0];
1242
- if (!singleSignature)
1243
- throw new Error('Signatures not present');
1244
- length = singleSignature.signature.length;
1245
- }
1246
- return (0, varuint_bitcoin_1.encodingLength)(length) + length;
1247
- };
1248
- const taprootSpendPath = __classPrivateFieldGet(this, _Output_taprootSpendPath, "f");
1249
- if (expansion ? expansion.startsWith('pkh(') : isPKH) {
1250
- return (
1251
- // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (sig:73) + (pubkey:34)
1252
- (32 + 4 + 4 + 1 + resolveEcdsaSignatureSize() + 34) * 4 +
1253
- //Segwit:
1254
- (isSegwitTx ? 1 : 0));
1255
- }
1256
- else if (expansion ? expansion.startsWith('wpkh(') : isWPKH) {
1257
- if (!isSegwitTx)
1258
- throw new Error('Should be SegwitTx');
1259
- return (
1260
- // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
1261
- 41 * 4 +
1262
- // Segwit: (push_count:1) + (sig:73) + (pubkey:34)
1263
- (1 + resolveEcdsaSignatureSize() + 34));
1264
- }
1265
- else if (expansion ? expansion.startsWith('sh(wpkh(') : isSH) {
1266
- if (!isSegwitTx)
1267
- throw new Error('Should be SegwitTx');
1268
- return (
1269
- // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (p2wpkh:23)
1270
- // -> p2wpkh_script: OP_0 OP_PUSH20 <public_key_hash>
1271
- // -> p2wpkh: (script_len:1) + (script:22)
1272
- 64 * 4 +
1273
- // Segwit: (push_count:1) + (sig:73) + (pubkey:34)
1274
- (1 + resolveEcdsaSignatureSize() + 34));
1275
- }
1276
- else if (expansion?.startsWith('sh(wsh(')) {
1277
- if (!isSegwitTx)
1278
- throw new Error('Should be SegwitTx');
1279
- const witnessScript = this.getWitnessScript();
1280
- if (!witnessScript)
1281
- throw new Error('sh(wsh) must provide witnessScript');
1282
- const payment = bitcoinjs_lib_1.payments.p2sh({
1283
- redeem: bitcoinjs_lib_1.payments.p2wsh({
1284
- redeem: {
1285
- input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1286
- .scriptSatisfaction,
1287
- output: witnessScript
1288
- }
1289
- })
1290
- });
1291
- if (!payment || !payment.input || !payment.witness)
1292
- throw new Error('Could not create payment');
1293
- return (
1294
- //Non-segwit
1295
- 4 * (40 + varSliceSize(payment.input)) +
1296
- //Segwit
1297
- vectorSize(payment.witness));
1298
- }
1299
- else if (expansion?.startsWith('sh(')) {
1300
- const redeemScript = this.getRedeemScript();
1301
- if (!redeemScript)
1302
- throw new Error('sh() must provide redeemScript');
1303
- const payment = bitcoinjs_lib_1.payments.p2sh({
1304
- redeem: {
1305
- input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1306
- .scriptSatisfaction,
1307
- output: redeemScript
1308
- }
1309
- });
1310
- if (!payment || !payment.input)
1311
- throw new Error('Could not create payment');
1312
- if (payment.witness?.length)
1313
- throw new Error('A legacy p2sh payment should not cointain a witness');
1314
- return (
1315
- //Non-segwit
1316
- 4 * (40 + varSliceSize(payment.input)) +
1317
- //Segwit:
1318
- (isSegwitTx ? 1 : 0));
1319
- }
1320
- else if (expansion?.startsWith('wsh(')) {
1321
- const witnessScript = this.getWitnessScript();
1322
- if (!witnessScript)
1323
- throw new Error('wsh must provide witnessScript');
1324
- const payment = bitcoinjs_lib_1.payments.p2wsh({
1325
- redeem: {
1326
- input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1327
- .scriptSatisfaction,
1328
- output: witnessScript
1329
- }
1330
- });
1331
- if (!payment || !payment.input || !payment.witness)
1332
- throw new Error('Could not create payment');
1333
- return (
1334
- //Non-segwit
1335
- 4 * (40 + varSliceSize(payment.input)) +
1336
- //Segwit
1337
- vectorSize(payment.witness));
1338
- // when tr(KEY,TREE): choose key-path or script-path based on
1339
- // constructor taprootSpendPath policy.
1340
- }
1341
- else if (expansion?.startsWith('tr(@0,')) {
1342
- if (!isSegwitTx)
1343
- throw new Error('Should be SegwitTx');
1344
- if (taprootSpendPath === 'key')
1345
- return 41 * 4 + (0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize();
1346
- const taprootSatisfaction = this.getTapScriptSatisfaction(resolveTaprootSignatures());
1347
- return 41 * 4 + taprootSatisfaction.totalWitnessSize;
1348
- }
1349
- else if (isTR && (!expansion || expansion === 'tr(@0)')) {
1350
- if (!isSegwitTx)
1351
- throw new Error('Should be SegwitTx');
1352
- return (
1353
- // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
1354
- 41 * 4 +
1355
- // Segwit: (push_count:1) + (sig_length(1) + schnorr_sig(64/65))
1356
- ((0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize()));
1357
- }
1358
- else {
1359
- throw new Error(errorMsg);
1360
- }
1361
- }
1362
- /**
1363
- * Computes the Weight Unit contributions of this Output as if it were the
1364
- * output in a tx.
1365
- */
1366
- outputWeight() {
1367
- //expand any miniscript-based descriptor. If not miniscript-based, then it's
1368
- //an addr() descriptor. For those, we can only guess their type.
1369
- const { isPKH, isWPKH, isSH, isWSH, isTR } = this.guessOutput();
1370
- const errorMsg = `Output type not implemented. Currently supported: pkh=${isPKH}, wpkh=${isWPKH}, tr=${isTR}, sh=${isSH} and wsh=${isWSH}.`;
1371
- if (isPKH) {
1372
- // (p2pkh:26) + (amount:8)
1373
- return 34 * 4;
1374
- }
1375
- else if (isWPKH) {
1376
- // (p2wpkh:23) + (amount:8)
1377
- return 31 * 4;
1378
- }
1379
- else if (isSH) {
1380
- // (p2sh:24) + (amount:8)
1381
- return 32 * 4;
1382
- }
1383
- else if (isWSH) {
1384
- // (p2wsh:35) + (amount:8)
1385
- return 43 * 4;
1386
- }
1387
- else if (isTR) {
1388
- // (script_pubKey_length:1) + (p2t2(OP_1 OP_PUSH32 <schnorr_public_key>):34) + (amount:8)
1389
- return 43 * 4;
1390
- }
1391
- else {
1392
- throw new Error(errorMsg);
1393
- }
1394
- }
1395
- /**
1396
- * Sets this output as an input of the provided `psbt` and updates the
1397
- * `psbt` locktime if required by the descriptor.
1398
- *
1399
- * `psbt` and `vout` are mandatory. Include `txHex` as well. The pair
1400
- * `vout` and `txHex` define the transaction and output number this instance
1401
- * pertains to.
1402
- *
1403
- * Though not advised, for Segwit inputs you can pass `txId` and `value`
1404
- * in lieu of `txHex`. If doing so, ensure `value` accuracy to avoid
1405
- * potential fee attacks -
1406
- * [See this issue](https://github.com/bitcoinjs/bitcoinjs-lib/issues/1625).
1407
- *
1408
- * Note: Hardware wallets need the [full `txHex` for Segwit](https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd).
1409
- *
1410
- * When unsure, always use `txHex`, and skip `txId` and `value` for safety.
1411
- *
1412
- * Use `rbf` to mark whether this tx can be replaced with another with
1413
- * higher fee while being in the mempool. Note that a tx will automatically
1414
- * be marked as replacable if a single input requests it.
1415
- * Note that any transaction using a relative timelock (nSequence < 0x80000000)
1416
- * also falls within the RBF range (nSequence < 0xFFFFFFFE), making it
1417
- * inherently replaceable. So don't set `rbf` to false if this is tx uses
1418
- * relative time locks.
1419
- *
1420
- * @returns A finalizer function to be used after signing the `psbt`.
1421
- * This function ensures that this input is properly finalized.
1422
- * The finalizer completes the PSBT input by adding the unlocking script
1423
- * (`scriptWitness` or `scriptSig`) that satisfies this `Output`'s spending
1424
- * conditions. Because these scripts include signatures, you should finish
1425
- * all signing operations before calling the finalizer.
1426
- * The finalizer has this signature:
1427
- *
1428
- * `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
1429
- *
1430
- */
1431
- updatePsbtAsInput({ psbt, txHex, txId, value, vout, //vector output index
1432
- rbf = true }) {
1433
- if (value !== undefined && typeof value !== 'bigint')
1434
- throw new Error(`Error: value must be a bigint`);
1435
- if (value !== undefined && value < 0n)
1436
- throw new Error(`Error: value must be >= 0n`);
1437
- if (txHex === undefined) {
1438
- console.warn(`Warning: missing txHex may allow fee attacks`);
1439
- }
1440
- const isSegwit = this.isSegwit();
1441
- if (isSegwit === undefined) {
1442
- //This should only happen when using addr() expressions
1443
- throw new Error(`Error: could not determine whether this is a segwit descriptor`);
1444
- }
1445
- const isTaproot = this.isTaproot();
1446
- if (isTaproot === undefined) {
1447
- //This should only happen when using addr() expressions
1448
- throw new Error(`Error: could not determine whether this is a taproot descriptor`);
1449
- }
1450
- const paymentInternalPubkey = this.getPayment().internalPubkey;
1451
- const tapInternalKey = isTaproot
1452
- ? paymentInternalPubkey
1453
- ? paymentInternalPubkey
1454
- : undefined
1455
- : undefined;
1456
- let tapLeafScript;
1457
- let tapBip32Derivation;
1458
- if (isTaproot && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1459
- const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1460
- if (!tapTreeInfo)
1461
- throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
1462
- if (!tapInternalKey)
1463
- throw new Error(`Error: taprootSpendPath=script requires taproot internal key`);
1464
- const taprootLeafMetadata = (0, tapMiniscript_1.buildTaprootLeafPsbtMetadata)({
1465
- tapTreeInfo,
1466
- internalPubkey: tapInternalKey
1467
- });
1468
- tapLeafScript = taprootLeafMetadata.map(({ leaf, controlBlock }) => ({
1469
- script: leaf.tapScript,
1470
- leafVersion: leaf.version,
1471
- controlBlock
1472
- }));
1473
- const internalKeyInfo = __classPrivateFieldGet(this, _Output_expansionMap, "f")?.['@0'];
1474
- if (!internalKeyInfo)
1475
- throw new Error(`Error: taproot internal key info not available in expansionMap`);
1476
- tapBip32Derivation = (0, tapMiniscript_1.buildTaprootBip32Derivations)({
1477
- tapTreeInfo,
1478
- internalKeyInfo
1479
- });
1480
- }
1481
- const index = (0, psbt_1.addPsbtInput)({
1482
- psbt,
1483
- vout,
1484
- ...(txHex !== undefined ? { txHex } : {}),
1485
- ...(txId !== undefined ? { txId } : {}),
1486
- ...(value !== undefined ? { value } : {}),
1487
- tapInternalKey,
1488
- tapLeafScript,
1489
- tapBip32Derivation,
1490
- sequence: this.getSequence(),
1491
- locktime: this.getLockTime(),
1492
- keysInfo: __classPrivateFieldGet(this, _Output_expansionMap, "f") ? Object.values(__classPrivateFieldGet(this, _Output_expansionMap, "f")) : [],
1493
- scriptPubKey: this.getScriptPubKey(),
1494
- isSegwit,
1495
- witnessScript: this.getWitnessScript(),
1496
- redeemScript: this.getRedeemScript(),
1497
- rbf
1498
- });
1499
- //The finalizer adds the unlocking script (scriptSig/scriptWitness) once
1500
- //signatures are ready.
1501
- const finalizer = ({ psbt, validate = true }) => {
1502
- if (validate &&
1503
- !psbt.validateSignaturesOfInput(index, signatureValidator)) {
1504
- throw new Error(`Error: invalid signatures on input ${index}`);
1505
- }
1506
- //An index must be passed since finding the index in the psbt cannot be
1507
- //done:
1508
- //Imagine the case where you received money twice to
1509
- //the same miniscript-based address. You would have the same scriptPubKey,
1510
- //same sequences, ... The descriptor does not store the hash of the previous
1511
- //transaction since it is a general Output instance. Indices must be kept
1512
- //out of the scope of this class and then passed.
1513
- __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertPsbtInput).call(this, { index, psbt });
1514
- if (__classPrivateFieldGet(this, _Output_isTaproot, "f") &&
1515
- __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script' &&
1516
- !__classPrivateFieldGet(this, _Output_tapTreeInfo, "f"))
1517
- throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
1518
- if (__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1519
- const input = psbt.data.inputs[index];
1520
- const tapLeafScript = input?.tapLeafScript;
1521
- if (!tapLeafScript || tapLeafScript.length === 0)
1522
- throw new Error(`Error: cannot finalize taproot script-path without tapLeafScript`);
1523
- const tapScriptSig = input?.tapScriptSig;
1524
- if (!tapScriptSig || tapScriptSig.length === 0)
1525
- throw new Error(`Error: cannot finalize taproot script-path without tapScriptSig`);
1526
- const taprootSatisfaction = this.getTapScriptSatisfaction(tapScriptSig);
1527
- const matchingLeaf = tapLeafScript.find(leafScript => (0, uint8array_tools_1.compare)((0, bitcoinjs_lib_internals_1.tapleafHash)({
1528
- output: leafScript.script,
1529
- version: leafScript.leafVersion
1530
- }), taprootSatisfaction.tapLeafHash) === 0);
1531
- if (!matchingLeaf)
1532
- throw new Error(`Error: tapLeafScript does not match planned tapLeafHash`);
1533
- if ((0, uint8array_tools_1.compare)(matchingLeaf.script, taprootSatisfaction.leaf.tapScript) !==
1534
- 0 ||
1535
- matchingLeaf.leafVersion !== taprootSatisfaction.leaf.version)
1536
- throw new Error(`Error: tapLeafScript does not match planned leaf script`);
1537
- const witness = [
1538
- ...taprootSatisfaction.stackItems,
1539
- matchingLeaf.script,
1540
- matchingLeaf.controlBlock
1541
- ];
1542
- const finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(witness);
1543
- psbt.finalizeTaprootInput(index, taprootSatisfaction.tapLeafHash, () => ({ finalScriptWitness }));
1544
- }
1545
- else if (!__classPrivateFieldGet(this, _Output_miniscript, "f")) {
1546
- //Use standard finalizers
1547
- psbt.finalizeInput(index);
1548
- }
1549
- else {
1550
- const signatures = psbt.data.inputs[index]?.partialSig;
1551
- if (!signatures)
1552
- throw new Error(`Error: cannot finalize without signatures`);
1553
- const { scriptSatisfaction } = this.getScriptSatisfaction(signatures);
1554
- psbt.finalizeInput(index, (0, psbt_1.finalScriptsFuncFactory)(scriptSatisfaction, __classPrivateFieldGet(this, _Output_network, "f")));
1555
- }
1556
- };
1557
- return finalizer;
1558
- }
1559
- /**
1560
- * Adds this output as an output of the provided `psbt` with the given
1561
- * value.
1562
- * @param params - The parameters for the method.
1563
- * @param params.psbt - The Partially Signed Bitcoin Transaction.
1564
- * @param params.value - The value for the output in satoshis.
1565
- */
1566
- updatePsbtAsOutput({ psbt, value }) {
1567
- if (typeof value !== 'bigint')
1568
- throw new Error(`Error: value must be a bigint`);
1569
- if (value < 0n)
1570
- throw new Error(`Error: value must be >= 0n`);
1571
- psbt.addOutput({ script: this.getScriptPubKey(), value });
1572
- }
1573
- /**
1574
- * Decomposes the descriptor used to form this `Output` into its elemental
1575
- * parts. See {@link ExpansionMap ExpansionMap} for a detailed explanation.
1576
- */
1577
- expand() {
1578
- return {
1579
- ...(__classPrivateFieldGet(this, _Output_expandedExpression, "f") !== undefined
1580
- ? { expandedExpression: __classPrivateFieldGet(this, _Output_expandedExpression, "f") }
1581
- : {}),
1582
- ...(__classPrivateFieldGet(this, _Output_miniscript, "f") !== undefined
1583
- ? { miniscript: __classPrivateFieldGet(this, _Output_miniscript, "f") }
1584
- : {}),
1585
- ...(__classPrivateFieldGet(this, _Output_expandedMiniscript, "f") !== undefined
1586
- ? { expandedMiniscript: __classPrivateFieldGet(this, _Output_expandedMiniscript, "f") }
1587
- : {}),
1588
- ...(__classPrivateFieldGet(this, _Output_tapTreeExpression, "f") !== undefined
1589
- ? { tapTreeExpression: __classPrivateFieldGet(this, _Output_tapTreeExpression, "f") }
1590
- : {}),
1591
- ...(__classPrivateFieldGet(this, _Output_tapTree, "f") !== undefined ? { tapTree: __classPrivateFieldGet(this, _Output_tapTree, "f") } : {}),
1592
- ...(__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") !== undefined
1593
- ? { tapTreeInfo: __classPrivateFieldGet(this, _Output_tapTreeInfo, "f") }
1594
- : {}),
1595
- ...(__classPrivateFieldGet(this, _Output_expansionMap, "f") !== undefined
1596
- ? { expansionMap: __classPrivateFieldGet(this, _Output_expansionMap, "f") }
1597
- : {})
1598
- };
1599
- }
1600
- }
1601
- _Output_payment = new WeakMap(), _Output_preimages = new WeakMap(), _Output_signersPubKeys = new WeakMap(), _Output_miniscript = new WeakMap(), _Output_witnessScript = new WeakMap(), _Output_redeemScript = new WeakMap(), _Output_isSegwit = new WeakMap(), _Output_isTaproot = new WeakMap(), _Output_expandedExpression = new WeakMap(), _Output_expandedMiniscript = new WeakMap(), _Output_tapTreeExpression = new WeakMap(), _Output_tapTree = new WeakMap(), _Output_tapTreeInfo = new WeakMap(), _Output_taprootSpendPath = new WeakMap(), _Output_tapLeaf = new WeakMap(), _Output_expansionMap = new WeakMap(), _Output_network = new WeakMap(), _Output_instances = new WeakSet(), _Output_resolveMiniscriptSignersPubKeys = function _Output_resolveMiniscriptSignersPubKeys() {
1602
- //If the user did not provide a pubkey subset (signersPubKeys), assume all
1603
- //miniscript pubkeys can sign.
1604
- if (__classPrivateFieldGet(this, _Output_signersPubKeys, "f"))
1605
- return __classPrivateFieldGet(this, _Output_signersPubKeys, "f");
1606
- const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
1607
- if (!expansionMap)
1608
- throw new Error(`Error: expansionMap not available for miniscript`);
1609
- return Object.values(expansionMap).map(keyInfo => {
1610
- const pubkey = keyInfo.pubkey;
1611
- if (!pubkey)
1612
- throw new Error(`Error: miniscript key missing pubkey`);
1613
- return pubkey;
1614
- });
1615
- }, _Output_assertMiniscriptSatisfactionResourceLimits = function _Output_assertMiniscriptSatisfactionResourceLimits(scriptSatisfaction) {
1616
- if (!__classPrivateFieldGet(this, _Output_miniscript, "f"))
1617
- return;
1618
- const satisfactionStackItems = bitcoinjs_lib_1.script.toStack(scriptSatisfaction);
1619
- // For wsh(...) and sh(wsh(...)), enforce witness stack limits.
1620
- if (__classPrivateFieldGet(this, _Output_isSegwit, "f") && !__classPrivateFieldGet(this, _Output_isTaproot, "f")) {
1621
- (0, resourceLimits_1.assertWitnessV0SatisfactionResourceLimits)({
1622
- stackItems: satisfactionStackItems
1623
- });
1624
- return;
1625
- }
1626
- if (!__classPrivateFieldGet(this, _Output_isSegwit, "f")) {
1627
- // In legacy P2SH, after scriptSig execution, the stack is:
1628
- // [ ...satisfactionStackItems, redeemScript ]
1629
- // Consensus limits apply here: each element must be <= 520 bytes and total
1630
- // stack items must be <= 1000.
1631
- // redeemScript size is already validated during script construction
1632
- // to fail early (look for MAX_SCRIPT_ELEMENT_SIZE checks in this file).
1633
- // Below we re-validate again the redeemScript + the rest of stack items.
1634
- const redeemScript = __classPrivateFieldGet(this, _Output_redeemScript, "f");
1635
- if (!redeemScript)
1636
- throw new Error(`Error: redeemScript not available for P2SH spend`);
1637
- (0, resourceLimits_1.assertConsensusStackResourceLimits)({
1638
- stackItems: [...satisfactionStackItems, redeemScript]
1639
- });
1640
- (0, resourceLimits_1.assertP2shScriptSigStandardSize)({
1641
- scriptSatisfaction,
1642
- redeemScript,
1643
- network: __classPrivateFieldGet(this, _Output_network, "f")
1644
- });
1645
- }
1646
- }, _Output_resolveTapTreeSignersPubKeys = function _Output_resolveTapTreeSignersPubKeys() {
1647
- //If the user did not provide a pubkey subset (signersPubKeys), assume all
1648
- //taproot leaf pubkeys can sign.
1649
- const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1650
- if (!tapTreeInfo)
1651
- throw new Error(`Error: taproot tree info not available`);
1652
- const candidatePubkeys = __classPrivateFieldGet(this, _Output_signersPubKeys, "f")
1653
- ? __classPrivateFieldGet(this, _Output_signersPubKeys, "f").map(tapMiniscript_1.normalizeTaprootPubkey)
1654
- : (0, tapMiniscript_1.collectTapTreePubkeys)(tapTreeInfo);
1655
- return Array.from(new Set(candidatePubkeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)))).map(hex => (0, uint8array_tools_1.fromHex)(hex));
1656
- }, _Output_getConstraints = function _Output_getConstraints() {
1657
- const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
1658
- const preimages = __classPrivateFieldGet(this, _Output_preimages, "f");
1659
- const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
1660
- const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
1661
- const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1662
- //Create a method. solvePreimages to solve them.
1663
- if (miniscript) {
1664
- if (expandedMiniscript === undefined || expansionMap === undefined)
1665
- throw new Error(`Error: cannot get time constraints from not expanded miniscript ${miniscript}`);
1666
- //We create some fakeSignatures since we may not have them yet.
1667
- //We only want to retrieve the nLockTime and nSequence of the satisfaction and
1668
- //signatures don't matter
1669
- const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this).map(pubkey => ({
1670
- pubkey,
1671
- signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
1672
- }));
1673
- const satisfaction = (0, miniscript_1.satisfyMiniscript)({
1674
- expandedMiniscript,
1675
- expansionMap,
1676
- signatures: fakeSignatures,
1677
- preimages
1678
- });
1679
- __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
1680
- const { nLockTime, nSequence } = satisfaction;
1681
- return { nLockTime, nSequence, tapLeafHash: undefined };
1682
- }
1683
- else if (tapTreeInfo && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1684
- const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
1685
- pubkey,
1686
- signature: new Uint8Array(TAPROOT_FAKE_SIGNATURE_SIZE)
1687
- }));
1688
- const { nLockTime, nSequence, tapLeafHash } = (0, tapMiniscript_1.satisfyTapTree)({
1689
- tapTreeInfo,
1690
- preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
1691
- signatures: fakeSignatures,
1692
- ...(__classPrivateFieldGet(this, _Output_tapLeaf, "f") !== undefined ? { tapLeaf: __classPrivateFieldGet(this, _Output_tapLeaf, "f") } : {})
1693
- });
1694
- return { nLockTime, nSequence, tapLeafHash };
1695
- }
1696
- return undefined;
1697
- }, _Output_assertPsbtInput = function _Output_assertPsbtInput({ psbt, index }) {
1698
- const input = psbt.data.inputs[index];
1699
- const txInput = psbt.txInputs[index];
1700
- if (!input || !txInput)
1701
- throw new Error(`Error: invalid input or txInput`);
1702
- const { sequence: inputSequence, index: vout } = txInput;
1703
- let scriptPubKey;
1704
- if (input.witnessUtxo)
1705
- scriptPubKey = input.witnessUtxo.script;
1706
- else {
1707
- if (!input.nonWitnessUtxo)
1708
- throw new Error(`Error: input should have either witnessUtxo or nonWitnessUtxo`);
1709
- const tx = bitcoinjs_lib_1.Transaction.fromBuffer(input.nonWitnessUtxo);
1710
- const out = tx.outs[vout];
1711
- if (!out)
1712
- throw new Error(`Error: utxo should exist`);
1713
- scriptPubKey = out.script;
1714
- }
1715
- const locktime = this.getLockTime() || 0;
1716
- const sequence = this.getSequence();
1717
- //We don't know whether the user opted for RBF or not. So check that
1718
- //at least one of the 2 sequences matches.
1719
- const sequenceNoRBF = sequence !== undefined
1720
- ? sequence
1721
- : locktime === 0
1722
- ? 0xffffffff
1723
- : 0xfffffffe;
1724
- const sequenceRBF = sequence !== undefined ? sequence : 0xfffffffd;
1725
- const eqBytes = (bytes1, bytes2) => bytes1 === undefined || bytes2 === undefined
1726
- ? bytes1 === bytes2
1727
- : (0, uint8array_tools_1.compare)(bytes1, bytes2) === 0;
1728
- if ((0, uint8array_tools_1.compare)(scriptPubKey, this.getScriptPubKey()) !== 0 ||
1729
- (sequenceRBF !== inputSequence && sequenceNoRBF !== inputSequence) ||
1730
- locktime !== psbt.locktime ||
1731
- !eqBytes(this.getWitnessScript(), input.witnessScript) ||
1732
- !eqBytes(this.getRedeemScript(), input.redeemScript)) {
1733
- throw new Error(`Error: cannot finalize psbt index ${index} since it does not correspond to this descriptor`);
1734
- }
1735
- };
1736
- return {
1737
- Output,
1738
- parseKeyExpression,
1739
- expand,
1740
- ECPair,
1741
- BIP32
1742
- };
1743
- }