@atomiqlabs/lp-lib 14.0.0-dev.16 → 14.0.0-dev.19

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.
@@ -32,6 +32,7 @@ export type FromBtcLnAutoRequestType = {
32
32
  export declare class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, FromBtcLnAutoSwapState> {
33
33
  readonly type = SwapHandlerType.FROM_BTCLN_AUTO;
34
34
  readonly swapType = ChainSwapType.HTLC;
35
+ activeSubscriptions: Set<string>;
35
36
  readonly config: FromBtcLnAutoConfig;
36
37
  readonly lightning: ILightningWallet;
37
38
  readonly LightningAssertions: LightningAssertions;
@@ -46,6 +47,12 @@ export declare class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoS
46
47
  protected processInitializeEvent(chainIdentifier: string, savedSwap: FromBtcLnAutoSwap, event: InitializeEvent<SwapData>): Promise<void>;
47
48
  protected processClaimEvent(chainIdentifier: string, savedSwap: FromBtcLnAutoSwap, event: ClaimEvent<SwapData>): Promise<void>;
48
49
  protected processRefundEvent(chainIdentifier: string, savedSwap: FromBtcLnAutoSwap, event: RefundEvent<SwapData>): Promise<void>;
50
+ /**
51
+ * Subscribe to a lightning network invoice
52
+ *
53
+ * @param swap
54
+ */
55
+ private subscribeToInvoice;
49
56
  /**
50
57
  * Called when lightning HTLC is received, also signs an init transaction on the smart chain side, expiry of the
51
58
  * smart chain authorization starts ticking as soon as this HTLC is received
@@ -19,6 +19,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
19
19
  super(storageDirectory, path, chains, swapPricing, config);
20
20
  this.type = SwapHandler_1.SwapHandlerType.FROM_BTCLN_AUTO;
21
21
  this.swapType = base_1.ChainSwapType.HTLC;
22
+ this.activeSubscriptions = new Set();
22
23
  this.config = config;
23
24
  this.config.invoiceTimeoutSeconds = this.config.invoiceTimeoutSeconds || 90;
24
25
  this.lightning = lightning;
@@ -34,10 +35,12 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
34
35
  if (!isBeingPaid) {
35
36
  //Not paid
36
37
  const isInvoiceExpired = parsedPR.expiryEpochMillis < Date.now();
37
- if (!isInvoiceExpired)
38
+ if (isInvoiceExpired) {
39
+ this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap LN invoice expired, cancelling, invoice: " + swap.pr);
40
+ await this.cancelSwapAndInvoice(swap);
38
41
  return null;
39
- this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap LN invoice expired, cancelling, invoice: " + swap.pr);
40
- await this.cancelSwapAndInvoice(swap);
42
+ }
43
+ this.subscribeToInvoice(swap);
41
44
  return null;
42
45
  }
43
46
  //Adjust the state of the swap and expiry
@@ -219,6 +222,24 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
219
222
  // await this.lightning.cancelHodlInvoice(savedSwap.lnPaymentHash);
220
223
  await this.removeSwapData(savedSwap, FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.REFUNDED);
221
224
  }
225
+ /**
226
+ * Subscribe to a lightning network invoice
227
+ *
228
+ * @param swap
229
+ */
230
+ subscribeToInvoice(swap) {
231
+ const paymentHash = swap.lnPaymentHash;
232
+ if (this.activeSubscriptions.has(paymentHash))
233
+ return false;
234
+ this.lightning.waitForInvoice(paymentHash).then(result => {
235
+ this.swapLogger.info(swap, "subscribeToInvoice(): result callback, outcome: " + result.status + " invoice: " + swap.pr);
236
+ this.htlcReceived(swap, result).catch(e => this.swapLogger.error(swap, "subscribeToInvoice(): HTLC received result", e));
237
+ this.activeSubscriptions.delete(paymentHash);
238
+ });
239
+ this.swapLogger.info(swap, "subscribeToInvoice(): subscribe to invoice: " + swap.pr);
240
+ this.activeSubscriptions.add(paymentHash);
241
+ return true;
242
+ }
222
243
  /**
223
244
  * Called when lightning HTLC is received, also signs an init transaction on the smart chain side, expiry of the
224
245
  * smart chain authorization starts ticking as soon as this HTLC is received
@@ -227,6 +248,8 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
227
248
  * @param invoice
228
249
  */
229
250
  async htlcReceived(invoiceData, invoice) {
251
+ if (invoiceData.state !== FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.CREATED)
252
+ return;
230
253
  this.swapLogger.debug(invoiceData, "htlcReceived(): invoice: ", invoice);
231
254
  if (invoiceData.metadata != null)
232
255
  invoiceData.metadata.times.htlcReceived = Date.now();
@@ -257,8 +280,8 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
257
280
  //Setting the state variable is done outside the promise, so is done synchronously
258
281
  await invoiceData.setState(FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED);
259
282
  await this.saveSwapData(invoiceData);
283
+ await this.offerHtlc(invoiceData);
260
284
  }
261
- await this.offerHtlc(invoiceData);
262
285
  }
263
286
  async offerHtlc(invoiceData) {
264
287
  if (invoiceData.state !== FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.RECEIVED)
@@ -310,7 +333,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
310
333
  //Setting the state variable is done outside the promise, so is done synchronously
311
334
  await invoiceData.setState(FromBtcLnAutoSwap_1.FromBtcLnAutoSwapState.TXS_SENT);
312
335
  await this.saveSwapData(invoiceData);
313
- await chainInterface.sendAndConfirm(signer, [...txWithdraw, ...txInit], true);
336
+ await chainInterface.sendAndConfirm(signer, [...txWithdraw, ...txInit], true, undefined, true);
314
337
  }
315
338
  return true;
316
339
  }
@@ -577,6 +600,7 @@ class FromBtcLnAuto extends FromBtcBaseSwapHandler_1.FromBtcBaseSwapHandler {
577
600
  await PluginManager_1.PluginManager.swapCreate(createdSwap);
578
601
  await this.saveSwapData(createdSwap);
579
602
  this.swapLogger.info(createdSwap, "REST: /createInvoice: Created swap invoice: " + hodlInvoice.request + " amount: " + totalBtcInput.toString(10));
603
+ this.subscribeToInvoice(createdSwap);
580
604
  await responseStream.writeParamsAndEnd({
581
605
  code: 20000,
582
606
  msg: "Success",
@@ -232,6 +232,8 @@ class ToBtcLnAbs extends ToBtcBaseSwapHandler_1.ToBtcBaseSwapHandler {
232
232
  " maxFee: " + maxFee.toString(10) +
233
233
  " invoice: " + swap.pr);
234
234
  const blockHeight = await this.lightning.getBlockheight();
235
+ swap.payInitiated = true;
236
+ await this.saveSwapData(swap);
235
237
  try {
236
238
  await this.lightning.pay({
237
239
  request: swap.pr,
@@ -12,6 +12,7 @@ export declare enum ToBtcLnSwapState {
12
12
  export declare class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap<T, ToBtcLnSwapState> {
13
13
  lnPaymentHash: string;
14
14
  readonly pr: string;
15
+ payInitiated: boolean;
15
16
  secret: string;
16
17
  constructor(chainIdentifier: string, lnPaymentHash: string, pr: string, amountMtokens: bigint, swapFee: bigint, swapFeeInToken: bigint, quotedNetworkFee: bigint, quotedNetworkFeeInToken: bigint);
17
18
  constructor(obj: any);
@@ -27,6 +27,7 @@ class ToBtcLnSwapAbs extends ToBtcBaseSwap_1.ToBtcBaseSwap {
27
27
  this.pr = chainIdOrObj.pr;
28
28
  this.secret = chainIdOrObj.secret;
29
29
  this.lnPaymentHash = chainIdOrObj.lnPaymentHash;
30
+ this.payInitiated = chainIdOrObj.payInitiated;
30
31
  //Compatibility with older versions
31
32
  this.quotedNetworkFee ?? (this.quotedNetworkFee = (0, Utils_1.deserializeBN)(chainIdOrObj.maxFee));
32
33
  this.realNetworkFee ?? (this.realNetworkFee = (0, Utils_1.deserializeBN)(chainIdOrObj.realRoutingFee));
@@ -41,6 +42,7 @@ class ToBtcLnSwapAbs extends ToBtcBaseSwap_1.ToBtcBaseSwap {
41
42
  partialSerialized.pr = this.pr;
42
43
  partialSerialized.lnPaymentHash = this.lnPaymentHash;
43
44
  partialSerialized.secret = this.secret;
45
+ partialSerialized.payInitiated = this.payInitiated;
44
46
  return partialSerialized;
45
47
  }
46
48
  isInitiated() {
@@ -12,6 +12,7 @@ const FromBtcAmountAssertions_1 = require("../assertions/FromBtcAmountAssertions
12
12
  const crypto_1 = require("crypto");
13
13
  const btc_signer_1 = require("@scure/btc-signer");
14
14
  const SpvVaults_1 = require("./SpvVaults");
15
+ const BitcoinUtils_1 = require("../../utils/BitcoinUtils");
15
16
  const TX_MAX_VSIZE = 16 * 1024;
16
17
  class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
17
18
  constructor(storageDirectory, vaultStorage, path, chainsData, swapPricing, bitcoin, bitcoinRpc, spvVaultSigner, config) {
@@ -342,7 +343,7 @@ class SpvVaultSwapHandler extends SwapHandler_1.SwapHandler {
342
343
  //Check correct psbt
343
344
  for (let i = 1; i < transaction.inputsLength; i++) { //Skip first vault input
344
345
  const txIn = transaction.getInput(i);
345
- if ((0, btc_signer_1.getInputType)(txIn).txType === "legacy")
346
+ if ((0, BitcoinUtils_1.isLegacyInput)(txIn))
346
347
  throw {
347
348
  code: 20514,
348
349
  msg: "Legacy (pre-segwit) inputs in tx are not allowed!"
@@ -217,7 +217,7 @@ class SpvVaults {
217
217
  }
218
218
  const txs = await spvVaultContract.txsOpen(signer.getAddress(), vault.data);
219
219
  let numTx = 0;
220
- const txIds = await chainInterface.sendAndConfirm(signer, txs, true, undefined, false, async (txId, rawTx) => {
220
+ const txIds = await chainInterface.sendAndConfirm(signer, txs, true, undefined, true, async (txId, rawTx) => {
221
221
  numTx++;
222
222
  if (numTx === txs.length) {
223
223
  //Final tx
@@ -0,0 +1,2 @@
1
+ import { TransactionInput } from "@scure/btc-signer/psbt";
2
+ export declare function isLegacyInput(input: TransactionInput): boolean;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isLegacyInput = void 0;
4
+ const utxo_1 = require("@scure/btc-signer/utxo");
5
+ const btc_signer_1 = require("@scure/btc-signer");
6
+ function parsePushOpcode(script) {
7
+ if (script[0] === 0x00) {
8
+ return Uint8Array.from([]);
9
+ }
10
+ if (script[0] <= 0x4b) {
11
+ return script.slice(1, 1 + script[0]);
12
+ }
13
+ if (script[0] === 0x4c) {
14
+ return script.slice(2, 2 + script[1]);
15
+ }
16
+ if (script[0] === 0x4d) {
17
+ const length = Buffer.from(script.slice(1, 3)).readUInt16LE();
18
+ return script.slice(3, 3 + length);
19
+ }
20
+ if (script[0] === 0x4e) {
21
+ const length = Buffer.from(script.slice(1, 5)).readUInt32LE();
22
+ return script.slice(5, 5 + length);
23
+ }
24
+ if (script[0] === 0x4f) {
25
+ return Uint8Array.from([0x81]);
26
+ }
27
+ if (script[0] >= 0x51 && script[0] <= 0x60) {
28
+ return Uint8Array.from([script[0] - 0x50]);
29
+ }
30
+ throw new Error("No push opcode detected");
31
+ }
32
+ function isLegacyInput(input) {
33
+ const prevOut = (0, utxo_1.getPrevOut)(input);
34
+ const first = btc_signer_1.OutScript.decode(prevOut.script);
35
+ if (first.type === "tr" || first.type === "wsh" || first.type === "wpkh")
36
+ return false;
37
+ if (first.type === "sh") {
38
+ const redeemScript = input.redeemScript ?? parsePushOpcode(input.finalScriptSig);
39
+ const second = btc_signer_1.OutScript.decode(redeemScript);
40
+ if (second.type === "wsh" || second.type === "wpkh")
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+ exports.isLegacyInput = isLegacyInput;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atomiqlabs/lp-lib",
3
- "version": "14.0.0-dev.16",
3
+ "version": "14.0.0-dev.19",
4
4
  "description": "Main functionality implementation for atomiq LP node",
5
5
  "main": "./dist/index.js",
6
6
  "types:": "./dist/index.d.ts",
@@ -20,6 +20,7 @@ import {
20
20
  } from "../../../wallets/ILightningWallet";
21
21
  import {LightningAssertions} from "../../assertions/LightningAssertions";
22
22
  import {FromBtcLnSwapState} from "../frombtcln_abstract/FromBtcLnSwapAbs";
23
+ import {ToBtcLnSwapAbs} from "../tobtcln_abstract/ToBtcLnSwapAbs";
23
24
 
24
25
  export type FromBtcLnAutoConfig = FromBtcBaseConfig & {
25
26
  invoiceTimeoutSeconds?: number,
@@ -47,6 +48,8 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
47
48
  readonly type = SwapHandlerType.FROM_BTCLN_AUTO;
48
49
  readonly swapType = ChainSwapType.HTLC;
49
50
 
51
+ activeSubscriptions: Set<string> = new Set<string>();
52
+
50
53
  readonly config: FromBtcLnAutoConfig;
51
54
  readonly lightning: ILightningWallet;
52
55
  readonly LightningAssertions: LightningAssertions;
@@ -77,10 +80,12 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
77
80
  if(!isBeingPaid) {
78
81
  //Not paid
79
82
  const isInvoiceExpired = parsedPR.expiryEpochMillis<Date.now();
80
- if(!isInvoiceExpired) return null;
81
-
82
- this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap LN invoice expired, cancelling, invoice: "+swap.pr);
83
- await this.cancelSwapAndInvoice(swap);
83
+ if(isInvoiceExpired) {
84
+ this.swapLogger.info(swap, "processPastSwap(state=CREATED): swap LN invoice expired, cancelling, invoice: "+swap.pr);
85
+ await this.cancelSwapAndInvoice(swap);
86
+ return null;
87
+ }
88
+ this.subscribeToInvoice(swap);
84
89
  return null;
85
90
  }
86
91
 
@@ -282,6 +287,26 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
282
287
  await this.removeSwapData(savedSwap, FromBtcLnAutoSwapState.REFUNDED)
283
288
  }
284
289
 
290
+ /**
291
+ * Subscribe to a lightning network invoice
292
+ *
293
+ * @param swap
294
+ */
295
+ private subscribeToInvoice(swap: FromBtcLnAutoSwap): boolean {
296
+ const paymentHash = swap.lnPaymentHash;
297
+ if(this.activeSubscriptions.has(paymentHash)) return false;
298
+
299
+ this.lightning.waitForInvoice(paymentHash).then(result => {
300
+ this.swapLogger.info(swap, "subscribeToInvoice(): result callback, outcome: "+result.status+" invoice: "+swap.pr);
301
+ this.htlcReceived(swap, result).catch(e => this.swapLogger.error(swap, "subscribeToInvoice(): HTLC received result", e));
302
+ this.activeSubscriptions.delete(paymentHash);
303
+ });
304
+ this.swapLogger.info(swap, "subscribeToInvoice(): subscribe to invoice: "+swap.pr);
305
+
306
+ this.activeSubscriptions.add(paymentHash);
307
+ return true;
308
+ }
309
+
285
310
  /**
286
311
  * Called when lightning HTLC is received, also signs an init transaction on the smart chain side, expiry of the
287
312
  * smart chain authorization starts ticking as soon as this HTLC is received
@@ -290,6 +315,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
290
315
  * @param invoice
291
316
  */
292
317
  private async htlcReceived(invoiceData: FromBtcLnAutoSwap, invoice: LightningNetworkInvoice) {
318
+ if(invoiceData.state!==FromBtcLnAutoSwapState.CREATED) return;
293
319
  this.swapLogger.debug(invoiceData, "htlcReceived(): invoice: ", invoice);
294
320
  if(invoiceData.metadata!=null) invoiceData.metadata.times.htlcReceived = Date.now();
295
321
 
@@ -336,9 +362,9 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
336
362
  await invoiceData.setState(FromBtcLnAutoSwapState.RECEIVED);
337
363
 
338
364
  await this.saveSwapData(invoiceData);
339
- }
340
365
 
341
- await this.offerHtlc(invoiceData);
366
+ await this.offerHtlc(invoiceData);
367
+ }
342
368
  }
343
369
 
344
370
  private async offerHtlc(invoiceData: FromBtcLnAutoSwap) {
@@ -394,7 +420,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
394
420
  //Setting the state variable is done outside the promise, so is done synchronously
395
421
  await invoiceData.setState(FromBtcLnAutoSwapState.TXS_SENT);
396
422
  await this.saveSwapData(invoiceData);
397
- await chainInterface.sendAndConfirm(signer, [...txWithdraw, ...txInit], true);
423
+ await chainInterface.sendAndConfirm(signer, [...txWithdraw, ...txInit], true, undefined, true);
398
424
  }
399
425
 
400
426
  return true;
@@ -722,6 +748,7 @@ export class FromBtcLnAuto extends FromBtcBaseSwapHandler<FromBtcLnAutoSwap, Fro
722
748
  await this.saveSwapData(createdSwap);
723
749
 
724
750
  this.swapLogger.info(createdSwap, "REST: /createInvoice: Created swap invoice: "+hodlInvoice.request+" amount: "+totalBtcInput.toString(10));
751
+ this.subscribeToInvoice(createdSwap);
725
752
 
726
753
  await responseStream.writeParamsAndEnd({
727
754
  code: 20000,
@@ -338,6 +338,8 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
338
338
 
339
339
  const blockHeight = await this.lightning.getBlockheight();
340
340
 
341
+ swap.payInitiated = true;
342
+ await this.saveSwapData(swap);
341
343
  try {
342
344
  await this.lightning.pay({
343
345
  request: swap.pr,
@@ -17,6 +17,7 @@ export class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap
17
17
 
18
18
  lnPaymentHash: string;
19
19
  readonly pr: string;
20
+ payInitiated: boolean;
20
21
 
21
22
  secret: string;
22
23
 
@@ -43,6 +44,7 @@ export class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap
43
44
  this.pr = chainIdOrObj.pr;
44
45
  this.secret = chainIdOrObj.secret;
45
46
  this.lnPaymentHash = chainIdOrObj.lnPaymentHash;
47
+ this.payInitiated = chainIdOrObj.payInitiated;
46
48
 
47
49
  //Compatibility with older versions
48
50
  this.quotedNetworkFee ??= deserializeBN(chainIdOrObj.maxFee);
@@ -60,6 +62,7 @@ export class ToBtcLnSwapAbs<T extends SwapData = SwapData> extends ToBtcBaseSwap
60
62
  partialSerialized.pr = this.pr;
61
63
  partialSerialized.lnPaymentHash = this.lnPaymentHash;
62
64
  partialSerialized.secret = this.secret;
65
+ partialSerialized.payInitiated = this.payInitiated;
63
66
  return partialSerialized;
64
67
  }
65
68
 
@@ -29,6 +29,7 @@ import {FromBtcAmountAssertions} from "../assertions/FromBtcAmountAssertions";
29
29
  import {randomBytes} from "crypto";
30
30
  import {getInputType, OutScript, Transaction} from "@scure/btc-signer";
31
31
  import {SpvVaults, VAULT_DUST_AMOUNT} from "./SpvVaults";
32
+ import {isLegacyInput} from "../../utils/BitcoinUtils";
32
33
 
33
34
  export type SpvVaultSwapHandlerConfig = SwapBaseConfig & {
34
35
  vaultsCheckInterval: number,
@@ -471,7 +472,7 @@ export class SpvVaultSwapHandler extends SwapHandler<SpvVaultSwap, SpvVaultSwapS
471
472
  //Check correct psbt
472
473
  for(let i=1;i<transaction.inputsLength;i++) { //Skip first vault input
473
474
  const txIn = transaction.getInput(i);
474
- if(getInputType(txIn).txType==="legacy") throw {
475
+ if(isLegacyInput(txIn)) throw {
475
476
  code: 20514,
476
477
  msg: "Legacy (pre-segwit) inputs in tx are not allowed!"
477
478
  };
@@ -272,7 +272,7 @@ export class SpvVaults {
272
272
  const txs = await spvVaultContract.txsOpen(signer.getAddress(), vault.data);
273
273
  let numTx = 0;
274
274
  const txIds = await chainInterface.sendAndConfirm(
275
- signer, txs, true, undefined, false,
275
+ signer, txs, true, undefined, true,
276
276
  async (txId: string, rawTx: string) => {
277
277
  numTx++;
278
278
  if(numTx===txs.length) {
@@ -0,0 +1,42 @@
1
+ import {TransactionInput} from "@scure/btc-signer/psbt";
2
+ import {getPrevOut} from "@scure/btc-signer/utxo";
3
+ import {OutScript} from "@scure/btc-signer";
4
+
5
+ function parsePushOpcode(script: Uint8Array): Uint8Array {
6
+ if(script[0]===0x00) {
7
+ return Uint8Array.from([]);
8
+ }
9
+ if(script[0]<=0x4b) {
10
+ return script.slice(1, 1+script[0]);
11
+ }
12
+ if(script[0]===0x4c) {
13
+ return script.slice(2, 2+script[1]);
14
+ }
15
+ if(script[0]===0x4d) {
16
+ const length = Buffer.from(script.slice(1, 3)).readUInt16LE();
17
+ return script.slice(3, 3+length);
18
+ }
19
+ if(script[0]===0x4e) {
20
+ const length = Buffer.from(script.slice(1, 5)).readUInt32LE();
21
+ return script.slice(5, 5+length);
22
+ }
23
+ if(script[0]===0x4f) {
24
+ return Uint8Array.from([0x81]);
25
+ }
26
+ if(script[0]>=0x51 && script[0]<=0x60) {
27
+ return Uint8Array.from([script[0] - 0x50]);
28
+ }
29
+ throw new Error("No push opcode detected");
30
+ }
31
+
32
+ export function isLegacyInput(input: TransactionInput): boolean {
33
+ const prevOut = getPrevOut(input);
34
+ const first = OutScript.decode(prevOut.script);
35
+ if(first.type==="tr" || first.type==="wsh" || first.type==="wpkh") return false;
36
+ if(first.type==="sh") {
37
+ const redeemScript = input.redeemScript ?? parsePushOpcode(input.finalScriptSig);
38
+ const second = OutScript.decode(redeemScript);
39
+ if(second.type==="wsh" || second.type==="wpkh") return false;
40
+ }
41
+ return true;
42
+ }