ln-sync
Version:
LN metadata persistence methods
141 lines (115 loc) • 3.75 kB
JavaScript
const asyncAuto = require('async/auto');
const asyncMap = require('async/map');
const asyncRetry = require('async/retry');
const {getChannels} = require('lightning/lnd_methods');
const {getNode} = require('lightning/lnd_methods');
const {returnResult} = require('asyncjs-util');
const {isArray} = Array;
const isPublicKey = n => /^[0-9A-F]{66}$/i.test(n);
const uniq = arr => Array.from(new Set(arr));
/** Find a public key given a query
{
[channels]: [{
partner_public_key: <Partner Public Key Hex String>
}]
lnd: <Authenticated LND API Object>
[query]: <Query String>
}
@returns via cbk or Promise
{
[public_key]: <Public Key Hex String>
}
*/
module.exports = ({channels, lnd, query}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!lnd) {
return cbk([400, 'ExpectedAuthenticatedLndToFindPublicKey']);
}
return cbk();
},
// Get channels if necessary
getChannels: ['validate', ({}, cbk) => {
if (!!channels) {
return cbk(null, {channels});
}
return getChannels({lnd}, cbk);
}],
// Check arguments
publicKey: ['validate', ({}, cbk) => {
// Exit early when there is no public key
if (!query) {
return cbk();
}
// Exit early when query is an alias search
if (!isPublicKey(query)) {
return cbk();
}
return cbk(null, query);
}],
// Get nodes
getNodes: [
'getChannels',
'publicKey',
({getChannels, publicKey}, cbk) =>
{
// Exit early when there is no query or the query is a public key
if (!query || !!publicKey) {
return cbk();
}
if (!query.toLowerCase) {
return cbk([400, 'InvalidEmptyQuerySpecifiedForMatchSearchQuery']);
}
const keys = uniq(getChannels.channels.map(n => n.partner_public_key));
const q = query.toLowerCase();
return asyncMap(keys, (key, cbk) => {
return asyncRetry({}, cbk => {
return getNode({
lnd,
is_omitting_channels: true,
public_key: key,
},
(err, node) => {
// Supress errors on not found node
if (isArray(err) && err.slice().shift() === 404) {
return cbk();
}
if (!!err) {
return cbk(err);
}
const alias = node.alias || String();
const isAliasMatch = alias.toLowerCase().includes(q);
const isPublicKeyMatch = key.startsWith(q);
// Exit early when the node doesn't match the query
if (!isAliasMatch && !isPublicKeyMatch) {
return cbk();
}
return cbk(null, {alias, public_key: key});
});
},
cbk);
},
cbk);
}],
// Found public key
key: ['getNodes', 'publicKey', ({getNodes, publicKey}, cbk) => {
// Exit early when this is not a nodes search
if (!getNodes) {
return cbk(null, {public_key: publicKey});
}
const matching = getNodes.filter(n => !!n);
if (!matching.length) {
return cbk([400, 'FailedToFindPeerAliasMatch', {not_found: query}]);
}
const [match, secondMatch] = matching;
if (!!secondMatch) {
return cbk([400, 'AmbiguousAliasSpecified', {matching}]);
}
return cbk(null, {public_key: match.public_key});
}],
},
returnResult({reject, resolve, of: 'key'}, cbk));
});
};