balanceofsatoshis 11.5.1 → 11.7.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/CHANGELOG.md +8 -0
- package/bos +21 -0
- package/chain/fund_transaction.js +144 -41
- package/commands/api.json +16 -0
- package/package.json +3 -3
- package/wallets/clean_failed_payments.js +153 -0
- package/wallets/index.js +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## Version 11.7.0
|
|
4
|
+
|
|
5
|
+
- `fund`: Add `MAX` variable to allow spending down specified UTXOs
|
|
6
|
+
|
|
7
|
+
## Version 11.6.2
|
|
8
|
+
|
|
9
|
+
- `clean-failed-payments`: Add method to clean out failed past payments
|
|
10
|
+
|
|
3
11
|
## Version 11.5.1
|
|
4
12
|
|
|
5
13
|
- `--avoid`: Correct naming`OUT_FEE_RATE`/`IN_FEE_RATE` to `OPPOSITE_FEE_RATE`
|
package/bos
CHANGED
|
@@ -395,6 +395,26 @@ prog
|
|
|
395
395
|
});
|
|
396
396
|
})
|
|
397
397
|
|
|
398
|
+
// Clean out failed payments
|
|
399
|
+
.command('clean-failed-payments', 'Clean out past failed payment data')
|
|
400
|
+
.help('Remove old failed payment data for probes and other failed payments')
|
|
401
|
+
.option('--dryrun', 'Avoid actually deleting the failed payment record')
|
|
402
|
+
.option('--node <node_name>', 'Clean failed payments on a saved node')
|
|
403
|
+
.action((args, options, logger) => {
|
|
404
|
+
return new Promise(async (resolve, reject) => {
|
|
405
|
+
try {
|
|
406
|
+
return wallets.cleanFailedPayments({
|
|
407
|
+
logger,
|
|
408
|
+
is_dry_run: !!options.dryrun,
|
|
409
|
+
lnd: (await lnd.authenticatedLnd({logger, node: options.node})).lnd,
|
|
410
|
+
},
|
|
411
|
+
responses.returnObject({logger, reject, resolve}));
|
|
412
|
+
} catch (err) {
|
|
413
|
+
return reject(err);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
})
|
|
417
|
+
|
|
398
418
|
// Determine the outcomes of channel closings
|
|
399
419
|
.command('closed', 'Get the status of a channel closings')
|
|
400
420
|
.help('Channel closes with chain-transaction derived resolution details')
|
|
@@ -616,6 +636,7 @@ prog
|
|
|
616
636
|
.command('fund', 'Make a signed transaction spending on-chain funds')
|
|
617
637
|
.help('Use LND UTXOs to craft a signed raw transaction sending to addresses')
|
|
618
638
|
.help('Specify <address> <amount> <address> <amount> for addresses, amounts')
|
|
639
|
+
.help('Amounts support formulas, use MAX to reference selected utxos total')
|
|
619
640
|
.argument('<address_amount...>', 'Address and amount to send')
|
|
620
641
|
.option('--dryrun', 'Avoid locking up UTXOs')
|
|
621
642
|
.option('--fee-rate <fee>', 'Per vbyte fee rate for on-chain tx fee', INT)
|
|
@@ -3,9 +3,11 @@ const asyncEach = require('async/each');
|
|
|
3
3
|
const {formatTokens} = require('ln-sync');
|
|
4
4
|
const {fundPsbt} = require('ln-service');
|
|
5
5
|
const {getChainFeeRate} = require('ln-service');
|
|
6
|
+
const {getMaxFundAmount} = require('ln-sync');
|
|
6
7
|
const {getUtxos} = require('ln-service');
|
|
7
8
|
const {returnResult} = require('asyncjs-util');
|
|
8
9
|
const {signPsbt} = require('ln-service');
|
|
10
|
+
const {Transaction} = require('bitcoinjs-lib');
|
|
9
11
|
const {unlockUtxo} = require('ln-service');
|
|
10
12
|
|
|
11
13
|
const {parseAmount} = require('./../display');
|
|
@@ -15,11 +17,15 @@ const asOutpoint = utxo => `${utxo.transaction_id}:${utxo.transaction_vout}`;
|
|
|
15
17
|
const asInput = n => ({transaction_id: n.id, transaction_vout: n.vout});
|
|
16
18
|
const asUtxo = n => ({id: n.slice(0, 64), vout: Number(n.slice(65))});
|
|
17
19
|
const dustValue = 293;
|
|
20
|
+
const formattedFeeRate = n => n.toFixed(2);
|
|
21
|
+
const {fromHex} = Transaction;
|
|
22
|
+
const hasMaxAmount = amounts => !!amounts.find(n => !!n && !!/max/gim.test(n));
|
|
18
23
|
const {isArray} = Array;
|
|
19
24
|
const isOutpoint = n => !!n && /^[0-9A-F]{64}:[0-9]{1,6}$/i.test(n);
|
|
20
25
|
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
21
26
|
const minConfs = 1;
|
|
22
27
|
const sumOf = arr => arr.reduce((sum, n) => sum + n, Number());
|
|
28
|
+
const txHashAsTxId = hash => hash.reverse().toString('hex');
|
|
23
29
|
|
|
24
30
|
/** Fund and sign a transaction
|
|
25
31
|
|
|
@@ -92,8 +98,13 @@ module.exports = (args, cbk) => {
|
|
|
92
98
|
// Get the current fee rate
|
|
93
99
|
getFee: ['validate', ({}, cbk) => getChainFeeRate({lnd: args.lnd}, cbk)],
|
|
94
100
|
|
|
95
|
-
// Derive
|
|
101
|
+
// Derive a list of outputs to guide input selection
|
|
96
102
|
outputs: ['validate', ({}, cbk) => {
|
|
103
|
+
// Exit early when the amount is open ended and thus depends on inputs
|
|
104
|
+
if (hasMaxAmount(args.amounts)) {
|
|
105
|
+
return cbk();
|
|
106
|
+
}
|
|
107
|
+
|
|
97
108
|
try {
|
|
98
109
|
const outputs = args.addresses.map((address, i) => {
|
|
99
110
|
const {tokens} = parseAmount({amount: args.amounts[i]});
|
|
@@ -107,19 +118,12 @@ module.exports = (args, cbk) => {
|
|
|
107
118
|
}
|
|
108
119
|
}],
|
|
109
120
|
|
|
110
|
-
// Get UTXOs
|
|
111
|
-
getUtxos: ['validate', ({}, cbk) => {
|
|
112
|
-
// Exit early when not selecting UTXOs
|
|
113
|
-
if (!args.is_selecting_utxos) {
|
|
114
|
-
return cbk();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return getUtxos({lnd: args.lnd, min_confirmations: minConfs}, cbk);
|
|
118
|
-
}],
|
|
121
|
+
// Get UTXOs to use for input selection and final fee rate calculation
|
|
122
|
+
getUtxos: ['validate', ({}, cbk) => getUtxos({lnd: args.lnd}, cbk)],
|
|
119
123
|
|
|
120
124
|
// Select inputs to spend
|
|
121
125
|
utxos: ['getUtxos', 'outputs', ({getUtxos, outputs}, cbk) => {
|
|
122
|
-
// Exit early when UTXOs are specified already
|
|
126
|
+
// Exit early when UTXOs are all specified already
|
|
123
127
|
if (!!args.utxos.length) {
|
|
124
128
|
return cbk(null, args.utxos);
|
|
125
129
|
}
|
|
@@ -129,15 +133,16 @@ module.exports = (args, cbk) => {
|
|
|
129
133
|
return cbk(null, []);
|
|
130
134
|
}
|
|
131
135
|
|
|
136
|
+
// Only selecting confirmed utxos is supported
|
|
137
|
+
const utxos = getUtxos.utxos.filter(n => !!n.confirmation_count);
|
|
138
|
+
|
|
132
139
|
// Make sure there are some UTXOs to select
|
|
133
|
-
if (!
|
|
140
|
+
if (!utxos.length) {
|
|
134
141
|
return cbk([400, 'WalletHasZeroConfirmedUtxos']);
|
|
135
142
|
}
|
|
136
143
|
|
|
137
|
-
const amounts = outputs.map(n => n.tokens);
|
|
138
|
-
|
|
139
144
|
return args.ask({
|
|
140
|
-
choices:
|
|
145
|
+
choices: utxos.map(utxo => ({
|
|
141
146
|
name: `${asBigUnit(utxo.tokens)} ${asOutpoint(utxo)}`,
|
|
142
147
|
value: asOutpoint(utxo),
|
|
143
148
|
})),
|
|
@@ -145,14 +150,22 @@ module.exports = (args, cbk) => {
|
|
|
145
150
|
name: 'inputs',
|
|
146
151
|
type: 'checkbox',
|
|
147
152
|
validate: input => {
|
|
153
|
+
// A selection is required
|
|
148
154
|
if (!input.length) {
|
|
149
155
|
return false;
|
|
150
156
|
}
|
|
151
157
|
|
|
152
158
|
const tokens = sumOf(input.map(utxo => {
|
|
153
|
-
return
|
|
159
|
+
return utxos.find(n => asOutpoint(n) === utxo).tokens;
|
|
154
160
|
}));
|
|
155
161
|
|
|
162
|
+
// Exit early when the amount is open ended
|
|
163
|
+
if (hasMaxAmount(args.amounts)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const amounts = outputs.map(n => n.tokens);
|
|
168
|
+
|
|
156
169
|
const missingTok = asBigUnit(sumOf(amounts) - tokens);
|
|
157
170
|
|
|
158
171
|
if (tokens < sumOf(amounts)) {
|
|
@@ -165,33 +178,113 @@ module.exports = (args, cbk) => {
|
|
|
165
178
|
({inputs}) => cbk(null, inputs));
|
|
166
179
|
}],
|
|
167
180
|
|
|
181
|
+
// Calculate the maximum possible amount to fund for selected inputs
|
|
182
|
+
getMax: [
|
|
183
|
+
'getFee',
|
|
184
|
+
'getUtxos',
|
|
185
|
+
'outputs',
|
|
186
|
+
'utxos',
|
|
187
|
+
({getFee, getUtxos, outputs, utxos}, cbk) =>
|
|
188
|
+
{
|
|
189
|
+
// Exit early when the amount is not open ended
|
|
190
|
+
if (!hasMaxAmount(args.amounts)) {
|
|
191
|
+
return cbk(null, {});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Because of anchor channel requirements, don't allow open ended max
|
|
195
|
+
if (!utxos.length) {
|
|
196
|
+
return cbk([400, 'MaxAmountOnlySupportedWhenUtxosSpecified']);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const feeRate = args.fee_tokens_per_vbyte || getFee.tokens_per_vbyte;
|
|
200
|
+
|
|
201
|
+
// Find the local UTXOs in order to get the input values
|
|
202
|
+
const spend = utxos.map(outpoint => {
|
|
203
|
+
return getUtxos.utxos.find(n => asOutpoint(n) === outpoint);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Make sure that all inputs are known
|
|
207
|
+
if (spend.filter(n => !n).length) {
|
|
208
|
+
return cbk([400, 'UnknownInputSelected', {known_utxos: spend}]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return getMaxFundAmount({
|
|
212
|
+
addresses: args.addresses,
|
|
213
|
+
fee_tokens_per_vbyte: feeRate,
|
|
214
|
+
inputs: spend.map(utxo => ({
|
|
215
|
+
tokens: utxo.tokens,
|
|
216
|
+
transaction_id: utxo.transaction_id,
|
|
217
|
+
transaction_vout: utxo.transaction_vout,
|
|
218
|
+
})),
|
|
219
|
+
lnd: args.lnd,
|
|
220
|
+
},
|
|
221
|
+
cbk);
|
|
222
|
+
}],
|
|
223
|
+
|
|
224
|
+
// Parse amounts and put together the final set of outputs
|
|
225
|
+
finalOutputs: ['getMax', ({getMax}, cbk) => {
|
|
226
|
+
try {
|
|
227
|
+
const outputs = args.addresses.map((address, i) => {
|
|
228
|
+
const amount = args.amounts[i];
|
|
229
|
+
const variables = {max: getMax.max_tokens};
|
|
230
|
+
|
|
231
|
+
return {address, tokens: parseAmount({amount, variables}).tokens};
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return cbk(null, outputs);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
return cbk([400, err.message]);
|
|
237
|
+
}
|
|
238
|
+
}],
|
|
239
|
+
|
|
168
240
|
// Create a funded PSBT
|
|
169
|
-
fund: [
|
|
241
|
+
fund: [
|
|
242
|
+
'finalOutputs',
|
|
243
|
+
'getFee',
|
|
244
|
+
'utxos',
|
|
245
|
+
({finalOutputs, getFee, utxos}, cbk) =>
|
|
246
|
+
{
|
|
170
247
|
const inputs = utxos.map(asUtxo).map(asInput);
|
|
171
|
-
const
|
|
248
|
+
const feeRate = args.fee_tokens_per_vbyte || getFee.tokens_per_vbyte;
|
|
172
249
|
|
|
173
|
-
if (!!
|
|
250
|
+
if (!!finalOutputs.filter(n => n.tokens < dustValue).length) {
|
|
174
251
|
return cbk([400, 'ExpectedNonDustAmountValueForFundingAmount']);
|
|
175
252
|
}
|
|
176
253
|
|
|
177
254
|
args.logger.info({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
[output.address]: formatTokens({tokens: output.tokens}).display,
|
|
255
|
+
send_to: finalOutputs.map(({address, tokens}) => ({
|
|
256
|
+
[address]: formatTokens({tokens}).display,
|
|
181
257
|
})),
|
|
258
|
+
requested_fee_rate: feeRate,
|
|
182
259
|
});
|
|
183
260
|
|
|
184
261
|
return fundPsbt({
|
|
185
|
-
|
|
262
|
+
fee_tokens_per_vbyte: feeRate,
|
|
186
263
|
inputs: !!inputs.length ? inputs : undefined,
|
|
187
264
|
lnd: args.lnd,
|
|
188
|
-
|
|
265
|
+
outputs: finalOutputs,
|
|
189
266
|
},
|
|
190
267
|
cbk);
|
|
191
268
|
}],
|
|
192
269
|
|
|
193
|
-
//
|
|
194
|
-
|
|
270
|
+
// Sign the funded PSBT
|
|
271
|
+
sign: ['fund', ({fund}, cbk) => {
|
|
272
|
+
const [change] = fund.outputs.filter(n => !!n.is_change);
|
|
273
|
+
const total = sumOf(fund.outputs.map(n => n.tokens));
|
|
274
|
+
|
|
275
|
+
const tokens = !!change ? change.tokens : undefined;
|
|
276
|
+
|
|
277
|
+
args.logger.info({
|
|
278
|
+
change: !!tokens ? formatTokens({tokens}).display : undefined,
|
|
279
|
+
sum_of_outputs: formatTokens({tokens: total}).display,
|
|
280
|
+
spending_utxos: fund.inputs.map(asOutpoint),
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
return signPsbt({lnd: args.lnd, psbt: fund.psbt}, cbk);
|
|
284
|
+
}],
|
|
285
|
+
|
|
286
|
+
// Unlock the locked UTXOs in a dry run scenario
|
|
287
|
+
unlock: ['fund', 'sign', ({fund}, cbk) => {
|
|
195
288
|
// Exit early and keep UTXOs locked when not a dry run
|
|
196
289
|
if (!args.is_dry_run) {
|
|
197
290
|
return cbk();
|
|
@@ -209,25 +302,35 @@ module.exports = (args, cbk) => {
|
|
|
209
302
|
cbk);
|
|
210
303
|
}],
|
|
211
304
|
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
305
|
+
// Final funded transaction
|
|
306
|
+
funded: ['getUtxos', 'sign', ({getUtxos, sign}, cbk) => {
|
|
307
|
+
// Match the inputs of the tx up to the wallet outputs
|
|
308
|
+
const tx = fromHex(sign.transaction);
|
|
309
|
+
|
|
310
|
+
// Find the UTXOs that are being spent in the final transaction
|
|
311
|
+
const spending = tx.ins.map(input => {
|
|
312
|
+
const outpoint = asOutpoint({
|
|
313
|
+
transaction_id: txHashAsTxId(input.hash),
|
|
314
|
+
transaction_vout: input.index,
|
|
315
|
+
});
|
|
218
316
|
|
|
219
|
-
|
|
220
|
-
change: !!tokens ? formatTokens({tokens}).display : undefined,
|
|
221
|
-
sum_of_outputs: formatTokens({tokens: total}).display,
|
|
222
|
-
spending_utxos: fund.inputs.map(asOutpoint),
|
|
317
|
+
return getUtxos.utxos.find(n => asOutpoint(n) === outpoint);
|
|
223
318
|
});
|
|
224
319
|
|
|
225
|
-
|
|
226
|
-
|
|
320
|
+
// Make sure the spending UTXOs are known
|
|
321
|
+
if (spending.filter(n => !n).length) {
|
|
322
|
+
return cbk([503, 'ExpectedSpendingKnownUtxosForFundedTx']);
|
|
323
|
+
}
|
|
227
324
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
325
|
+
const inputsValue = sumOf(spending.map(n => n.tokens));
|
|
326
|
+
const outputsValue = sumOf(tx.outs.map(n => n.value));
|
|
327
|
+
|
|
328
|
+
const feeTotal = inputsValue - outputsValue;
|
|
329
|
+
|
|
330
|
+
return cbk(null, {
|
|
331
|
+
fee_tokens_per_vbyte: formattedFeeRate(feeTotal / tx.virtualSize()),
|
|
332
|
+
signed_transaction: sign.transaction,
|
|
333
|
+
});
|
|
231
334
|
}],
|
|
232
335
|
},
|
|
233
336
|
returnResult({reject, resolve, of: 'funded'}, cbk));
|
package/commands/api.json
CHANGED
|
@@ -287,6 +287,22 @@
|
|
|
287
287
|
{
|
|
288
288
|
"method": "getConnectedWatchtowers"
|
|
289
289
|
},
|
|
290
|
+
{
|
|
291
|
+
"arguments": [
|
|
292
|
+
{
|
|
293
|
+
"description": "Results limit",
|
|
294
|
+
"named": "limit",
|
|
295
|
+
"optional": true,
|
|
296
|
+
"type": "number"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"description": "Paging token",
|
|
300
|
+
"named": "token",
|
|
301
|
+
"optional": true
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
"method": "getFailedPayments"
|
|
305
|
+
},
|
|
290
306
|
{
|
|
291
307
|
"method": "getFeeRates"
|
|
292
308
|
},
|
package/package.json
CHANGED
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"inquirer": "8.2.0",
|
|
36
36
|
"invoices": "2.0.0",
|
|
37
37
|
"ln-accounting": "5.0.3",
|
|
38
|
-
"ln-service": "52.
|
|
39
|
-
"ln-sync": "2.0
|
|
38
|
+
"ln-service": "52.12.1",
|
|
39
|
+
"ln-sync": "2.1.0",
|
|
40
40
|
"ln-telegram": "3.3.1",
|
|
41
41
|
"moment": "2.29.1",
|
|
42
42
|
"paid-services": "3.0.0",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
|
|
81
81
|
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/fiat/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/wallets/*.js"
|
|
82
82
|
},
|
|
83
|
-
"version": "11.
|
|
83
|
+
"version": "11.7.0"
|
|
84
84
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const asyncEachSeries = require('async/eachSeries');
|
|
3
|
+
const asyncWhilst = require('async/whilst');
|
|
4
|
+
const {deletePayment} = require('ln-service');
|
|
5
|
+
const {getFailedPayments} = require('ln-service');
|
|
6
|
+
const {getWalletVersion} = require('ln-service');
|
|
7
|
+
const {returnResult} = require('asyncjs-util');
|
|
8
|
+
|
|
9
|
+
const defaultPagingLimit = 1000;
|
|
10
|
+
const {isArray} = Array;
|
|
11
|
+
const unsupported = 501;
|
|
12
|
+
|
|
13
|
+
/** Clean out failed payments from the wallet
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
is_dry_run: <Avoid Actually Deleting Payments>
|
|
17
|
+
lnd: <Authenticated LND API Object>
|
|
18
|
+
logger: <Winston Logger Object>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@returns via cbk or Promise
|
|
22
|
+
*/
|
|
23
|
+
module.exports = (args, cbk) => {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
return asyncAuto({
|
|
26
|
+
// Check arguments
|
|
27
|
+
validate: cbk => {
|
|
28
|
+
if (!args.lnd) {
|
|
29
|
+
return cbk([400, 'ExpectedAuthenticatedLndApiToCleanPayments']);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!args.logger) {
|
|
33
|
+
return cbk([400, 'ExpectedWinstonLoggerToCleanFailedPayments']);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return cbk();
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// Try and determine what wallet version this is
|
|
40
|
+
getWalletVersion: ['validate', ({}, cbk) => {
|
|
41
|
+
return getWalletVersion({lnd: args.lnd}, cbk);
|
|
42
|
+
}],
|
|
43
|
+
|
|
44
|
+
// Deny known unsupported versions
|
|
45
|
+
checkWalletVersion: ['getWalletVersion', ({getWalletVersion}, cbk) => {
|
|
46
|
+
// Exit early when not actually deleting
|
|
47
|
+
if (!!args.is_dry_run) {
|
|
48
|
+
return cbk();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
switch (getWalletVersion.version) {
|
|
52
|
+
case '0.11.0-beta':
|
|
53
|
+
case '0.11.1-beta':
|
|
54
|
+
case '0.12.0-beta':
|
|
55
|
+
case '0.12.1-beta':
|
|
56
|
+
case '0.13.0-beta':
|
|
57
|
+
case '0.13.1-beta':
|
|
58
|
+
case '0.13.2-beta':
|
|
59
|
+
case '0.13.3-beta':
|
|
60
|
+
return cbk([501, 'CleanFailedPaymentsUnsupportedOnThisLndVersion']);
|
|
61
|
+
|
|
62
|
+
default:
|
|
63
|
+
return cbk();
|
|
64
|
+
}
|
|
65
|
+
}],
|
|
66
|
+
|
|
67
|
+
// Delete failed payments
|
|
68
|
+
fetchAndDelete: ['checkWalletVersion', ({}, cbk) => {
|
|
69
|
+
const ids = [];
|
|
70
|
+
let token;
|
|
71
|
+
|
|
72
|
+
if (!!args.is_dry_run) {
|
|
73
|
+
args.logger.info({finding_failed_payments: true});
|
|
74
|
+
} else {
|
|
75
|
+
args.logger.info({deleting_failed_payments: true});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return asyncWhilst(
|
|
79
|
+
cbk => cbk(null, token !== false),
|
|
80
|
+
cbk => {
|
|
81
|
+
return getFailedPayments({
|
|
82
|
+
token,
|
|
83
|
+
limit: !token ? defaultPagingLimit : undefined,
|
|
84
|
+
lnd: args.lnd,
|
|
85
|
+
},
|
|
86
|
+
(err, res) => {
|
|
87
|
+
if (!!err) {
|
|
88
|
+
return cbk([503, 'UnexpectedErrorGettingPayFailures', {err}]);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Setting token to false will signal the end of paging
|
|
92
|
+
token = res.next || false;
|
|
93
|
+
|
|
94
|
+
// Exit early when there
|
|
95
|
+
if (!res.payments.length) {
|
|
96
|
+
const date = new Date().toISOString();
|
|
97
|
+
|
|
98
|
+
args.logger.info({searching_for_failed_payments: date});
|
|
99
|
+
|
|
100
|
+
return cbk();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
res.payments.forEach(({id}) => ids.push(id));
|
|
104
|
+
|
|
105
|
+
// Delete each payment
|
|
106
|
+
return asyncEachSeries(res.payments, ({id}, cbk) => {
|
|
107
|
+
// Exit early when doing a dry run
|
|
108
|
+
if (!!args.is_dry_run) {
|
|
109
|
+
return cbk();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return deletePayment({id, lnd: args.lnd}, err => {
|
|
113
|
+
// LND 0.13.3 and below do not support deleting payments
|
|
114
|
+
if (!!isArray(err) && err.slice().shift() === unsupported) {
|
|
115
|
+
return cbk([501, 'LndVersionDoesNotSupportFailDeletion']);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!!err) {
|
|
119
|
+
return cbk([503, 'UnexpectedErrorDeletingPayment', {err}]);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return cbk();
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
err => {
|
|
126
|
+
if (!!err) {
|
|
127
|
+
return cbk(err);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
args.logger.info({failed_payments: ids.length});
|
|
131
|
+
|
|
132
|
+
return cbk();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
err => {
|
|
137
|
+
if (!!err) {
|
|
138
|
+
return cbk(err);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Exit early when this is a test
|
|
142
|
+
if (!!args.is_dry_run) {
|
|
143
|
+
return cbk(null, {total_failed_payments_found: ids.length});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return cbk(null, {total_failed_payments_deleted: ids.length});
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
}],
|
|
150
|
+
},
|
|
151
|
+
returnResult({reject, resolve, of: 'fetchAndDelete'}, cbk));
|
|
152
|
+
});
|
|
153
|
+
};
|
package/wallets/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
const cleanFailedPayments = require('./clean_failed_payments');
|
|
1
2
|
const getReceivedChart = require('./get_received_chart');
|
|
2
3
|
const getReport = require('./get_report');
|
|
3
4
|
const unlockWallet = require('./unlock_wallet');
|
|
4
5
|
|
|
5
|
-
module.exports = {
|
|
6
|
+
module.exports = {
|
|
7
|
+
cleanFailedPayments,
|
|
8
|
+
getReceivedChart,
|
|
9
|
+
getReport,
|
|
10
|
+
unlockWallet,
|
|
11
|
+
};
|