lightning
Version:
Lightning Network client library
219 lines (197 loc) • 7.82 kB
JavaScript
const asyncAuto = require('async/auto');
const {returnResult} = require('asyncjs-util');
const {isLnd} = require('./../../lnd_requests');
const {rpcPaymentAsPayment} = require('./../../lnd_responses');
const {sortBy} = require('./../../arrays');
const asStart = n => !!n ? Math.floor(new Date(n).getTime() / 1e3) : undefined;
const asEnd = n => !!n ? Math.ceil(new Date(n).getTime() / 1e3) : undefined;
const defaultLimit = 250;
const {isArray} = Array;
const isFailed = payment => !!payment && payment.status === 'FAILED';
const isPending = payment => !!payment && payment.status === 'IN_FLIGHT';
const lastPageFirstIndexOffset = 1;
const method = 'listPayments';
const {parse} = JSON;
const {stringify} = JSON;
const type = 'default';
/** Fetch payments
{
[created_after]: <Creation Date After or Equal to ISO 8601 Date String>
[created_before]: <Creation Date Before or Equal to ISO 8601 Date String>
[is_failed]: <Fetch Failed Payments Bool>
[is_pending]: <Fetch Pending Payments Bool>
[limit]: <Page Result Limit Number>
lnd: <Authenticated LND API Object>
[token]: <Opaque Paging Token String>
}
@returns via cbk or Promise
{
payments: [{
attempts: [{
[failure]: {
code: <Error Type Code Number>
[details]: {
[channel]: <Standard Format Channel Id String>
[height]: <Error Associated Block Height Number>
[index]: <Failed Hop Index Number>
[mtokens]: <Error Millitokens String>
[policy]: {
base_fee_mtokens: <Base Fee Millitokens String>
cltv_delta: <Locktime Delta Number>
fee_rate: <Fees Charged in Millitokens Per Million Number>
[is_disabled]: <Channel is Disabled Bool>
max_htlc_mtokens: <Maximum HLTC Millitokens Value String>
min_htlc_mtokens: <Minimum HTLC Millitokens Value String>
updated_at: <Updated At ISO 8601 Date String>
}
[timeout_height]: <Error CLTV Timeout Height Number>
[update]: {
chain: <Chain Id Hex String>
channel_flags: <Channel Flags Number>
extra_opaque_data: <Extra Opaque Data Hex String>
message_flags: <Message Flags Number>
signature: <Channel Update Signature Hex String>
}
}
message: <Error Message String>
}
[index]: <Payment Add Index Number>
[confirmed_at]: <Payment Attempt Succeeded At ISO 8601 Date String>
created_at: <Attempt Was Started At ISO 8601 Date String>
[failed_at]: <Payment Attempt Failed At ISO 8601 Date String>
is_confirmed: <Payment Attempt Succeeded Bool>
is_failed: <Payment Attempt Failed Bool>
is_pending: <Payment Attempt is Waiting For Resolution Bool>
route: {
fee: <Route Fee Tokens Number>
fee_mtokens: <Route Fee Millitokens String>
hops: [{
channel: <Standard Format Channel Id String>
channel_capacity: <Channel Capacity Tokens Number>
fee: <Fee Number>
fee_mtokens: <Fee Millitokens String>
forward: <Forward Tokens Number>
forward_mtokens: <Forward Millitokens String>
[public_key]: <Forward Edge Public Key Hex String>
[timeout]: <Timeout Block Height Number>
}]
mtokens: <Total Fee-Inclusive Millitokens String>
[payment]: <Payment Identifier Hex String>
timeout: <Timeout Block Height Number>
tokens: <Total Fee-Inclusive Tokens Number>
[total_mtokens]: <Total Millitokens String>
}
}]
confirmed_at: <Payment Confirmed At ISO 8601 Date String>
created_at: <Payment at ISO-8601 Date String>
destination: <Destination Node Public Key Hex String>
fee: <Paid Routing Fee Rounded Down Tokens Number>
fee_mtokens: <Paid Routing Fee in Millitokens String>
hops: [<First Route Hop Public Key Hex String>]
id: <Payment Preimage Hash String>
[index]: <Payment Add Index Number>
is_confirmed: <Payment is Confirmed Bool>
is_outgoing: <Transaction Is Outgoing Bool>
mtokens: <Millitokens Sent to Destination String>
[request]: <BOLT 11 Payment Request String>
safe_fee: <Payment Forwarding Fee Rounded Up Tokens Number>
safe_tokens: <Payment Tokens Rounded Up Number>
secret: <Payment Preimage Hex String>
tokens: <Rounded Down Tokens Sent to Destination Number>
}]
[next]: <Next Opaque Paging Token String>
}
*/
module.exports = (args, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!!args.is_failed && !!args.is_pending) {
return cbk([400, 'EitherFailedOrPendingPaymentsIsSupportedNotBoth']);
}
return cbk();
},
// Get all payments
listPayments: ['validate', ({}, cbk) => {
let after = asStart(args.created_after);
let before = asEnd(args.created_before);
let limit = args.limit || defaultLimit;
let offset;
if (!!args.token) {
try {
const pagingToken = parse(args.token);
after = pagingToken.after;
before = pagingToken.before;
limit = pagingToken.limit;
offset = pagingToken.offset;
} catch (err) {
return cbk([400, 'ExpectedValidPagingTokenForPaymentReq', {err}]);
}
}
return args.lnd[type][method]({
creation_date_start: after,
creation_date_end: before,
include_incomplete: !!args.is_failed || !!args.is_pending,
index_offset: offset || Number(),
max_payments: limit,
reversed: true,
},
(err, res) => {
if (!!err) {
return cbk([503, 'UnexpectedGetPaymentsError', {err}]);
}
if (!res || !isArray(res.payments)) {
return cbk([503, 'ExpectedPaymentsInListPaymentsResponse']);
}
if (typeof res.last_index_offset !== 'string') {
return cbk([503, 'ExpectedLastIndexOffsetWhenRequestingPayments']);
}
const offset = Number(res.first_index_offset);
const token = stringify({before, after, limit, offset});
return cbk(null, {
payments: res.payments,
token: offset <= lastPageFirstIndexOffset ? undefined : token,
});
});
}],
// Check and map payments
foundPayments: ['listPayments', ({listPayments}, cbk) => {
try {
const payments = listPayments.payments
.filter(payment => {
// Exit early when looking for failed payments only
if (!!args.is_failed) {
return isFailed(payment);
}
// Exit early when looking for pending payments only
if (!!args.is_pending) {
return isPending(payment);
}
return true;
})
.map(rpcPaymentAsPayment);
return cbk(null, payments);
} catch (err) {
return cbk([503, err.message]);
}
}],
// Final found payments
payments: [
'foundPayments',
'listPayments',
({foundPayments, listPayments}, cbk) =>
{
const payments = sortBy({
array: foundPayments,
attribute: 'created_at',
});
return cbk(null, {
next: listPayments.token || undefined,
payments: payments.sorted.reverse(),
});
}],
},
returnResult({reject, resolve, of: 'payments'}, cbk));
});
};