@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.
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.d.ts +7 -0
- package/dist/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.js +29 -5
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.js +2 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +1 -0
- package/dist/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.js +2 -0
- package/dist/swaps/spv_vault_swap/SpvVaultSwapHandler.js +2 -1
- package/dist/swaps/spv_vault_swap/SpvVaults.js +1 -1
- package/dist/utils/BitcoinUtils.d.ts +2 -0
- package/dist/utils/BitcoinUtils.js +45 -0
- package/package.json +1 -1
- package/src/swaps/escrow/frombtcln_autoinit/FromBtcLnAuto.ts +34 -7
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnAbs.ts +2 -0
- package/src/swaps/escrow/tobtcln_abstract/ToBtcLnSwapAbs.ts +3 -0
- package/src/swaps/spv_vault_swap/SpvVaultSwapHandler.ts +2 -1
- package/src/swaps/spv_vault_swap/SpvVaults.ts +1 -1
- package/src/utils/BitcoinUtils.ts +42 -0
|
@@ -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 (
|
|
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
|
-
|
|
40
|
-
|
|
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,
|
|
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,
|
|
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,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
|
@@ -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(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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
|
+
}
|