lightning
Version:
Lightning Network client library
166 lines (144 loc) • 5.79 kB
JavaScript
const asyncAuto = require('async/auto');
const asyncTimeout = require('async/timeout');
const {returnResult} = require('asyncjs-util');
const subscribeToProbeForRoute = require('./subscribe_to_probe_for_route');
const defaultProbeTimeoutMs = 1000 * 60;
const {isArray} = Array;
const isHex = n => !(n.length % 2) && /^[0-9A-F]*$/i.test(n);
/** Probe to find a successful route
When probing to a payment request, make sure to specify the fields encoded in
the payment request such as `cltv_delta`.
If `total_mtokens` are specified, a `payment` nonce is required.
Requires `offchain:write` permission
Preferred `confidence` is not supported on LND 0.14.5 and below
{
[cltv_delta]: <Final CLTV Delta Number>
[confidence]: <Preferred Route Confidence Number Out of One Million Number>
destination: <Destination Public Key Hex String>
[features]: [{
bit: <Feature Bit Number>
}]
[ignore]: [{
[channel]: <Channel Id String>
from_public_key: <Public Key Hex String>
[to_public_key]: <To Public Key Hex String>
}]
[incoming_peer]: <Incoming Peer Public Key Hex String>
[is_ignoring_past_failures]: <Adjust Probe For Past Routing Failures Bool>
lnd: <Authenticated LND API Object>
[max_fee]: <Maximum Fee Tokens Number>
[max_fee_mtokens]: <Maximum Fee Millitokens to Pay String>
[max_timeout_height]: <Maximum Height of Payment Timeout Number>
[messages]: [{
type: <Message To Final Destination Type Number String>
value: <Message To Final Destination Raw Value Hex Encoded String>
}]
[mtokens]: <Millitokens to Pay String>
[outgoing_channel]: <Outgoing Channel Id String>
[path_timeout_ms]: <Time to Spend On A Path Milliseconds Number>
[payment]: <Payment Identifier Hex String>
[probe_timeout_ms]: <Probe Timeout Milliseconds Number>
[routes]: [[{
[base_fee_mtokens]: <Base Routing Fee In Millitokens String>
[channel_capacity]: <Channel Capacity Tokens Number>
[channel]: <Standard Format Channel Id String>
[cltv_delta]: <CLTV Blocks Delta Number>
[fee_rate]: <Fee Rate In Millitokens Per Million Number>
public_key: <Forward Edge Public Key Hex String>
}]]
tokens: <Tokens Number>
[total_mtokens]: <Total Millitokens Across Paths String>
}
@returns via cbk or Promise
{
[route]: {
[confidence]: <Route Confidence Score Out Of One Million Number>
fee: <Route Fee Tokens Rounded Down 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>
}]
[messages]: [{
type: <Message Type Number String>
value: <Message Raw Value Hex Encoded String>
}]
mtokens: <Total Fee-Inclusive Millitokens String>
[payment]: <Payment Identifier Hex String>
safe_fee: <Payment Forwarding Fee Rounded Up Tokens Number>
safe_tokens: <Payment Tokens Rounded Up Number>
timeout: <Timeout Block Height Number>
tokens: <Total Fee-Inclusive Tokens Rounded Down Number>
[total_mtokens]: <Total Millitokens String>
}
}
*/
module.exports = (args, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!args.destination || !isHex(args.destination)) {
return cbk([400, 'ExpectedDestinationKeyHexStringForRouteProbe']);
}
if (!!args.ignore && !isArray(args.ignore)) {
return cbk([400, 'ExpectedIgnoreAsArrayWhenProbingForRoute'])
}
if (!args.lnd || !args.lnd.router) {
return cbk([400, 'ExpectedAuthenticatedLndToProbeForRoute']);
}
if (!args.mtokens && !args.tokens) {
return cbk([400, 'ExpectedTokensValueToProbeForRoute']);
}
return cbk();
},
// Start probe and return a successful route if found
probe: ['validate', ({}, cbk) => {
const result = {};
let isFinished = false;
let timeout;
const sub = subscribeToProbeForRoute({
cltv_delta: args.cltv_delta,
confidence: args.confidence,
destination: args.destination,
features: args.features,
ignore: args.ignore,
incoming_peer: args.incoming_peer,
is_ignoring_past_failures: args.is_ignoring_past_failures,
lnd: args.lnd,
max_fee: args.max_fee,
max_fee_mtokens: args.max_fee_mtokens,
max_timeout_height: args.max_timeout_height,
messages: args.messages,
mtokens: args.mtokens,
outgoing_channel: args.outgoing_channel,
path_timeout_ms: args.path_timeout_ms,
payment: args.payment,
routes: args.routes,
tokens: args.tokens,
total_mtokens: args.total_mtokens,
});
const finished = (err, res) => {
sub.removeAllListeners();
clearTimeout(timeout);
return cbk(err, res);
};
timeout = setTimeout(
() => finished([503, 'ProbeForRouteTimedOut']),
args.probe_timeout_ms || defaultProbeTimeoutMs
);
sub.once('end', () => finished(null, {}));
sub.once('error', err => finished(err));
sub.once('probe_success', ({route}) => finished(null, {route}));
return;
}],
},
returnResult({reject, resolve, of: 'probe'}, cbk));
});
};