@btc-vision/transaction 1.1.0 → 1.1.2

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.
@@ -47,7 +47,7 @@ export declare abstract class TweakedTransaction extends Logger {
47
47
  protected generateTapData(): Payment;
48
48
  protected generateScriptAddress(): Payment;
49
49
  protected getSignerKey(): Signer | ECPairInterface;
50
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer | ECPairInterface): Promise<void>;
50
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface): Promise<void>;
51
51
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
52
52
  protected signInputs(transaction: Psbt): Promise<void>;
53
53
  protected internalPubKeyToXOnly(): Buffer;
@@ -64,4 +64,12 @@ export declare abstract class TweakedTransaction extends Logger {
64
64
  finalScriptSig: Buffer | undefined;
65
65
  finalScriptWitness: Buffer | undefined;
66
66
  };
67
+ private isTaprootScriptSpend;
68
+ private isTaprootInput;
69
+ private canSignNonTaprootInput;
70
+ private getInputRelevantScript;
71
+ private pubkeyInScript;
72
+ private pubkeyPositionInScript;
73
+ private signTaprootInput;
74
+ private signNonTaprootInput;
67
75
  }
@@ -1 +1 @@
1
- export declare const version = "1.1.0";
1
+ export declare const version = "1.1.2";
package/build/_version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.1.0';
1
+ export const version = '1.1.2';
@@ -47,7 +47,7 @@ export declare abstract class TweakedTransaction extends Logger {
47
47
  protected generateTapData(): Payment;
48
48
  protected generateScriptAddress(): Payment;
49
49
  protected getSignerKey(): Signer | ECPairInterface;
50
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer?: Signer | ECPairInterface): Promise<void>;
50
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface): Promise<void>;
51
51
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
52
52
  protected signInputs(transaction: Psbt): Promise<void>;
53
53
  protected internalPubKeyToXOnly(): Buffer;
@@ -64,4 +64,12 @@ export declare abstract class TweakedTransaction extends Logger {
64
64
  finalScriptSig: Buffer | undefined;
65
65
  finalScriptWitness: Buffer | undefined;
66
66
  };
67
+ private isTaprootScriptSpend;
68
+ private isTaprootInput;
69
+ private canSignNonTaprootInput;
70
+ private getInputRelevantScript;
71
+ private pubkeyInScript;
72
+ private pubkeyPositionInScript;
73
+ private signTaprootInput;
74
+ private signNonTaprootInput;
67
75
  }
@@ -4,6 +4,7 @@ import { TweakedSigner } from '../../signer/TweakedSigner.js';
4
4
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
5
5
  import { AddressTypes, AddressVerificator } from '../../keypair/AddressVerificator.js';
6
6
  import { varuint } from '@btc-vision/bitcoin/src/bufferutils.js';
7
+ import * as bscript from '@btc-vision/bitcoin/src/script.js';
7
8
  export var TransactionSequence;
8
9
  (function (TransactionSequence) {
9
10
  TransactionSequence[TransactionSequence["REPLACE_BY_FEE"] = 4294967293] = "REPLACE_BY_FEE";
@@ -27,10 +28,7 @@ export class TweakedTransaction extends Logger {
27
28
  const inputDecoded = this.inputs[inputIndex];
28
29
  if (isP2SH && input.partialSig && inputDecoded && inputDecoded.redeemScript) {
29
30
  const signatures = input.partialSig.map((sig) => sig.signature);
30
- const scriptSig = script.compile([
31
- ...signatures,
32
- inputDecoded.redeemScript,
33
- ]);
31
+ const scriptSig = script.compile([...signatures, inputDecoded.redeemScript]);
34
32
  return {
35
33
  finalScriptSig: scriptSig,
36
34
  finalScriptWitness: undefined,
@@ -157,56 +155,58 @@ export class TweakedTransaction extends Logger {
157
155
  return this.signer;
158
156
  }
159
157
  async signInput(transaction, input, i, signer) {
160
- const signHash = this.sighashTypes && this.sighashTypes.length
161
- ? [TweakedTransaction.calculateSignHash(this.sighashTypes)]
162
- : undefined;
163
- signer = signer || this.getSignerKey();
164
- let testedTap = false;
165
- if (input.tapInternalKey) {
166
- if (!this.tweakedSigner)
167
- this.tweakSigner();
168
- let tweakedSigner;
169
- if (signer !== this.signer) {
170
- tweakedSigner = this.getTweakedSigner(true, signer);
158
+ const publicKey = signer.publicKey;
159
+ const isTaproot = this.isTaprootInput(input);
160
+ let signed = false;
161
+ if (isTaproot) {
162
+ const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
163
+ if (isScriptSpend) {
164
+ try {
165
+ await this.signTaprootInput(signer, transaction, i);
166
+ signed = true;
167
+ }
168
+ catch (e) {
169
+ this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
170
+ }
171
171
  }
172
172
  else {
173
- tweakedSigner = this.tweakedSigner;
174
- }
175
- if (tweakedSigner) {
176
- testedTap = true;
177
- try {
178
- if ('signTaprootInput' in signer) {
179
- return await signer.signTaprootInput(transaction, i, signHash);
173
+ let tweakedSigner;
174
+ if (signer !== this.signer) {
175
+ tweakedSigner = this.getTweakedSigner(true, signer);
176
+ }
177
+ else {
178
+ if (!this.tweakedSigner)
179
+ this.tweakSigner();
180
+ tweakedSigner = this.tweakedSigner;
181
+ }
182
+ if (tweakedSigner) {
183
+ try {
184
+ await this.signTaprootInput(tweakedSigner, transaction, i);
185
+ signed = true;
180
186
  }
181
- else {
182
- transaction.signTaprootInput(i, tweakedSigner, undefined, signHash);
187
+ catch (e) {
188
+ this.error(`Failed to sign Taproot key path input ${i}: ${e}`);
183
189
  }
184
- return;
185
190
  }
186
- catch { }
187
- }
188
- }
189
- try {
190
- if ('signInput' in signer) {
191
- return await signer.signInput(transaction, i, signHash);
192
- }
193
- else {
194
- transaction.signInput(i, signer, signHash);
191
+ else {
192
+ this.error(`Failed to obtain tweaked signer for input ${i}.`);
193
+ }
195
194
  }
196
195
  }
197
- catch (e) {
198
- if (!testedTap) {
199
- if ('signTaprootInput' in signer) {
200
- return await signer.signTaprootInput(transaction, i, signHash);
201
- }
202
- else if (this.tweakedSigner) {
203
- transaction.signTaprootInput(i, this.tweakedSigner, undefined, signHash);
196
+ else {
197
+ if (this.canSignNonTaprootInput(input, publicKey)) {
198
+ try {
199
+ await this.signNonTaprootInput(signer, transaction, i);
200
+ signed = true;
204
201
  }
205
- else {
206
- throw e;
202
+ catch (e) {
203
+ this.error(`Failed to sign non-Taproot input ${i}: ${e}`);
207
204
  }
208
205
  }
209
206
  }
207
+ if (!signed) {
208
+ throw new Error(`Cannot sign input ${i} with the provided signer.`);
209
+ }
210
210
  }
211
211
  splitArray(arr, chunkSize) {
212
212
  if (chunkSize <= 0) {
@@ -230,7 +230,7 @@ export class TweakedTransaction extends Logger {
230
230
  const index = offset + j;
231
231
  const input = batch[j];
232
232
  try {
233
- promises.push(this.signInput(transaction, input, index));
233
+ promises.push(this.signInput(transaction, input, index, this.signer));
234
234
  }
235
235
  catch (e) {
236
236
  this.log(`Failed to sign input ${index}: ${e.stack}`);
@@ -365,7 +365,6 @@ export class TweakedTransaction extends Logger {
365
365
  }
366
366
  if (i === 0 && this.nonWitnessUtxo) {
367
367
  input.nonWitnessUtxo = this.nonWitnessUtxo;
368
- this.log(`Using non-witness utxo for input ${i}`);
369
368
  }
370
369
  if (utxo.scriptPubKey.address &&
371
370
  AddressVerificator.isValidP2TRAddress(utxo.scriptPubKey.address, this.network)) {
@@ -374,4 +373,77 @@ export class TweakedTransaction extends Logger {
374
373
  }
375
374
  return input;
376
375
  }
376
+ isTaprootScriptSpend(input, publicKey) {
377
+ if (input.tapLeafScript && input.tapLeafScript.length > 0) {
378
+ for (const tapLeafScript of input.tapLeafScript) {
379
+ if (this.pubkeyInScript(publicKey, tapLeafScript.script)) {
380
+ return true;
381
+ }
382
+ }
383
+ }
384
+ return false;
385
+ }
386
+ isTaprootInput(input) {
387
+ if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
388
+ return true;
389
+ }
390
+ if (input.witnessUtxo) {
391
+ const script = input.witnessUtxo.script;
392
+ return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
393
+ }
394
+ return false;
395
+ }
396
+ canSignNonTaprootInput(input, publicKey) {
397
+ const script = this.getInputRelevantScript(input);
398
+ if (script) {
399
+ return this.pubkeyInScript(publicKey, script);
400
+ }
401
+ return false;
402
+ }
403
+ getInputRelevantScript(input) {
404
+ if (input.redeemScript) {
405
+ return input.redeemScript;
406
+ }
407
+ if (input.witnessScript) {
408
+ return input.witnessScript;
409
+ }
410
+ if (input.witnessUtxo) {
411
+ return input.witnessUtxo.script;
412
+ }
413
+ if (input.nonWitnessUtxo) {
414
+ return null;
415
+ }
416
+ return null;
417
+ }
418
+ pubkeyInScript(pubkey, script) {
419
+ return this.pubkeyPositionInScript(pubkey, script) !== -1;
420
+ }
421
+ pubkeyPositionInScript(pubkey, script) {
422
+ const pubkeyHash = bitCrypto.hash160(pubkey);
423
+ const pubkeyXOnly = toXOnly(pubkey);
424
+ const decompiled = bscript.decompile(script);
425
+ if (decompiled === null)
426
+ throw new Error('Unknown script error');
427
+ return decompiled.findIndex((element) => {
428
+ if (typeof element === 'number')
429
+ return false;
430
+ return (element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly));
431
+ });
432
+ }
433
+ async signTaprootInput(signer, transaction, i, tapLeafHash) {
434
+ if ('signTaprootInput' in signer) {
435
+ await signer.signTaprootInput(transaction, i, tapLeafHash);
436
+ }
437
+ else {
438
+ transaction.signTaprootInput(i, signer);
439
+ }
440
+ }
441
+ async signNonTaprootInput(signer, transaction, i) {
442
+ if ('signInput' in signer) {
443
+ await signer.signInput(transaction, i);
444
+ }
445
+ else {
446
+ transaction.signInput(i, signer);
447
+ }
448
+ }
377
449
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@btc-vision/transaction",
3
3
  "type": "module",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "author": "BlobMaster41",
6
6
  "description": "OPNet transaction library allows you to create and sign transactions for the OPNet network.",
7
7
  "engines": {
@@ -63,6 +63,9 @@
63
63
  "browserBuild": "webpack --mode production",
64
64
  "docs": "typedoc --out docs --exclude 'src/tests/*.ts' --tsconfig tsconfig.json --readme README.md --name OPNet --plugin typedoc-material-theme --themeColor '#cb9820' --exclude src/tests/test.ts --exclude src/index.ts src"
65
65
  },
66
+ "peerDependencies": {
67
+ "@btc-vision/bitcoin": "^6.3.0"
68
+ },
66
69
  "devDependencies": {
67
70
  "@babel/core": "^7.26.0",
68
71
  "@babel/plugin-proposal-class-properties": "^7.18.6",
package/src/_version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.1.0';
1
+ export const version = '1.1.2';
@@ -14,6 +14,7 @@ import {
14
14
  Signer,
15
15
  Transaction,
16
16
  } from '@btc-vision/bitcoin';
17
+
17
18
  import { TweakedSigner, TweakSettings } from '../../signer/TweakedSigner.js';
18
19
  import { ECPairInterface } from 'ecpair';
19
20
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
@@ -22,6 +23,7 @@ import { TapLeafScript } from '../interfaces/Tap.js';
22
23
  import { AddressTypes, AddressVerificator } from '../../keypair/AddressVerificator.js';
23
24
  import { ChainId } from '../../network/ChainId.js';
24
25
  import { varuint } from '@btc-vision/bitcoin/src/bufferutils.js';
26
+ import * as bscript from '@btc-vision/bitcoin/src/script.js';
25
27
 
26
28
  export interface ITweakedTransactionData {
27
29
  readonly signer: Signer | ECPairInterface;
@@ -360,78 +362,65 @@ export abstract class TweakedTransaction extends Logger {
360
362
  * @param {Psbt} transaction - The transaction to sign
361
363
  * @param {PsbtInput} input - The input to sign
362
364
  * @param {number} i - The index of the input
363
- * @param {Signer} [signer] - The signer to use
365
+ * @param {Signer} signer - The signer to use
364
366
  * @protected
365
367
  */
366
368
  protected async signInput(
367
369
  transaction: Psbt,
368
370
  input: PsbtInput,
369
371
  i: number,
370
- signer?: Signer | ECPairInterface,
372
+ signer: Signer | ECPairInterface,
371
373
  ): Promise<void> {
372
- const signHash =
373
- this.sighashTypes && this.sighashTypes.length
374
- ? [TweakedTransaction.calculateSignHash(this.sighashTypes)]
375
- : undefined;
374
+ const publicKey = signer.publicKey;
375
+ const isTaproot = this.isTaprootInput(input);
376
376
 
377
- signer = signer || this.getSignerKey();
377
+ let signed = false;
378
378
 
379
- let testedTap: boolean = false;
380
- if (input.tapInternalKey) {
381
- if (!this.tweakedSigner) this.tweakSigner();
379
+ if (isTaproot) {
380
+ const isScriptSpend = this.isTaprootScriptSpend(input, publicKey);
382
381
 
383
- let tweakedSigner: ECPairInterface | undefined;
384
- if (signer !== this.signer) {
385
- tweakedSigner = this.getTweakedSigner(true, signer);
382
+ if (isScriptSpend) {
383
+ try {
384
+ await this.signTaprootInput(signer, transaction, i);
385
+ signed = true;
386
+ } catch (e) {
387
+ this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
388
+ }
386
389
  } else {
387
- tweakedSigner = this.tweakedSigner;
388
- }
389
-
390
- if (tweakedSigner) {
391
- testedTap = true;
390
+ let tweakedSigner: ECPairInterface | undefined;
391
+ if (signer !== this.signer) {
392
+ tweakedSigner = this.getTweakedSigner(true, signer);
393
+ } else {
394
+ if (!this.tweakedSigner) this.tweakSigner();
395
+ tweakedSigner = this.tweakedSigner;
396
+ }
392
397
 
393
- try {
394
- if ('signTaprootInput' in signer) {
395
- // @ts-expect-error - we know it's a taproot signer
396
- return await (signer.signTaprootInput(
397
- transaction,
398
- i,
399
- signHash,
400
- ) as Promise<void>);
401
- } else {
402
- transaction.signTaprootInput(i, tweakedSigner, undefined, signHash);
398
+ if (tweakedSigner) {
399
+ try {
400
+ await this.signTaprootInput(tweakedSigner, transaction, i);
401
+ signed = true;
402
+ } catch (e) {
403
+ this.error(`Failed to sign Taproot key path input ${i}: ${e}`);
403
404
  }
404
-
405
- return;
406
- } catch {}
407
- }
408
- }
409
-
410
- try {
411
- if ('signInput' in signer) {
412
- // @ts-expect-error - we know it's a signer
413
- return await (signer.signInput(transaction, i, signHash) as Promise<void>);
414
- } else {
415
- transaction.signInput(i, signer, signHash);
416
- }
417
- } catch (e) {
418
- if (!testedTap) {
419
- // and we try again taproot...
420
-
421
- if ('signTaprootInput' in signer) {
422
- // @ts-expect-error - we know it's a taproot signer
423
- return await (signer.signTaprootInput(
424
- transaction,
425
- i,
426
- signHash,
427
- ) as Promise<void>);
428
- } else if (this.tweakedSigner) {
429
- transaction.signTaprootInput(i, this.tweakedSigner, undefined, signHash);
430
405
  } else {
431
- throw e;
406
+ this.error(`Failed to obtain tweaked signer for input ${i}.`);
407
+ }
408
+ }
409
+ } else {
410
+ // Non-Taproot input
411
+ if (this.canSignNonTaprootInput(input, publicKey)) {
412
+ try {
413
+ await this.signNonTaprootInput(signer, transaction, i);
414
+ signed = true;
415
+ } catch (e) {
416
+ this.error(`Failed to sign non-Taproot input ${i}: ${e}`);
432
417
  }
433
418
  }
434
419
  }
420
+
421
+ if (!signed) {
422
+ throw new Error(`Cannot sign input ${i} with the provided signer.`);
423
+ }
435
424
  }
436
425
 
437
426
  protected splitArray<T>(arr: T[], chunkSize: number): T[][] {
@@ -440,7 +429,6 @@ export abstract class TweakedTransaction extends Logger {
440
429
  }
441
430
 
442
431
  const result: T[][] = [];
443
-
444
432
  for (let i = 0; i < arr.length; i += chunkSize) {
445
433
  result.push(arr.slice(i, i + chunkSize));
446
434
  }
@@ -470,7 +458,7 @@ export abstract class TweakedTransaction extends Logger {
470
458
  const input = batch[j];
471
459
 
472
460
  try {
473
- promises.push(this.signInput(transaction, input, index));
461
+ promises.push(this.signInput(transaction, input, index, this.signer));
474
462
  } catch (e) {
475
463
  this.log(`Failed to sign input ${index}: ${(e as Error).stack}`);
476
464
  }
@@ -678,10 +666,9 @@ export abstract class TweakedTransaction extends Logger {
678
666
 
679
667
  if (i === 0 && this.nonWitnessUtxo) {
680
668
  input.nonWitnessUtxo = this.nonWitnessUtxo;
681
- this.log(`Using non-witness utxo for input ${i}`);
682
669
  }
683
670
 
684
- // automatically detect p2tr inputs.
671
+ // Automatically detect P2TR inputs.
685
672
  if (
686
673
  utxo.scriptPubKey.address &&
687
674
  AddressVerificator.isValidP2TRAddress(utxo.scriptPubKey.address, this.network)
@@ -695,11 +682,11 @@ export abstract class TweakedTransaction extends Logger {
695
682
  }
696
683
 
697
684
  protected customFinalizerP2SH = (
698
- inputIndex: number, // Which input is it?
699
- input: PsbtInput, // The PSBT input contents
700
- scriptA: Buffer, // The "meaningful" locking script Buffer (redeemScript for P2SH etc.)
701
- isSegwit: boolean, // Is it segwit?
702
- isP2SH: boolean, // Is it P2SH?
685
+ inputIndex: number,
686
+ input: PsbtInput,
687
+ scriptA: Buffer,
688
+ isSegwit: boolean,
689
+ isP2SH: boolean,
703
690
  isP2WSH: boolean,
704
691
  ): {
705
692
  finalScriptSig: Buffer | undefined;
@@ -708,31 +695,120 @@ export abstract class TweakedTransaction extends Logger {
708
695
  const inputDecoded = this.inputs[inputIndex];
709
696
  if (isP2SH && input.partialSig && inputDecoded && inputDecoded.redeemScript) {
710
697
  const signatures = input.partialSig.map((sig) => sig.signature);
711
-
712
- /*const fakeSignature = Buffer.from([
713
- 0x30,
714
- 0x45, // DER prefix: 0x30 (Compound), 0x45 (length = 69 bytes)
715
- 0x02,
716
- 0x20, // Integer marker: 0x02 (integer), 0x20 (length = 32 bytes)
717
- ...Buffer.alloc(32, 0x00), // 32-byte fake 'r' value (all zeros)
718
- 0x02,
719
- 0x21, // Integer marker: 0x02 (integer), 0x21 (length = 33 bytes)
720
- ...Buffer.alloc(33, 0x00), // 33-byte fake 's' value (all zeros)
721
- 0x01, // SIGHASH_ALL flag (0x01)
722
- ]);*/
723
-
724
- const scriptSig = script.compile([
725
- ...signatures,
726
- //fakeSignature,
727
- inputDecoded.redeemScript,
728
- ]);
698
+ const scriptSig = script.compile([...signatures, inputDecoded.redeemScript]);
729
699
 
730
700
  return {
731
- finalScriptSig: scriptSig, // Manually set the final scriptSig
732
- finalScriptWitness: undefined, // Manually set the final scriptWitness
701
+ finalScriptSig: scriptSig,
702
+ finalScriptWitness: undefined,
733
703
  };
734
704
  }
735
705
 
736
706
  return getFinalScripts(inputIndex, input, scriptA, isSegwit, isP2SH, isP2WSH);
737
707
  };
708
+
709
+ private isTaprootScriptSpend(input: PsbtInput, publicKey: Buffer): boolean {
710
+ if (input.tapLeafScript && input.tapLeafScript.length > 0) {
711
+ // Check if the signer's public key is involved in any tapLeafScript
712
+ for (const tapLeafScript of input.tapLeafScript) {
713
+ if (this.pubkeyInScript(publicKey, tapLeafScript.script)) {
714
+ // The public key is in the script; it's a script spend
715
+ return true;
716
+ }
717
+ }
718
+ }
719
+ return false;
720
+ }
721
+
722
+ // Helper method to determine if an input is Taproot
723
+ private isTaprootInput(input: PsbtInput): boolean {
724
+ if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
725
+ return true;
726
+ }
727
+
728
+ if (input.witnessUtxo) {
729
+ const script = input.witnessUtxo.script;
730
+ // Check if the script is a P2TR output (OP_1 [32-byte key])
731
+ return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
732
+ }
733
+
734
+ return false;
735
+ }
736
+
737
+ // Check if the signer can sign the non-Taproot input
738
+ private canSignNonTaprootInput(input: PsbtInput, publicKey: Buffer): boolean {
739
+ const script = this.getInputRelevantScript(input);
740
+ if (script) {
741
+ return this.pubkeyInScript(publicKey, script);
742
+ }
743
+ return false;
744
+ }
745
+
746
+ // Helper method to extract the relevant script from the input
747
+ private getInputRelevantScript(input: PsbtInput): Buffer | null {
748
+ if (input.redeemScript) {
749
+ return input.redeemScript;
750
+ }
751
+ if (input.witnessScript) {
752
+ return input.witnessScript;
753
+ }
754
+ if (input.witnessUtxo) {
755
+ return input.witnessUtxo.script;
756
+ }
757
+ if (input.nonWitnessUtxo) {
758
+ // Additional logic can be added to extract script from nonWitnessUtxo
759
+ return null;
760
+ }
761
+ return null;
762
+ }
763
+
764
+ // Helper method to check if a public key is in a script
765
+ private pubkeyInScript(pubkey: Buffer, script: Buffer): boolean {
766
+ return this.pubkeyPositionInScript(pubkey, script) !== -1;
767
+ }
768
+
769
+ private pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number {
770
+ const pubkeyHash = bitCrypto.hash160(pubkey);
771
+ const pubkeyXOnly = toXOnly(pubkey);
772
+
773
+ const decompiled = bscript.decompile(script);
774
+ if (decompiled === null) throw new Error('Unknown script error');
775
+
776
+ return decompiled.findIndex((element) => {
777
+ if (typeof element === 'number') return false;
778
+ return (
779
+ element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly)
780
+ );
781
+ });
782
+ }
783
+
784
+ private async signTaprootInput(
785
+ signer: Signer | ECPairInterface,
786
+ transaction: Psbt,
787
+ i: number,
788
+ tapLeafHash?: Buffer,
789
+ ): Promise<void> {
790
+ if ('signTaprootInput' in signer) {
791
+ await (
792
+ signer.signTaprootInput as (
793
+ tx: Psbt,
794
+ i: number,
795
+ tapLeafHash?: Buffer,
796
+ ) => Promise<void>
797
+ )(transaction, i, tapLeafHash);
798
+ } else {
799
+ transaction.signTaprootInput(i, signer); //tapLeafHash
800
+ }
801
+ }
802
+
803
+ private async signNonTaprootInput(
804
+ signer: Signer | ECPairInterface,
805
+ transaction: Psbt,
806
+ i: number,
807
+ ): Promise<void> {
808
+ if ('signInput' in signer) {
809
+ await (signer.signInput as (tx: Psbt, i: number) => Promise<void>)(transaction, i);
810
+ } else {
811
+ transaction.signInput(i, signer);
812
+ }
813
+ }
738
814
  }