balanceofsatoshis
Version:
Lightning balance CLI
140 lines (114 loc) • 4.1 kB
JavaScript
const {createHash} = require('crypto');
const asyncAuto = require('async/auto');
const {returnResult} = require('asyncjs-util');
const {isArray} = Array;
const isNumber = n => !isNaN(n);
const lowestSendableValue = 1000;
const {max} = Math;
const minMaxSendable = 1000;
const minMinSendable = 1;
const mtokensAsTokens = n => Math.floor(n / 1000);
const {parse} = JSON;
const payRequestTag = 'payRequest';
const sha256 = n => createHash('sha256').update(n).digest().toString('hex');
const sslProtocol = 'https:';
const textPlain = 'text/plain';
const utf8AsBuffer = utf8 => Buffer.from(utf8, 'utf8');
/** Get payment terms
{
request: <Request Function>
url: <URL String>
}
@returns via cbk or Promise
{
description: <Payment Description String>
hash: <Expected Description Hash Hex String>
max: <Maximum Tokens Number>
min: <Minimum Tokens Number>
url: <Callback URL String>
}
*/
module.exports = ({request, url}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!request) {
return cbk([400, 'ExpectedRequestFunctionToGetPayTerms']);
}
if (!url) {
return cbk([400, 'ExpectedUrlToGetPayTerms']);
}
return cbk();
},
// Get payment terms
getTerms: ['validate', ({}, cbk) => {
return request({url, json: true}, (err, r, json) => {
if (!!err) {
return cbk([503, 'FailureGettingLnUrlDataFromUrl', {err}]);
}
if (!json) {
return cbk([503, 'ExpectedJsonObjectReturnedInLnurlResponse']);
}
if (!json.callback) {
return cbk([503, 'ExpectedCallbackInLnurlResponseJson']);
}
try {
new URL(json.callback);
} catch (err) {
return cbk([503, 'ExpectedValidCallbackUrlInLnurlResponseJson']);
}
if ((new URL(json.callback)).protocol !== sslProtocol) {
return cbk([400, 'LnurlsThatSpecifyNonSslUrlsAreUnsupported']);
}
if (!isNumber(json.maxSendable)) {
return cbk([503, 'ExpectedNumericValueForMaxSendable']);
}
if (!json.maxSendable) {
return cbk([503, 'ExpectedNonZeroMaxSendableInLnurlResponse']);
}
if (json.maxSendable < minMaxSendable) {
return cbk([400, 'MaxSendableValueIsLowerThanSupportedValue']);
}
if (!json.metadata) {
return cbk([503, 'ExpectedLnUrlMetadataInLnurlResponse']);
}
try {
parse(json.metadata);
} catch (err) {
return cbk([503, 'ExpectedValidMetadataInLnurlResponse']);
}
if (!isArray(parse(json.metadata))) {
return cbk([503, 'ExpectedMetadataArrayInLnurlResponse', json]);
}
const [, description] = parse(json.metadata)
.filter(isArray)
.find(([entry, text]) => entry === textPlain && !!text);
if (!description) {
return cbk([503, 'ExpectedTextPlainEntryInLnurlResponse']);
}
if (!isNumber(json.minSendable)) {
return cbk([503, 'ExpectedNumericValueForMinSendable']);
}
if (json.minSendable < minMinSendable) {
return cbk([503, 'ExpectedHigherMinSendableValueInLnurlResponse']);
}
if (json.minSendable > json.maxSendable) {
return cbk([503, 'ExpectedMaxSendableMoreThanMinSendable']);
}
if (json.tag !== payRequestTag) {
return cbk([503, 'ExpectedPaymentRequestTagInLnurlResponse']);
}
return cbk(null, {
description,
hash: sha256(utf8AsBuffer(json.metadata)),
max: mtokensAsTokens(json.maxSendable),
min: mtokensAsTokens(max(lowestSendableValue, json.minSendable)),
url: json.callback,
});
});
}],
},
returnResult({reject, resolve, of: 'getTerms'}, cbk));
});
};