@bitcoinerlab/descriptors 2.3.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,21 @@ 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
+ function parseTrExpression(expression) {
143
+ if (!expression.startsWith('tr(') || !expression.endsWith(')'))
144
+ throw new Error(`Error: invalid descriptor ${expression}`);
145
+ const innerExpression = expression.slice(3, -1).trim();
146
+ if (!innerExpression)
147
+ throw new Error(`Error: invalid descriptor ${expression}`);
148
+ const splitResult = (0, parseUtils_1.splitTopLevelComma)({
149
+ expression: innerExpression,
150
+ onError: () => new Error(`Error: invalid descriptor ${expression}`)
151
+ });
152
+ //if no commas: innerExpression === keyExpression
153
+ if (!splitResult)
154
+ return { keyExpression: innerExpression };
155
+ return { keyExpression: splitResult.left, treeExpression: splitResult.right };
156
+ }
151
157
  /**
152
158
  * Constructs the necessary functions and classes for working with descriptors
153
159
  * using an external elliptic curve (ecc) library.
@@ -156,12 +162,8 @@ function parseSortedMulti(inner) {
156
162
  * provides methods to create, sign, and finalize PSBTs based on descriptor
157
163
  * expressions.
158
164
  *
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
165
  * The Factory also returns utility methods like `expand` (detailed below)
164
- * and `parseKeyExpression` (see {@link ParseKeyExpression}).
166
+ * and `parseKeyExpression` (see {@link KeyExpressionParser}).
165
167
  *
166
168
  * Additionally, for convenience, the function returns `BIP32` and `ECPair`.
167
169
  * These are {@link https://github.com/bitcoinjs bitcoinjs-lib} classes designed
@@ -174,7 +176,7 @@ function parseSortedMulti(inner) {
174
176
  * [@bitcoinerlab/secp256k1](https://github.com/bitcoinerlab/secp256k1).
175
177
  */
176
178
  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;
179
+ 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
180
  (0, bitcoinjs_lib_1.initEccLib)(ecc); //Taproot requires initEccLib
179
181
  const BIP32 = (0, bip32_1.BIP32Factory)(ecc);
180
182
  const ECPair = (0, ecpair_1.ECPairFactory)(ecc);
@@ -203,15 +205,7 @@ function DescriptorsFactory(ecc) {
203
205
  BIP32
204
206
  });
205
207
  };
206
- /**
207
- * @overload
208
- * To be removed in v3.0 and replaced by the version with the signature that
209
- * does not accept descriptors
210
- */
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;
208
+ function expand({ descriptor, index, change, checksumRequired = false, network = bitcoinjs_lib_1.networks.bitcoin, allowMiniscriptInP2SH = false }) {
215
209
  if (!descriptor)
216
210
  throw new Error(`descriptor not provided`);
217
211
  let expandedExpression;
@@ -220,6 +214,9 @@ function DescriptorsFactory(ecc) {
220
214
  let isSegwit;
221
215
  let isTaproot;
222
216
  let expandedMiniscript;
217
+ let tapTreeExpression;
218
+ let tapTree;
219
+ let tapTreeInfo;
223
220
  let payment;
224
221
  let witnessScript;
225
222
  let redeemScript;
@@ -232,6 +229,7 @@ function DescriptorsFactory(ecc) {
232
229
  const canonicalExpression = evaluate({
233
230
  descriptor,
234
231
  ...(index !== undefined ? { index } : {}),
232
+ ...(change !== undefined ? { change } : {}),
235
233
  checksumRequired
236
234
  });
237
235
  const isCanonicalRanged = canonicalExpression.indexOf('*') !== -1;
@@ -398,7 +396,7 @@ function DescriptorsFactory(ecc) {
398
396
  throw new Error(`Error: key has no pubkey`);
399
397
  return p.pubkey;
400
398
  });
401
- pubkeys.sort((a, b) => a.compare(b));
399
+ pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
402
400
  const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
403
401
  redeemScript = redeem.output;
404
402
  if (!redeemScript)
@@ -428,7 +426,7 @@ function DescriptorsFactory(ecc) {
428
426
  throw new Error(`Error: key has no pubkey`);
429
427
  return p.pubkey;
430
428
  });
431
- pubkeys.sort((a, b) => a.compare(b));
429
+ pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
432
430
  const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
433
431
  witnessScript = redeem.output;
434
432
  if (!witnessScript)
@@ -458,7 +456,7 @@ function DescriptorsFactory(ecc) {
458
456
  throw new Error(`Error: key has no pubkey`);
459
457
  return p.pubkey;
460
458
  });
461
- pubkeys.sort((a, b) => a.compare(b));
459
+ pubkeys.sort((a, b) => (0, uint8array_tools_1.compare)(a, b));
462
460
  const redeem = bitcoinjs_lib_1.payments.p2ms({ m, pubkeys, network });
463
461
  const wsh = bitcoinjs_lib_1.payments.p2wsh({ redeem, network });
464
462
  witnessScript = redeem.output;
@@ -482,13 +480,10 @@ function DescriptorsFactory(ecc) {
482
480
  if (!isCanonicalRanged) {
483
481
  const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
484
482
  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}`);
483
+ if (script.byteLength > resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
484
+ throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
491
485
  }
486
+ (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
492
487
  payment = p2sh({
493
488
  redeem: p2wsh({ redeem: { output: script, network }, network }),
494
489
  network
@@ -523,13 +518,10 @@ function DescriptorsFactory(ecc) {
523
518
  if (!isCanonicalRanged) {
524
519
  const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
525
520
  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}`);
521
+ if (script.byteLength > resourceLimits_1.MAX_SCRIPT_ELEMENT_SIZE) {
522
+ throw new Error(`Error: P2SH script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_SCRIPT_ELEMENT_SIZE} bytes`);
532
523
  }
524
+ (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
533
525
  payment = p2sh({ redeem: { output: script, network }, network });
534
526
  }
535
527
  }
@@ -549,26 +541,21 @@ function DescriptorsFactory(ecc) {
549
541
  if (!isCanonicalRanged) {
550
542
  const script = (0, miniscript_1.miniscript2Script)({ expandedMiniscript, expansionMap });
551
543
  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}`);
544
+ if (script.byteLength > resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE) {
545
+ throw new Error(`Error: script is too large, ${script.byteLength} bytes is larger than ${resourceLimits_1.MAX_STANDARD_P2WSH_SCRIPT_SIZE} bytes`);
558
546
  }
547
+ (0, resourceLimits_1.assertScriptNonPushOnlyOpsLimit)({ script });
559
548
  payment = p2wsh({ redeem: { output: script, network }, network });
560
549
  }
561
550
  }
562
- //tr(KEY) - taproot - TODO: tr(KEY,TREE) not yet supported
563
- else if (canonicalExpression.match(RE.reTrSingleKeyAnchored)) {
551
+ //tr(KEY) or tr(KEY,TREE) - taproot
552
+ else if (canonicalExpression.startsWith('tr(')) {
564
553
  isSegwit = true;
565
554
  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)';
555
+ const { keyExpression, treeExpression } = parseTrExpression(canonicalExpression);
556
+ expandedExpression = treeExpression
557
+ ? `tr(@0,${treeExpression})`
558
+ : 'tr(@0)';
572
559
  const pKE = parseKeyExpression({
573
560
  keyExpression,
574
561
  network,
@@ -576,17 +563,30 @@ function DescriptorsFactory(ecc) {
576
563
  isTaproot
577
564
  });
578
565
  expansionMap = { '@0': pKE };
566
+ if (treeExpression) {
567
+ tapTreeExpression = treeExpression;
568
+ tapTree = (0, tapTree_1.parseTapTreeExpression)(treeExpression);
569
+ if (!isCanonicalRanged) {
570
+ tapTreeInfo = (0, tapMiniscript_1.buildTapTreeInfo)({ tapTree, network, BIP32, ECPair });
571
+ }
572
+ }
579
573
  if (!isCanonicalRanged) {
580
574
  const pubkey = pKE.pubkey;
581
575
  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
- //});
576
+ throw new Error(`Error: could not extract a pubkey from ${descriptor}`);
577
+ const internalPubkey = (0, tapMiniscript_1.normalizeTaprootPubkey)(pubkey);
578
+ if (treeExpression) {
579
+ if (!tapTreeInfo)
580
+ throw new Error(`Error: taproot tree info not available`);
581
+ payment = p2tr({
582
+ internalPubkey,
583
+ scriptTree: (0, tapMiniscript_1.tapTreeInfoToScriptTree)(tapTreeInfo),
584
+ network
585
+ });
586
+ }
587
+ else {
588
+ payment = p2tr({ internalPubkey, network });
589
+ }
590
590
  }
591
591
  }
592
592
  else {
@@ -600,6 +600,9 @@ function DescriptorsFactory(ecc) {
600
600
  ...(isSegwit !== undefined ? { isSegwit } : {}),
601
601
  ...(isTaproot !== undefined ? { isTaproot } : {}),
602
602
  ...(expandedMiniscript !== undefined ? { expandedMiniscript } : {}),
603
+ ...(tapTreeExpression !== undefined ? { tapTreeExpression } : {}),
604
+ ...(tapTree !== undefined ? { tapTree } : {}),
605
+ ...(tapTreeInfo !== undefined ? { tapTreeInfo } : {}),
603
606
  ...(redeemScript !== undefined ? { redeemScript } : {}),
604
607
  ...(witnessScript !== undefined ? { witnessScript } : {}),
605
608
  isRanged,
@@ -634,7 +637,7 @@ function DescriptorsFactory(ecc) {
634
637
  * @param options
635
638
  * @throws {Error} - when descriptor is invalid
636
639
  */
637
- constructor({ descriptor, index, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys }) {
640
+ constructor({ descriptor, index, change, checksumRequired = false, allowMiniscriptInP2SH = false, network = bitcoinjs_lib_1.networks.bitcoin, preimages = [], signersPubKeys, taprootSpendPath, tapLeaf }) {
638
641
  _Output_instances.add(this);
639
642
  _Output_payment.set(this, void 0);
640
643
  _Output_preimages.set(this, []);
@@ -648,6 +651,11 @@ function DescriptorsFactory(ecc) {
648
651
  _Output_isTaproot.set(this, void 0);
649
652
  _Output_expandedExpression.set(this, void 0);
650
653
  _Output_expandedMiniscript.set(this, void 0);
654
+ _Output_tapTreeExpression.set(this, void 0);
655
+ _Output_tapTree.set(this, void 0);
656
+ _Output_tapTreeInfo.set(this, void 0);
657
+ _Output_taprootSpendPath.set(this, void 0);
658
+ _Output_tapLeaf.set(this, void 0);
651
659
  _Output_expansionMap.set(this, void 0);
652
660
  _Output_network.set(this, void 0);
653
661
  __classPrivateFieldSet(this, _Output_network, network, "f");
@@ -657,10 +665,26 @@ function DescriptorsFactory(ecc) {
657
665
  const expandedResult = expand({
658
666
  descriptor,
659
667
  ...(index !== undefined ? { index } : {}),
668
+ ...(change !== undefined ? { change } : {}),
660
669
  checksumRequired,
661
670
  network,
662
671
  allowMiniscriptInP2SH
663
672
  });
673
+ const isTaprootDescriptor = expandedResult.isTaproot === true;
674
+ const hasTapTree = expandedResult.expandedExpression?.startsWith('tr(@0,') ?? false;
675
+ const resolvedTaprootSpendPath = taprootSpendPath ?? (hasTapTree ? 'script' : 'key');
676
+ if (!isTaprootDescriptor) {
677
+ if (taprootSpendPath !== undefined || tapLeaf !== undefined)
678
+ throw new Error(`Error: taprootSpendPath/tapLeaf require a taproot descriptor`);
679
+ }
680
+ else {
681
+ if (taprootSpendPath === 'script' && !hasTapTree)
682
+ throw new Error(`Error: taprootSpendPath=script requires a tr(KEY,TREE) descriptor`);
683
+ if (resolvedTaprootSpendPath === 'key' && tapLeaf !== undefined)
684
+ throw new Error(`Error: tapLeaf cannot be used when taprootSpendPath is key`);
685
+ if (tapLeaf !== undefined && !hasTapTree)
686
+ throw new Error(`Error: tapLeaf can only be used with tr(KEY,TREE) descriptors`);
687
+ }
664
688
  if (expandedResult.isRanged && index === undefined)
665
689
  throw new Error(`Error: index was not provided for ranged descriptor`);
666
690
  if (!expandedResult.payment)
@@ -678,83 +702,55 @@ function DescriptorsFactory(ecc) {
678
702
  __classPrivateFieldSet(this, _Output_isTaproot, expandedResult.isTaproot, "f");
679
703
  if (expandedResult.expandedMiniscript !== undefined)
680
704
  __classPrivateFieldSet(this, _Output_expandedMiniscript, expandedResult.expandedMiniscript, "f");
705
+ if (expandedResult.tapTreeExpression !== undefined)
706
+ __classPrivateFieldSet(this, _Output_tapTreeExpression, expandedResult.tapTreeExpression, "f");
707
+ if (expandedResult.tapTree !== undefined)
708
+ __classPrivateFieldSet(this, _Output_tapTree, expandedResult.tapTree, "f");
709
+ if (expandedResult.tapTreeInfo !== undefined)
710
+ __classPrivateFieldSet(this, _Output_tapTreeInfo, expandedResult.tapTreeInfo, "f");
681
711
  if (expandedResult.redeemScript !== undefined)
682
712
  __classPrivateFieldSet(this, _Output_redeemScript, expandedResult.redeemScript, "f");
683
713
  if (expandedResult.witnessScript !== undefined)
684
714
  __classPrivateFieldSet(this, _Output_witnessScript, expandedResult.witnessScript, "f");
685
- if (signersPubKeys) {
715
+ if (signersPubKeys)
686
716
  __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
- }
717
+ __classPrivateFieldSet(this, _Output_taprootSpendPath, resolvedTaprootSpendPath, "f");
718
+ if (tapLeaf !== undefined)
719
+ __classPrivateFieldSet(this, _Output_tapLeaf, tapLeaf, "f");
705
720
  this.getSequence = (0, lodash_memoize_1.default)(this.getSequence);
706
721
  this.getLockTime = (0, lodash_memoize_1.default)(this.getLockTime);
707
722
  const getSignaturesKey = (signatures) => signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'
708
723
  ? signatures
709
724
  : signatures
710
- .map(s => `${s.pubkey.toString('hex')}-${s.signature.toString('hex')}`)
725
+ .map(s => `${(0, uint8array_tools_1.toHex)(s.pubkey)}-${(0, uint8array_tools_1.toHex)(s.signature)}`)
711
726
  .join('|');
712
- this.getScriptSatisfaction = (0, lodash_memoize_1.default)(this.getScriptSatisfaction,
713
- // resolver function:
714
- getSignaturesKey);
715
727
  this.guessOutput = (0, lodash_memoize_1.default)(this.guessOutput);
716
728
  this.inputWeight = (0, lodash_memoize_1.default)(this.inputWeight,
717
729
  // resolver function:
718
- (isSegwitTx, signatures) => {
730
+ (isSegwitTx, signatures, options) => {
719
731
  const segwitKey = isSegwitTx ? 'segwit' : 'non-segwit';
720
732
  const signaturesKey = getSignaturesKey(signatures);
721
- return `${segwitKey}-${signaturesKey}`;
733
+ const taprootSighashKey = options?.taprootSighash ?? 'SIGHASH_DEFAULT';
734
+ return `${segwitKey}-${signaturesKey}-taprootSighash:${taprootSighashKey}`;
722
735
  });
723
736
  this.outputWeight = (0, lodash_memoize_1.default)(this.outputWeight);
724
737
  }
725
738
  /**
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.
739
+ * Returns the compiled Script Satisfaction for a miniscript-based Output.
740
+ * The satisfaction is the unlocking script, derived by the Satisfier
741
+ * algorithm (https://bitcoin.sipa.be/miniscript/).
751
742
  *
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/).
743
+ * This method uses a two-pass flow:
744
+ * 1) Planning: constraints (nLockTime/nSequence) are computed using fake
745
+ * signatures. This is done since the final solution may not need all the
746
+ * signatures in signersPubKeys. And we may avoid the user do extra
747
+ * signing (tedious op with HWW).
748
+ * 2) Signing: the provided signatures are used to build the final
749
+ * satisfaction, while enforcing the planned constraints so the same
750
+ * solution is selected. Not all the signatures of signersPubKeys may
751
+ * be required.
755
752
  *
756
- * Important: As mentioned above, note that this function only applies to
757
- * miniscript descriptors.
753
+ * The return value includes the satisfaction script and the constraints.
758
754
  */
759
755
  getScriptSatisfaction(
760
756
  /**
@@ -762,29 +758,9 @@ function DescriptorsFactory(ecc) {
762
758
  * build the Satisfaction of this miniscript-based `Output`.
763
759
  *
764
760
  * `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
- *
761
+ * `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
780
762
  */
781
763
  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
764
  const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
789
765
  const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
790
766
  const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
@@ -792,12 +768,9 @@ function DescriptorsFactory(ecc) {
792
768
  expandedMiniscript === undefined ||
793
769
  expansionMap === undefined)
794
770
  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)({
771
+ //This crates the plans using fake signatures
772
+ const constraints = __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this);
773
+ const satisfaction = (0, miniscript_1.satisfyMiniscript)({
801
774
  expandedMiniscript,
802
775
  expansionMap,
803
776
  signatures,
@@ -806,25 +779,96 @@ function DescriptorsFactory(ecc) {
806
779
  //verify that the solutions found using the final signatures have not
807
780
  //changed
808
781
  timeConstraints: {
809
- nLockTime: this.getLockTime(),
810
- nSequence: this.getSequence()
782
+ nLockTime: constraints?.nLockTime,
783
+ nSequence: constraints?.nSequence
811
784
  }
812
- }).scriptSatisfaction;
813
- if (!scriptSatisfaction)
814
- throw new Error(`Error: could not produce a valid satisfaction`);
815
- return scriptSatisfaction;
785
+ });
786
+ __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
787
+ return satisfaction;
788
+ }
789
+ /**
790
+ * Returns the taproot script‑path satisfaction for a tap miniscript
791
+ * descriptor. This mirrors {@link getScriptSatisfaction} and uses the same
792
+ * two‑pass plan/sign flow.
793
+ *
794
+ * In addition to nLockTime/nSequence, it returns the selected tapLeafHash
795
+ * (the leaf chosen during planning) and the leaf’s tapscript.
796
+ */
797
+ getTapScriptSatisfaction(
798
+ /**
799
+ * An array with all the signatures needed to
800
+ * build the Satisfaction of this miniscript-based `Output`.
801
+ *
802
+ * `signatures` must be passed using this format (pairs of `pubKey/signature`):
803
+ * `interface PartialSig { pubkey: Uint8Array; signature: Uint8Array; }`
804
+ */
805
+ signatures) {
806
+ if (__classPrivateFieldGet(this, _Output_taprootSpendPath, "f") !== 'script')
807
+ throw new Error(`Error: taprootSpendPath is key; script-path satisfaction is not allowed`);
808
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
809
+ if (!tapTreeInfo)
810
+ throw new Error(`Error: taproot tree info not available`);
811
+ const constraints = __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this);
812
+ return (0, tapMiniscript_1.satisfyTapTree)({
813
+ tapTreeInfo,
814
+ preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
815
+ signatures,
816
+ ...(constraints?.tapLeafHash
817
+ ? { tapLeaf: constraints.tapLeafHash }
818
+ : {}),
819
+ ...(constraints
820
+ ? {
821
+ timeConstraints: {
822
+ nLockTime: constraints.nLockTime,
823
+ nSequence: constraints.nSequence
824
+ }
825
+ }
826
+ : {})
827
+ });
828
+ }
829
+ /**
830
+ * Creates and returns an instance of bitcoinjs-lib
831
+ * [`Payment`](https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/ts_src/payments/index.ts)'s interface with the `scriptPubKey` of this `Output`.
832
+ */
833
+ getPayment() {
834
+ return __classPrivateFieldGet(this, _Output_payment, "f");
835
+ }
836
+ /**
837
+ * Returns the Bitcoin Address of this `Output`.
838
+ */
839
+ getAddress() {
840
+ if (!__classPrivateFieldGet(this, _Output_payment, "f").address)
841
+ throw new Error(`Error: could extract an address from the payment`);
842
+ return __classPrivateFieldGet(this, _Output_payment, "f").address;
843
+ }
844
+ /**
845
+ * Returns this `Output`'s scriptPubKey.
846
+ */
847
+ getScriptPubKey() {
848
+ if (!__classPrivateFieldGet(this, _Output_payment, "f").output)
849
+ throw new Error(`Error: could extract output.script from the payment`);
850
+ return __classPrivateFieldGet(this, _Output_payment, "f").output;
816
851
  }
817
852
  /**
818
853
  * Gets the nSequence required to fulfill this `Output`.
819
854
  */
820
855
  getSequence() {
821
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getTimeConstraints).call(this)?.nSequence;
856
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nSequence;
822
857
  }
823
858
  /**
824
859
  * Gets the nLockTime required to fulfill this `Output`.
825
860
  */
826
861
  getLockTime() {
827
- return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getTimeConstraints).call(this)?.nLockTime;
862
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.nLockTime;
863
+ }
864
+ /**
865
+ * Returns the tapleaf hash selected during planning for taproot script-path
866
+ * spends. If signersPubKeys are provided, selection is optimized for those
867
+ * pubkeys. If a specific tapLeaf selector is used in spending calls, this
868
+ * reflects that selection.
869
+ */
870
+ getTapLeafHash() {
871
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_getConstraints).call(this)?.tapLeafHash;
828
872
  }
829
873
  /**
830
874
  * Gets the witnessScript required to fulfill this `Output`. Only applies to
@@ -955,10 +999,19 @@ function DescriptorsFactory(ecc) {
955
999
  *, Also any `addr(SINGLE_KEY_ADDRESS)` * is assumed to be a single key Taproot
956
1000
  * address (like those defined in BIP86).
957
1001
  * 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.
1002
+ * use a descriptor in the format `sh(MINISCRIPT)`, `wsh(MINISCRIPT)` or
1003
+ * `tr(KEY,TREE)` for taproot script-path expressions.
961
1004
  */
1005
+ // NOTE(taproot-weight): Output instances are concrete. If descriptor has
1006
+ // wildcards, constructor requires `index`. No ranged-without-index
1007
+ // estimation is attempted here.
1008
+ // TODO(taproot-weight): Remaining items:
1009
+ // - Annex: not modeled; if annex is used, add witness item sizing.
1010
+ // - Taproot sighash defaults: options.taprootSighash currently drives fake
1011
+ // signature sizing; ensure coinselector passes the intended mode.
1012
+ // - After PSBT taproot script-path fields are fully populated, add regtest
1013
+ // integration fixtures comparing real tx vsize with inputWeight/outputWeight
1014
+ // estimates for taproot key-path and script-path spends.
962
1015
  inputWeight(
963
1016
  /**
964
1017
  * Indicates if the transaction is a Segwit transaction.
@@ -973,12 +1026,23 @@ function DescriptorsFactory(ecc) {
973
1026
  * Array of `PartialSig`. Each `PartialSig` includes
974
1027
  * a public key and its corresponding signature. This parameter
975
1028
  * 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.
1029
+ * Pass 'DANGEROUSLY_USE_FAKE_SIGNATURES' to assume
1030
+ * ECDSA_FAKE_SIGNATURE_SIZE bytes for ECDSA.
1031
+ * For taproot, the fake signature size is controlled by
1032
+ * options.taprootSighash (64 for 'SIGHASH_DEFAULT', 65
1033
+ * for 'non-SIGHASH_DEFAULT'). default value is SIGHASH_DEFAULT
979
1034
  * Mainly used for testing.
980
1035
  */
981
- signatures) {
1036
+ signatures,
1037
+ /*
1038
+ * Options that affect taproot fake signature sizing.
1039
+ * taprootSighash: 'SIGHASH_DEFAULT' | 'non-SIGHASH_DEFAULT' (default: 'SIGHASH_DEFAULT').
1040
+ * This is only used when signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES'.
1041
+ */
1042
+ options = {
1043
+ taprootSighash: 'SIGHASH_DEFAULT'
1044
+ }) {
1045
+ const taprootSighash = options.taprootSighash ?? 'SIGHASH_DEFAULT';
982
1046
  if (this.isSegwit() && !isSegwitTx)
983
1047
  throw new Error(`a tx is segwit if at least one input is segwit`);
984
1048
  //expand any miniscript-based descriptor. If not miniscript-based, then it's
@@ -991,13 +1055,59 @@ addr(PKH_ADDRESS), addr(WPKH_ADDRESS), addr(SH_WPKH_ADDRESS), addr(SINGLE_KEY_AD
991
1055
  expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${isTR}.`;
992
1056
  if (!expansion && !isPKH && !isWPKH && !isSH && !isTR)
993
1057
  throw new Error(errorMsg);
994
- const firstSignature = signatures && typeof signatures[0] === 'object'
995
- ? signatures[0]
996
- : 'DANGEROUSLY_USE_FAKE_SIGNATURES';
1058
+ const resolveEcdsaSignatureSize = () => {
1059
+ if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES')
1060
+ return ((0, varuint_bitcoin_1.encodingLength)(ECDSA_FAKE_SIGNATURE_SIZE) +
1061
+ ECDSA_FAKE_SIGNATURE_SIZE);
1062
+ if (signatures.length !== 1)
1063
+ throw new Error('More than one signture was not expected');
1064
+ const singleSignature = signatures[0];
1065
+ if (!singleSignature)
1066
+ throw new Error('Signatures not present');
1067
+ const length = singleSignature.signature.length;
1068
+ return (0, varuint_bitcoin_1.encodingLength)(length) + length;
1069
+ };
1070
+ const resolveMiniscriptSignatures = () => {
1071
+ if (signatures !== 'DANGEROUSLY_USE_FAKE_SIGNATURES')
1072
+ return signatures;
1073
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this).map(pubkey => ({
1074
+ pubkey,
1075
+ // https://transactionfee.info/charts/bitcoin-script-ecdsa-length/
1076
+ signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
1077
+ }));
1078
+ };
1079
+ const taprootFakeSignatureSize = taprootSighash === 'SIGHASH_DEFAULT'
1080
+ ? TAPROOT_FAKE_SIGNATURE_SIZE
1081
+ : TAPROOT_FAKE_SIGNATURE_SIZE + 1;
1082
+ const resolveTaprootSignatures = () => {
1083
+ if (signatures !== 'DANGEROUSLY_USE_FAKE_SIGNATURES')
1084
+ return signatures;
1085
+ return __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
1086
+ pubkey,
1087
+ signature: new Uint8Array(taprootFakeSignatureSize)
1088
+ }));
1089
+ };
1090
+ const resolveTaprootSignatureSize = () => {
1091
+ let length;
1092
+ if (signatures === 'DANGEROUSLY_USE_FAKE_SIGNATURES') {
1093
+ length = taprootFakeSignatureSize;
1094
+ }
1095
+ else {
1096
+ if (signatures.length !== 1)
1097
+ throw new Error('More than one signture was not expected');
1098
+ const singleSignature = signatures[0];
1099
+ if (!singleSignature)
1100
+ throw new Error('Signatures not present');
1101
+ length = singleSignature.signature.length;
1102
+ }
1103
+ return (0, varuint_bitcoin_1.encodingLength)(length) + length;
1104
+ };
1105
+ const taprootSpendPath = __classPrivateFieldGet(this, _Output_taprootSpendPath, "f");
1106
+ const tapLeaf = __classPrivateFieldGet(this, _Output_tapLeaf, "f");
997
1107
  if (expansion ? expansion.startsWith('pkh(') : isPKH) {
998
1108
  return (
999
1109
  // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1) + (sig:73) + (pubkey:34)
1000
- (32 + 4 + 4 + 1 + signatureSize(firstSignature) + 34) * 4 +
1110
+ (32 + 4 + 4 + 1 + resolveEcdsaSignatureSize() + 34) * 4 +
1001
1111
  //Segwit:
1002
1112
  (isSegwitTx ? 1 : 0));
1003
1113
  }
@@ -1008,7 +1118,7 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1008
1118
  // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
1009
1119
  41 * 4 +
1010
1120
  // Segwit: (push_count:1) + (sig:73) + (pubkey:34)
1011
- (1 + signatureSize(firstSignature) + 34));
1121
+ (1 + resolveEcdsaSignatureSize() + 34));
1012
1122
  }
1013
1123
  else if (expansion ? expansion.startsWith('sh(wpkh(') : isSH) {
1014
1124
  if (!isSegwitTx)
@@ -1019,7 +1129,7 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1019
1129
  // -> p2wpkh: (script_len:1) + (script:22)
1020
1130
  64 * 4 +
1021
1131
  // Segwit: (push_count:1) + (sig:73) + (pubkey:34)
1022
- (1 + signatureSize(firstSignature) + 34));
1132
+ (1 + resolveEcdsaSignatureSize() + 34));
1023
1133
  }
1024
1134
  else if (expansion?.startsWith('sh(wsh(')) {
1025
1135
  if (!isSegwitTx)
@@ -1030,7 +1140,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1030
1140
  const payment = bitcoinjs_lib_1.payments.p2sh({
1031
1141
  redeem: bitcoinjs_lib_1.payments.p2wsh({
1032
1142
  redeem: {
1033
- input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
1143
+ input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1144
+ .scriptSatisfaction,
1034
1145
  output: witnessScript
1035
1146
  }
1036
1147
  })
@@ -1049,7 +1160,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1049
1160
  throw new Error('sh() must provide redeemScript');
1050
1161
  const payment = bitcoinjs_lib_1.payments.p2sh({
1051
1162
  redeem: {
1052
- input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
1163
+ input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1164
+ .scriptSatisfaction,
1053
1165
  output: redeemScript
1054
1166
  }
1055
1167
  });
@@ -1069,7 +1181,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1069
1181
  throw new Error('wsh must provide witnessScript');
1070
1182
  const payment = bitcoinjs_lib_1.payments.p2wsh({
1071
1183
  redeem: {
1072
- input: this.getScriptSatisfaction(signatures || 'DANGEROUSLY_USE_FAKE_SIGNATURES'),
1184
+ input: this.getScriptSatisfaction(resolveMiniscriptSignatures())
1185
+ .scriptSatisfaction,
1073
1186
  output: witnessScript
1074
1187
  }
1075
1188
  });
@@ -1080,8 +1193,24 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1080
1193
  4 * (40 + varSliceSize(payment.input)) +
1081
1194
  //Segwit
1082
1195
  vectorSize(payment.witness));
1083
- //when addr(SINGLE_KEY_ADDRESS) or tr(KEY) (single key):
1084
- //TODO: only single-key taproot outputs currently supported
1196
+ // when tr(KEY,TREE): choose key-path or script-path based on
1197
+ // constructor taprootSpendPath policy.
1198
+ }
1199
+ else if (expansion?.startsWith('tr(@0,')) {
1200
+ if (!isSegwitTx)
1201
+ throw new Error('Should be SegwitTx');
1202
+ if (taprootSpendPath === 'key')
1203
+ return 41 * 4 + (0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize();
1204
+ const resolvedTapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1205
+ if (!resolvedTapTreeInfo)
1206
+ throw new Error(`Error: taproot tree info not available`);
1207
+ const taprootSatisfaction = (0, tapMiniscript_1.satisfyTapTree)({
1208
+ tapTreeInfo: resolvedTapTreeInfo,
1209
+ preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
1210
+ signatures: resolveTaprootSignatures(),
1211
+ ...(tapLeaf !== undefined ? { tapLeaf } : {})
1212
+ });
1213
+ return 41 * 4 + taprootSatisfaction.totalWitnessSize;
1085
1214
  }
1086
1215
  else if (isTR && (!expansion || expansion === 'tr(@0)')) {
1087
1216
  if (!isSegwitTx)
@@ -1089,8 +1218,8 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1089
1218
  return (
1090
1219
  // Non-segwit: (txid:32) + (vout:4) + (sequence:4) + (script_len:1)
1091
1220
  41 * 4 +
1092
- // Segwit: (push_count:1) + (sig_length(1) + schnorr_sig(64): 65)
1093
- (1 + 65));
1221
+ // Segwit: (push_count:1) + (sig_length(1) + schnorr_sig(64/65))
1222
+ ((0, varuint_bitcoin_1.encodingLength)(1) + resolveTaprootSignatureSize()));
1094
1223
  }
1095
1224
  else {
1096
1225
  throw new Error(errorMsg);
@@ -1129,13 +1258,6 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1129
1258
  throw new Error(errorMsg);
1130
1259
  }
1131
1260
  }
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
1261
  /**
1140
1262
  * Sets this output as an input of the provided `psbt` and updates the
1141
1263
  * `psbt` locktime if required by the descriptor.
@@ -1163,6 +1285,10 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1163
1285
  *
1164
1286
  * @returns A finalizer function to be used after signing the `psbt`.
1165
1287
  * This function ensures that this input is properly finalized.
1288
+ * The finalizer completes the PSBT input by adding the unlocking script
1289
+ * (`scriptWitness` or `scriptSig`) that satisfies this `Output`'s spending
1290
+ * conditions. Because these scripts include signatures, you should finish
1291
+ * all signing operations before calling the finalizer.
1166
1292
  * The finalizer has this signature:
1167
1293
  *
1168
1294
  * `( { psbt, validate = true } : { psbt: Psbt; validate: boolean | undefined } ) => void`
@@ -1170,6 +1296,10 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1170
1296
  */
1171
1297
  updatePsbtAsInput({ psbt, txHex, txId, value, vout, //vector output index
1172
1298
  rbf = true }) {
1299
+ if (value !== undefined && typeof value !== 'bigint')
1300
+ throw new Error(`Error: value must be a bigint`);
1301
+ if (value !== undefined && value < 0n)
1302
+ throw new Error(`Error: value must be >= 0n`);
1173
1303
  if (txHex === undefined) {
1174
1304
  console.warn(`Warning: missing txHex may allow fee attacks`);
1175
1305
  }
@@ -1183,15 +1313,46 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1183
1313
  //This should only happen when using addr() expressions
1184
1314
  throw new Error(`Error: could not determine whether this is a taproot descriptor`);
1185
1315
  }
1186
- const index = (0, psbt_1.updatePsbt)({
1316
+ const paymentInternalPubkey = this.getPayment().internalPubkey;
1317
+ const tapInternalKey = isTaproot
1318
+ ? paymentInternalPubkey
1319
+ ? paymentInternalPubkey
1320
+ : undefined
1321
+ : undefined;
1322
+ let tapLeafScript;
1323
+ let tapBip32Derivation;
1324
+ if (isTaproot && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1325
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1326
+ if (!tapTreeInfo)
1327
+ throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
1328
+ if (!tapInternalKey)
1329
+ throw new Error(`Error: taprootSpendPath=script requires taproot internal key`);
1330
+ const taprootLeafMetadata = (0, tapMiniscript_1.buildTaprootLeafPsbtMetadata)({
1331
+ tapTreeInfo,
1332
+ internalPubkey: tapInternalKey
1333
+ });
1334
+ tapLeafScript = taprootLeafMetadata.map(({ leaf, controlBlock }) => ({
1335
+ script: leaf.tapScript,
1336
+ leafVersion: leaf.version,
1337
+ controlBlock
1338
+ }));
1339
+ const internalKeyInfo = __classPrivateFieldGet(this, _Output_expansionMap, "f")?.['@0'];
1340
+ if (!internalKeyInfo)
1341
+ throw new Error(`Error: taproot internal key info not available in expansionMap`);
1342
+ tapBip32Derivation = (0, tapMiniscript_1.buildTaprootBip32Derivations)({
1343
+ tapTreeInfo,
1344
+ internalKeyInfo
1345
+ });
1346
+ }
1347
+ const index = (0, psbt_1.addPsbtInput)({
1187
1348
  psbt,
1188
1349
  vout,
1189
1350
  ...(txHex !== undefined ? { txHex } : {}),
1190
1351
  ...(txId !== undefined ? { txId } : {}),
1191
1352
  ...(value !== undefined ? { value } : {}),
1192
- ...(isTaproot
1193
- ? { tapInternalKey: this.getPayment().internalPubkey }
1194
- : {}),
1353
+ tapInternalKey,
1354
+ tapLeafScript,
1355
+ tapBip32Derivation,
1195
1356
  sequence: this.getSequence(),
1196
1357
  locktime: this.getLockTime(),
1197
1358
  keysInfo: __classPrivateFieldGet(this, _Output_expansionMap, "f") ? Object.values(__classPrivateFieldGet(this, _Output_expansionMap, "f")) : [],
@@ -1201,7 +1362,64 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1201
1362
  redeemScript: this.getRedeemScript(),
1202
1363
  rbf
1203
1364
  });
1204
- const finalizer = ({ psbt, validate = true }) => this.finalizePsbtInput({ index, psbt, validate });
1365
+ //The finalizer adds the unlocking script (scriptSig/scriptWitness) once
1366
+ //signatures are ready.
1367
+ const finalizer = ({ psbt, validate = true }) => {
1368
+ if (validate &&
1369
+ !psbt.validateSignaturesOfInput(index, signatureValidator)) {
1370
+ throw new Error(`Error: invalid signatures on input ${index}`);
1371
+ }
1372
+ //An index must be passed since finding the index in the psbt cannot be
1373
+ //done:
1374
+ //Imagine the case where you received money twice to
1375
+ //the same miniscript-based address. You would have the same scriptPubKey,
1376
+ //same sequences, ... The descriptor does not store the hash of the previous
1377
+ //transaction since it is a general Output instance. Indices must be kept
1378
+ //out of the scope of this class and then passed.
1379
+ __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertPsbtInput).call(this, { index, psbt });
1380
+ if (__classPrivateFieldGet(this, _Output_isTaproot, "f") &&
1381
+ __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script' &&
1382
+ !__classPrivateFieldGet(this, _Output_tapTreeInfo, "f"))
1383
+ throw new Error(`Error: taprootSpendPath=script requires taproot tree info`);
1384
+ if (__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1385
+ const input = psbt.data.inputs[index];
1386
+ const tapLeafScript = input?.tapLeafScript;
1387
+ if (!tapLeafScript || tapLeafScript.length === 0)
1388
+ throw new Error(`Error: cannot finalize taproot script-path without tapLeafScript`);
1389
+ const tapScriptSig = input?.tapScriptSig;
1390
+ if (!tapScriptSig || tapScriptSig.length === 0)
1391
+ throw new Error(`Error: cannot finalize taproot script-path without tapScriptSig`);
1392
+ const taprootSatisfaction = this.getTapScriptSatisfaction(tapScriptSig);
1393
+ const matchingLeaf = tapLeafScript.find(leafScript => (0, uint8array_tools_1.compare)((0, bitcoinjs_lib_internals_1.tapleafHash)({
1394
+ output: leafScript.script,
1395
+ version: leafScript.leafVersion
1396
+ }), taprootSatisfaction.tapLeafHash) === 0);
1397
+ if (!matchingLeaf)
1398
+ throw new Error(`Error: tapLeafScript does not match planned tapLeafHash`);
1399
+ if ((0, uint8array_tools_1.compare)(matchingLeaf.script, taprootSatisfaction.leaf.tapScript) !==
1400
+ 0 ||
1401
+ matchingLeaf.leafVersion !== taprootSatisfaction.leaf.version)
1402
+ throw new Error(`Error: tapLeafScript does not match planned leaf script`);
1403
+ const witness = [
1404
+ ...taprootSatisfaction.stackItems,
1405
+ matchingLeaf.script,
1406
+ matchingLeaf.controlBlock
1407
+ ];
1408
+ const finalScriptWitness = (0, bitcoinjs_lib_internals_1.witnessStackToScriptWitness)(witness);
1409
+ psbt.finalizeTaprootInput(index, taprootSatisfaction.tapLeafHash, () => ({ finalScriptWitness }));
1410
+ }
1411
+ else if (!__classPrivateFieldGet(this, _Output_miniscript, "f")) {
1412
+ //Use standard finalizers
1413
+ psbt.finalizeInput(index);
1414
+ }
1415
+ else {
1416
+ const signatures = psbt.data.inputs[index]?.partialSig;
1417
+ if (!signatures)
1418
+ throw new Error(`Error: cannot finalize without signatures`);
1419
+ const { scriptSatisfaction } = this.getScriptSatisfaction(signatures);
1420
+ psbt.finalizeInput(index, (0, psbt_1.finalScriptsFuncFactory)(scriptSatisfaction, __classPrivateFieldGet(this, _Output_network, "f")));
1421
+ }
1422
+ };
1205
1423
  return finalizer;
1206
1424
  }
1207
1425
  /**
@@ -1212,63 +1430,12 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1212
1430
  * @param params.value - The value for the output in satoshis.
1213
1431
  */
1214
1432
  updatePsbtAsOutput({ psbt, value }) {
1433
+ if (typeof value !== 'bigint')
1434
+ throw new Error(`Error: value must be a bigint`);
1435
+ if (value < 0n)
1436
+ throw new Error(`Error: value must be >= 0n`);
1215
1437
  psbt.addOutput({ script: this.getScriptPubKey(), value });
1216
1438
  }
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
1439
  /**
1273
1440
  * Decomposes the descriptor used to form this `Output` into its elemental
1274
1441
  * parts. See {@link ExpansionMap ExpansionMap} for a detailed explanation.
@@ -1284,18 +1451,80 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1284
1451
  ...(__classPrivateFieldGet(this, _Output_expandedMiniscript, "f") !== undefined
1285
1452
  ? { expandedMiniscript: __classPrivateFieldGet(this, _Output_expandedMiniscript, "f") }
1286
1453
  : {}),
1454
+ ...(__classPrivateFieldGet(this, _Output_tapTreeExpression, "f") !== undefined
1455
+ ? { tapTreeExpression: __classPrivateFieldGet(this, _Output_tapTreeExpression, "f") }
1456
+ : {}),
1457
+ ...(__classPrivateFieldGet(this, _Output_tapTree, "f") !== undefined ? { tapTree: __classPrivateFieldGet(this, _Output_tapTree, "f") } : {}),
1458
+ ...(__classPrivateFieldGet(this, _Output_tapTreeInfo, "f") !== undefined
1459
+ ? { tapTreeInfo: __classPrivateFieldGet(this, _Output_tapTreeInfo, "f") }
1460
+ : {}),
1287
1461
  ...(__classPrivateFieldGet(this, _Output_expansionMap, "f") !== undefined
1288
1462
  ? { expansionMap: __classPrivateFieldGet(this, _Output_expansionMap, "f") }
1289
1463
  : {})
1290
1464
  };
1291
1465
  }
1292
1466
  }
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() {
1467
+ _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() {
1468
+ //If the user did not provide a pubkey subset (signersPubKeys), assume all
1469
+ //miniscript pubkeys can sign.
1470
+ if (__classPrivateFieldGet(this, _Output_signersPubKeys, "f"))
1471
+ return __classPrivateFieldGet(this, _Output_signersPubKeys, "f");
1472
+ const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
1473
+ if (!expansionMap)
1474
+ throw new Error(`Error: expansionMap not available for miniscript`);
1475
+ return Object.values(expansionMap).map(keyInfo => {
1476
+ const pubkey = keyInfo.pubkey;
1477
+ if (!pubkey)
1478
+ throw new Error(`Error: miniscript key missing pubkey`);
1479
+ return pubkey;
1480
+ });
1481
+ }, _Output_assertMiniscriptSatisfactionResourceLimits = function _Output_assertMiniscriptSatisfactionResourceLimits(scriptSatisfaction) {
1482
+ if (!__classPrivateFieldGet(this, _Output_miniscript, "f"))
1483
+ return;
1484
+ const satisfactionStackItems = bitcoinjs_lib_1.script.toStack(scriptSatisfaction);
1485
+ // For wsh(...) and sh(wsh(...)), enforce witness stack limits.
1486
+ if (__classPrivateFieldGet(this, _Output_isSegwit, "f") && !__classPrivateFieldGet(this, _Output_isTaproot, "f")) {
1487
+ (0, resourceLimits_1.assertWitnessV0SatisfactionResourceLimits)({
1488
+ stackItems: satisfactionStackItems
1489
+ });
1490
+ return;
1491
+ }
1492
+ if (!__classPrivateFieldGet(this, _Output_isSegwit, "f")) {
1493
+ // In legacy P2SH, after scriptSig execution, the stack is:
1494
+ // [ ...satisfactionStackItems, redeemScript ]
1495
+ // Consensus limits apply here: each element must be <= 520 bytes and total
1496
+ // stack items must be <= 1000.
1497
+ // redeemScript size is already validated during script construction
1498
+ // to fail early (look for MAX_SCRIPT_ELEMENT_SIZE checks in this file).
1499
+ // Below we re-validate again the redeemScript + the rest of stack items.
1500
+ const redeemScript = __classPrivateFieldGet(this, _Output_redeemScript, "f");
1501
+ if (!redeemScript)
1502
+ throw new Error(`Error: redeemScript not available for P2SH spend`);
1503
+ (0, resourceLimits_1.assertConsensusStackResourceLimits)({
1504
+ stackItems: [...satisfactionStackItems, redeemScript]
1505
+ });
1506
+ (0, resourceLimits_1.assertP2shScriptSigStandardSize)({
1507
+ scriptSatisfaction,
1508
+ redeemScript,
1509
+ network: __classPrivateFieldGet(this, _Output_network, "f")
1510
+ });
1511
+ }
1512
+ }, _Output_resolveTapTreeSignersPubKeys = function _Output_resolveTapTreeSignersPubKeys() {
1513
+ //If the user did not provide a pubkey subset (signersPubKeys), assume all
1514
+ //taproot leaf pubkeys can sign.
1515
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1516
+ if (!tapTreeInfo)
1517
+ throw new Error(`Error: taproot tree info not available`);
1518
+ const candidatePubkeys = __classPrivateFieldGet(this, _Output_signersPubKeys, "f")
1519
+ ? __classPrivateFieldGet(this, _Output_signersPubKeys, "f").map(tapMiniscript_1.normalizeTaprootPubkey)
1520
+ : (0, tapMiniscript_1.collectTapTreePubkeys)(tapTreeInfo);
1521
+ return Array.from(new Set(candidatePubkeys.map(pubkey => (0, uint8array_tools_1.toHex)(pubkey)))).map(hex => (0, uint8array_tools_1.fromHex)(hex));
1522
+ }, _Output_getConstraints = function _Output_getConstraints() {
1294
1523
  const miniscript = __classPrivateFieldGet(this, _Output_miniscript, "f");
1295
1524
  const preimages = __classPrivateFieldGet(this, _Output_preimages, "f");
1296
1525
  const expandedMiniscript = __classPrivateFieldGet(this, _Output_expandedMiniscript, "f");
1297
1526
  const expansionMap = __classPrivateFieldGet(this, _Output_expansionMap, "f");
1298
- const signersPubKeys = __classPrivateFieldGet(this, _Output_signersPubKeys, "f");
1527
+ const tapTreeInfo = __classPrivateFieldGet(this, _Output_tapTreeInfo, "f");
1299
1528
  //Create a method. solvePreimages to solve them.
1300
1529
  if (miniscript) {
1301
1530
  if (expandedMiniscript === undefined || expansionMap === undefined)
@@ -1303,21 +1532,34 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1303
1532
  //We create some fakeSignatures since we may not have them yet.
1304
1533
  //We only want to retrieve the nLockTime and nSequence of the satisfaction and
1305
1534
  //signatures don't matter
1306
- const fakeSignatures = signersPubKeys.map(pubkey => ({
1535
+ const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveMiniscriptSignersPubKeys).call(this).map(pubkey => ({
1307
1536
  pubkey,
1308
- // https://transactionfee.info/charts/bitcoin-script-ecdsa-length/
1309
- signature: Buffer.alloc(72, 0)
1537
+ signature: new Uint8Array(ECDSA_FAKE_SIGNATURE_SIZE)
1310
1538
  }));
1311
- const { nLockTime, nSequence } = (0, miniscript_1.satisfyMiniscript)({
1539
+ const satisfaction = (0, miniscript_1.satisfyMiniscript)({
1312
1540
  expandedMiniscript,
1313
1541
  expansionMap,
1314
1542
  signatures: fakeSignatures,
1315
1543
  preimages
1316
1544
  });
1317
- return { nLockTime, nSequence };
1545
+ __classPrivateFieldGet(this, _Output_instances, "m", _Output_assertMiniscriptSatisfactionResourceLimits).call(this, satisfaction.scriptSatisfaction);
1546
+ const { nLockTime, nSequence } = satisfaction;
1547
+ return { nLockTime, nSequence, tapLeafHash: undefined };
1318
1548
  }
1319
- else
1320
- return undefined;
1549
+ else if (tapTreeInfo && __classPrivateFieldGet(this, _Output_taprootSpendPath, "f") === 'script') {
1550
+ const fakeSignatures = __classPrivateFieldGet(this, _Output_instances, "m", _Output_resolveTapTreeSignersPubKeys).call(this).map(pubkey => ({
1551
+ pubkey,
1552
+ signature: new Uint8Array(TAPROOT_FAKE_SIGNATURE_SIZE)
1553
+ }));
1554
+ const { nLockTime, nSequence, tapLeafHash } = (0, tapMiniscript_1.satisfyTapTree)({
1555
+ tapTreeInfo,
1556
+ preimages: __classPrivateFieldGet(this, _Output_preimages, "f"),
1557
+ signatures: fakeSignatures,
1558
+ ...(__classPrivateFieldGet(this, _Output_tapLeaf, "f") !== undefined ? { tapLeaf: __classPrivateFieldGet(this, _Output_tapLeaf, "f") } : {})
1559
+ });
1560
+ return { nLockTime, nSequence, tapLeafHash };
1561
+ }
1562
+ return undefined;
1321
1563
  }, _Output_assertPsbtInput = function _Output_assertPsbtInput({ psbt, index }) {
1322
1564
  const input = psbt.data.inputs[index];
1323
1565
  const txInput = psbt.txInputs[index];
@@ -1346,29 +1588,18 @@ expansion=${expansion}, isPKH=${isPKH}, isWPKH=${isWPKH}, isSH=${isSH}, isTR=${i
1346
1588
  ? 0xffffffff
1347
1589
  : 0xfffffffe;
1348
1590
  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 ||
1591
+ const eqBytes = (bytes1, bytes2) => bytes1 === undefined || bytes2 === undefined
1592
+ ? bytes1 === bytes2
1593
+ : (0, uint8array_tools_1.compare)(bytes1, bytes2) === 0;
1594
+ if ((0, uint8array_tools_1.compare)(scriptPubKey, this.getScriptPubKey()) !== 0 ||
1353
1595
  (sequenceRBF !== inputSequence && sequenceNoRBF !== inputSequence) ||
1354
1596
  locktime !== psbt.locktime ||
1355
- !eqBuffers(this.getWitnessScript(), input.witnessScript) ||
1356
- !eqBuffers(this.getRedeemScript(), input.redeemScript)) {
1597
+ !eqBytes(this.getWitnessScript(), input.witnessScript) ||
1598
+ !eqBytes(this.getRedeemScript(), input.redeemScript)) {
1357
1599
  throw new Error(`Error: cannot finalize psbt index ${index} since it does not correspond to this descriptor`);
1358
1600
  }
1359
1601
  };
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
1602
  return {
1370
- // deprecated TAG must also be below so it is exported to descriptors.d.ts
1371
- /** @deprecated @hidden */ Descriptor,
1372
1603
  Output,
1373
1604
  parseKeyExpression,
1374
1605
  expand,