@bitcoinerlab/descriptors 2.3.6 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // Copyright (c) 2025 Jose-Luis Landabaso - https://bitcoinerlab.com
2
+ // Copyright (c) 2026 Jose-Luis Landabaso - https://bitcoinerlab.com
3
3
  // Distributed under the MIT software license
4
4
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
5
  if (k2 === undefined) k2 = k;
@@ -50,9 +50,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
50
50
  };
51
51
  Object.defineProperty(exports, "__esModule", { value: true });
52
52
  exports.DescriptorsFactory = DescriptorsFactory;
53
- const lodash_memoize_1 = __importDefault(require("lodash.memoize"));
53
+ const lodash_memoize_1 = __importDefault(require("lodash.memoize")); //TODO: make sure this is propoely used
54
54
  const bitcoinjs_lib_1 = require("bitcoinjs-lib");
55
+ const bitcoinjs_lib_internals_1 = require("./bitcoinjs-lib-internals");
55
56
  const varuint_bitcoin_1 = require("varuint-bitcoin");
57
+ const uint8array_tools_1 = require("uint8array-tools");
56
58
  const { p2sh, p2wpkh, p2pkh, p2pk, p2wsh, p2tr } = bitcoinjs_lib_1.payments;
57
59
  const bip32_1 = require("bip32");
58
60
  const ecpair_1 = require("ecpair");
@@ -61,17 +63,13 @@ const checksum_1 = require("./checksum");
61
63
  const keyExpressions_1 = require("./keyExpressions");
62
64
  const RE = __importStar(require("./re"));
63
65
  const miniscript_1 = require("./miniscript");
64
- //See "Resource limitations" https://bitcoin.sipa.be/miniscript/
65
- //https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-September/017306.html
66
- const MAX_SCRIPT_ELEMENT_SIZE = 520;
67
- const MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
68
- const MAX_OPS_PER_SCRIPT = 201;
69
- function countNonPushOnlyOPs(script) {
70
- const decompile = bitcoinjs_lib_1.script.decompile(script);
71
- if (!decompile)
72
- throw new Error(`Error: cound not decompile ${script}`);
73
- return decompile.filter(op => typeof op === 'number' && op > bitcoinjs_lib_1.script.OPS['OP_16']).length;
74
- }
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;
75
73
  function vectorSize(someVector) {
76
74
  const length = someVector.length;
77
75
  return ((0, varuint_bitcoin_1.encodingLength)(length) +
@@ -83,23 +81,12 @@ function varSliceSize(someScript) {
83
81
  const length = someScript.length;
84
82
  return (0, varuint_bitcoin_1.encodingLength)(length) + length;
85
83
  }
86
- /**
87
- * This function will typically return 73; since it assumes a signature size of
88
- * 72 bytes (this is the max size of a DER encoded signature) and it adds 1
89
- * extra byte for encoding its length
90
- */
91
- function signatureSize(signature) {
92
- const length = signature === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
93
- ? 72
94
- : signature.signature.length;
95
- return (0, varuint_bitcoin_1.encodingLength)(length) + length;
96
- }
97
84
  /*
98
85
  * Returns a bare descriptor without checksum and particularized for a certain
99
86
  * index (if desc was a range descriptor)
100
87
  * @hidden
101
88
  */
102
- function evaluate({ descriptor, checksumRequired, index }) {
89
+ function evaluate({ descriptor, checksumRequired, index, change }) {
103
90
  if (!descriptor)
104
91
  throw new Error('You must provide a descriptor.');
105
92
  const mChecksum = descriptor.match(String.raw `(${RE.reChecksum})$`);
@@ -115,6 +102,10 @@ function evaluate({ descriptor, checksumRequired, index }) {
115
102
  throw new Error(`Error: invalid descriptor checksum for ${descriptor}`);
116
103
  }
117
104
  }
105
+ evaluatedDescriptor = (0, multipath_1.resolveMultipathDescriptor)({
106
+ descriptor: evaluatedDescriptor,
107
+ ...(change !== undefined ? { change } : {})
108
+ });
118
109
  if (index !== undefined) {
119
110
  const mWildcard = evaluatedDescriptor.match(/\*/g);
120
111
  if (mWildcard && mWildcard.length > 0) {
@@ -148,6 +139,37 @@ function parseSortedMulti(inner) {
148
139
  throw new Error(`sortedmulti(): descriptors support up to 20 keys (per BIP 380/383).`);
149
140
  return { m, keyExpressions };
150
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
+ }
151
173
  /**
152
174
  * Constructs the necessary functions and classes for working with descriptors
153
175
  * using an external elliptic curve (ecc) library.
@@ -156,12 +178,8 @@ function parseSortedMulti(inner) {
156
178
  * provides methods to create, sign, and finalize PSBTs based on descriptor
157
179
  * expressions.
158
180
  *
159
- * While this Factory function includes the `Descriptor` class, note that
160
- * this class was deprecated in v2.0 in favor of `Output`. For backward
161
- * compatibility, the `Descriptor` class remains, but using `Output` is advised.
162
- *
163
181
  * The Factory also returns utility methods like `expand` (detailed below)
164
- * and `parseKeyExpression` (see {@link ParseKeyExpression}).
182
+ * and `parseKeyExpression` (see {@link KeyExpressionParser}).
165
183
  *
166
184
  * Additionally, for convenience, the function returns `BIP32` and `ECPair`.
167
185
  * These are {@link https://github.com/bitcoinjs bitcoinjs-lib} classes designed
@@ -174,7 +192,7 @@ function parseSortedMulti(inner) {
174
192
  * [@bitcoinerlab/secp256k1](https://github.com/bitcoinerlab/secp256k1).
175
193
  */
176
194
  function DescriptorsFactory(ecc) {
177
- var _Output_instances, _Output_payment, _Output_preimages, _Output_signersPubKeys, _Output_miniscript, _Output_witnessScript, _Output_redeemScript, _Output_isSegwit, _Output_isTaproot, _Output_expandedExpression, _Output_expandedMiniscript, _Output_expansionMap, _Output_network, _Output_getTimeConstraints, _Output_assertPsbtInput;
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;
178
196
  (0, bitcoinjs_lib_1.initEccLib)(ecc); //Taproot requires initEccLib
179
197
  const BIP32 = (0, bip32_1.BIP32Factory)(ecc);
180
198
  const ECPair = (0, ecpair_1.ECPairFactory)(ecc);
@@ -204,14 +222,69 @@ function DescriptorsFactory(ecc) {
204
222
  });
205
223
  };
206
224
  /**
207
- * @overload
208
- * To be removed in v3.0 and replaced by the version with the signature that
209
- * does not accept descriptors
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.
210
242
  */
211
- function expand({ descriptor, expression, index, checksumRequired = false, network = bitcoinjs_lib_1.networks.bitcoin, allowMiniscriptInP2SH = false }) {
212
- if (descriptor && expression)
213
- throw new Error(`expression param has been deprecated`);
214
- descriptor = descriptor || expression;
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 }) {
215
288
  if (!descriptor)
216
289
  throw new Error(`descriptor not provided`);
217
290
  let expandedExpression;
@@ -220,6 +293,9 @@ function DescriptorsFactory(ecc) {
220
293
  let isSegwit;
221
294
  let isTaproot;
222
295
  let expandedMiniscript;
296
+ let tapTreeExpression;
297
+ let tapTree;
298
+ let tapTreeInfo;
223
299
  let payment;
224
300
  let witnessScript;
225
301
  let redeemScript;
@@ -232,6 +308,7 @@ function DescriptorsFactory(ecc) {
232
308
  const canonicalExpression = evaluate({
233
309
  descriptor,
234
310
  ...(index !== undefined ? { index } : {}),
311
+ ...(change !== undefined ? { change } : {}),
235
312
  checksumRequired
236
313
  });
237
314
  const isCanonicalRanged = canonicalExpression.indexOf('*') !== -1;
@@ -398,7 +475,7 @@ function DescriptorsFactory(ecc) {
398
475
  throw new Error(`Error: key has no pubkey`);
399
476
  return p.pubkey;
400
477
  });
401
- pubkeys.sort((a, b) => a.compare(b));
478
+ pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
402
479
  const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
403
480
  redeemScript = redeem.output;
404
481
  if (!redeemScript)
@@ -428,7 +505,7 @@ function DescriptorsFactory(ecc) {
428
505
  throw new Error(`Error: key has no pubkey`);
429
506
  return p.pubkey;
430
507
  });
431
- pubkeys.sort((a, b) => a.compare(b));
508
+ pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
432
509
  const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
433
510
  witnessScript = redeem.output;
434
511
  if (!witnessScript)
@@ -458,7 +535,7 @@ function DescriptorsFactory(ecc) {
458
535
  throw new Error(`Error: key has no pubkey`);
459
536
  return p.pubkey;
460
537
  });
461
- pubkeys.sort((a, b) => a.compare(b));
538
+ pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
462
539
  const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
463
540
  const wsh = bitcoinjs_lib_1.payments.p2wsh({ redeem, network });
464
541
  witnessScript = redeem.output;
@@ -482,13 +559,10 @@ function DescriptorsFactory(ecc) {
482
559
  if (!isCanonicalRanged) {
483
560
  const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
484
561
  witnessScript = script;
485
- if (script.byteLength > MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
486
- throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
487
- }
488
- const nonPushOnlyOps = countNonPushOnlyOPs(script);
489
- if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
490
- throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_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`);
491
564
  }
565
+ (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
492
566
  payment = p2sh({
493
567
  redeem: p2wsh({ redeem: { output: script, network }, network }),
494
568
  network
@@ -508,10 +582,14 @@ function DescriptorsFactory(ecc) {
508
582
  if (!miniscript)
509
583
  throw new Error(`Error: could not get miniscript in ${descriptor}`);
510
584
  if (allowMiniscriptInP2SH === false &&
511
- //These top-level expressions within sh are allowed within sh.
512
- //They can be parsed with miniscript2Script, but first we must make sure
513
- //that other expressions are not accepted (unless forced with allowMiniscriptInP2SH).
514
- miniscript.search(/^(pk\(|pkh\(|wpkh\(|combo\(|multi\(|sortedmulti\(|multi_a\(|sortedmulti_a\()/) !== 0) {
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) {
515
593
  throw new Error(`Error: Miniscript expressions can only be used in wsh`);
516
594
  }
517
595
  ({ expandedMiniscript, expansionMap } = expandMiniscript({
@@ -523,13 +601,10 @@ function DescriptorsFactory(ecc) {
523
601
  if (!isCanonicalRanged) {
524
602
  const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
525
603
  redeemScript = script;
526
- if (script.byteLength > MAX_SCRIPT_ELEMENT_SIZE) {
527
- throw new Error(`Error: P2SH script is too large, ${script.byteLength} bytes is larger than ${MAX_SCRIPT_ELEMENT_SIZE} bytes`);
528
- }
529
- const nonPushOnlyOps = countNonPushOnlyOPs(script);
530
- if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
531
- throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_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`);
532
606
  }
607
+ (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
533
608
  payment = p2sh({ redeem: { output: script, network }, network });
534
609
  }
535
610
  }
@@ -549,26 +624,21 @@ function DescriptorsFactory(ecc) {
549
624
  if (!isCanonicalRanged) {
550
625
  const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
551
626
  witnessScript = script;
552
- if (script.byteLength > MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
553
- throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
554
- }
555
- const nonPushOnlyOps = countNonPushOnlyOPs(script);
556
- if (nonPushOnlyOps > MAX_OPS_PER_SCRIPT) {
557
- throw new Error(`Error: too many non-push ops, ${nonPushOnlyOps} non-push ops is larger than ${MAX_OPS_PER_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`);
558
629
  }
630
+ (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
559
631
  payment = p2wsh({ redeem: { output: script, network }, network });
560
632
  }
561
633
  }
562
- //tr(KEY) - taproot - TODO: tr(KEY,TREE) not yet supported
563
- else if (canonicalExpression.match(RE.reTrSingleKeyAnchored)) {
634
+ //tr(KEY) or tr(KEY,TREE) - taproot
635
+ else if (canonicalExpression.startsWith('tr(')) {
564
636
  isSegwit = true;
565
637
  isTaproot = true;
566
- const keyExpression = canonicalExpression.match(RE.reTaprootKeyExp)?.[0];
567
- if (!keyExpression)
568
- throw new Error(`Error: keyExpression could not me extracted`);
569
- if (canonicalExpression !== `tr(${keyExpression})`)
570
- throw new Error(`Error: invalid expression ${expression}`);
571
- expandedExpression = 'tr(@0)';
638
+ const { keyExpression, treeExpression } = parseTrExpression(canonicalExpression);
639
+ expandedExpression = treeExpression
640
+ ? `tr(@0,${treeExpression})`
641
+ : 'tr(@0)';
572
642
  const pKE = parseKeyExpression({
573
643
  keyExpression,
574
644
  network,
@@ -576,17 +646,41 @@ function DescriptorsFactory(ecc) {
576
646
  isTaproot
577
647
  });
578
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
+ }
579
667
  if (!isCanonicalRanged) {
580
668
  const pubkey = pKE.pubkey;
581
669
  if (!pubkey)
582
- throw new Error(`Error: could not extract a pubkey from ${expression}`);
583
- payment = p2tr({ internalPubkey: pubkey, network });
584
- //console.log('TRACE', {
585
- // pKE,
586
- // pubkey: pubkey?.toString('hex'),
587
- // payment,
588
- // paymentPubKey: payment.pubkey
589
- //});
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
+ }
590
684
  }
591
685
  }
592
686
  else {
@@ -600,6 +694,9 @@ function DescriptorsFactory(ecc) {
600
694
  ...(isSegwit !== undefined ? { isSegwit } : {}),
601
695
  ...(isTaproot !== undefined ? { isTaproot } : {}),
602
696
  ...(expandedMiniscript !== undefined ? { expandedMiniscript } : {}),
697
+ ...(tapTreeExpression !== undefined ? { tapTreeExpression } : {}),
698
+ ...(tapTree !== undefined ? { tapTree } : {}),
699
+ ...(tapTreeInfo !== undefined ? { tapTreeInfo } : {}),
603
700
  ...(redeemScript !== undefined ? { redeemScript } : {}),
604
701
  ...(witnessScript !== undefined ? { witnessScript } : {}),
605
702
  isRanged,
@@ -634,7 +731,7 @@ function DescriptorsFactory(ecc) {
634
731
  * @param options
635
732
  * @throws {Error} - when descriptor is invalid
636
733
  */
637
- constructor({ descriptor, index, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys }) {
734
+ constructor({ descriptor, index, change, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys, taprootSpendPath, tapLeaf }) {
638
735
  _Output_instances.add(this);
639
736
  _Output_payment.set(this, void 0);
640
737
  _Output_preimages.set(this, []);
@@ -648,6 +745,11 @@ function DescriptorsFactory(ecc) {
648
745
  _Output_isTaproot.set(this, void 0);
649
746
  _Output_expandedExpression.set(this, void 0);
650
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);
651
753
  _Output_expansionMap.set(this, void 0);
652
754
  _Output_network.set(this, void 0);
653
755
  __classPrivateFieldSet(this, _Output_network, network, "f");
@@ -657,10 +759,26 @@ function DescriptorsFactory(ecc) {
657
759
  const expandedResult = expand({
658
760
  descriptor,
659
761
  ...(index !== undefined ? { index } : {}),
762
+ ...(change !== undefined ? { change } : {}),
660
763
  checksumRequired,
661
764
  network,
662
765
  allowMiniscriptInP2SH
663
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
+ }
664
782
  if (expandedResult.isRanged && index === undefined)
665
783
  throw new Error(`Error: index was not provided for ranged descriptor`);
666
784
  if (!expandedResult.payment)
@@ -678,83 +796,55 @@ function DescriptorsFactory(ecc) {
678
796
  __classPrivateFieldSet(this, _Output_isTaproot, expandedResult.isTaproot, "f");
679
797
  if (expandedResult.expandedMiniscript !== undefined)
680
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");
681
805
  if (expandedResult.redeemScript !== undefined)
682
806
  __classPrivateFieldSet(this, _Output_redeemScript, expandedResult.redeemScript, "f");
683
807
  if (expandedResult.witnessScript !== undefined)
684
808
  __classPrivateFieldSet(this, _Output_witnessScript, expandedResult.witnessScript, "f");
685
- if (signersPubKeys) {
809
+ if (signersPubKeys)
686
810
  __classPrivateFieldSet(this, _Output_signersPubKeys, signersPubKeys, "f");
687
- }
688
- else {
689
- if (__classPrivateFieldGet(this, _Output_expansionMap, "f")) {
690
- __classPrivateFieldSet(this, _Output_signersPubKeys, Object.values(__classPrivateFieldGet(this, _Output_expansionMap, "f")).map(keyInfo => {
691
- const pubkey = keyInfo.pubkey;
692
- if (!pubkey)
693
- throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
694
- return pubkey;
695
- }), "f");
696
- }
697
- else {
698
- //We should only miss expansionMap in addr() expressions:
699
- if (!expandedResult.canonicalExpression.match(RE.reAddrAnchored)) {
700
- throw new Error(`Error: expansionMap not available for expression ${descriptor} that is not an address`);
701
- }
702
- __classPrivateFieldSet(this, _Output_signersPubKeys, [this.getScriptPubKey()], "f");
703
- }
704
- }
811
+ __classPrivateFieldSet(this, _Output_taprootSpendPath, resolvedTaprootSpendPath, "f");
812
+ if (tapLeaf !== undefined)
813
+ __classPrivateFieldSet(this, _Output_tapLeaf, tapLeaf, "f");
705
814
  this.getSequence = (0, lodash_memoize_1.default)(this.getSequence);
706
815
  this.getLockTime = (0, lodash_memoize_1.default)(this.getLockTime);
707
816
  const getSignaturesKey = (signatures) => signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
708
817
  ? signatures
709
818
  : signatures
710
- .map(s => `${s.pubkey.toString('hex')}-${s.signature.toString('hex')}`)
819
+ .map(s => `${(0, uint8array_tools_1.toHex)(s.pubkey)}-${(0, uint8array_tools_1.toHex)(s.signature)}`)
711
820
  .join('|');
712
- this.getScriptSatisfaction = (0, lodash_memoize_1.default)(this.getScriptSatisfaction,
713
- // resolver function:
714
- getSignaturesKey);
715
821
  this.guessOutput = (0, lodash_memoize_1.default)(this.guessOutput);
716
822
  this.inputWeight = (0, lodash_memoize_1.default)(this.inputWeight,
717
823
  // resolver function:
718
- (isSegwitTx, signatures) => {
824
+ (isSegwitTx, signatures, options) => {
719
825
  const segwitKey = isSegwitTx ? 'segwit' : 'non-segwit';
720
826
  const signaturesKey = getSignaturesKey(signatures);
721
- return `${segwitKey}-${signaturesKey}`;
827
+ const taprootSighashKey = options?.taprootSighash ?? 'SIGHASH_DEFAULT';
828
+ return `${segwitKey}-${signaturesKey}-taprootSighash:${taprootSighashKey}`;
722
829
  });
723
830
  this.outputWeight = (0, lodash_memoize_1.default)(this.outputWeight);
724
831
  }
725
832
  /**
726
- * Creates and returns an instance of bitcoinjs-lib
727
- * [`Payment`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts)'s interface with the `scriptPubKey` of this `Output`.
728
- */
729
- getPayment() {
730
- return __classPrivateFieldGet(this, _Output_payment, "f");
731
- }
732
- /**
733
- * Returns the Bitcoin Address of this `Output`.
734
- */
735
- getAddress() {
736
- if (!__classPrivateFieldGet(this, _Output_payment, "f").address)
737
- throw new Error(`Error: could extract an address from the payment`);
738
- return __classPrivateFieldGet(this, _Output_payment, "f").address;
739
- }
740
- /**
741
- * Returns this `Output`'s scriptPubKey.
742
- */
743
- getScriptPubKey() {
744
- if (!__classPrivateFieldGet(this, _Output_payment, "f").output)
745
- throw new Error(`Error: could extract output.script from the payment`);
746
- return __classPrivateFieldGet(this, _Output_payment, "f").output;
747
- }
748
- /**
749
- * Returns the compiled Script Satisfaction if this `Output` was created
750
- * using a miniscript-based descriptor.
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/).
751
836
  *
752
- * The Satisfaction is the unlocking script that fulfills
753
- * (satisfies) this `Output` and it is derived using the Safisfier algorithm
754
- * [described here](https://bitcoin.sipa.be/miniscript/).
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.
755
846
  *
756
- * Important: As mentioned above, note that this function only applies to
757
- * miniscript descriptors.
847
+ * The return value includes the satisfaction script and the constraints.
758
848
  */
759
849
  getScriptSatisfaction(
760
850
  /**
@@ -762,29 +852,9 @@ function DescriptorsFactory(ecc) {
762
852
  * build the Satisfaction of this miniscript-based `Output`.
763
853
  *
764
854
  * `signatures` must be passed using this format (pairs of `pubKey/signature`):
765
- * `interface PartialSig { pubkey: Buffer; signature: Buffer; }`
766
- *
767
- * * Alternatively, if you do not have the signatures, you can use the option
768
- * `'DANGEROUSLY_USE_FAKE_SIGNATURES'`. This will generate script satisfactions
769
- * using 72-byte zero-padded signatures. While this can be useful in
770
- * modules like coinselector that require estimating transaction size before
771
- * signing, it is critical to understand the risks:
772
- * - Using this option generales invalid unlocking scripts.
773
- * - It should NEVER be used with real transactions.
774
- * - Its primary use is for testing and size estimation purposes only.
775
- *
776
- * ⚠️ Warning: Misuse of 'DANGEROUSLY_USE_FAKE_SIGNATURES' can lead to security
777
- * vulnerabilities, including but not limited to invalid transaction generation.
778
- * Ensure you fully understand the implications before use.
779
- *
855
+ * `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
780
856
  */
781
857
  signatures) {
782
- if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES')
783
- signatures = __classPrivateFieldGet(this, _Output_signersPubKeys, "f").map(pubkey => ({
784
- pubkey,
785
- // https://transactionfee.info/charts/bitcoin-script-ecdsa-length/
786
- signature: Buffer.alloc(72, 0)
787
- }));
788
858
  const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
789
859
  const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
790
860
  const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
@@ -792,12 +862,9 @@ function DescriptorsFactory(ecc) {
792
862
  expandedMiniscript === undefined ||
793
863
  expansionMap === undefined)
794
864
  throw new Error(`Error: cannot get satisfaction from not expanded miniscript ${miniscript}`);
795
- //Note that we pass the nLockTime and nSequence that is deduced
796
- //using preimages and signersPubKeys.
797
- //satisfyMiniscript will make sure
798
- //that the actual solution given, using real signatures, still meets the
799
- //same nLockTime and nSequence constraints
800
- const scriptSatisfaction = (0, miniscript_1.satisfyMiniscript)({
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)({
801
868
  expandedMiniscript,
802
869
  expansionMap,
803
870
  signatures,
@@ -806,25 +873,96 @@ function DescriptorsFactory(ecc) {
806
873
  //verify that the solutions found using the final signatures have not
807
874
  //changed
808
875
  timeConstraints: {
809
- nLockTime: this.getLockTime(),
810
- nSequence: this.getSequence()
876
+ nLockTime: constraints?.nLockTime,
877
+ nSequence: constraints?.nSequence
811
878
  }
812
- }).scriptSatisfaction;
813
- if (!scriptSatisfaction)
814
- throw new Error(`Error: could not produce a valid satisfaction`);
815
- return scriptSatisfaction;
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;
816
945
  }
817
946
  /**
818
947
  * Gets the nSequence required to fulfill this `Output`.
819
948
  */
820
949
  getSequence() {
821
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getTimeConstraints).call(this)?.nSequence;
950
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nSequence;
822
951
  }
823
952
  /**
824
953
  * Gets the nLockTime required to fulfill this `Output`.
825
954
  */
826
955
  getLockTime() {
827
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getTimeConstraints).call(this)?.nLockTime;
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;
828
966
  }
829
967
  /**
830
968
  * Gets the witnessScript required to fulfill this `Output`. Only applies to
@@ -955,10 +1093,19 @@ function DescriptorsFactory(ecc) {
955
1093
  *, Also any `addr(SINGLE_KEY_ADDRESS)` * is assumed to be a single key Taproot
956
1094
  * address (like those defined in BIP86).
957
1095
  * For inputs using arbitrary scripts (not standard addresses),
958
- * use a descriptor in the format `sh(MINISCRIPT)` or `tr(MINISCRIPT)`.
959
- * Note however that tr(MINISCRIPT) is not yet supported for non-single-key
960
- * expressions.
1096
+ * use a descriptor in the format `sh(MINISCRIPT)`, `wsh(MINISCRIPT)` or
1097
+ * `tr(KEY,TREE)` for taproot script-path expressions.
961
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.
962
1109
  inputWeight(
963
1110
  /**
964
1111
  * Indicates if the transaction is a Segwit transaction.
@@ -973,12 +1120,23 @@ function DescriptorsFactory(ecc) {
973
1120
  * Array of `PartialSig`. Each `PartialSig` includes
974
1121
  * a public key and its corresponding signature. This parameter
975
1122
  * enables the accurate calculation of signature sizes for ECDSA.
976
- * Pass 'DANGEROUSLY_USE_FAKE_SIGNATURES' to assume 72 bytes in length
977
- * for ECDSA.
978
- * Schnorr signatures are always 64 bytes.
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
979
1128
  * Mainly used for testing.
980
1129
  */
981
- signatures) {
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';
982
1140
  if (this.isSegwit() && !isSegwitTx)
983
1141
  throw new Error(`a tx is segwit if at least one input is segwit`);
984
1142
  //expand any miniscript-based descriptor. If not miniscript-based, then it's
@@ -991,13 +1149,59 @@ addr(PKH_ADDRESS), addr(WPKH_ADDRESS), addr(SH_WPKH_ADDRESS), addr(SINGLE_KEY_AD
991
1149
  expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${isTR}.`;
992
1150
  if (!expansion && !isPKH && !isWPKH && !isSH && !isTR)
993
1151
  throw new Error(errorMsg);
994
- const firstSignature = signatures && typeof signatures[0] === 'object'
995
- ? signatures[0]
996
- : 'DANGEROUSLY_USE_FAKE_SIGNATURES';
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
+ if (signatures !== 'DANGEROUSLY_USE_FAKE_SIGNATURES')
1166
+ return signatures;
1167
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this).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 taprootFakeSignatureSize = taprootSighash === 'SIGHASH_DEFAULT'
1174
+ ? TAPROOT_FAKE_SIGNATURE_SIZE
1175
+ : TAPROOT_FAKE_SIGNATURE_SIZE + 1;
1176
+ const resolveTaprootSignatures = () => {
1177
+ if (signatures !== 'DANGEROUSLY_USE_FAKE_SIGNATURES')
1178
+ return signatures;
1179
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
1180
+ pubkey,
1181
+ signature: new Uint8Array(taprootFakeSignatureSize)
1182
+ }));
1183
+ };
1184
+ const resolveTaprootSignatureSize = () => {
1185
+ let length;
1186
+ if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
1187
+ length = taprootFakeSignatureSize;
1188
+ }
1189
+ else {
1190
+ if (signatures.length !== 1)
1191
+ throw new Error('More than one signture was not expected');
1192
+ const singleSignature = signatures[0];
1193
+ if (!singleSignature)
1194
+ throw new Error('Signatures not present');
1195
+ length = singleSignature.signature.length;
1196
+ }
1197
+ return (0, varuint_bitcoin_1.encodingLength)(length) + length;
1198
+ };
1199
+ const taprootSpendPath = __classPrivateFieldGet(this, _Output_taprootSpendPath, "f");
1200
+ const tapLeaf = __classPrivateFieldGet(this, _Output_tapLeaf, "f");
997
1201
  if (expansion ? expansion.startsWith('pkh(') : isPKH) {
998
1202
  return (
999
1203
  // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (sig:73) + (pubkey:34)
1000
- (32 + 4 + 4 + 1 + signatureSize(firstSignature) + 34) * 4 +
1204
+ (32 + 4 + 4 + 1 + resolveEcdsaSignatureSize() + 34) * 4 +
1001
1205
  //Segwit:
1002
1206
  (isSegwitTx ? 1 : 0));
1003
1207
  }
@@ -1008,7 +1212,7 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1008
1212
  // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
1009
1213
  41 * 4 +
1010
1214
  // Segwit: (push_count:1) + (sig:73) + (pubkey:34)
1011
- (1 + signatureSize(firstSignature) + 34));
1215
+ (1 + resolveEcdsaSignatureSize() + 34));
1012
1216
  }
1013
1217
  else if (expansion ? expansion.startsWith('sh(wpkh(') : isSH) {
1014
1218
  if (!isSegwitTx)
@@ -1019,7 +1223,7 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1019
1223
  // -> p2wpkh: (script_len:1) + (script:22)
1020
1224
  64 * 4 +
1021
1225
  // Segwit: (push_count:1) + (sig:73) + (pubkey:34)
1022
- (1 + signatureSize(firstSignature) + 34));
1226
+ (1 + resolveEcdsaSignatureSize() + 34));
1023
1227
  }
1024
1228
  else if (expansion?.startsWith('sh(wsh(')) {
1025
1229
  if (!isSegwitTx)
@@ -1030,7 +1234,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1030
1234
  const payment = bitcoinjs_lib_1.payments.p2sh({
1031
1235
  redeem: bitcoinjs_lib_1.payments.p2wsh({
1032
1236
  redeem: {
1033
- input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
1237
+ input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1238
+ .scriptSatisfaction,
1034
1239
  output: witnessScript
1035
1240
  }
1036
1241
  })
@@ -1049,7 +1254,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1049
1254
  throw new Error('sh() must provide redeemScript');
1050
1255
  const payment = bitcoinjs_lib_1.payments.p2sh({
1051
1256
  redeem: {
1052
- input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
1257
+ input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1258
+ .scriptSatisfaction,
1053
1259
  output: redeemScript
1054
1260
  }
1055
1261
  });
@@ -1069,7 +1275,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1069
1275
  throw new Error('wsh must provide witnessScript');
1070
1276
  const payment = bitcoinjs_lib_1.payments.p2wsh({
1071
1277
  redeem: {
1072
- input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
1278
+ input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1279
+ .scriptSatisfaction,
1073
1280
  output: witnessScript
1074
1281
  }
1075
1282
  });
@@ -1080,8 +1287,24 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1080
1287
  4 * (40 + varSliceSize(payment.input)) +
1081
1288
  //Segwit
1082
1289
  vectorSize(payment.witness));
1083
- //when addr(SINGLE_KEY_ADDRESS) or tr(KEY) (single key):
1084
- //TODO: only single-key taproot outputs currently supported
1290
+ // when tr(KEY,TREE): choose key-path or script-path based on
1291
+ // constructor taprootSpendPath policy.
1292
+ }
1293
+ else if (expansion?.startsWith('tr(@0,')) {
1294
+ if (!isSegwitTx)
1295
+ throw new Error('Should be SegwitTx');
1296
+ if (taprootSpendPath === 'key')
1297
+ return 41 * 4 + (0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize();
1298
+ const resolvedTapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1299
+ if (!resolvedTapTreeInfo)
1300
+ throw new Error(`Error: taproot tree info not available`);
1301
+ const taprootSatisfaction = (0, tapMiniscript_1.satisfyTapTree)({
1302
+ tapTreeInfo: resolvedTapTreeInfo,
1303
+ preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
1304
+ signatures: resolveTaprootSignatures(),
1305
+ ...(tapLeaf !== undefined ? { tapLeaf } : {})
1306
+ });
1307
+ return 41 * 4 + taprootSatisfaction.totalWitnessSize;
1085
1308
  }
1086
1309
  else if (isTR && (!expansion || expansion === 'tr(@0)')) {
1087
1310
  if (!isSegwitTx)
@@ -1089,8 +1312,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1089
1312
  return (
1090
1313
  // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
1091
1314
  41 * 4 +
1092
- // Segwit: (push_count:1) + (sig_length(1) + schnorr_sig(64): 65)
1093
- (1 + 65));
1315
+ // Segwit: (push_count:1) + (sig_length(1) + schnorr_sig(64/65))
1316
+ ((0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize()));
1094
1317
  }
1095
1318
  else {
1096
1319
  throw new Error(errorMsg);
@@ -1129,13 +1352,6 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1129
1352
  throw new Error(errorMsg);
1130
1353
  }
1131
1354
  }
1132
- /** @deprecated - Use updatePsbtAsInput instead
1133
- * @hidden
1134
- */
1135
- updatePsbt(params) {
1136
- this.updatePsbtAsInput(params);
1137
- return params.psbt.data.inputs.length - 1;
1138
- }
1139
1355
  /**
1140
1356
  * Sets this output as an input of the provided `psbt` and updates the
1141
1357
  * `psbt` locktime if required by the descriptor.
@@ -1163,6 +1379,10 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1163
1379
  *
1164
1380
  * @returns A finalizer function to be used after signing the `psbt`.
1165
1381
  * This function ensures that this input is properly finalized.
1382
+ * The finalizer completes the PSBT input by adding the unlocking script
1383
+ * (`scriptWitness` or `scriptSig`) that satisfies this `Output`'s spending
1384
+ * conditions. Because these scripts include signatures, you should finish
1385
+ * all signing operations before calling the finalizer.
1166
1386
  * The finalizer has this signature:
1167
1387
  *
1168
1388
  * `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
@@ -1170,6 +1390,10 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1170
1390
  */
1171
1391
  updatePsbtAsInput({ psbt, txHex, txId, value, vout, //vector output index
1172
1392
  rbf = true }) {
1393
+ if (value !== undefined && typeof value !== 'bigint')
1394
+ throw new Error(`Error: value must be a bigint`);
1395
+ if (value !== undefined && value < 0n)
1396
+ throw new Error(`Error: value must be >= 0n`);
1173
1397
  if (txHex === undefined) {
1174
1398
  console.warn(`Warning: missing txHex may allow fee attacks`);
1175
1399
  }
@@ -1183,15 +1407,46 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1183
1407
  //This should only happen when using addr() expressions
1184
1408
  throw new Error(`Error: could not determine whether this is a taproot descriptor`);
1185
1409
  }
1186
- const index = (0, psbt_1.updatePsbt)({
1410
+ const paymentInternalPubkey = this.getPayment().internalPubkey;
1411
+ const tapInternalKey = isTaproot
1412
+ ? paymentInternalPubkey
1413
+ ? paymentInternalPubkey
1414
+ : undefined
1415
+ : undefined;
1416
+ let tapLeafScript;
1417
+ let tapBip32Derivation;
1418
+ if (isTaproot && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1419
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1420
+ if (!tapTreeInfo)
1421
+ throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
1422
+ if (!tapInternalKey)
1423
+ throw new Error(`Error: taprootSpendPath=script requires taproot internal key`);
1424
+ const taprootLeafMetadata = (0, tapMiniscript_1.buildTaprootLeafPsbtMetadata)({
1425
+ tapTreeInfo,
1426
+ internalPubkey: tapInternalKey
1427
+ });
1428
+ tapLeafScript = taprootLeafMetadata.map(({ leaf, controlBlock }) => ({
1429
+ script: leaf.tapScript,
1430
+ leafVersion: leaf.version,
1431
+ controlBlock
1432
+ }));
1433
+ const internalKeyInfo = __classPrivateFieldGet(this, _Output_expansionMap, "f")?.['@0'];
1434
+ if (!internalKeyInfo)
1435
+ throw new Error(`Error: taproot internal key info not available in expansionMap`);
1436
+ tapBip32Derivation = (0, tapMiniscript_1.buildTaprootBip32Derivations)({
1437
+ tapTreeInfo,
1438
+ internalKeyInfo
1439
+ });
1440
+ }
1441
+ const index = (0, psbt_1.addPsbtInput)({
1187
1442
  psbt,
1188
1443
  vout,
1189
1444
  ...(txHex !== undefined ? { txHex } : {}),
1190
1445
  ...(txId !== undefined ? { txId } : {}),
1191
1446
  ...(value !== undefined ? { value } : {}),
1192
- ...(isTaproot
1193
- ? { tapInternalKey: this.getPayment().internalPubkey }
1194
- : {}),
1447
+ tapInternalKey,
1448
+ tapLeafScript,
1449
+ tapBip32Derivation,
1195
1450
  sequence: this.getSequence(),
1196
1451
  locktime: this.getLockTime(),
1197
1452
  keysInfo: __classPrivateFieldGet(this, _Output_expansionMap, "f") ? Object.values(__classPrivateFieldGet(this, _Output_expansionMap, "f")) : [],
@@ -1201,7 +1456,64 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1201
1456
  redeemScript: this.getRedeemScript(),
1202
1457
  rbf
1203
1458
  });
1204
- const finalizer = ({ psbt, validate = true }) => this.finalizePsbtInput({ index, psbt, validate });
1459
+ //The finalizer adds the unlocking script (scriptSig/scriptWitness) once
1460
+ //signatures are ready.
1461
+ const finalizer = ({ psbt, validate = true }) => {
1462
+ if (validate &&
1463
+ !psbt.validateSignaturesOfInput(index, signatureValidator)) {
1464
+ throw new Error(`Error: invalid signatures on input ${index}`);
1465
+ }
1466
+ //An index must be passed since finding the index in the psbt cannot be
1467
+ //done:
1468
+ //Imagine the case where you received money twice to
1469
+ //the same miniscript-based address. You would have the same scriptPubKey,
1470
+ //same sequences, ... The descriptor does not store the hash of the previous
1471
+ //transaction since it is a general Output instance. Indices must be kept
1472
+ //out of the scope of this class and then passed.
1473
+ __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertPsbtInput).call(this, { index, psbt });
1474
+ if (__classPrivateFieldGet(this, _Output_isTaproot, "f") &&
1475
+ __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script' &&
1476
+ !__classPrivateFieldGet(this, _Output_tapTreeInfo, "f"))
1477
+ throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
1478
+ if (__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1479
+ const input = psbt.data.inputs[index];
1480
+ const tapLeafScript = input?.tapLeafScript;
1481
+ if (!tapLeafScript || tapLeafScript.length === 0)
1482
+ throw new Error(`Error: cannot finalize taproot script-path without tapLeafScript`);
1483
+ const tapScriptSig = input?.tapScriptSig;
1484
+ if (!tapScriptSig || tapScriptSig.length === 0)
1485
+ throw new Error(`Error: cannot finalize taproot script-path without tapScriptSig`);
1486
+ const taprootSatisfaction = this.getTapScriptSatisfaction(tapScriptSig);
1487
+ const matchingLeaf = tapLeafScript.find(leafScript => (0, uint8array_tools_1.compare)((0, bitcoinjs_lib_internals_1.tapleafHash)({
1488
+ output: leafScript.script,
1489
+ version: leafScript.leafVersion
1490
+ }), taprootSatisfaction.tapLeafHash) === 0);
1491
+ if (!matchingLeaf)
1492
+ throw new Error(`Error: tapLeafScript does not match planned tapLeafHash`);
1493
+ if ((0, uint8array_tools_1.compare)(matchingLeaf.script, taprootSatisfaction.leaf.tapScript) !==
1494
+ 0 ||
1495
+ matchingLeaf.leafVersion !== taprootSatisfaction.leaf.version)
1496
+ throw new Error(`Error: tapLeafScript does not match planned leaf script`);
1497
+ const witness = [
1498
+ ...taprootSatisfaction.stackItems,
1499
+ matchingLeaf.script,
1500
+ matchingLeaf.controlBlock
1501
+ ];
1502
+ const finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(witness);
1503
+ psbt.finalizeTaprootInput(index, taprootSatisfaction.tapLeafHash, () => ({ finalScriptWitness }));
1504
+ }
1505
+ else if (!__classPrivateFieldGet(this, _Output_miniscript, "f")) {
1506
+ //Use standard finalizers
1507
+ psbt.finalizeInput(index);
1508
+ }
1509
+ else {
1510
+ const signatures = psbt.data.inputs[index]?.partialSig;
1511
+ if (!signatures)
1512
+ throw new Error(`Error: cannot finalize without signatures`);
1513
+ const { scriptSatisfaction } = this.getScriptSatisfaction(signatures);
1514
+ psbt.finalizeInput(index, (0, psbt_1.finalScriptsFuncFactory)(scriptSatisfaction, __classPrivateFieldGet(this, _Output_network, "f")));
1515
+ }
1516
+ };
1205
1517
  return finalizer;
1206
1518
  }
1207
1519
  /**
@@ -1212,63 +1524,12 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1212
1524
  * @param params.value - The value for the output in satoshis.
1213
1525
  */
1214
1526
  updatePsbtAsOutput({ psbt, value }) {
1527
+ if (typeof value !== 'bigint')
1528
+ throw new Error(`Error: value must be a bigint`);
1529
+ if (value < 0n)
1530
+ throw new Error(`Error: value must be >= 0n`);
1215
1531
  psbt.addOutput({ script: this.getScriptPubKey(), value });
1216
1532
  }
1217
- /**
1218
- * Finalizes a PSBT input by adding the necessary unlocking script that satisfies this `Output`'s
1219
- * spending conditions.
1220
- *
1221
- * 🔴 IMPORTANT 🔴
1222
- * It is STRONGLY RECOMMENDED to use the finalizer function returned by
1223
- * {@link _Internal_.Output.updatePsbtAsInput | `updatePsbtAsInput`} instead
1224
- * of calling this method directly.
1225
- * This approach eliminates the need to manage the `Output` instance and the
1226
- * input's index, simplifying the process.
1227
- *
1228
- * The `finalizePsbtInput` method completes a PSBT input by adding the
1229
- * unlocking script (`scriptWitness` or `scriptSig`) that satisfies
1230
- * this `Output`'s spending conditions. Bear in mind that both
1231
- * `scriptSig` and `scriptWitness` incorporate signatures. As such, you
1232
- * should complete all necessary signing operations before calling this
1233
- * method.
1234
- *
1235
- * For each unspent output from a previous transaction that you're
1236
- * referencing in a `psbt` as an input to be spent, apply this method as
1237
- * follows: `output.finalizePsbtInput({ index, psbt })`.
1238
- *
1239
- * It's essential to specify the exact position (or `index`) of the input in
1240
- * the `psbt` that references this unspent `Output`. This `index` should
1241
- * align with the value returned by the `updatePsbtAsInput` method.
1242
- * Note:
1243
- * The `index` corresponds to the position of the input in the `psbt`.
1244
- * To get this index, right after calling `updatePsbtAsInput()`, use:
1245
- * `index = psbt.data.inputs.length - 1`.
1246
- */
1247
- finalizePsbtInput({ index, psbt, validate = true }) {
1248
- if (validate &&
1249
- !psbt.validateSignaturesOfInput(index, signatureValidator)) {
1250
- throw new Error(`Error: invalid signatures on input ${index}`);
1251
- }
1252
- //An index must be passed since finding the index in the psbt cannot be
1253
- //done:
1254
- //Imagine the case where you received money twice to
1255
- //the same miniscript-based address. You would have the same scriptPubKey,
1256
- //same sequences, ... The descriptor does not store the hash of the previous
1257
- //transaction since it is a general Descriptor object. Indices must be kept
1258
- //out of the scope of this class and then passed.
1259
- __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertPsbtInput).call(this, { index, psbt });
1260
- if (!__classPrivateFieldGet(this, _Output_miniscript, "f")) {
1261
- //Use standard finalizers
1262
- psbt.finalizeInput(index);
1263
- }
1264
- else {
1265
- const signatures = psbt.data.inputs[index]?.partialSig;
1266
- if (!signatures)
1267
- throw new Error(`Error: cannot finalize without signatures`);
1268
- const scriptSatisfaction = this.getScriptSatisfaction(signatures);
1269
- psbt.finalizeInput(index, (0, psbt_1.finalScriptsFuncFactory)(scriptSatisfaction, __classPrivateFieldGet(this, _Output_network, "f")));
1270
- }
1271
- }
1272
1533
  /**
1273
1534
  * Decomposes the descriptor used to form this `Output` into its elemental
1274
1535
  * parts. See {@link ExpansionMap ExpansionMap} for a detailed explanation.
@@ -1284,18 +1545,80 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1284
1545
  ...(__classPrivateFieldGet(this, _Output_expandedMiniscript, "f") !== undefined
1285
1546
  ? { expandedMiniscript: __classPrivateFieldGet(this, _Output_expandedMiniscript, "f") }
1286
1547
  : {}),
1548
+ ...(__classPrivateFieldGet(this, _Output_tapTreeExpression, "f") !== undefined
1549
+ ? { tapTreeExpression: __classPrivateFieldGet(this, _Output_tapTreeExpression, "f") }
1550
+ : {}),
1551
+ ...(__classPrivateFieldGet(this, _Output_tapTree, "f") !== undefined ? { tapTree: __classPrivateFieldGet(this, _Output_tapTree, "f") } : {}),
1552
+ ...(__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") !== undefined
1553
+ ? { tapTreeInfo: __classPrivateFieldGet(this, _Output_tapTreeInfo, "f") }
1554
+ : {}),
1287
1555
  ...(__classPrivateFieldGet(this, _Output_expansionMap, "f") !== undefined
1288
1556
  ? { expansionMap: __classPrivateFieldGet(this, _Output_expansionMap, "f") }
1289
1557
  : {})
1290
1558
  };
1291
1559
  }
1292
1560
  }
1293
- _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_expansionMap = new WeakMap(), _Output_network = new WeakMap(), _Output_instances = new WeakSet(), _Output_getTimeConstraints = function _Output_getTimeConstraints() {
1561
+ _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() {
1562
+ //If the user did not provide a pubkey subset (signersPubKeys), assume all
1563
+ //miniscript pubkeys can sign.
1564
+ if (__classPrivateFieldGet(this, _Output_signersPubKeys, "f"))
1565
+ return __classPrivateFieldGet(this, _Output_signersPubKeys, "f");
1566
+ const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
1567
+ if (!expansionMap)
1568
+ throw new Error(`Error: expansionMap not available for miniscript`);
1569
+ return Object.values(expansionMap).map(keyInfo => {
1570
+ const pubkey = keyInfo.pubkey;
1571
+ if (!pubkey)
1572
+ throw new Error(`Error: miniscript key missing pubkey`);
1573
+ return pubkey;
1574
+ });
1575
+ }, _Output_assertMiniscriptSatisfactionResourceLimits = function _Output_assertMiniscriptSatisfactionResourceLimits(scriptSatisfaction) {
1576
+ if (!__classPrivateFieldGet(this, _Output_miniscript, "f"))
1577
+ return;
1578
+ const satisfactionStackItems = bitcoinjs_lib_1.script.toStack(scriptSatisfaction);
1579
+ // For wsh(...) and sh(wsh(...)), enforce witness stack limits.
1580
+ if (__classPrivateFieldGet(this, _Output_isSegwit, "f") && !__classPrivateFieldGet(this, _Output_isTaproot, "f")) {
1581
+ (0, resourceLimits_1.assertWitnessV0SatisfactionResourceLimits)({
1582
+ stackItems: satisfactionStackItems
1583
+ });
1584
+ return;
1585
+ }
1586
+ if (!__classPrivateFieldGet(this, _Output_isSegwit, "f")) {
1587
+ // In legacy P2SH, after scriptSig execution, the stack is:
1588
+ // [ ...satisfactionStackItems, redeemScript ]
1589
+ // Consensus limits apply here: each element must be <= 520 bytes and total
1590
+ // stack items must be <= 1000.
1591
+ // redeemScript size is already validated during script construction
1592
+ // to fail early (look for MAX_SCRIPT_ELEMENT_SIZE checks in this file).
1593
+ // Below we re-validate again the redeemScript + the rest of stack items.
1594
+ const redeemScript = __classPrivateFieldGet(this, _Output_redeemScript, "f");
1595
+ if (!redeemScript)
1596
+ throw new Error(`Error: redeemScript not available for P2SH spend`);
1597
+ (0, resourceLimits_1.assertConsensusStackResourceLimits)({
1598
+ stackItems: [...satisfactionStackItems, redeemScript]
1599
+ });
1600
+ (0, resourceLimits_1.assertP2shScriptSigStandardSize)({
1601
+ scriptSatisfaction,
1602
+ redeemScript,
1603
+ network: __classPrivateFieldGet(this, _Output_network, "f")
1604
+ });
1605
+ }
1606
+ }, _Output_resolveTapTreeSignersPubKeys = function _Output_resolveTapTreeSignersPubKeys() {
1607
+ //If the user did not provide a pubkey subset (signersPubKeys), assume all
1608
+ //taproot leaf pubkeys can sign.
1609
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1610
+ if (!tapTreeInfo)
1611
+ throw new Error(`Error: taproot tree info not available`);
1612
+ const candidatePubkeys = __classPrivateFieldGet(this, _Output_signersPubKeys, "f")
1613
+ ? __classPrivateFieldGet(this, _Output_signersPubKeys, "f").map(tapMiniscript_1.normalizeTaprootPubkey)
1614
+ : (0, tapMiniscript_1.collectTapTreePubkeys)(tapTreeInfo);
1615
+ return Array.from(new Set(candidatePubkeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)))).map(hex => (0, uint8array_tools_1.fromHex)(hex));
1616
+ }, _Output_getConstraints = function _Output_getConstraints() {
1294
1617
  const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
1295
1618
  const preimages = __classPrivateFieldGet(this, _Output_preimages, "f");
1296
1619
  const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
1297
1620
  const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
1298
- const signersPubKeys = __classPrivateFieldGet(this, _Output_signersPubKeys, "f");
1621
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1299
1622
  //Create a method. solvePreimages to solve them.
1300
1623
  if (miniscript) {
1301
1624
  if (expandedMiniscript === undefined || expansionMap === undefined)
@@ -1303,21 +1626,34 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1303
1626
  //We create some fakeSignatures since we may not have them yet.
1304
1627
  //We only want to retrieve the nLockTime and nSequence of the satisfaction and
1305
1628
  //signatures don't matter
1306
- const fakeSignatures = signersPubKeys.map(pubkey => ({
1629
+ const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this).map(pubkey => ({
1307
1630
  pubkey,
1308
- // https://transactionfee.info/charts/bitcoin-script-ecdsa-length/
1309
- signature: Buffer.alloc(72, 0)
1631
+ signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
1310
1632
  }));
1311
- const { nLockTime, nSequence } = (0, miniscript_1.satisfyMiniscript)({
1633
+ const satisfaction = (0, miniscript_1.satisfyMiniscript)({
1312
1634
  expandedMiniscript,
1313
1635
  expansionMap,
1314
1636
  signatures: fakeSignatures,
1315
1637
  preimages
1316
1638
  });
1317
- return { nLockTime, nSequence };
1639
+ __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
1640
+ const { nLockTime, nSequence } = satisfaction;
1641
+ return { nLockTime, nSequence, tapLeafHash: undefined };
1318
1642
  }
1319
- else
1320
- return undefined;
1643
+ else if (tapTreeInfo && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1644
+ const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
1645
+ pubkey,
1646
+ signature: new Uint8Array(TAPROOT_FAKE_SIGNATURE_SIZE)
1647
+ }));
1648
+ const { nLockTime, nSequence, tapLeafHash } = (0, tapMiniscript_1.satisfyTapTree)({
1649
+ tapTreeInfo,
1650
+ preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
1651
+ signatures: fakeSignatures,
1652
+ ...(__classPrivateFieldGet(this, _Output_tapLeaf, "f") !== undefined ? { tapLeaf: __classPrivateFieldGet(this, _Output_tapLeaf, "f") } : {})
1653
+ });
1654
+ return { nLockTime, nSequence, tapLeafHash };
1655
+ }
1656
+ return undefined;
1321
1657
  }, _Output_assertPsbtInput = function _Output_assertPsbtInput({ psbt, index }) {
1322
1658
  const input = psbt.data.inputs[index];
1323
1659
  const txInput = psbt.txInputs[index];
@@ -1346,29 +1682,18 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1346
1682
  ? 0xffffffff
1347
1683
  : 0xfffffffe;
1348
1684
  const sequenceRBF = sequence !== undefined ? sequence : 0xfffffffd;
1349
- const eqBuffers = (buf1, buf2) => buf1 instanceof Buffer && buf2 instanceof Buffer
1350
- ? Buffer.compare(buf1, buf2) === 0
1351
- : buf1 === buf2;
1352
- if (Buffer.compare(scriptPubKey, this.getScriptPubKey()) !== 0 ||
1685
+ const eqBytes = (bytes1, bytes2) => bytes1 === undefined || bytes2 === undefined
1686
+ ? bytes1 === bytes2
1687
+ : (0, uint8array_tools_1.compare)(bytes1, bytes2) === 0;
1688
+ if ((0, uint8array_tools_1.compare)(scriptPubKey, this.getScriptPubKey()) !== 0 ||
1353
1689
  (sequenceRBF !== inputSequence && sequenceNoRBF !== inputSequence) ||
1354
1690
  locktime !== psbt.locktime ||
1355
- !eqBuffers(this.getWitnessScript(), input.witnessScript) ||
1356
- !eqBuffers(this.getRedeemScript(), input.redeemScript)) {
1691
+ !eqBytes(this.getWitnessScript(), input.witnessScript) ||
1692
+ !eqBytes(this.getRedeemScript(), input.redeemScript)) {
1357
1693
  throw new Error(`Error: cannot finalize psbt index ${index} since it does not correspond to this descriptor`);
1358
1694
  }
1359
1695
  };
1360
- /**
1361
- * @hidden
1362
- * @deprecated Use `Output` instead
1363
- */
1364
- class Descriptor extends Output {
1365
- constructor({ expression, ...rest }) {
1366
- super({ descriptor: expression, ...rest });
1367
- }
1368
- }
1369
1696
  return {
1370
- // deprecated TAG must also be below so it is exported to descriptors.d.ts
1371
- /** @deprecated @hidden */ Descriptor,
1372
1697
  Output,
1373
1698
  parseKeyExpression,
1374
1699
  expand,