@btc-vision/transaction 1.1.16 → 1.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/browser/_version.d.ts +1 -1
  2. package/browser/index.js +1 -1
  3. package/browser/keypair/Address.d.ts +2 -0
  4. package/browser/signer/SignerUtils.d.ts +6 -0
  5. package/browser/transaction/shared/TweakedTransaction.d.ts +5 -6
  6. package/browser/utxo/interfaces/IUTXO.d.ts +1 -0
  7. package/build/_version.d.ts +1 -1
  8. package/build/_version.js +1 -1
  9. package/build/keypair/Address.d.ts +2 -0
  10. package/build/keypair/Address.js +9 -0
  11. package/build/signer/SignerUtils.d.ts +6 -0
  12. package/build/signer/SignerUtils.js +56 -0
  13. package/build/transaction/browser/extensions/UnisatSigner.js +5 -32
  14. package/build/transaction/browser/extensions/XverseSigner.js +5 -48
  15. package/build/transaction/builders/FundingTransaction.js +6 -1
  16. package/build/transaction/builders/TransactionBuilder.js +3 -1
  17. package/build/transaction/shared/TweakedTransaction.d.ts +5 -6
  18. package/build/transaction/shared/TweakedTransaction.js +121 -91
  19. package/build/utils/BitcoinUtils.js +4 -4
  20. package/build/utxo/OPNetLimitedProvider.js +1 -0
  21. package/build/utxo/interfaces/IUTXO.d.ts +1 -0
  22. package/package.json +2 -5
  23. package/src/_version.ts +1 -1
  24. package/src/keypair/Address.ts +15 -0
  25. package/src/signer/SignerUtils.ts +78 -0
  26. package/src/transaction/TransactionFactory.ts +0 -253
  27. package/src/transaction/browser/extensions/UnisatSigner.ts +4 -40
  28. package/src/transaction/browser/extensions/XverseSigner.ts +9 -68
  29. package/src/transaction/builders/FundingTransaction.ts +7 -2
  30. package/src/transaction/builders/TransactionBuilder.ts +3 -1
  31. package/src/transaction/shared/TweakedTransaction.ts +224 -77
  32. package/src/utils/BitcoinUtils.ts +4 -4
  33. package/src/utxo/OPNetLimitedProvider.ts +1 -0
  34. package/src/utxo/interfaces/IUTXO.ts +2 -0
@@ -9,11 +9,13 @@ export declare class Address extends Uint8Array {
9
9
  static wrap(bytes: ArrayLike<number>): Address;
10
10
  toHex(): string;
11
11
  toBuffer(): Buffer;
12
+ originalPublicKeyBuffer(): Buffer;
12
13
  equals(a: Address): boolean;
13
14
  lessThan(a: Address): boolean;
14
15
  greaterThan(a: Address): boolean;
15
16
  set(publicKey: ArrayLike<number>): void;
16
17
  isValid(network: Network): boolean;
18
+ p2pk(): string;
17
19
  p2wpkh(network: Network): string;
18
20
  p2pkh(network: Network): string;
19
21
  p2shp2wpkh(network: Network): string;
@@ -0,0 +1,6 @@
1
+ import { PsbtInput } from '@btc-vision/bitcoin';
2
+ export declare function isTaprootInput(input: PsbtInput): boolean;
3
+ export declare function getInputRelevantScript(input: PsbtInput): Buffer | null;
4
+ export declare function canSignNonTaprootInput(input: PsbtInput, publicKey: Buffer): boolean;
5
+ export declare function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number;
6
+ export declare function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean;
@@ -48,7 +48,7 @@ export declare abstract class TweakedTransaction extends Logger {
48
48
  protected generateTapData(): Payment;
49
49
  protected generateScriptAddress(): Payment;
50
50
  protected getSignerKey(): Signer | ECPairInterface;
51
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean): Promise<void>;
51
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean, errored?: boolean): Promise<void>;
52
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
53
53
  protected signInputs(transaction: Psbt): Promise<void>;
54
54
  protected internalPubKeyToXOnly(): Buffer;
@@ -60,6 +60,10 @@ export declare abstract class TweakedTransaction extends Logger {
60
60
  redeemScript: Buffer;
61
61
  outputScript: Buffer;
62
62
  } | undefined;
63
+ protected generateP2SHP2PKHRedeemScript(inputAddr: string): {
64
+ redeemScript: Buffer;
65
+ outputScript: Buffer;
66
+ } | undefined;
63
67
  protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
64
68
  protected customFinalizerP2SH: (inputIndex: number, input: PsbtInput, scriptA: Buffer, isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => {
65
69
  finalScriptSig: Buffer | undefined;
@@ -68,11 +72,6 @@ export declare abstract class TweakedTransaction extends Logger {
68
72
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
69
73
  private attemptSignTaproot;
70
74
  private isTaprootScriptSpend;
71
- private isTaprootInput;
72
- private canSignNonTaprootInput;
73
- private getInputRelevantScript;
74
- private pubkeyInScript;
75
- private pubkeyPositionInScript;
76
75
  private signTaprootInput;
77
76
  private signNonTaprootInput;
78
77
  }
@@ -27,4 +27,5 @@ export interface RawUTXOResponse {
27
27
  readonly outputIndex: number;
28
28
  readonly value: string;
29
29
  readonly scriptPubKey: ScriptPubKey;
30
+ readonly raw: string;
30
31
  }
@@ -1 +1 @@
1
- export declare const version = "1.1.16";
1
+ export declare const version = "1.1.17";
package/build/_version.js CHANGED
@@ -1 +1 @@
1
- export const version = '1.1.16';
1
+ export const version = '1.1.17';
@@ -9,11 +9,13 @@ export declare class Address extends Uint8Array {
9
9
  static wrap(bytes: ArrayLike<number>): Address;
10
10
  toHex(): string;
11
11
  toBuffer(): Buffer;
12
+ originalPublicKeyBuffer(): Buffer;
12
13
  equals(a: Address): boolean;
13
14
  lessThan(a: Address): boolean;
14
15
  greaterThan(a: Address): boolean;
15
16
  set(publicKey: ArrayLike<number>): void;
16
17
  isValid(network: Network): boolean;
18
+ p2pk(): string;
17
19
  p2wpkh(network: Network): string;
18
20
  p2pkh(network: Network): string;
19
21
  p2shp2wpkh(network: Network): string;
@@ -63,6 +63,12 @@ export class Address extends Uint8Array {
63
63
  toBuffer() {
64
64
  return Buffer.from(this);
65
65
  }
66
+ originalPublicKeyBuffer() {
67
+ if (!__classPrivateFieldGet(this, _Address_originalPublicKey, "f")) {
68
+ throw new Error('Public key not set');
69
+ }
70
+ return Buffer.from(__classPrivateFieldGet(this, _Address_originalPublicKey, "f"));
71
+ }
66
72
  equals(a) {
67
73
  const b = this;
68
74
  if (a.length !== b.length) {
@@ -123,6 +129,9 @@ export class Address extends Uint8Array {
123
129
  isValid(network) {
124
130
  return AddressVerificator.isValidPublicKey(Buffer.from(this).toString('hex'), network);
125
131
  }
132
+ p2pk() {
133
+ return this.toHex();
134
+ }
126
135
  p2wpkh(network) {
127
136
  return EcKeyPair.getP2WPKHAddress(this.keyPair, network);
128
137
  }
@@ -0,0 +1,6 @@
1
+ import { PsbtInput } from '@btc-vision/bitcoin';
2
+ export declare function isTaprootInput(input: PsbtInput): boolean;
3
+ export declare function getInputRelevantScript(input: PsbtInput): Buffer | null;
4
+ export declare function canSignNonTaprootInput(input: PsbtInput, publicKey: Buffer): boolean;
5
+ export declare function pubkeyPositionInScript(pubkey: Buffer, script: Buffer): number;
6
+ export declare function pubkeyInScript(pubkey: Buffer, script: Buffer): boolean;
@@ -0,0 +1,56 @@
1
+ import { crypto as bitCrypto } from '@btc-vision/bitcoin';
2
+ import { isP2TR } from '@btc-vision/bitcoin/src/psbt/psbtutils.js';
3
+ import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
4
+ import * as bscript from '@btc-vision/bitcoin/src/script.js';
5
+ export function isTaprootInput(input) {
6
+ return (input &&
7
+ !!(input.tapInternalKey ||
8
+ input.tapMerkleRoot ||
9
+ (input.tapLeafScript && input.tapLeafScript.length) ||
10
+ (input.tapBip32Derivation && input.tapBip32Derivation.length) ||
11
+ (input.witnessUtxo && isP2TR(input.witnessUtxo.script))));
12
+ }
13
+ export function getInputRelevantScript(input) {
14
+ if (input.redeemScript) {
15
+ return input.redeemScript;
16
+ }
17
+ if (input.witnessScript) {
18
+ return input.witnessScript;
19
+ }
20
+ if (input.witnessUtxo) {
21
+ return input.witnessUtxo.script;
22
+ }
23
+ if (input.nonWitnessUtxo) {
24
+ }
25
+ return null;
26
+ }
27
+ export function canSignNonTaprootInput(input, publicKey) {
28
+ if ((input.nonWitnessUtxo &&
29
+ !input.redeemScript &&
30
+ !input.witnessScript &&
31
+ !input.witnessUtxo) ||
32
+ input.redeemScript) {
33
+ return true;
34
+ }
35
+ const script = getInputRelevantScript(input);
36
+ if (script) {
37
+ return pubkeyInScript(publicKey, script);
38
+ }
39
+ return false;
40
+ }
41
+ export function pubkeyPositionInScript(pubkey, script) {
42
+ const pubkeyHash = bitCrypto.hash160(pubkey);
43
+ const pubkeyXOnly = toXOnly(pubkey);
44
+ const decompiled = bscript.decompile(script);
45
+ if (decompiled === null)
46
+ throw new Error('Unknown script error');
47
+ const a = decompiled.findIndex((element) => {
48
+ if (typeof element === 'number')
49
+ return false;
50
+ return element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly);
51
+ });
52
+ return a;
53
+ }
54
+ export function pubkeyInScript(pubkey, script) {
55
+ return pubkeyPositionInScript(pubkey, script) !== -1;
56
+ }
@@ -1,8 +1,9 @@
1
- import { crypto as bitCrypto, networks, opcodes, Psbt, script as bitScript, } from '@btc-vision/bitcoin';
1
+ import { crypto as bitCrypto, networks, Psbt, script as bitScript, } from '@btc-vision/bitcoin';
2
2
  import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
3
3
  import { CustomKeypair } from '../BrowserSignerBase.js';
4
4
  import { UnisatNetwork } from '../types/Unisat.js';
5
5
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
6
+ import { canSignNonTaprootInput, isTaprootInput } from '../../../signer/SignerUtils.js';
6
7
  export class UnisatSigner extends CustomKeypair {
7
8
  constructor() {
8
9
  super();
@@ -147,12 +148,9 @@ export class UnisatSigner extends CustomKeypair {
147
148
  }
148
149
  }
149
150
  }
150
- else {
151
- const script = getInputRelevantScript(input);
152
- if (script && pubkeyInScript(this.publicKey, script)) {
153
- needsToSign = true;
154
- viaTaproot = false;
155
- }
151
+ else if (canSignNonTaprootInput(input, this.publicKey)) {
152
+ needsToSign = true;
153
+ viaTaproot = false;
156
154
  }
157
155
  if (needsToSign) {
158
156
  return {
@@ -248,31 +246,6 @@ export class UnisatSigner extends CustomKeypair {
248
246
  return nonDuplicate;
249
247
  }
250
248
  }
251
- function isTaprootInput(input) {
252
- if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
253
- return true;
254
- }
255
- if (input.witnessUtxo) {
256
- const script = input.witnessUtxo.script;
257
- return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
258
- }
259
- return false;
260
- }
261
- function getInputRelevantScript(input) {
262
- if (input.redeemScript) {
263
- return input.redeemScript;
264
- }
265
- if (input.witnessScript) {
266
- return input.witnessScript;
267
- }
268
- if (input.witnessUtxo) {
269
- return input.witnessUtxo.script;
270
- }
271
- if (input.nonWitnessUtxo) {
272
- return null;
273
- }
274
- return null;
275
- }
276
249
  function pubkeyInScript(pubkey, script) {
277
250
  return pubkeyPositionInScript(pubkey, script) !== -1;
278
251
  }
@@ -1,7 +1,8 @@
1
- import { crypto as bitCrypto, script as bitScript, networks, opcodes, Psbt, } from '@btc-vision/bitcoin';
1
+ import { networks, Psbt } from '@btc-vision/bitcoin';
2
2
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
3
3
  import { EcKeyPair } from '../../../keypair/EcKeyPair.js';
4
4
  import { CustomKeypair } from '../BrowserSignerBase.js';
5
+ import { canSignNonTaprootInput, isTaprootInput, pubkeyInScript, } from '../../../signer/SignerUtils.js';
5
6
  export class XverseSigner extends CustomKeypair {
6
7
  constructor() {
7
8
  super();
@@ -145,12 +146,9 @@ export class XverseSigner extends CustomKeypair {
145
146
  }
146
147
  }
147
148
  }
148
- else {
149
- const script = getInputRelevantScript(input);
150
- if (script && pubkeyInScript(this.publicKey, script)) {
151
- needsToSign = true;
152
- viaTaproot = false;
153
- }
149
+ else if (canSignNonTaprootInput(input, this.publicKey)) {
150
+ needsToSign = true;
151
+ viaTaproot = false;
154
152
  }
155
153
  if (needsToSign) {
156
154
  return {
@@ -256,44 +254,3 @@ export class XverseSigner extends CustomKeypair {
256
254
  return nonDuplicate;
257
255
  }
258
256
  }
259
- function isTaprootInput(input) {
260
- if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
261
- return true;
262
- }
263
- if (input.witnessUtxo) {
264
- const script = input.witnessUtxo.script;
265
- return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
266
- }
267
- return false;
268
- }
269
- function getInputRelevantScript(input) {
270
- if (input.redeemScript) {
271
- return input.redeemScript;
272
- }
273
- if (input.witnessScript) {
274
- return input.witnessScript;
275
- }
276
- if (input.witnessUtxo) {
277
- return input.witnessUtxo.script;
278
- }
279
- if (input.nonWitnessUtxo) {
280
- return null;
281
- }
282
- return null;
283
- }
284
- function pubkeyInScript(pubkey, script) {
285
- return pubkeyPositionInScript(pubkey, script) !== -1;
286
- }
287
- function pubkeyPositionInScript(pubkey, script) {
288
- const pubkeyHash = bitCrypto.hash160(pubkey);
289
- const pubkeyXOnly = toXOnly(pubkey);
290
- const decompiled = bitScript.decompile(script);
291
- if (decompiled === null)
292
- throw new Error('Unknown script error');
293
- return decompiled.findIndex((element) => {
294
- if (typeof element === 'number')
295
- return false;
296
- return (Buffer.isBuffer(element) &&
297
- (element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly)));
298
- });
299
- }
@@ -1,4 +1,5 @@
1
1
  import { TransactionType } from '../enums/TransactionType.js';
2
+ import { opcodes, script } from '@btc-vision/bitcoin';
2
3
  import { TransactionBuilder } from './TransactionBuilder.js';
3
4
  export class FundingTransaction extends TransactionBuilder {
4
5
  constructor(parameters) {
@@ -17,9 +18,13 @@ export class FundingTransaction extends TransactionBuilder {
17
18
  this.splitInputs(this.amount);
18
19
  }
19
20
  else if (this.isPubKeyDestination) {
21
+ const p2pkScript = script.compile([
22
+ Buffer.from(this.to.replace('0x', ''), 'hex'),
23
+ opcodes.OP_CHECKSIG,
24
+ ]);
20
25
  this.addOutput({
21
26
  value: Number(this.amount),
22
- script: Buffer.from(this.to.slice(2), 'hex'),
27
+ script: p2pkScript,
23
28
  });
24
29
  }
25
30
  else {
@@ -25,7 +25,9 @@ export class TransactionBuilder extends TweakedTransaction {
25
25
  this.priorityFee = parameters.priorityFee ?? 0n;
26
26
  this.utxos = parameters.utxos;
27
27
  this.to = parameters.to || undefined;
28
- this.isPubKeyDestination = this.to ? this.to.startsWith('0x') : false;
28
+ this.isPubKeyDestination = this.to
29
+ ? AddressVerificator.isValidPublicKey(this.to, this.network)
30
+ : false;
29
31
  this.optionalOutputs = parameters.optionalOutputs;
30
32
  this.from = TransactionBuilder.getFrom(parameters.from, this.signer, this.network);
31
33
  this.totalInputAmount = this.calculateTotalUTXOAmount();
@@ -48,7 +48,7 @@ export declare abstract class TweakedTransaction extends Logger {
48
48
  protected generateTapData(): Payment;
49
49
  protected generateScriptAddress(): Payment;
50
50
  protected getSignerKey(): Signer | ECPairInterface;
51
- protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean): Promise<void>;
51
+ protected signInput(transaction: Psbt, input: PsbtInput, i: number, signer: Signer | ECPairInterface, reverse?: boolean, errored?: boolean): Promise<void>;
52
52
  protected splitArray<T>(arr: T[], chunkSize: number): T[][];
53
53
  protected signInputs(transaction: Psbt): Promise<void>;
54
54
  protected internalPubKeyToXOnly(): Buffer;
@@ -60,6 +60,10 @@ export declare abstract class TweakedTransaction extends Logger {
60
60
  redeemScript: Buffer;
61
61
  outputScript: Buffer;
62
62
  } | undefined;
63
+ protected generateP2SHP2PKHRedeemScript(inputAddr: string): {
64
+ redeemScript: Buffer;
65
+ outputScript: Buffer;
66
+ } | undefined;
63
67
  protected generatePsbtInputExtended(utxo: UTXO, i: number): PsbtInputExtended;
64
68
  protected customFinalizerP2SH: (inputIndex: number, input: PsbtInput, scriptA: Buffer, isSegwit: boolean, isP2SH: boolean, isP2WSH: boolean) => {
65
69
  finalScriptSig: Buffer | undefined;
@@ -68,11 +72,6 @@ export declare abstract class TweakedTransaction extends Logger {
68
72
  protected signInputsWalletBased(transaction: Psbt): Promise<void>;
69
73
  private attemptSignTaproot;
70
74
  private isTaprootScriptSpend;
71
- private isTaprootInput;
72
- private canSignNonTaprootInput;
73
- private getInputRelevantScript;
74
- private pubkeyInScript;
75
- private pubkeyPositionInScript;
76
75
  private signTaprootInput;
77
76
  private signNonTaprootInput;
78
77
  }
@@ -2,9 +2,9 @@ import { Logger } from '@btc-vision/logger';
2
2
  import { address as bitAddress, crypto as bitCrypto, getFinalScripts, opcodes, payments, script, } from '@btc-vision/bitcoin';
3
3
  import { TweakedSigner } from '../../signer/TweakedSigner.js';
4
4
  import { toXOnly } from '@btc-vision/bitcoin/src/psbt/bip371.js';
5
- import { AddressTypes, AddressVerificator } from '../../keypair/AddressVerificator.js';
6
5
  import { varuint } from '@btc-vision/bitcoin/src/bufferutils.js';
7
- import * as bscript from '@btc-vision/bitcoin/src/script.js';
6
+ import { canSignNonTaprootInput, isTaprootInput, pubkeyInScript, } from '../../signer/SignerUtils.js';
7
+ import { isP2MS, isP2PK, isP2PKH, isP2SHScript, isP2TR, isP2WPKH, isP2WSHScript, } from '@btc-vision/bitcoin/src/psbt/psbtutils.js';
8
8
  export var TransactionSequence;
9
9
  (function (TransactionSequence) {
10
10
  TransactionSequence[TransactionSequence["REPLACE_BY_FEE"] = 4294967293] = "REPLACE_BY_FEE";
@@ -154,36 +154,42 @@ export class TweakedTransaction extends Logger {
154
154
  getSignerKey() {
155
155
  return this.signer;
156
156
  }
157
- async signInput(transaction, input, i, signer, reverse = false) {
157
+ async signInput(transaction, input, i, signer, reverse = false, errored = false) {
158
158
  const publicKey = signer.publicKey;
159
- let isTaproot = this.isTaprootInput(input);
159
+ let isTaproot = isTaprootInput(input);
160
160
  if (reverse) {
161
161
  isTaproot = !isTaproot;
162
162
  }
163
163
  let signed = false;
164
+ let didError = false;
164
165
  if (isTaproot) {
165
166
  try {
166
167
  await this.attemptSignTaproot(transaction, input, i, signer, publicKey);
167
168
  signed = true;
168
169
  }
169
170
  catch (e) {
170
- this.error(`Failed to sign Taproot script path input ${i}: ${e}`);
171
+ this.error(`Failed to sign Taproot script path input ${i} (reverse: ${reverse}): ${e.message}`);
172
+ didError = true;
171
173
  }
172
174
  }
173
175
  else {
174
- if (!reverse ? this.canSignNonTaprootInput(input, publicKey) : true) {
176
+ if (!reverse ? canSignNonTaprootInput(input, publicKey) : true) {
175
177
  try {
176
178
  await this.signNonTaprootInput(signer, transaction, i);
177
179
  signed = true;
178
180
  }
179
181
  catch (e) {
180
- this.error(`Failed to sign non-Taproot input ${i}: ${e}`);
182
+ this.error(`Failed to sign non-Taproot input ${i}: ${e.stack}`);
183
+ didError = true;
181
184
  }
182
185
  }
183
186
  }
184
187
  if (!signed) {
188
+ if (didError && errored) {
189
+ throw new Error(`Failed to sign input ${i} with the provided signer.`);
190
+ }
185
191
  try {
186
- await this.signInput(transaction, input, i, signer, true);
192
+ await this.signInput(transaction, input, i, signer, true, didError);
187
193
  }
188
194
  catch {
189
195
  throw new Error(`Cannot sign input ${i} with the provided signer.`);
@@ -296,51 +302,127 @@ export class TweakedTransaction extends Logger {
296
302
  }
297
303
  return;
298
304
  }
305
+ generateP2SHP2PKHRedeemScript(inputAddr) {
306
+ const pubkey = Buffer.isBuffer(this.signer.publicKey)
307
+ ? this.signer.publicKey
308
+ : Buffer.from(this.signer.publicKey, 'hex');
309
+ const w = payments.p2wpkh({
310
+ pubkey: pubkey,
311
+ network: this.network,
312
+ });
313
+ const p = payments.p2sh({
314
+ redeem: w,
315
+ network: this.network,
316
+ });
317
+ const address = p.address;
318
+ const redeemScript = p.redeem?.output;
319
+ if (!redeemScript) {
320
+ throw new Error('Failed to generate P2SH-P2WPKH redeem script');
321
+ }
322
+ if (address === inputAddr && p.redeem && p.redeem.output && p.output) {
323
+ return {
324
+ redeemScript: p.redeem.output,
325
+ outputScript: p.output,
326
+ };
327
+ }
328
+ return;
329
+ }
299
330
  generatePsbtInputExtended(utxo, i) {
331
+ const script = Buffer.from(utxo.scriptPubKey.hex, 'hex');
300
332
  const input = {
301
333
  hash: utxo.transactionId,
302
334
  index: utxo.outputIndex,
303
335
  sequence: this.sequence,
304
336
  witnessUtxo: {
305
337
  value: Number(utxo.value),
306
- script: Buffer.from(utxo.scriptPubKey.hex, 'hex'),
338
+ script,
307
339
  },
308
340
  };
309
- if (utxo.scriptPubKey.address) {
310
- try {
311
- const addressType = AddressVerificator.detectAddressType(utxo.scriptPubKey.address, this.network);
312
- if (addressType === AddressTypes.P2SH_OR_P2SH_P2WPKH) {
313
- const redeemScript = this.generateP2SHRedeemScriptLegacy(utxo.scriptPubKey.address);
314
- if (!redeemScript) {
315
- throw new Error('Failed to generate redeem script');
316
- }
317
- input.redeemScript = redeemScript.outputScript;
318
- input.witnessScript = redeemScript.redeemScript;
341
+ if (isP2PKH(script)) {
342
+ if (utxo.nonWitnessUtxo) {
343
+ input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
344
+ ? utxo.nonWitnessUtxo
345
+ : Buffer.from(utxo.nonWitnessUtxo, 'hex');
346
+ }
347
+ else {
348
+ throw new Error('Missing nonWitnessUtxo for P2PKH UTXO');
349
+ }
350
+ }
351
+ else if (isP2WPKH(script)) {
352
+ }
353
+ else if (isP2WSHScript(script)) {
354
+ if (!utxo.witnessScript) {
355
+ throw new Error('Missing witnessScript for P2WSH UTXO');
356
+ }
357
+ input.witnessScript = Buffer.isBuffer(utxo.witnessScript)
358
+ ? utxo.witnessScript
359
+ : Buffer.from(utxo.witnessScript, 'hex');
360
+ }
361
+ else if (isP2SHScript(script)) {
362
+ let redeemScriptBuf;
363
+ if (utxo.redeemScript) {
364
+ redeemScriptBuf = Buffer.isBuffer(utxo.redeemScript)
365
+ ? utxo.redeemScript
366
+ : Buffer.from(utxo.redeemScript, 'hex');
367
+ }
368
+ else {
369
+ if (!utxo.scriptPubKey.address) {
370
+ throw new Error('Missing redeemScript and no address to regenerate it for P2SH UTXO');
371
+ }
372
+ const legacyScripts = this.generateP2SHP2PKHRedeemScript(utxo.scriptPubKey.address);
373
+ if (!legacyScripts) {
374
+ throw new Error('Missing redeemScript for P2SH UTXO and unable to regenerate');
319
375
  }
376
+ redeemScriptBuf = legacyScripts.redeemScript;
320
377
  }
321
- catch (e) {
322
- this.error(`Failed to detect address type for ${utxo.scriptPubKey.address} - ${e}`);
378
+ input.redeemScript = redeemScriptBuf;
379
+ const payment = payments.p2sh({ redeem: { output: input.redeemScript } });
380
+ if (!payment.redeem) {
381
+ throw new Error('Failed to extract redeem script from P2SH UTXO');
382
+ }
383
+ const redeemOutput = payment.redeem.output;
384
+ if (!redeemOutput) {
385
+ throw new Error('Failed to extract redeem output from P2SH UTXO');
386
+ }
387
+ if (utxo.nonWitnessUtxo) {
388
+ input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
389
+ ? utxo.nonWitnessUtxo
390
+ : Buffer.from(utxo.nonWitnessUtxo, 'hex');
391
+ }
392
+ if (isP2WPKH(redeemOutput)) {
393
+ delete input.nonWitnessUtxo;
394
+ }
395
+ else if (isP2WSHScript(redeemOutput)) {
396
+ delete input.nonWitnessUtxo;
397
+ if (!input.witnessScript) {
398
+ throw new Error('Missing witnessScript for P2SH-P2WSH UTXO');
399
+ }
400
+ }
401
+ else {
402
+ delete input.witnessUtxo;
323
403
  }
324
404
  }
325
- if (utxo.nonWitnessUtxo) {
326
- input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
327
- ? utxo.nonWitnessUtxo
328
- : Buffer.from(utxo.nonWitnessUtxo, 'hex');
329
- }
330
- if (utxo.redeemScript) {
331
- input.redeemScript = Buffer.isBuffer(utxo.redeemScript)
332
- ? utxo.redeemScript
333
- : Buffer.from(utxo.redeemScript, 'hex');
334
- if (utxo.witnessScript) {
335
- input.witnessScript = Buffer.isBuffer(utxo.witnessScript)
336
- ? utxo.witnessScript
337
- : Buffer.from(utxo.witnessScript, 'hex');
405
+ else if (isP2TR(script)) {
406
+ if (this.sighashTypes) {
407
+ const inputSign = TweakedTransaction.calculateSignHash(this.sighashTypes);
408
+ if (inputSign)
409
+ input.sighashType = inputSign;
410
+ }
411
+ this.tweakSigner();
412
+ input.tapInternalKey = this.internalPubKeyToXOnly();
413
+ }
414
+ else if (isP2PK(script) || isP2MS(script)) {
415
+ if (utxo.nonWitnessUtxo) {
416
+ input.nonWitnessUtxo = Buffer.isBuffer(utxo.nonWitnessUtxo)
417
+ ? utxo.nonWitnessUtxo
418
+ : Buffer.from(utxo.nonWitnessUtxo, 'hex');
419
+ }
420
+ else {
421
+ throw new Error('Missing nonWitnessUtxo for P2PK or P2MS UTXO');
338
422
  }
339
423
  }
340
- if (this.sighashTypes) {
341
- const inputSign = TweakedTransaction.calculateSignHash(this.sighashTypes);
342
- if (inputSign)
343
- input.sighashType = inputSign;
424
+ else {
425
+ this.error(`Unknown or unsupported script type for output: ${utxo.scriptPubKey.hex}`);
344
426
  }
345
427
  if (this.tapLeafScript) {
346
428
  input.tapLeafScript = [this.tapLeafScript];
@@ -348,11 +430,6 @@ export class TweakedTransaction extends Logger {
348
430
  if (i === 0 && this.nonWitnessUtxo) {
349
431
  input.nonWitnessUtxo = this.nonWitnessUtxo;
350
432
  }
351
- if (utxo.scriptPubKey.address &&
352
- AddressVerificator.isValidP2TRAddress(utxo.scriptPubKey.address, this.network)) {
353
- this.tweakSigner();
354
- input.tapInternalKey = this.internalPubKeyToXOnly();
355
- }
356
433
  return input;
357
434
  }
358
435
  async signInputsWalletBased(transaction) {
@@ -389,60 +466,13 @@ export class TweakedTransaction extends Logger {
389
466
  isTaprootScriptSpend(input, publicKey) {
390
467
  if (input.tapLeafScript && input.tapLeafScript.length > 0) {
391
468
  for (const tapLeafScript of input.tapLeafScript) {
392
- if (this.pubkeyInScript(publicKey, tapLeafScript.script)) {
469
+ if (pubkeyInScript(publicKey, tapLeafScript.script)) {
393
470
  return true;
394
471
  }
395
472
  }
396
473
  }
397
474
  return false;
398
475
  }
399
- isTaprootInput(input) {
400
- if (input.tapInternalKey || input.tapKeySig || input.tapScriptSig || input.tapLeafScript) {
401
- return true;
402
- }
403
- if (input.witnessUtxo) {
404
- const script = input.witnessUtxo.script;
405
- return script.length === 34 && script[0] === opcodes.OP_1 && script[1] === 0x20;
406
- }
407
- return false;
408
- }
409
- canSignNonTaprootInput(input, publicKey) {
410
- const script = this.getInputRelevantScript(input);
411
- if (script) {
412
- return this.pubkeyInScript(publicKey, script);
413
- }
414
- return false;
415
- }
416
- getInputRelevantScript(input) {
417
- if (input.redeemScript) {
418
- return input.redeemScript;
419
- }
420
- if (input.witnessScript) {
421
- return input.witnessScript;
422
- }
423
- if (input.witnessUtxo) {
424
- return input.witnessUtxo.script;
425
- }
426
- if (input.nonWitnessUtxo) {
427
- return null;
428
- }
429
- return null;
430
- }
431
- pubkeyInScript(pubkey, script) {
432
- return this.pubkeyPositionInScript(pubkey, script) !== -1;
433
- }
434
- pubkeyPositionInScript(pubkey, script) {
435
- const pubkeyHash = bitCrypto.hash160(pubkey);
436
- const pubkeyXOnly = toXOnly(pubkey);
437
- const decompiled = bscript.decompile(script);
438
- if (decompiled === null)
439
- throw new Error('Unknown script error');
440
- return decompiled.findIndex((element) => {
441
- if (typeof element === 'number')
442
- return false;
443
- return (element.equals(pubkey) || element.equals(pubkeyHash) || element.equals(pubkeyXOnly));
444
- });
445
- }
446
476
  async signTaprootInput(signer, transaction, i, tapLeafHash) {
447
477
  if ('signTaprootInput' in signer) {
448
478
  try {