@atomiqlabs/lp-lib 10.3.11 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -4
- package/dist/plugins/IPlugin.d.ts +3 -2
- package/dist/plugins/PluginManager.d.ts +3 -2
- package/dist/plugins/PluginManager.js +2 -2
- package/dist/swaps/FromBtcBaseSwap.d.ts +5 -1
- package/dist/swaps/FromBtcBaseSwap.js +20 -0
- package/dist/swaps/FromBtcBaseSwapHandler.d.ts +1 -0
- package/dist/swaps/FromBtcBaseSwapHandler.js +1 -1
- package/dist/swaps/FromBtcLnBaseSwapHandler.d.ts +8 -6
- package/dist/swaps/FromBtcLnBaseSwapHandler.js +7 -5
- package/dist/swaps/SwapHandler.d.ts +1 -4
- package/dist/swaps/SwapHandler.js +1 -2
- package/dist/swaps/SwapHandlerSwap.d.ts +4 -0
- package/dist/swaps/SwapHandlerSwap.js +9 -1
- package/dist/swaps/ToBtcBaseSwap.d.ts +3 -1
- package/dist/swaps/ToBtcBaseSwap.js +8 -2
- package/dist/swaps/ToBtcBaseSwapHandler.d.ts +1 -0
- package/dist/swaps/ToBtcBaseSwapHandler.js +1 -1
- package/dist/swaps/frombtc_abstract/FromBtcAbs.d.ts +3 -5
- package/dist/swaps/frombtc_abstract/FromBtcAbs.js +18 -25
- package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.d.ts +1 -4
- package/dist/swaps/frombtc_abstract/FromBtcSwapAbs.js +3 -16
- package/dist/swaps/frombtc_trusted/FromBtcTrusted.d.ts +6 -9
- package/dist/swaps/frombtc_trusted/FromBtcTrusted.js +238 -137
- package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.d.ts +9 -6
- package/dist/swaps/frombtc_trusted/FromBtcTrustedSwap.js +15 -10
- package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.d.ts +2 -2
- package/dist/swaps/frombtcln_abstract/FromBtcLnAbs.js +42 -62
- package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.d.ts +1 -6
- package/dist/swaps/frombtcln_abstract/FromBtcLnSwapAbs.js +2 -14
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.d.ts +3 -5
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrusted.js +64 -80
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.d.ts +1 -2
- package/dist/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.js +5 -8
- package/dist/swaps/tobtc_abstract/ToBtcAbs.d.ts +5 -125
- package/dist/swaps/tobtc_abstract/ToBtcAbs.js +41 -334
- package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.d.ts +1 -4
- package/dist/swaps/tobtc_abstract/ToBtcSwapAbs.js +2 -11
- package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.d.ts +5 -55
- package/dist/swaps/tobtcln_abstract/ToBtcLnAbs.js +152 -398
- package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.d.ts +1 -6
- package/dist/swaps/tobtcln_abstract/ToBtcLnSwapAbs.js +2 -15
- package/dist/utils/Utils.d.ts +0 -10
- package/dist/utils/Utils.js +1 -34
- package/dist/wallets/IBitcoinWallet.d.ts +62 -0
- package/dist/wallets/IBitcoinWallet.js +2 -0
- package/dist/wallets/ILightningWallet.d.ts +118 -0
- package/dist/wallets/ILightningWallet.js +37 -0
- package/package.json +4 -9
- package/src/index.ts +3 -5
- package/src/plugins/IPlugin.ts +4 -2
- package/src/plugins/PluginManager.ts +6 -3
- package/src/swaps/FromBtcBaseSwap.ts +24 -1
- package/src/swaps/FromBtcBaseSwapHandler.ts +6 -2
- package/src/swaps/FromBtcLnBaseSwapHandler.ts +22 -6
- package/src/swaps/SwapHandler.ts +1 -8
- package/src/swaps/SwapHandlerSwap.ts +14 -1
- package/src/swaps/ToBtcBaseSwap.ts +12 -3
- package/src/swaps/ToBtcBaseSwapHandler.ts +6 -2
- package/src/swaps/frombtc_abstract/FromBtcAbs.ts +24 -28
- package/src/swaps/frombtc_abstract/FromBtcSwapAbs.ts +3 -18
- package/src/swaps/frombtc_trusted/FromBtcTrusted.ts +260 -159
- package/src/swaps/frombtc_trusted/FromBtcTrustedSwap.ts +22 -15
- package/src/swaps/frombtcln_abstract/FromBtcLnAbs.ts +69 -79
- package/src/swaps/frombtcln_abstract/FromBtcLnSwapAbs.ts +3 -20
- package/src/swaps/frombtcln_trusted/FromBtcLnTrusted.ts +80 -97
- package/src/swaps/frombtcln_trusted/FromBtcLnTrustedSwap.ts +6 -9
- package/src/swaps/tobtc_abstract/ToBtcAbs.ts +52 -410
- package/src/swaps/tobtc_abstract/ToBtcSwapAbs.ts +3 -18
- package/src/swaps/tobtcln_abstract/ToBtcLnAbs.ts +157 -434
- package/src/swaps/tobtcln_abstract/ToBtcLnSwapAbs.ts +3 -20
- package/src/utils/Utils.ts +0 -31
- package/src/wallets/IBitcoinWallet.ts +66 -0
- package/src/wallets/ILightningWallet.ts +179 -0
- package/dist/fees/OneDollarFeeEstimator.d.ts +0 -16
- package/dist/fees/OneDollarFeeEstimator.js +0 -71
- package/dist/utils/coinselect2/accumulative.d.ts +0 -6
- package/dist/utils/coinselect2/accumulative.js +0 -44
- package/dist/utils/coinselect2/blackjack.d.ts +0 -6
- package/dist/utils/coinselect2/blackjack.js +0 -41
- package/dist/utils/coinselect2/index.d.ts +0 -16
- package/dist/utils/coinselect2/index.js +0 -40
- package/dist/utils/coinselect2/utils.d.ts +0 -64
- package/dist/utils/coinselect2/utils.js +0 -121
- package/src/fees/OneDollarFeeEstimator.ts +0 -95
- package/src/utils/coinselect2/accumulative.js +0 -32
- package/src/utils/coinselect2/accumulative.ts +0 -58
- package/src/utils/coinselect2/blackjack.js +0 -29
- package/src/utils/coinselect2/blackjack.ts +0 -54
- package/src/utils/coinselect2/index.js +0 -16
- package/src/utils/coinselect2/index.ts +0 -50
- package/src/utils/coinselect2/utils.js +0 -110
- package/src/utils/coinselect2/utils.ts +0 -183
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import * as BN from "bn.js";
|
|
2
2
|
import {Express, Request, Response} from "express";
|
|
3
|
-
import * as bolt11 from "@atomiqlabs/bolt11";
|
|
4
|
-
import * as lncli from "ln-service";
|
|
5
3
|
import {ToBtcLnSwapAbs, ToBtcLnSwapState} from "./ToBtcLnSwapAbs";
|
|
6
4
|
import {MultichainData, SwapHandlerType} from "../SwapHandler";
|
|
7
5
|
import {ISwapPrice} from "../ISwapPrice";
|
|
@@ -13,8 +11,7 @@ import {
|
|
|
13
11
|
SwapCommitStatus,
|
|
14
12
|
SwapData
|
|
15
13
|
} from "@atomiqlabs/base";
|
|
16
|
-
import {
|
|
17
|
-
import {expressHandlerWrapper, handleLndError, HEX_REGEX, isDefinedRuntimeError} from "../../utils/Utils";
|
|
14
|
+
import {expressHandlerWrapper, HEX_REGEX, isDefinedRuntimeError} from "../../utils/Utils";
|
|
18
15
|
import {PluginManager} from "../../plugins/PluginManager";
|
|
19
16
|
import {IIntermediaryStorage} from "../../storage/IIntermediaryStorage";
|
|
20
17
|
import {randomBytes} from "crypto";
|
|
@@ -23,7 +20,12 @@ import {IParamReader} from "../../utils/paramcoders/IParamReader";
|
|
|
23
20
|
import {FieldTypeEnum, verifySchema} from "../../utils/paramcoders/SchemaVerifier";
|
|
24
21
|
import {ServerParamEncoder} from "../../utils/paramcoders/server/ServerParamEncoder";
|
|
25
22
|
import {ToBtcBaseConfig, ToBtcBaseSwapHandler} from "../ToBtcBaseSwapHandler";
|
|
26
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
ILightningWallet,
|
|
25
|
+
OutgoingLightningNetworkPayment,
|
|
26
|
+
ParsedPaymentRequest, ProbeAndRouteInit,
|
|
27
|
+
ProbeAndRouteResponse, routesMatch
|
|
28
|
+
} from "../../wallets/ILightningWallet";
|
|
27
29
|
|
|
28
30
|
export type ToBtcLnConfig = ToBtcBaseConfig & {
|
|
29
31
|
routingFeeMultiplier: BN,
|
|
@@ -39,71 +41,13 @@ export type ToBtcLnConfig = ToBtcBaseConfig & {
|
|
|
39
41
|
exactInExpiry?: number
|
|
40
42
|
};
|
|
41
43
|
|
|
42
|
-
const SNOWFLAKE_LIST: Set<string> = new Set([
|
|
43
|
-
"038f8f113c580048d847d6949371726653e02b928196bad310e3eda39ff61723f6"
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
|
-
type ProbeAndRouteResponse = {
|
|
47
|
-
confidence: number,
|
|
48
|
-
fee: number,
|
|
49
|
-
fee_mtokens: string,
|
|
50
|
-
mtokens: string,
|
|
51
|
-
payment: string,
|
|
52
|
-
safe_fee: number,
|
|
53
|
-
safe_tokens: number,
|
|
54
|
-
timeout: number,
|
|
55
|
-
tokens: number
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
type LNRoutes = {
|
|
59
|
-
public_key: string,
|
|
60
|
-
fee_rate?: number,
|
|
61
|
-
cltv_delta?: number,
|
|
62
|
-
channel?: string,
|
|
63
|
-
base_fee_mtokens?: string
|
|
64
|
-
}[][];
|
|
65
|
-
|
|
66
|
-
function routesMatch(routesA: LNRoutes, routesB: LNRoutes) {
|
|
67
|
-
if(routesA===routesB) return true;
|
|
68
|
-
if(routesA==null || routesB==null) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
if(routesA.length!==routesB.length) return false;
|
|
72
|
-
for(let i=0;i<routesA.length;i++) {
|
|
73
|
-
if(routesA[i]===routesB[i]) continue;
|
|
74
|
-
if(routesA[i]==null || routesB[i]==null) {
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
if(routesA[i].length!==routesB[i].length) return false;
|
|
78
|
-
for(let e=0;e<routesA[i].length;e++) {
|
|
79
|
-
if(routesA[i][e]===routesB[i][e]) continue;
|
|
80
|
-
if(routesA[i][e]==null || routesB[i][e]==null) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
if(
|
|
84
|
-
routesA[i][e].public_key!==routesB[i][e].public_key ||
|
|
85
|
-
routesA[i][e].base_fee_mtokens!==routesB[i][e].base_fee_mtokens ||
|
|
86
|
-
routesA[i][e].channel!==routesB[i][e].channel ||
|
|
87
|
-
routesA[i][e].cltv_delta!==routesB[i][e].cltv_delta ||
|
|
88
|
-
routesA[i][e].fee_rate!==routesB[i][e].fee_rate
|
|
89
|
-
) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
44
|
type ExactInAuthorization = {
|
|
99
45
|
chainIdentifier: string,
|
|
100
46
|
reqId: string,
|
|
101
47
|
expiry: number,
|
|
102
48
|
|
|
103
49
|
amount: BN,
|
|
104
|
-
|
|
105
|
-
cltvDelta: number,
|
|
106
|
-
routes: LNRoutes,
|
|
50
|
+
initialInvoice: ParsedPaymentRequest,
|
|
107
51
|
|
|
108
52
|
quotedNetworkFeeInToken: BN,
|
|
109
53
|
swapFeeInToken: BN,
|
|
@@ -144,7 +88,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
144
88
|
|
|
145
89
|
activeSubscriptions: Set<string> = new Set<string>();
|
|
146
90
|
lightningLiquidityCache: {
|
|
147
|
-
|
|
91
|
+
liquidity: BN,
|
|
148
92
|
timestamp: number
|
|
149
93
|
};
|
|
150
94
|
|
|
@@ -156,15 +100,18 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
156
100
|
[reqId: string]: ExactInAuthorization
|
|
157
101
|
} = {};
|
|
158
102
|
|
|
103
|
+
readonly lightning: ILightningWallet;
|
|
104
|
+
|
|
159
105
|
constructor(
|
|
160
106
|
storageDirectory: IIntermediaryStorage<ToBtcLnSwapAbs>,
|
|
161
107
|
path: string,
|
|
162
108
|
chainData: MultichainData,
|
|
163
|
-
|
|
109
|
+
lightning: ILightningWallet,
|
|
164
110
|
swapPricing: ISwapPrice,
|
|
165
111
|
config: ToBtcLnConfig
|
|
166
112
|
) {
|
|
167
|
-
super(storageDirectory, path, chainData,
|
|
113
|
+
super(storageDirectory, path, chainData, swapPricing);
|
|
114
|
+
this.lightning = lightning;
|
|
168
115
|
const anyConfig = config as any;
|
|
169
116
|
anyConfig.minTsSendCltv = config.gracePeriod.add(config.bitcoinBlocktime.mul(config.minSendCltv).mul(config.safetyFactor));
|
|
170
117
|
this.config = anyConfig;
|
|
@@ -173,24 +120,6 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
173
120
|
this.config.exactInExpiry = this.config.exactInExpiry || 10*1000;
|
|
174
121
|
}
|
|
175
122
|
|
|
176
|
-
/**
|
|
177
|
-
* Fetches the payment info, returns null if payment not found
|
|
178
|
-
*
|
|
179
|
-
* @param paymentHash
|
|
180
|
-
* @private
|
|
181
|
-
*/
|
|
182
|
-
private async getPayment(paymentHash: string): Promise<any> {
|
|
183
|
-
try {
|
|
184
|
-
return await lncli.getPayment({
|
|
185
|
-
id: paymentHash,
|
|
186
|
-
lnd: this.LND
|
|
187
|
-
});
|
|
188
|
-
} catch (e) {
|
|
189
|
-
if (Array.isArray(e) && e[0] === 404 && e[1] === "SentPaymentNotFound") return null;
|
|
190
|
-
throw e;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
123
|
/**
|
|
195
124
|
* Cleans up exactIn authorization that are already past their expiry
|
|
196
125
|
*
|
|
@@ -207,12 +136,11 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
207
136
|
}
|
|
208
137
|
|
|
209
138
|
protected async processPastSwap(swap: ToBtcLnSwapAbs): Promise<void> {
|
|
210
|
-
|
|
211
|
-
const timestamp = new BN(Math.floor(Date.now()/1000)).sub(new BN(this.config.maxSkew));
|
|
139
|
+
const {swapContract, signer} = this.getChain(swap.chainIdentifier);
|
|
212
140
|
|
|
213
141
|
if (swap.state === ToBtcLnSwapState.SAVED) {
|
|
214
142
|
//Cancel the swaps where signature is expired
|
|
215
|
-
const isSignatureExpired = swap.
|
|
143
|
+
const isSignatureExpired = await swapContract.isInitAuthorizationExpired(swap.data, swap);
|
|
216
144
|
if(isSignatureExpired) {
|
|
217
145
|
this.swapLogger.info(swap, "processPastSwap(state=SAVED): signature expired, cancel uncommited swap, invoice: "+swap.pr);
|
|
218
146
|
await this.removeSwapData(swap, ToBtcLnSwapState.CANCELED);
|
|
@@ -220,8 +148,8 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
220
148
|
}
|
|
221
149
|
|
|
222
150
|
//Cancel the swaps where lightning invoice is expired
|
|
223
|
-
const decodedPR =
|
|
224
|
-
const isInvoiceExpired = decodedPR.
|
|
151
|
+
const decodedPR = await this.lightning.parsePaymentRequest(swap.pr);
|
|
152
|
+
const isInvoiceExpired = decodedPR.expiryEpochMillis < Date.now();
|
|
225
153
|
if (isInvoiceExpired) {
|
|
226
154
|
this.swapLogger.info(swap, "processPastSwap(state=SAVED): invoice expired, cancel uncommited swap, invoice: "+swap.pr);
|
|
227
155
|
await this.removeSwapData(swap, ToBtcLnSwapState.CANCELED);
|
|
@@ -237,8 +165,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
237
165
|
if (swap.state === ToBtcLnSwapState.NON_PAYABLE) {
|
|
238
166
|
//Remove expired swaps (as these can already be unilaterally refunded by the client), so we don't need
|
|
239
167
|
// to be able to cooperatively refund them
|
|
240
|
-
|
|
241
|
-
if(isSwapExpired) {
|
|
168
|
+
if(swapContract.isExpired(signer.getAddress(), swap.data)) {
|
|
242
169
|
this.swapLogger.info(swap, "processPastSwap(state=NON_PAYABLE): swap expired, removing swap data, invoice: "+swap.pr);
|
|
243
170
|
await this.removeSwapData(swap);
|
|
244
171
|
}
|
|
@@ -305,53 +232,51 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
305
232
|
* @param swap
|
|
306
233
|
* @param lnPaymentStatus
|
|
307
234
|
*/
|
|
308
|
-
private async processPaymentResult(swap: ToBtcLnSwapAbs, lnPaymentStatus:
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if(lnPaymentStatus.is_failed) {
|
|
314
|
-
this.swapLogger.info(swap, "processPaymentResult(): invoice payment failed, cancelling swap, invoice: "+swap.pr);
|
|
315
|
-
await swap.setState(ToBtcLnSwapState.NON_PAYABLE);
|
|
316
|
-
await this.storageManager.saveData(swap.data.getHash(), swap.data.getSequence(), swap);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
235
|
+
private async processPaymentResult(swap: ToBtcLnSwapAbs, lnPaymentStatus: OutgoingLightningNetworkPayment) {
|
|
236
|
+
switch(lnPaymentStatus.status) {
|
|
237
|
+
case "pending":
|
|
238
|
+
return;
|
|
319
239
|
|
|
320
|
-
|
|
240
|
+
case "failed":
|
|
241
|
+
this.swapLogger.info(swap, "processPaymentResult(): invoice payment failed, cancelling swap, invoice: "+swap.pr);
|
|
242
|
+
await swap.setState(ToBtcLnSwapState.NON_PAYABLE);
|
|
243
|
+
await this.storageManager.saveData(swap.data.getHash(), swap.data.getSequence(), swap);
|
|
244
|
+
return;
|
|
321
245
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
246
|
+
case "confirmed":
|
|
247
|
+
const {swapContract, signer} = this.getChain(swap.chainIdentifier);
|
|
248
|
+
|
|
249
|
+
swap.secret = lnPaymentStatus.secret;
|
|
250
|
+
swap.setRealNetworkFee(lnPaymentStatus.feeMtokens.div(new BN(1000)));
|
|
251
|
+
this.swapLogger.info(swap, "processPaymentResult(): invoice paid, secret: "+swap.secret+" realRoutingFee: "+swap.realNetworkFee.toString(10)+" invoice: "+swap.pr);
|
|
252
|
+
await swap.setState(ToBtcLnSwapState.PAID);
|
|
253
|
+
await this.storageManager.saveData(swap.data.getHash(), swap.data.getSequence(), swap);
|
|
254
|
+
|
|
255
|
+
//Check if escrow state exists
|
|
256
|
+
const isCommited = await swapContract.isCommited(swap.data);
|
|
257
|
+
if(!isCommited) {
|
|
258
|
+
const status = await swapContract.getCommitStatus(signer.getAddress(), swap.data);
|
|
259
|
+
if(status===SwapCommitStatus.PAID) {
|
|
260
|
+
//This is alright, we got the money
|
|
261
|
+
await this.removeSwapData(swap, ToBtcLnSwapState.CLAIMED);
|
|
262
|
+
return;
|
|
263
|
+
} else if(status===SwapCommitStatus.EXPIRED) {
|
|
264
|
+
//This means the user was able to refund before we were able to claim, no good
|
|
265
|
+
await this.removeSwapData(swap, ToBtcLnSwapState.REFUNDED);
|
|
266
|
+
}
|
|
267
|
+
this.swapLogger.warn(swap, "processPaymentResult(): tried to claim but escrow doesn't exist anymore,"+
|
|
268
|
+
" status: "+status+
|
|
269
|
+
" invoice: "+swap.pr);
|
|
337
270
|
return;
|
|
338
|
-
} else if(status===SwapCommitStatus.EXPIRED) {
|
|
339
|
-
//This means the user was able to refund before we were able to claim, no good
|
|
340
|
-
await this.removeSwapData(swap, ToBtcLnSwapState.REFUNDED);
|
|
341
271
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
272
|
+
|
|
273
|
+
const success = await this.tryClaimSwap(swap);
|
|
274
|
+
if(success) this.swapLogger.info(swap, "processPaymentResult(): swap claimed successfully, invoice: "+swap.pr);
|
|
345
275
|
return;
|
|
346
|
-
}
|
|
347
276
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return;
|
|
277
|
+
default:
|
|
278
|
+
throw new Error("Invalid lnPaymentStatus");
|
|
351
279
|
}
|
|
352
|
-
|
|
353
|
-
//This should never happen
|
|
354
|
-
throw new Error("Invalid lnPaymentStatus");
|
|
355
280
|
}
|
|
356
281
|
|
|
357
282
|
/**
|
|
@@ -363,25 +288,11 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
363
288
|
const paymentHash = invoiceData.data.getHash();
|
|
364
289
|
if(this.activeSubscriptions.has(paymentHash)) return false;
|
|
365
290
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const outcome = lnPaymentStatus.is_confirmed ? "success" : lnPaymentStatus.is_failed ? "failure" : null;
|
|
370
|
-
this.swapLogger.info(invoiceData, "subscribeToPayment(): result callback, outcome: "+outcome+" invoice: "+invoiceData.pr);
|
|
371
|
-
this.processPaymentResult(invoiceData, lnPaymentStatus).catch(e => this.swapLogger.error(invoiceData, "subscribeToPayment(): process payment result", e));
|
|
372
|
-
subscription.removeAllListeners();
|
|
291
|
+
this.lightning.waitForPayment(paymentHash).then(result => {
|
|
292
|
+
this.swapLogger.info(invoiceData, "subscribeToPayment(): result callback, outcome: "+result.status+" invoice: "+invoiceData.pr);
|
|
293
|
+
this.processPaymentResult(invoiceData, result).catch(e => this.swapLogger.error(invoiceData, "subscribeToPayment(): process payment result", e));
|
|
373
294
|
this.activeSubscriptions.delete(paymentHash);
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
subscription.on('confirmed', (payment) => onResult({
|
|
377
|
-
is_confirmed: true,
|
|
378
|
-
payment
|
|
379
|
-
}));
|
|
380
|
-
subscription.on('failed', (data) => onResult({
|
|
381
|
-
is_failed: true,
|
|
382
|
-
error: data
|
|
383
|
-
}));
|
|
384
|
-
|
|
295
|
+
});
|
|
385
296
|
this.swapLogger.info(invoiceData, "subscribeToPayment(): subscribe to payment outcome, invoice: "+invoiceData.pr);
|
|
386
297
|
|
|
387
298
|
this.activeSubscriptions.add(paymentHash);
|
|
@@ -389,7 +300,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
389
300
|
}
|
|
390
301
|
|
|
391
302
|
private async sendLightningPayment(swap: ToBtcLnSwapAbs): Promise<void> {
|
|
392
|
-
const decodedPR =
|
|
303
|
+
const decodedPR = await this.lightning.parsePaymentRequest(swap.pr);
|
|
393
304
|
const expiryTimestamp: BN = swap.data.getExpiry();
|
|
394
305
|
const currentTimestamp: BN = new BN(Math.floor(Date.now()/1000));
|
|
395
306
|
|
|
@@ -400,7 +311,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
400
311
|
msg: "Not enough time to reliably pay the invoice"
|
|
401
312
|
}
|
|
402
313
|
|
|
403
|
-
const isInvoiceExpired = decodedPR.
|
|
314
|
+
const isInvoiceExpired = decodedPR.expiryEpochMillis < Date.now();
|
|
404
315
|
if (isInvoiceExpired) throw {
|
|
405
316
|
code: 90006,
|
|
406
317
|
msg: "Invoice already expired"
|
|
@@ -411,23 +322,22 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
411
322
|
const maxUsableCLTVdelta = expiryTimestamp.sub(currentTimestamp).sub(this.config.gracePeriod).div(this.config.bitcoinBlocktime.mul(this.config.safetyFactor));
|
|
412
323
|
|
|
413
324
|
await swap.setState(ToBtcLnSwapState.COMMITED);
|
|
414
|
-
await this.storageManager.saveData(decodedPR.
|
|
325
|
+
await this.storageManager.saveData(decodedPR.id, swap.data.getSequence(), swap);
|
|
415
326
|
|
|
416
327
|
//Initiate payment
|
|
417
|
-
const { current_block_height } = await lncli.getHeight({lnd: this.LND});
|
|
418
|
-
const obj: any = {
|
|
419
|
-
request: swap.pr,
|
|
420
|
-
max_fee: maxFee.toString(10),
|
|
421
|
-
max_timeout_height: new BN(current_block_height).add(maxUsableCLTVdelta).toString(10),
|
|
422
|
-
lnd: this.LND
|
|
423
|
-
};
|
|
424
328
|
this.swapLogger.info(swap, "sendLightningPayment(): paying lightning network invoice,"+
|
|
425
329
|
" cltvDelta: "+maxUsableCLTVdelta.toString(10)+
|
|
426
330
|
" maxFee: "+maxFee.toString(10)+
|
|
427
331
|
" invoice: "+swap.pr);
|
|
428
332
|
|
|
333
|
+
const blockHeight = await this.lightning.getBlockheight();
|
|
334
|
+
|
|
429
335
|
try {
|
|
430
|
-
await
|
|
336
|
+
await this.lightning.pay({
|
|
337
|
+
request: swap.pr,
|
|
338
|
+
maxFeeMtokens: maxFee.mul(new BN(1000)),
|
|
339
|
+
maxTimeoutHeight: blockHeight+maxUsableCLTVdelta.toNumber()
|
|
340
|
+
})
|
|
431
341
|
} catch (e) {
|
|
432
342
|
throw {
|
|
433
343
|
code: 90007,
|
|
@@ -447,7 +357,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
447
357
|
*/
|
|
448
358
|
private async processInitialized(swap: ToBtcLnSwapAbs) {
|
|
449
359
|
//Check if payment was already made
|
|
450
|
-
let lnPaymentStatus = await this.getPayment(swap.getHash());
|
|
360
|
+
let lnPaymentStatus = await this.lightning.getPayment(swap.getHash());
|
|
451
361
|
if(swap.metadata!=null) swap.metadata.times.payPaymentChecked = Date.now();
|
|
452
362
|
|
|
453
363
|
const paymentExists = lnPaymentStatus!=null;
|
|
@@ -467,7 +377,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
467
377
|
return;
|
|
468
378
|
}
|
|
469
379
|
|
|
470
|
-
if(lnPaymentStatus.
|
|
380
|
+
if(lnPaymentStatus.status==="pending") {
|
|
471
381
|
this.subscribeToPayment(swap);
|
|
472
382
|
return;
|
|
473
383
|
}
|
|
@@ -559,14 +469,14 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
559
469
|
* @param pr
|
|
560
470
|
* @throws {DefinedRuntimeError} will throw an error if the pr is invalid, without amount or expired
|
|
561
471
|
*/
|
|
562
|
-
private checkPaymentRequest(pr: string): {
|
|
563
|
-
parsedPR:
|
|
472
|
+
private async checkPaymentRequest(pr: string): Promise<{
|
|
473
|
+
parsedPR: ParsedPaymentRequest,
|
|
564
474
|
halfConfidence: boolean
|
|
565
|
-
} {
|
|
566
|
-
let parsedPR:
|
|
475
|
+
}> {
|
|
476
|
+
let parsedPR: ParsedPaymentRequest;
|
|
567
477
|
|
|
568
478
|
try {
|
|
569
|
-
parsedPR =
|
|
479
|
+
parsedPR = await this.lightning.parsePaymentRequest(pr);
|
|
570
480
|
} catch (e) {
|
|
571
481
|
throw {
|
|
572
482
|
code: 20021,
|
|
@@ -574,19 +484,19 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
574
484
|
};
|
|
575
485
|
}
|
|
576
486
|
|
|
577
|
-
if(parsedPR.
|
|
487
|
+
if(parsedPR.mtokens==null) throw {
|
|
578
488
|
code: 20022,
|
|
579
489
|
msg: "Invalid request body (pr - needs to have amount)"
|
|
580
490
|
};
|
|
581
491
|
|
|
582
492
|
let halfConfidence = false;
|
|
583
|
-
if(parsedPR.
|
|
493
|
+
if(parsedPR.expiryEpochMillis < Date.now()+((this.config.authorizationTimeout+(2*60))*1000) ) {
|
|
584
494
|
if(!this.config.allowShortExpiry) {
|
|
585
495
|
throw {
|
|
586
496
|
code: 20020,
|
|
587
497
|
msg: "Invalid request body (pr - expired)"
|
|
588
498
|
};
|
|
589
|
-
} else if(parsedPR.
|
|
499
|
+
} else if(parsedPR.expiryEpochMillis < Date.now()) {
|
|
590
500
|
throw {
|
|
591
501
|
code: 20020,
|
|
592
502
|
msg: "Invalid request body (pr - expired)"
|
|
@@ -623,7 +533,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
623
533
|
* @throws {DefinedRuntimeError} will throw an error if payment already exists
|
|
624
534
|
*/
|
|
625
535
|
private async checkPriorPayment(paymentHash: string, abortSignal: AbortSignal): Promise<void> {
|
|
626
|
-
const payment = await this.getPayment(paymentHash);
|
|
536
|
+
const payment = await this.lightning.getPayment(paymentHash);
|
|
627
537
|
if(payment!=null) throw {
|
|
628
538
|
code: 20010,
|
|
629
539
|
msg: "Already processed"
|
|
@@ -640,15 +550,14 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
640
550
|
* @throws {DefinedRuntimeError} will throw an error if there isn't enough liquidity
|
|
641
551
|
*/
|
|
642
552
|
private async checkLiquidity(amount: BN, abortSignal: AbortSignal, useCached: boolean = false): Promise<void> {
|
|
643
|
-
const amountBDMtokens = amount.mul(new BN(1000));
|
|
644
553
|
if(!useCached || this.lightningLiquidityCache==null || this.lightningLiquidityCache.timestamp<Date.now()-this.LIGHTNING_LIQUIDITY_CACHE_TIMEOUT) {
|
|
645
|
-
const channelBalances = await
|
|
554
|
+
const channelBalances = await this.lightning.getLightningBalance();
|
|
646
555
|
this.lightningLiquidityCache = {
|
|
647
|
-
|
|
556
|
+
liquidity: channelBalances.localBalance,
|
|
648
557
|
timestamp: Date.now()
|
|
649
558
|
}
|
|
650
559
|
}
|
|
651
|
-
if(
|
|
560
|
+
if(amount.gt(this.lightningLiquidityCache.liquidity)) {
|
|
652
561
|
throw {
|
|
653
562
|
code: 20002,
|
|
654
563
|
msg: "Not enough liquidity"
|
|
@@ -657,179 +566,6 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
657
566
|
abortSignal.throwIfAborted();
|
|
658
567
|
}
|
|
659
568
|
|
|
660
|
-
/**
|
|
661
|
-
* Computes the route paying to the specified bolt11 invoice, estimating the fee, uses bLIP-39 blinded paths
|
|
662
|
-
*
|
|
663
|
-
* @param amountSats
|
|
664
|
-
* @param maxFee
|
|
665
|
-
* @param parsedRequest
|
|
666
|
-
* @param maxTimeoutBlockheight
|
|
667
|
-
* @param metadata
|
|
668
|
-
* @param maxUsableCLTV
|
|
669
|
-
* @private
|
|
670
|
-
*/
|
|
671
|
-
private async getRoutesInvoiceBLIP39(
|
|
672
|
-
amountSats: BN,
|
|
673
|
-
maxFee: BN,
|
|
674
|
-
parsedRequest: {destination: string, cltv_delta: number, payment: string, routes: LNRoutes, blindedPaths?: BlindedPayInfo[]},
|
|
675
|
-
maxTimeoutBlockheight: BN,
|
|
676
|
-
metadata: any,
|
|
677
|
-
maxUsableCLTV: BN
|
|
678
|
-
): Promise<ProbeAndRouteResponse> {
|
|
679
|
-
metadata.routeReq = [];
|
|
680
|
-
const routeReqs = parsedRequest.blindedPaths.map(async (blindedPath) => {
|
|
681
|
-
if(new BN(blindedPath.cltv_expiry_delta+10).gt(maxUsableCLTV)) return null;
|
|
682
|
-
|
|
683
|
-
const originalMsatAmount = amountSats.mul(new BN(1000));
|
|
684
|
-
const blindedFeeTotalMsat = new BN(blindedPath.fee_base_msat)
|
|
685
|
-
.add(originalMsatAmount.mul(new BN(blindedPath.fee_proportional_millionths)).div(new BN(1000000)));
|
|
686
|
-
|
|
687
|
-
const routeReq = {
|
|
688
|
-
destination: blindedPath.introduction_node,
|
|
689
|
-
cltv_delta: Math.max(blindedPath.cltv_expiry_delta, parsedRequest.cltv_delta),
|
|
690
|
-
mtokens: originalMsatAmount.add(blindedFeeTotalMsat).toString(10),
|
|
691
|
-
max_fee_mtokens: maxFee.mul(new BN(1000)).sub(blindedFeeTotalMsat).toString(10),
|
|
692
|
-
max_timeout_height: maxTimeoutBlockheight.toString(10),
|
|
693
|
-
// total_mtokens: amountSats.mul(new BN(1000)).toString(10),
|
|
694
|
-
routes: parsedRequest.routes,
|
|
695
|
-
is_ignoring_past_failures: true,
|
|
696
|
-
lnd: null
|
|
697
|
-
};
|
|
698
|
-
metadata.routeReq.push({...routeReq});
|
|
699
|
-
routeReq.lnd = this.LND;
|
|
700
|
-
|
|
701
|
-
let resp;
|
|
702
|
-
try {
|
|
703
|
-
resp = await lncli.getRouteToDestination(routeReq);
|
|
704
|
-
} catch (e) {
|
|
705
|
-
handleLndError(e);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if(resp==null || resp.route==null) return null;
|
|
709
|
-
|
|
710
|
-
const adjustedFeeMsats = new BN(resp.route.fee_mtokens).add(blindedFeeTotalMsat);
|
|
711
|
-
resp.route.fee_mtokens = adjustedFeeMsats.toString(10);
|
|
712
|
-
resp.route.fee = adjustedFeeMsats.div(new BN(1000)).toNumber();
|
|
713
|
-
resp.route.safe_fee = adjustedFeeMsats.add(new BN(999)).div(new BN(1000)).toNumber();
|
|
714
|
-
const totalAdjustedMsats = new BN(routeReq.mtokens).add(blindedFeeTotalMsat);
|
|
715
|
-
resp.route.mtokens = totalAdjustedMsats.toString(10);
|
|
716
|
-
resp.route.tokens = totalAdjustedMsats.div(new BN(1000)).toNumber();
|
|
717
|
-
resp.route.safe_tokens = totalAdjustedMsats.add(new BN(999)).div(new BN(1000)).toNumber();
|
|
718
|
-
|
|
719
|
-
return resp.route as ProbeAndRouteResponse;
|
|
720
|
-
});
|
|
721
|
-
|
|
722
|
-
const responses = await Promise.all(routeReqs);
|
|
723
|
-
|
|
724
|
-
metadata.routeResponsesBLIP39 = responses.map(resp => {return {...resp}});
|
|
725
|
-
|
|
726
|
-
return responses.reduce((prev, current) => {
|
|
727
|
-
if(prev==null) return current;
|
|
728
|
-
if(current==null) return prev;
|
|
729
|
-
current.fee_mtokens = BN.max(new BN(prev.fee_mtokens), new BN(current.fee_mtokens)).toString(10);
|
|
730
|
-
current.fee = Math.max(prev.fee, current.fee);
|
|
731
|
-
current.safe_fee = Math.max(prev.safe_fee, current.safe_fee);
|
|
732
|
-
current.mtokens = BN.max(new BN(prev.mtokens), new BN(current.mtokens)).toString(10);
|
|
733
|
-
current.tokens = Math.max(prev.tokens, current.tokens);
|
|
734
|
-
current.safe_tokens = Math.max(prev.safe_tokens, current.safe_tokens);
|
|
735
|
-
current.timeout = Math.max(prev.timeout, current.timeout);
|
|
736
|
-
return current;
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* Computes the route paying to the specified bolt11 invoice, estimating the fee
|
|
742
|
-
*
|
|
743
|
-
* @param amountSats
|
|
744
|
-
* @param maxFee
|
|
745
|
-
* @param parsedRequest
|
|
746
|
-
* @param maxTimeoutBlockheight
|
|
747
|
-
* @param metadata
|
|
748
|
-
* @param maxUsableCLTV
|
|
749
|
-
* @private
|
|
750
|
-
*/
|
|
751
|
-
private async getRoutesInvoice(
|
|
752
|
-
amountSats: BN,
|
|
753
|
-
maxFee: BN,
|
|
754
|
-
parsedRequest: {destination: string, cltv_delta: number, payment: string, routes: LNRoutes, blindedPaths?: BlindedPayInfo[]},
|
|
755
|
-
maxTimeoutBlockheight: BN,
|
|
756
|
-
metadata: any,
|
|
757
|
-
maxUsableCLTV: BN
|
|
758
|
-
): Promise<ProbeAndRouteResponse> {
|
|
759
|
-
if(parsedRequest.blindedPaths!=null && parsedRequest.blindedPaths.length>0)
|
|
760
|
-
return await this.getRoutesInvoiceBLIP39(amountSats, maxFee, parsedRequest, maxTimeoutBlockheight, metadata, maxUsableCLTV);
|
|
761
|
-
|
|
762
|
-
const routesReq: any = {
|
|
763
|
-
destination: parsedRequest.destination,
|
|
764
|
-
cltv_delta: parsedRequest.cltv_delta,
|
|
765
|
-
mtokens: amountSats.mul(new BN(1000)).toString(10),
|
|
766
|
-
max_fee_mtokens: maxFee.mul(new BN(1000)).toString(10),
|
|
767
|
-
payment: parsedRequest.payment,
|
|
768
|
-
max_timeout_height: maxTimeoutBlockheight.toString(10),
|
|
769
|
-
total_mtokens: amountSats.mul(new BN(1000)).toString(10),
|
|
770
|
-
routes: parsedRequest.routes,
|
|
771
|
-
is_ignoring_past_failures: true
|
|
772
|
-
};
|
|
773
|
-
metadata.routeReq = {...routesReq};
|
|
774
|
-
routesReq.lnd = this.LND;
|
|
775
|
-
|
|
776
|
-
let obj;
|
|
777
|
-
try {
|
|
778
|
-
obj = await lncli.getRouteToDestination(routesReq);
|
|
779
|
-
} catch (e) {
|
|
780
|
-
handleLndError(e);
|
|
781
|
-
}
|
|
782
|
-
return obj?.route==null ? null : obj.route;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/**
|
|
786
|
-
* Sends a probe payment to the specified bolt11 invoice to check if it is reachable
|
|
787
|
-
*
|
|
788
|
-
* @param amountSats
|
|
789
|
-
* @param maxFee
|
|
790
|
-
* @param parsedRequest
|
|
791
|
-
* @param maxTimeoutBlockheight
|
|
792
|
-
* @param metadata
|
|
793
|
-
* @private
|
|
794
|
-
*/
|
|
795
|
-
private async probeInvoice(
|
|
796
|
-
amountSats: BN,
|
|
797
|
-
maxFee: BN,
|
|
798
|
-
parsedRequest: {destination: string, cltv_delta: number, payment: string, routes: LNRoutes},
|
|
799
|
-
maxTimeoutBlockheight: BN,
|
|
800
|
-
metadata: any
|
|
801
|
-
): Promise<ProbeAndRouteResponse> {
|
|
802
|
-
const probeReq: any = {
|
|
803
|
-
destination: parsedRequest.destination,
|
|
804
|
-
cltv_delta: parsedRequest.cltv_delta,
|
|
805
|
-
mtokens: amountSats.mul(new BN(1000)).toString(10),
|
|
806
|
-
max_fee_mtokens: maxFee.mul(new BN(1000)).toString(10),
|
|
807
|
-
max_timeout_height: maxTimeoutBlockheight.toString(10),
|
|
808
|
-
payment: parsedRequest.payment,
|
|
809
|
-
total_mtokens: amountSats.mul(new BN(1000)).toString(10),
|
|
810
|
-
routes: parsedRequest.routes
|
|
811
|
-
};
|
|
812
|
-
metadata.probeRequest = {...probeReq};
|
|
813
|
-
probeReq.lnd = this.LND;
|
|
814
|
-
|
|
815
|
-
let is_snowflake: boolean = false;
|
|
816
|
-
if(parsedRequest.routes!=null) {
|
|
817
|
-
for(let route of parsedRequest.routes) {
|
|
818
|
-
if(SNOWFLAKE_LIST.has(route[0].public_key) || SNOWFLAKE_LIST.has(route[1].public_key)) {
|
|
819
|
-
is_snowflake = true;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
let obj;
|
|
825
|
-
if(!is_snowflake) try {
|
|
826
|
-
obj = await lncli.probeForRoute(probeReq);
|
|
827
|
-
} catch (e) {
|
|
828
|
-
handleLndError(e);
|
|
829
|
-
}
|
|
830
|
-
return obj?.route==null ? null : obj.route;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
569
|
/**
|
|
834
570
|
* Estimates the routing fee & confidence by either probing or routing (if probing fails), the fee is also adjusted
|
|
835
571
|
* according to routing fee multiplier, and subject to minimums set in config
|
|
@@ -845,30 +581,27 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
845
581
|
*/
|
|
846
582
|
private async checkAndGetNetworkFee(amountBD: BN, maxFee: BN, expiryTimestamp: BN, currentTimestamp: BN, pr: string, metadata: any, abortSignal: AbortSignal): Promise<{
|
|
847
583
|
confidence: number,
|
|
848
|
-
networkFee: BN
|
|
849
|
-
routes: LNRoutes
|
|
584
|
+
networkFee: BN
|
|
850
585
|
}> {
|
|
851
586
|
const maxUsableCLTV: BN = expiryTimestamp.sub(currentTimestamp).sub(this.config.gracePeriod).div(this.config.bitcoinBlocktime.mul(this.config.safetyFactor));
|
|
852
587
|
|
|
853
|
-
const
|
|
588
|
+
const blockHeight = await this.lightning.getBlockheight();
|
|
854
589
|
abortSignal.throwIfAborted();
|
|
855
590
|
metadata.times.blockheightFetched = Date.now();
|
|
856
591
|
|
|
857
|
-
const maxTimeoutBlockheight = new BN(
|
|
858
|
-
const parsedRequest = lncli.parsePaymentRequest({request: pr});
|
|
859
|
-
const bolt11Parsed = bolt11.decode(pr);
|
|
860
|
-
if(bolt11Parsed.tagsObject.blinded_payinfo!=null && bolt11Parsed.tagsObject.blinded_payinfo.length>0) {
|
|
861
|
-
parsedRequest.blindedPaths = bolt11Parsed.tagsObject.blinded_payinfo;
|
|
862
|
-
}
|
|
592
|
+
const maxTimeoutBlockheight = new BN(blockHeight).add(maxUsableCLTV);
|
|
863
593
|
|
|
864
|
-
|
|
594
|
+
const req: ProbeAndRouteInit = {
|
|
595
|
+
request: pr,
|
|
596
|
+
amountMtokens: amountBD.mul(new BN(1000)),
|
|
597
|
+
maxFeeMtokens: maxFee.mul(new BN(1000)),
|
|
598
|
+
maxTimeoutHeight: maxTimeoutBlockheight.toNumber()
|
|
599
|
+
};
|
|
865
600
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
abortSignal.throwIfAborted();
|
|
871
|
-
}
|
|
601
|
+
let probeOrRouteResp: ProbeAndRouteResponse = await this.lightning.probe(req);
|
|
602
|
+
metadata.times.probeResult = Date.now();
|
|
603
|
+
metadata.probeResponse = {...probeOrRouteResp};
|
|
604
|
+
abortSignal.throwIfAborted();
|
|
872
605
|
|
|
873
606
|
if(probeOrRouteResp==null) {
|
|
874
607
|
if(!this.config.allowProbeFailedSwaps) throw {
|
|
@@ -876,7 +609,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
876
609
|
msg: "Cannot route the payment!"
|
|
877
610
|
};
|
|
878
611
|
|
|
879
|
-
const routeResp = await this.
|
|
612
|
+
const routeResp = await this.lightning.route(req);
|
|
880
613
|
metadata.times.routingResult = Date.now();
|
|
881
614
|
metadata.routeResponse = {...routeResp};
|
|
882
615
|
abortSignal.throwIfAborted();
|
|
@@ -887,20 +620,21 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
887
620
|
};
|
|
888
621
|
|
|
889
622
|
this.logger.info("checkAndGetNetworkFee(): routing result,"+
|
|
890
|
-
" destination: "+
|
|
623
|
+
" destination: "+routeResp.destination+
|
|
891
624
|
" confidence: "+routeResp.confidence+
|
|
892
|
-
"
|
|
625
|
+
" fee mtokens: "+routeResp.feeMtokens.toString(10));
|
|
893
626
|
|
|
894
627
|
probeOrRouteResp = routeResp;
|
|
895
|
-
if(parsedRequest.blindedPaths==null) probeOrRouteResp.confidence = 0;
|
|
896
628
|
} else {
|
|
897
629
|
this.logger.info("checkAndGetNetworkFee(): route probed,"+
|
|
898
|
-
" destination: "+
|
|
630
|
+
" destination: "+probeOrRouteResp.destination+
|
|
899
631
|
" confidence: "+probeOrRouteResp.confidence+
|
|
900
|
-
"
|
|
632
|
+
" fee mtokens: "+probeOrRouteResp.feeMtokens.toString(10));
|
|
901
633
|
}
|
|
902
634
|
|
|
903
|
-
|
|
635
|
+
const safeFeeTokens = probeOrRouteResp.feeMtokens.add(new BN(999)).div(new BN(1000));
|
|
636
|
+
|
|
637
|
+
let actualRoutingFee: BN = safeFeeTokens.mul(this.config.routingFeeMultiplier);
|
|
904
638
|
|
|
905
639
|
const minRoutingFee: BN = amountBD.mul(this.config.minLnRoutingFeePPM).div(new BN(1000000)).add(this.config.minLnBaseFee);
|
|
906
640
|
if(actualRoutingFee.lt(minRoutingFee)) {
|
|
@@ -916,8 +650,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
916
650
|
|
|
917
651
|
return {
|
|
918
652
|
networkFee: actualRoutingFee,
|
|
919
|
-
confidence: probeOrRouteResp.confidence
|
|
920
|
-
routes: parsedRequest.routes
|
|
653
|
+
confidence: probeOrRouteResp.confidence
|
|
921
654
|
};
|
|
922
655
|
}
|
|
923
656
|
|
|
@@ -954,14 +687,12 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
954
687
|
* @throws {DefinedRuntimeError} will throw an error if the details don't match
|
|
955
688
|
*/
|
|
956
689
|
private async checkPaymentRequestMatchesInitial(pr: string, parsedAuth: ExactInAuthorization): Promise<void> {
|
|
957
|
-
const parsedRequest = await
|
|
958
|
-
request: pr
|
|
959
|
-
});
|
|
690
|
+
const parsedRequest = await this.lightning.parsePaymentRequest(pr);
|
|
960
691
|
|
|
961
692
|
if(
|
|
962
|
-
parsedRequest.destination!==parsedAuth.destination ||
|
|
963
|
-
parsedRequest.
|
|
964
|
-
!
|
|
693
|
+
parsedRequest.destination!==parsedAuth.initialInvoice.destination ||
|
|
694
|
+
parsedRequest.cltvDelta!==parsedAuth.initialInvoice.cltvDelta ||
|
|
695
|
+
!parsedRequest.mtokens.eq(parsedAuth.amount.mul(new BN(1000)))
|
|
965
696
|
) {
|
|
966
697
|
throw {
|
|
967
698
|
code: 20102,
|
|
@@ -969,7 +700,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
969
700
|
};
|
|
970
701
|
}
|
|
971
702
|
|
|
972
|
-
if(!routesMatch(parsedRequest.routes, parsedAuth.routes)) {
|
|
703
|
+
if(!routesMatch(parsedRequest.routes, parsedAuth.initialInvoice.routes)) {
|
|
973
704
|
throw {
|
|
974
705
|
code: 20102,
|
|
975
706
|
msg: "Provided PR doesn't match initial (routes)!"
|
|
@@ -1003,7 +734,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1003
734
|
|
|
1004
735
|
//Check request params
|
|
1005
736
|
const parsedAuth = this.checkExactInAuthorization(parsedBody.reqId);
|
|
1006
|
-
const {parsedPR, halfConfidence} = this.checkPaymentRequest(parsedBody.pr);
|
|
737
|
+
const {parsedPR, halfConfidence} = await this.checkPaymentRequest(parsedBody.pr);
|
|
1007
738
|
await this.checkPaymentRequestMatchesInitial(parsedBody.pr, parsedAuth);
|
|
1008
739
|
|
|
1009
740
|
const metadata = parsedAuth.metadata;
|
|
@@ -1019,7 +750,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1019
750
|
signer.getAddress(),
|
|
1020
751
|
parsedAuth.token,
|
|
1021
752
|
parsedAuth.total,
|
|
1022
|
-
parsedPR.
|
|
753
|
+
parsedPR.id,
|
|
1023
754
|
sequence,
|
|
1024
755
|
parsedAuth.swapExpiry,
|
|
1025
756
|
new BN(0),
|
|
@@ -1040,21 +771,21 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1040
771
|
const createdSwap = new ToBtcLnSwapAbs(
|
|
1041
772
|
parsedAuth.chainIdentifier,
|
|
1042
773
|
parsedBody.pr,
|
|
774
|
+
parsedPR.mtokens,
|
|
1043
775
|
parsedAuth.swapFee,
|
|
1044
776
|
parsedAuth.swapFeeInToken,
|
|
1045
777
|
parsedAuth.quotedNetworkFee,
|
|
1046
|
-
parsedAuth.quotedNetworkFeeInToken
|
|
1047
|
-
new BN(sigData.timeout)
|
|
778
|
+
parsedAuth.quotedNetworkFeeInToken
|
|
1048
779
|
);
|
|
1049
780
|
createdSwap.data = payObject;
|
|
1050
781
|
createdSwap.metadata = metadata;
|
|
1051
782
|
|
|
1052
783
|
await PluginManager.swapCreate(createdSwap);
|
|
1053
|
-
await this.storageManager.saveData(parsedPR.
|
|
784
|
+
await this.storageManager.saveData(parsedPR.id, sequence, createdSwap);
|
|
1054
785
|
|
|
1055
786
|
this.swapLogger.info(createdSwap, "REST: /payInvoiceExactIn: created exact in swap,"+
|
|
1056
787
|
" reqId: "+parsedBody.reqId+
|
|
1057
|
-
"
|
|
788
|
+
" mtokens: "+parsedPR.mtokens.toString(10)+
|
|
1058
789
|
" invoice: "+createdSwap.pr);
|
|
1059
790
|
|
|
1060
791
|
await responseStream.writeParamsAndEnd({
|
|
@@ -1144,10 +875,10 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1144
875
|
this.checkMaxFee(parsedBody.maxFee);
|
|
1145
876
|
this.checkExpiry(parsedBody.expiryTimestamp, currentTimestamp);
|
|
1146
877
|
await this.checkVaultInitialized(chainIdentifier, parsedBody.token);
|
|
1147
|
-
const {parsedPR, halfConfidence} = this.checkPaymentRequest(parsedBody.pr);
|
|
878
|
+
const {parsedPR, halfConfidence} = await this.checkPaymentRequest(parsedBody.pr);
|
|
1148
879
|
const requestedAmount = {
|
|
1149
880
|
input: !!parsedBody.exactIn,
|
|
1150
|
-
amount: !!parsedBody.exactIn ? parsedBody.amount :
|
|
881
|
+
amount: !!parsedBody.exactIn ? parsedBody.amount : parsedPR.mtokens.add(new BN(999)).div(new BN(1000))
|
|
1151
882
|
};
|
|
1152
883
|
const fees = await this.preCheckAmounts(request, requestedAmount, useToken);
|
|
1153
884
|
metadata.times.requestChecked = Date.now();
|
|
@@ -1159,7 +890,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1159
890
|
const {pricePrefetchPromise, signDataPrefetchPromise} = this.getToBtcPrefetches(chainIdentifier, useToken, responseStream, abortController);
|
|
1160
891
|
|
|
1161
892
|
//Check if prior payment has been made
|
|
1162
|
-
await this.checkPriorPayment(parsedPR.
|
|
893
|
+
await this.checkPriorPayment(parsedPR.id, abortController.signal);
|
|
1163
894
|
metadata.times.priorPaymentChecked = Date.now();
|
|
1164
895
|
|
|
1165
896
|
//Check amounts
|
|
@@ -1192,9 +923,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1192
923
|
expiry: Date.now() + this.config.exactInExpiry,
|
|
1193
924
|
|
|
1194
925
|
amount: amountBD,
|
|
1195
|
-
|
|
1196
|
-
cltvDelta: parsedPR.tagsObject.min_final_cltv_expiry,
|
|
1197
|
-
routes: networkFeeData.routes,
|
|
926
|
+
initialInvoice: parsedPR,
|
|
1198
927
|
|
|
1199
928
|
quotedNetworkFeeInToken: networkFeeInToken,
|
|
1200
929
|
swapFeeInToken,
|
|
@@ -1214,7 +943,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1214
943
|
this.logger.info("REST: /payInvoice: created exact in swap,"+
|
|
1215
944
|
" reqId: "+reqId+
|
|
1216
945
|
" amount: "+amountBD.toString(10)+
|
|
1217
|
-
" destination: "+parsedPR.
|
|
946
|
+
" destination: "+parsedPR.destination);
|
|
1218
947
|
|
|
1219
948
|
await responseStream.writeParamsAndEnd({
|
|
1220
949
|
code: 20000,
|
|
@@ -1236,7 +965,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1236
965
|
signer.getAddress(),
|
|
1237
966
|
useToken,
|
|
1238
967
|
totalInToken,
|
|
1239
|
-
parsedPR.
|
|
968
|
+
parsedPR.id,
|
|
1240
969
|
sequence,
|
|
1241
970
|
parsedBody.expiryTimestamp,
|
|
1242
971
|
new BN(0),
|
|
@@ -1254,12 +983,24 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1254
983
|
metadata.times.swapSigned = Date.now();
|
|
1255
984
|
|
|
1256
985
|
//Create swap
|
|
1257
|
-
const createdSwap = new ToBtcLnSwapAbs(
|
|
986
|
+
const createdSwap = new ToBtcLnSwapAbs(
|
|
987
|
+
chainIdentifier,
|
|
988
|
+
parsedBody.pr,
|
|
989
|
+
parsedPR.mtokens,
|
|
990
|
+
swapFee,
|
|
991
|
+
swapFeeInToken,
|
|
992
|
+
networkFeeData.networkFee,
|
|
993
|
+
networkFeeInToken
|
|
994
|
+
);
|
|
1258
995
|
createdSwap.data = payObject;
|
|
1259
996
|
createdSwap.metadata = metadata;
|
|
997
|
+
createdSwap.prefix = sigData.prefix;
|
|
998
|
+
createdSwap.timeout = sigData.timeout;
|
|
999
|
+
createdSwap.signature = sigData.signature
|
|
1000
|
+
createdSwap.feeRate = sigData.feeRate;
|
|
1260
1001
|
|
|
1261
1002
|
await PluginManager.swapCreate(createdSwap);
|
|
1262
|
-
await this.storageManager.saveData(parsedPR.
|
|
1003
|
+
await this.storageManager.saveData(parsedPR.id, sequence, createdSwap);
|
|
1263
1004
|
|
|
1264
1005
|
this.swapLogger.info(createdSwap, "REST: /payInvoice: created swap,"+
|
|
1265
1006
|
" amount: "+amountBD.toString(10)+
|
|
@@ -1309,14 +1050,14 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1309
1050
|
|
|
1310
1051
|
const isSwapFound = data!=null;
|
|
1311
1052
|
if(isSwapFound) {
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1053
|
+
const {signer, swapContract} = this.getChain(data.chainIdentifier);
|
|
1054
|
+
|
|
1055
|
+
if(swapContract.isExpired(signer.getAddress(), data.data)) throw {
|
|
1314
1056
|
_httpStatus: 200,
|
|
1315
1057
|
code: 20010,
|
|
1316
1058
|
msg: "Payment expired"
|
|
1317
1059
|
};
|
|
1318
1060
|
|
|
1319
|
-
const {signer, swapContract} = this.getChain(data.chainIdentifier);
|
|
1320
1061
|
if(data.state===ToBtcLnSwapState.NON_PAYABLE) {
|
|
1321
1062
|
const refundSigData = await swapContract.getRefundSignature(signer, data.data, this.config.authorizationTimeout);
|
|
1322
1063
|
|
|
@@ -1342,7 +1083,7 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1342
1083
|
}
|
|
1343
1084
|
}
|
|
1344
1085
|
|
|
1345
|
-
const payment = await this.getPayment(parsedBody.paymentHash);
|
|
1086
|
+
const payment = await this.lightning.getPayment(parsedBody.paymentHash);
|
|
1346
1087
|
|
|
1347
1088
|
if(payment==null) throw {
|
|
1348
1089
|
_httpStatus: 200,
|
|
@@ -1350,54 +1091,29 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1350
1091
|
msg: "Payment not found"
|
|
1351
1092
|
};
|
|
1352
1093
|
|
|
1353
|
-
if(payment.
|
|
1094
|
+
if(payment.status==="pending") throw {
|
|
1354
1095
|
_httpStatus: 200,
|
|
1355
1096
|
code: 20008,
|
|
1356
1097
|
msg: "Payment in-flight"
|
|
1357
1098
|
};
|
|
1358
1099
|
|
|
1359
|
-
if(payment.
|
|
1100
|
+
if(payment.status==="confirmed") throw {
|
|
1360
1101
|
_httpStatus: 200,
|
|
1361
1102
|
code: 20006,
|
|
1362
1103
|
msg: "Already paid",
|
|
1363
1104
|
data: {
|
|
1364
|
-
secret: payment.
|
|
1105
|
+
secret: payment.secret
|
|
1365
1106
|
}
|
|
1366
1107
|
};
|
|
1367
1108
|
|
|
1368
|
-
if(payment.
|
|
1109
|
+
if(payment.status==="failed") throw {
|
|
1369
1110
|
_httpStatus: 200,
|
|
1370
1111
|
code: 20010,
|
|
1371
|
-
msg: "Payment expired"
|
|
1112
|
+
msg: "Payment expired",
|
|
1113
|
+
data: {
|
|
1114
|
+
reason: payment.failedReason
|
|
1115
|
+
}
|
|
1372
1116
|
};
|
|
1373
|
-
|
|
1374
|
-
// NOTE: Fixed by not removing swap data until the HTLC is either expired, claimed or refunded.
|
|
1375
|
-
// //TODO_old: Fix this by providing chain identifier as part of the invoice description, or maybe just do it the proper
|
|
1376
|
-
// // way and just keep storing the data until the HTLC expiry
|
|
1377
|
-
// if(payment.is_failed) {
|
|
1378
|
-
// //TODO_old: This might not be the best idea with EVM chains
|
|
1379
|
-
// const commitedData = await this.swapContract.getCommitedData(parsedBody.paymentHash);
|
|
1380
|
-
//
|
|
1381
|
-
// if(commitedData==null) throw {
|
|
1382
|
-
// code: 20005,
|
|
1383
|
-
// msg: "Not committed"
|
|
1384
|
-
// };
|
|
1385
|
-
//
|
|
1386
|
-
// const refundSigData = await this.swapContract.getRefundSignature(commitedData, this.config.authorizationTimeout);
|
|
1387
|
-
//
|
|
1388
|
-
// this.swapLogger.info(commitedData, "REST: /getRefundAuthorization: returning refund authorization, because invoice payment failed");
|
|
1389
|
-
//
|
|
1390
|
-
// res.status(200).json({
|
|
1391
|
-
// code: 20000,
|
|
1392
|
-
// msg: "Success",
|
|
1393
|
-
// data: {
|
|
1394
|
-
// address: this.swapContract.getAddress(),
|
|
1395
|
-
// prefix: refundSigData.prefix,
|
|
1396
|
-
// timeout: refundSigData.timeout,
|
|
1397
|
-
// signature: refundSigData.signature
|
|
1398
|
-
// }
|
|
1399
|
-
// });
|
|
1400
|
-
// }
|
|
1401
1117
|
});
|
|
1402
1118
|
|
|
1403
1119
|
restServer.post(this.path+'/getRefundAuthorization', getRefundAuthorization);
|
|
@@ -1408,6 +1124,13 @@ export class ToBtcLnAbs extends ToBtcBaseSwapHandler<ToBtcLnSwapAbs, ToBtcLnSwap
|
|
|
1408
1124
|
|
|
1409
1125
|
async init() {
|
|
1410
1126
|
await this.storageManager.loadData(ToBtcLnSwapAbs);
|
|
1127
|
+
//Check if all swaps contain a valid amount
|
|
1128
|
+
for(let swap of await this.storageManager.query([])) {
|
|
1129
|
+
if(swap.amount==null) {
|
|
1130
|
+
const parsedPR = await this.lightning.parsePaymentRequest(swap.pr);
|
|
1131
|
+
swap.amount = parsedPR.mtokens.add(new BN(999)).div(new BN(1000));
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1411
1134
|
this.subscribeToEvents();
|
|
1412
1135
|
await PluginManager.serviceInitialize(this);
|
|
1413
1136
|
}
|