lightning
Version:
Lightning Network client library
126 lines (99 loc) • 3.7 kB
JavaScript
const asyncAuto = require('async/auto');
const {returnResult} = require('asyncjs-util');
const {getAutopilot} = require('./../info');
const {isLnd} = require('./../../lnd_requests');
const externalType = 'externalscore';
const {floor} = Math;
const {isArray} = Array;
const maxScore = 100000000;
const method = 'status';
const type = 'autopilot';
const unknownScore = '2 UNKNOWN: heuristic with name externalscore not found';
const wrongLnd = '12 UNIMPLEMENTED: unknown service autopilotrpc.Autopilot';
/** Configure Autopilot settings
Either candidate_nodes or is_enabled is required
Candidate node scores range from 1 to 100,000,000
Permissions `info:read`, `offchain:write`, `onchain:write` are required
{
[candidate_nodes]: [{
public_key: <Node Public Key Hex String>
score: <Score Number>
}]
[is_enabled]: <Enable Autopilot Bool>
lnd: <Authenticated LND Object>
}
@returns via cbk or Promise
*/
module.exports = (args, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!isArray(args.candidate_nodes) && args.is_enabled === undefined) {
return cbk([400, 'ExpectedNodesOrEnabledSettingToAdjustAutopilot']);
}
if (!isLnd({method, type, lnd: args.lnd})) {
return cbk([400, 'ExpectedAutopilotEnabledLndToSetAutopilot']);
}
const nodes = args.candidate_nodes || [];
if (!!nodes.find(n => !n.public_key)) {
return cbk([400, 'ExpectedAllCandidateNodesToHavePublicKeys']);
}
if (!!nodes.find(({score}) => !floor(score))) {
return cbk([400, 'ExpectedAllCandidateNodesToHaveScore']);
}
if (!!nodes.find(({score}) => score > maxScore)) {
return cbk([400, 'ExpectedCandidateNodesToHaveValidScores']);
}
return cbk();
},
// Get existing status
getStatus: ['validate', ({}, cbk) => getAutopilot({lnd: args.lnd}, cbk)],
// Adjust candidate nodes
setNodes: ['validate', ({}, cbk) => {
// Exit early when there are no adjustments to candidate nodes
if (!args.candidate_nodes || !args.candidate_nodes.length) {
return cbk();
}
const heuristic = externalType;
const scores = {};
const nodes = args.candidate_nodes.map(node => ({
public_key: node.public_key,
score: node.score / maxScore,
}));
nodes.forEach(n => scores[n.public_key] = n.score);
return args.lnd.autopilot.setScores({
heuristic,
scores,
},
(err, res) => {
if (!!err && err.message === unknownScore) {
return cbk([400, 'ExternalScoreHeuristicNotEnabled']);
}
if (!!err) {
return cbk([503, 'FailedToSetAutopilotCandidateScores', {err}]);
}
return cbk();
});
}],
// Modify the autopilot status
setStatus: ['getStatus', 'setNodes', ({getStatus}, cbk) => {
const enable = args.is_enabled;
// Exit early when autopilot status does not need to be set
if (enable === undefined || getStatus.is_enabled === enable) {
return cbk();
}
return args.lnd.autopilot.modifyStatus({enable}, err => {
if (!!err && err.message === wrongLnd) {
return cbk([400, 'ExpectedAuthenticatedLndToSetAutopilotStatus']);
}
if (!!err) {
return cbk([503, 'UnexpectedErrorSettingAutopilotStatus', {err}]);
}
return cbk();
});
}],
},
returnResult({reject, resolve}, cbk));
});
};