lightning 4.10.1 → 4.10.5
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 +9 -0
- package/grpc/protos/lightning.proto +20 -0
- package/grpc/protos/signer.proto +3 -0
- package/lnd_messages/close_channel_request.js +35 -0
- package/lnd_messages/index.js +5 -0
- package/lnd_messages/open_channel_request.js +51 -0
- package/lnd_messages/pay_via_route_request.js +61 -0
- package/lnd_methods/index.js +2 -0
- package/lnd_methods/macaroon/accept_rpc_request.js +44 -0
- package/lnd_methods/macaroon/handle_rpc_request_update.js +148 -0
- package/lnd_methods/macaroon/methods.json +9 -5
- package/lnd_methods/macaroon/reject_rpc_request.js +52 -0
- package/lnd_methods/macaroon/subscribe_to_rpc_requests.js +150 -49
- package/lnd_methods/macaroon/uris_for_method.js +8 -5
- package/lnd_methods/offchain/get_failed_payments.js +190 -0
- package/lnd_methods/offchain/get_payments.js +1 -3
- package/lnd_methods/offchain/index.js +2 -0
- package/lnd_methods/offchain/probe_for_route.js +5 -0
- package/lnd_responses/rpc_invoice_as_invoice.js +4 -2
- package/lnd_responses/rpc_payment_as_payment.js +71 -21
- package/package.json +3 -3
- package/test/lnd_messages/test_close_channel_request.js +72 -0
- package/test/lnd_messages/test_open_channel_request.js +87 -0
- package/test/lnd_messages/test_pay_via_route_request.js +79 -0
- package/test/lnd_methods/macaroon/test_accept_rpc_request.js +37 -0
- package/test/lnd_methods/macaroon/test_handle_rpc_request_update.js +237 -0
- package/test/lnd_methods/macaroon/test_reject_rpc_request.js +41 -0
- package/test/lnd_methods/macaroon/test_subscribe_to_rpc_requests.js +242 -0
- package/test/lnd_methods/macaroon/test_uris_for_method.js +10 -0
- package/test/lnd_methods/offchain/test_get_failed_payments.js +160 -0
- package/test/lnd_methods/offchain/test_get_payments.js +2 -2
- package/test/lnd_methods/onchain/test_close_channel.js +0 -1
- package/test/lnd_responses/test_rpc_invoice_as_invoice.js +34 -0
- package/test/lnd_responses/test_rpc_payment_as_payment.js +43 -8
- package/test/protos/protos.json +4 -4
|
@@ -6,25 +6,29 @@ const {returnResult} = require('asyncjs-util');
|
|
|
6
6
|
|
|
7
7
|
const {emitSubscriptionError} = require('./../../grpc');
|
|
8
8
|
const {handleRemoveListener} = require('./../../grpc');
|
|
9
|
+
const handleRpcRequestUpdate = require('./handle_rpc_request_update');
|
|
9
10
|
const {isLnd} = require('./../../lnd_requests');
|
|
10
|
-
const {rpcRequestUpdateAsEvent} = require('./../../lnd_responses');
|
|
11
11
|
|
|
12
|
-
const cancelError = 'Cancelled on client';
|
|
13
|
-
const events = ['request', 'response'];
|
|
14
12
|
const makeId = () => randomBytes(32).toString('hex');
|
|
15
13
|
const method = 'RegisterRPCMiddleware';
|
|
16
|
-
const
|
|
14
|
+
const minIdLength = 5;
|
|
17
15
|
const type = 'default';
|
|
18
16
|
|
|
19
17
|
/** Subscribe to RPC requests and their responses
|
|
20
18
|
|
|
19
|
+
`accept` and `reject` methods can be used with cbk or Promise syntax
|
|
20
|
+
|
|
21
21
|
Requires `macaroon:write` permission
|
|
22
22
|
|
|
23
|
-
LND must be running with
|
|
23
|
+
LND must be running with "RPC middleware" enabled: `rpcmiddleware.enable=1`
|
|
24
24
|
|
|
25
25
|
This method is not supported in LND 0.13.3 and below
|
|
26
26
|
|
|
27
27
|
{
|
|
28
|
+
[id]: <RPC Middleware Interception Name String>
|
|
29
|
+
[is_intercepting_close_channel_requests]: <Intercept Channel Closes Bool>
|
|
30
|
+
[is_intercepting_open_channel_requests]: <Intercept Channel Opens Bool>
|
|
31
|
+
[is_intercepting_pay_via_routes_requests]: <Intercept Pay Via Route Bool>
|
|
28
32
|
lnd: <Authenticated LND API Object>
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -33,6 +37,77 @@ const type = 'default';
|
|
|
33
37
|
subscription: <RPC Request Subscription EventEmitter Object>
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
// A channel close request was intercepted: by default it will be rejected
|
|
41
|
+
@event 'close_channel_request'
|
|
42
|
+
{
|
|
43
|
+
accept: ({}, [cbk]) => {}
|
|
44
|
+
id: <Request Id Number>
|
|
45
|
+
macaroon: <Base64 Encoded Macaroon String>
|
|
46
|
+
reject: ({message: <Rejection String>}, [cbk]) => {}
|
|
47
|
+
request: {
|
|
48
|
+
[address]: <Request Sending Local Channel Funds To Address String>
|
|
49
|
+
[is_force_close]: <Is Force Close Bool>
|
|
50
|
+
[target_confirmations]: <Confirmation Target Number>
|
|
51
|
+
[tokens_per_vbyte]: <Tokens Per Virtual Byte Number>
|
|
52
|
+
transaction_id: <Transaction Id Hex String>
|
|
53
|
+
transaction_vout: <Transaction Output Index Number>
|
|
54
|
+
}
|
|
55
|
+
uri: <RPC URI String>
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// A channel open request was intercepted: by default it will be rejected
|
|
59
|
+
@event 'open_channel_request'
|
|
60
|
+
{
|
|
61
|
+
accept: ({}, [cbk]) => {}
|
|
62
|
+
id: <Request Id Number>
|
|
63
|
+
macaroon: <Base64 Encoded Macaroon String>
|
|
64
|
+
reject: ({message: <Rejection String>}, [cbk]) => {}
|
|
65
|
+
request: {
|
|
66
|
+
[chain_fee_tokens_per_vbyte]: <Chain Fee Tokens Per VByte Number>
|
|
67
|
+
[cooperative_close_address]: <Prefer Cooperative Close To Address String>
|
|
68
|
+
[give_tokens]: <Tokens to Gift To Partner Number>
|
|
69
|
+
[is_private]: <Channel is Private Bool>
|
|
70
|
+
local_tokens: <Local Tokens Number>
|
|
71
|
+
[min_confirmations]: <Spend UTXOs With Minimum Confirmations Number>
|
|
72
|
+
[min_htlc_mtokens]: <Minimum HTLC Millitokens String>
|
|
73
|
+
partner_public_key: <Public Key Hex String>
|
|
74
|
+
[partner_csv_delay]: <Peer Output CSV Delay Number>
|
|
75
|
+
}
|
|
76
|
+
uri: <RPC URI String>
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// A pay to route request was intercepted: by default it will be rejected
|
|
80
|
+
@event 'pay_via_route_request'
|
|
81
|
+
{
|
|
82
|
+
accept: ({}, [cbk]) => {}
|
|
83
|
+
id: <Request Id Number>
|
|
84
|
+
macaroon: <Base64 Encoded Macaroon String>
|
|
85
|
+
reject: ({message: <Rejection String>}, [cbk]) => {}
|
|
86
|
+
request: {
|
|
87
|
+
id: <Payment Hash Hex String>
|
|
88
|
+
route: {
|
|
89
|
+
fee: <Route Fee Tokens Number>
|
|
90
|
+
fee_mtokens: <Route Fee Millitokens String>
|
|
91
|
+
hops: [{
|
|
92
|
+
channel: <Standard Format Channel Id String>
|
|
93
|
+
channel_capacity: <Channel Capacity Tokens Number>
|
|
94
|
+
fee: <Fee Tokens Number>
|
|
95
|
+
fee_mtokens: <Fee Millitokens String>
|
|
96
|
+
forward: <Forward Tokens Number>
|
|
97
|
+
forward_mtokens: <Forward Millitokens String>
|
|
98
|
+
public_key: <Forward Edge Public Key Hex String>
|
|
99
|
+
[timeout]: <Timeout Block Height Number>
|
|
100
|
+
}]
|
|
101
|
+
mtokens: <Total Fee-Inclusive Millitokens String>
|
|
102
|
+
[payment]: <Payment Identifier Hex String>
|
|
103
|
+
[timeout]: <Timeout Block Height Number>
|
|
104
|
+
tokens: <Total Fee-Inclusive Tokens Number>
|
|
105
|
+
[total_mtokens]: <Total Payment Millitokens String>
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
uri: <RPC URI String>
|
|
109
|
+
}
|
|
110
|
+
|
|
36
111
|
@event 'request'
|
|
37
112
|
{
|
|
38
113
|
id: <Request Id Number>
|
|
@@ -47,65 +122,91 @@ const type = 'default';
|
|
|
47
122
|
[uri]: <RPC URI String>
|
|
48
123
|
}
|
|
49
124
|
*/
|
|
50
|
-
module.exports = (
|
|
125
|
+
module.exports = (args, cbk) => {
|
|
51
126
|
return new Promise((resolve, reject) => {
|
|
52
127
|
return asyncAuto({
|
|
53
128
|
// Check arguments
|
|
54
129
|
validate: cbk => {
|
|
55
|
-
if (
|
|
130
|
+
if (!!args.id && args.id.length < minIdLength) {
|
|
131
|
+
return cbk([400, 'ExpectedLongerIdLengthToSpecifyMiddlewareName']);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!isLnd({method, type, lnd: args.lnd})) {
|
|
56
135
|
return cbk([400, 'ExpectedLndToSubscribeToRpcRequests']);
|
|
57
136
|
}
|
|
58
137
|
|
|
59
138
|
return cbk();
|
|
60
139
|
},
|
|
61
140
|
|
|
62
|
-
//
|
|
63
|
-
|
|
141
|
+
// Initialize subscription
|
|
142
|
+
init: ['validate', ({}, cbk) => {
|
|
64
143
|
const emitter = new EventEmitter();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
144
|
+
const isInterceptCloses = args.is_intercepting_close_channel_requests;
|
|
145
|
+
const isInterceptOpens = args.is_intercepting_open_channel_requests;
|
|
146
|
+
const isInterceptRoute = args.is_intercepting_pay_via_routes_requests;
|
|
147
|
+
const subscription = args.lnd[type][method]({});
|
|
148
|
+
|
|
149
|
+
const events = [
|
|
150
|
+
'close_channel_request',
|
|
151
|
+
'open_channel_request',
|
|
152
|
+
'pay_via_route_request',
|
|
153
|
+
'request',
|
|
154
|
+
'response',
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const errored = emitSubscriptionError({emitter, subscription});
|
|
158
|
+
|
|
159
|
+
// Terminate subscription when all listeners are removed
|
|
160
|
+
handleRemoveListener({subscription, emitter, events});
|
|
161
|
+
|
|
162
|
+
subscription.on('end', () => emitter.emit('end'));
|
|
163
|
+
subscription.on('error', err => errored(err));
|
|
164
|
+
subscription.on('status', n => emitter.emit('status', n));
|
|
165
|
+
|
|
166
|
+
subscription.on('data', update => {
|
|
167
|
+
try {
|
|
168
|
+
const {accept, data, event} = handleRpcRequestUpdate({
|
|
169
|
+
subscription,
|
|
170
|
+
update,
|
|
171
|
+
is_intercepting_close_channel_requests: isInterceptCloses,
|
|
172
|
+
is_intercepting_open_channel_requests: isInterceptOpens,
|
|
173
|
+
is_intercepting_pay_via_routes_requests: isInterceptRoute,
|
|
174
|
+
lnd: args.lnd,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Notify listeners of the RPC update
|
|
178
|
+
emitter.emit(event, data);
|
|
179
|
+
|
|
180
|
+
// Exit early when accepting/rejecting is left to the caller
|
|
181
|
+
if (!accept) {
|
|
182
|
+
return;
|
|
75
183
|
}
|
|
76
184
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
185
|
+
// Accept traffic by default
|
|
186
|
+
return accept({}, err => !!err ? errored(err) : undefined);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
return errored([503, err.message]);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
83
191
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const details = rpcRequestUpdateAsEvent(update);
|
|
87
|
-
|
|
88
|
-
emitter.emit(details.event, {
|
|
89
|
-
id: details.id,
|
|
90
|
-
macaroon: details.macaroon,
|
|
91
|
-
uri: details.uri,
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
return subscription.write({
|
|
95
|
-
feedback: {replace_response: false},
|
|
96
|
-
request_id: details.id,
|
|
97
|
-
});
|
|
98
|
-
} catch (err) {
|
|
99
|
-
return errored([503, err.message]);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
192
|
+
return cbk(null, {emitter, subscription});
|
|
193
|
+
}],
|
|
102
194
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
195
|
+
// Start the interception of RPC requests
|
|
196
|
+
intercept: ['init', ({init}, cbk) => {
|
|
197
|
+
return init.subscription.write({
|
|
198
|
+
register: {
|
|
199
|
+
middleware_name: args.id || makeId(),
|
|
200
|
+
read_only_mode: true,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
err => {
|
|
204
|
+
if (!!err) {
|
|
205
|
+
return cbk([503, 'UnexpectedErrInterceptingRpcRequests', {err}]);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return cbk(null, {subscription: init.emitter});
|
|
209
|
+
});
|
|
109
210
|
}],
|
|
110
211
|
},
|
|
111
212
|
returnResult({reject, resolve, of: 'intercept'}, cbk));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
const methods = require('./methods');
|
|
2
2
|
|
|
3
|
+
const {packageTypes} = require('./../../grpc');
|
|
3
4
|
const {serviceTypes} = require('./../../grpc');
|
|
4
5
|
|
|
5
6
|
const flatten = arr => arr.flat(Infinity);
|
|
6
7
|
const type = n => [methods[n]].concat((methods[n].depends_on || []).map(type));
|
|
7
|
-
const uriForMethod = (service, method) =>
|
|
8
|
+
const uriForMethod = (rpc, service, method) => `/${rpc}.${service}/${method}`;
|
|
8
9
|
|
|
9
10
|
/** Given a method, derive URI macaroon permission strings required
|
|
10
11
|
|
|
@@ -31,19 +32,21 @@ module.exports = ({method}) => {
|
|
|
31
32
|
|
|
32
33
|
const requiredMethods = flatten(type(method)).filter(({type}) => type);
|
|
33
34
|
|
|
34
|
-
const uris = requiredMethods.map(({method, methods, type}) => {
|
|
35
|
+
const uris = flatten(requiredMethods.map(({method, methods, type}) => {
|
|
35
36
|
const service = serviceTypes[type];
|
|
36
37
|
|
|
37
38
|
if (!service) {
|
|
38
39
|
throw new Error('ExpectedKnownMethodServiceToDeriveMacaroonUris');
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
const rpc = packageTypes[service];
|
|
43
|
+
|
|
41
44
|
return (methods || [method]).map(methodName => {
|
|
42
|
-
return uriForMethod(service, methodName);
|
|
45
|
+
return uriForMethod(rpc, service, methodName);
|
|
43
46
|
});
|
|
44
|
-
});
|
|
47
|
+
}));
|
|
45
48
|
|
|
46
49
|
uris.sort();
|
|
47
50
|
|
|
48
|
-
return {uris
|
|
51
|
+
return {uris};
|
|
49
52
|
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const {isLnd} = require('./../../lnd_requests');
|
|
5
|
+
const {rpcPaymentAsPayment} = require('./../../lnd_responses');
|
|
6
|
+
const {sortBy} = require('./../../arrays');
|
|
7
|
+
|
|
8
|
+
const defaultLimit = 250;
|
|
9
|
+
const {isArray} = Array;
|
|
10
|
+
const isFailed = payment => !!payment && payment.status === 'FAILED';
|
|
11
|
+
const lastPageFirstIndexOffset = 1;
|
|
12
|
+
const method = 'listPayments';
|
|
13
|
+
const {parse} = JSON;
|
|
14
|
+
const {stringify} = JSON;
|
|
15
|
+
const type = 'default';
|
|
16
|
+
|
|
17
|
+
/** Get failed payments made through channels.
|
|
18
|
+
|
|
19
|
+
Requires `offchain:read` permission
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
[limit]: <Page Result Limit Number>
|
|
23
|
+
lnd: <Authenticated LND API Object>
|
|
24
|
+
[token]: <Opaque Paging Token String>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@returns via cbk or Promise
|
|
28
|
+
{
|
|
29
|
+
payments: [{
|
|
30
|
+
attempts: [{
|
|
31
|
+
[failure]: {
|
|
32
|
+
code: <Error Type Code Number>
|
|
33
|
+
[details]: {
|
|
34
|
+
[channel]: <Standard Format Channel Id String>
|
|
35
|
+
[height]: <Error Associated Block Height Number>
|
|
36
|
+
[index]: <Failed Hop Index Number>
|
|
37
|
+
[mtokens]: <Error Millitokens String>
|
|
38
|
+
[policy]: {
|
|
39
|
+
base_fee_mtokens: <Base Fee Millitokens String>
|
|
40
|
+
cltv_delta: <Locktime Delta Number>
|
|
41
|
+
fee_rate: <Fees Charged in Millitokens Per Million Number>
|
|
42
|
+
[is_disabled]: <Channel is Disabled Bool>
|
|
43
|
+
max_htlc_mtokens: <Maximum HLTC Millitokens Value String>
|
|
44
|
+
min_htlc_mtokens: <Minimum HTLC Millitokens Value String>
|
|
45
|
+
updated_at: <Updated At ISO 8601 Date String>
|
|
46
|
+
}
|
|
47
|
+
[timeout_height]: <Error CLTV Timeout Height Number>
|
|
48
|
+
[update]: {
|
|
49
|
+
chain: <Chain Id Hex String>
|
|
50
|
+
channel_flags: <Channel Flags Number>
|
|
51
|
+
extra_opaque_data: <Extra Opaque Data Hex String>
|
|
52
|
+
message_flags: <Message Flags Number>
|
|
53
|
+
signature: <Channel Update Signature Hex String>
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
message: <Error Message String>
|
|
57
|
+
}
|
|
58
|
+
[index]: <Payment Add Index Number>
|
|
59
|
+
[confirmed_at]: <Payment Confirmed At ISO 8601 Date String>
|
|
60
|
+
is_confirmed: <Payment Attempt Succeeded Bool>
|
|
61
|
+
is_failed: <Payment Attempt Failed Bool>
|
|
62
|
+
is_pending: <Payment Attempt is Waiting For Resolution Bool>
|
|
63
|
+
route: {
|
|
64
|
+
fee: <Route Fee Tokens Number>
|
|
65
|
+
fee_mtokens: <Route Fee Millitokens String>
|
|
66
|
+
hops: [{
|
|
67
|
+
channel: <Standard Format Channel Id String>
|
|
68
|
+
channel_capacity: <Channel Capacity Tokens Number>
|
|
69
|
+
fee: <Fee Number>
|
|
70
|
+
fee_mtokens: <Fee Millitokens String>
|
|
71
|
+
forward: <Forward Tokens Number>
|
|
72
|
+
forward_mtokens: <Forward Millitokens String>
|
|
73
|
+
[public_key]: <Forward Edge Public Key Hex String>
|
|
74
|
+
[timeout]: <Timeout Block Height Number>
|
|
75
|
+
}]
|
|
76
|
+
mtokens: <Total Fee-Inclusive Millitokens String>
|
|
77
|
+
[payment]: <Payment Identifier Hex String>
|
|
78
|
+
timeout: <Timeout Block Height Number>
|
|
79
|
+
tokens: <Total Fee-Inclusive Tokens Number>
|
|
80
|
+
[total_mtokens]: <Total Millitokens String>
|
|
81
|
+
}
|
|
82
|
+
}]
|
|
83
|
+
created_at: <Payment at ISO-8601 Date String>
|
|
84
|
+
[destination]: <Destination Node Public Key Hex String>
|
|
85
|
+
id: <Payment Preimage Hash String>
|
|
86
|
+
[index]: <Payment Add Index Number>
|
|
87
|
+
is_confirmed: <Payment is Confirmed Bool>
|
|
88
|
+
is_outgoing: <Transaction Is Outgoing Bool>
|
|
89
|
+
mtokens: <Millitokens Attempted to Pay to Destination String>
|
|
90
|
+
[request]: <BOLT 11 Payment Request String>
|
|
91
|
+
safe_tokens: <Payment Tokens Attempted to Pay Rounded Up Number>
|
|
92
|
+
tokens: <Rounded Down Tokens Attempted to Pay to Destination Number>
|
|
93
|
+
}]
|
|
94
|
+
[next]: <Next Opaque Paging Token String>
|
|
95
|
+
}
|
|
96
|
+
*/
|
|
97
|
+
module.exports = ({limit, lnd, token}, cbk) => {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
return asyncAuto({
|
|
100
|
+
// Check arguments
|
|
101
|
+
validate: cbk => {
|
|
102
|
+
if (!!limit && !!token) {
|
|
103
|
+
return cbk([400, 'ExpectedNoLimitWhenPagingPayFailuresWithToken']);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!isLnd({lnd, method, type})) {
|
|
107
|
+
return cbk([400, 'ExpectedLndForGetFailedPaymentsRequest']);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return cbk();
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// Get all payments
|
|
114
|
+
listPayments: ['validate', ({}, cbk) => {
|
|
115
|
+
let offset;
|
|
116
|
+
let resultsLimit = limit || defaultLimit;
|
|
117
|
+
|
|
118
|
+
if (!!token) {
|
|
119
|
+
try {
|
|
120
|
+
const pagingToken = parse(token);
|
|
121
|
+
|
|
122
|
+
offset = pagingToken.offset;
|
|
123
|
+
resultsLimit = pagingToken.limit;
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return cbk([400, 'ExpectedValidPagingTokenForGetFailed', {err}]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return lnd[type][method]({
|
|
130
|
+
include_incomplete: true,
|
|
131
|
+
index_offset: offset || Number(),
|
|
132
|
+
max_payments: resultsLimit,
|
|
133
|
+
reversed: true,
|
|
134
|
+
},
|
|
135
|
+
(err, res) => {
|
|
136
|
+
if (!!err) {
|
|
137
|
+
return cbk([503, 'UnexpectedGetFailedPaymentsError', {err}]);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!res || !isArray(res.payments)) {
|
|
141
|
+
return cbk([503, 'ExpectedFailedPaymentsInListPaymentsResponse']);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (typeof res.last_index_offset !== 'string') {
|
|
145
|
+
return cbk([503, 'ExpectedLastIndexOffsetWhenRequestingFailed']);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const lastOffset = Number(res.last_index_offset);
|
|
149
|
+
const offset = Number(res.first_index_offset);
|
|
150
|
+
|
|
151
|
+
const token = stringify({offset, limit: resultsLimit});
|
|
152
|
+
|
|
153
|
+
return cbk(null, {
|
|
154
|
+
payments: res.payments.filter(isFailed),
|
|
155
|
+
token: offset === lastPageFirstIndexOffset ? undefined : token,
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}],
|
|
159
|
+
|
|
160
|
+
// Check and map payments
|
|
161
|
+
foundPayments: ['listPayments', ({listPayments}, cbk) => {
|
|
162
|
+
try {
|
|
163
|
+
const payments = listPayments.payments.map(rpcPaymentAsPayment);
|
|
164
|
+
|
|
165
|
+
return cbk(null, payments);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
return cbk([503, err.message]);
|
|
168
|
+
}
|
|
169
|
+
}],
|
|
170
|
+
|
|
171
|
+
// Final found failed payments
|
|
172
|
+
payments: [
|
|
173
|
+
'foundPayments',
|
|
174
|
+
'listPayments',
|
|
175
|
+
({foundPayments, listPayments}, cbk) =>
|
|
176
|
+
{
|
|
177
|
+
const payments = sortBy({
|
|
178
|
+
array: foundPayments,
|
|
179
|
+
attribute: 'created_at',
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return cbk(null, {
|
|
183
|
+
next: listPayments.token || undefined,
|
|
184
|
+
payments: payments.sorted.reverse(),
|
|
185
|
+
});
|
|
186
|
+
}],
|
|
187
|
+
},
|
|
188
|
+
returnResult({reject, resolve, of: 'payments'}, cbk));
|
|
189
|
+
});
|
|
190
|
+
};
|
|
@@ -131,8 +131,6 @@ module.exports = ({limit, lnd, token}, cbk) => {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
const start = Date.now();
|
|
135
|
-
|
|
136
134
|
return lnd[type][method]({
|
|
137
135
|
include_incomplete: false,
|
|
138
136
|
index_offset: offset || Number(),
|
|
@@ -187,8 +185,8 @@ module.exports = ({limit, lnd, token}, cbk) => {
|
|
|
187
185
|
});
|
|
188
186
|
|
|
189
187
|
return cbk(null, {
|
|
190
|
-
payments: payments.sorted.reverse(),
|
|
191
188
|
next: !!foundPayments.length ? listPayments.token : undefined,
|
|
189
|
+
payments: payments.sorted.reverse(),
|
|
192
190
|
});
|
|
193
191
|
}],
|
|
194
192
|
},
|
|
@@ -14,6 +14,7 @@ const getChannelBalance = require('./get_channel_balance');
|
|
|
14
14
|
const getChannels = require('./get_channels');
|
|
15
15
|
const getClosedChannels = require('./get_closed_channels');
|
|
16
16
|
const getConnectedWatchtowers = require('./get_connected_watchtowers');
|
|
17
|
+
const getFailedPayments = require('./get_failed_payments');
|
|
17
18
|
const getFeeRates = require('./get_fee_rates');
|
|
18
19
|
const getForwardingConfidence = require('./get_forwarding_confidence');
|
|
19
20
|
const getForwardingReputations = require('./get_forwarding_reputations');
|
|
@@ -65,6 +66,7 @@ module.exports = {
|
|
|
65
66
|
getChannels,
|
|
66
67
|
getClosedChannels,
|
|
67
68
|
getConnectedWatchtowers,
|
|
69
|
+
getFailedPayments,
|
|
68
70
|
getFeeRates,
|
|
69
71
|
getForwardingConfidence,
|
|
70
72
|
getForwardingReputations,
|
|
@@ -10,6 +10,11 @@ const isHex = n => !(n.length % 2) && /^[0-9A-F]*$/i.test(n);
|
|
|
10
10
|
|
|
11
11
|
/** Probe to find a successful route
|
|
12
12
|
|
|
13
|
+
When probing to a payment request, make sure to specify the fields encoded in
|
|
14
|
+
the payment request such as `cltv_delta`.
|
|
15
|
+
|
|
16
|
+
If `total_mtokens` are specified, a `payment` nonce is required.
|
|
17
|
+
|
|
13
18
|
Requires `offchain:write` permission
|
|
14
19
|
|
|
15
20
|
{
|
|
@@ -3,6 +3,7 @@ const {featureFlagDetails} = require('bolt09');
|
|
|
3
3
|
const htlcAsPayment = require('./htlc_as_payment');
|
|
4
4
|
|
|
5
5
|
const dateFrom = epoch => new Date(1e3 * epoch).toISOString();
|
|
6
|
+
const emptyHash = Buffer.alloc(32).toString('hex');
|
|
6
7
|
const {isArray} = Array;
|
|
7
8
|
const {keys} = Object;
|
|
8
9
|
const msPerSec = 1e3;
|
|
@@ -178,12 +179,13 @@ module.exports = args => {
|
|
|
178
179
|
const createdAtEpochTime = Number(args.creation_date);
|
|
179
180
|
const descHash = args.description_hash;
|
|
180
181
|
const expiresInMs = Number(args.expiry) * msPerSec;
|
|
181
|
-
const isAmpPush = !args.
|
|
182
|
+
const isAmpPush = !args.payment_request && !!args.is_amp;
|
|
182
183
|
const mtok = (BigInt(args.value) * mtokensPerToken).toString();
|
|
183
184
|
const payment = args.payment_addr.toString('hex');
|
|
184
185
|
const settleDate = args.settle_date;
|
|
185
186
|
|
|
186
187
|
const createdAtMs = createdAtEpochTime * msPerSec;
|
|
188
|
+
const hasPaymentId = !!payment && payment !== emptyHash;
|
|
187
189
|
|
|
188
190
|
return {
|
|
189
191
|
chain_address: args.fallback_addr || undefined,
|
|
@@ -208,7 +210,7 @@ module.exports = args => {
|
|
|
208
210
|
is_private: args.private,
|
|
209
211
|
is_push: isAmpPush || args.is_keysend || undefined,
|
|
210
212
|
mtokens: args.value_msat === '0' ? mtok : args.value_msat,
|
|
211
|
-
payment: payment
|
|
213
|
+
payment: hasPaymentId ? payment : undefined,
|
|
212
214
|
payments: args.htlcs.map(htlcAsPayment),
|
|
213
215
|
received: Number(args.amt_paid_sat),
|
|
214
216
|
received_mtokens: args.amt_paid_msat,
|