add more lnd path selection options

This commit is contained in:
Alex Bosworth 2022-06-09 19:04:07 -07:00
parent e94a9ad14a
commit 22317a992f
No known key found for this signature in database
GPG key ID: E80D2F3F311FD87E
10 changed files with 179 additions and 24 deletions

View file

@ -1,5 +1,10 @@
# Versions
## 12.12.0
- Add `BOS_DEFAULT_LND_PATH` to set the default path to LND data directory
- Adjust support for Umbrel LND path detection
## 12.11.2
- `telegram`: Add inbound and outbound fee rates to /liquidity message

View file

@ -204,6 +204,10 @@ Default LND directories:
It will check first for a mainnet macaroon, then a testnet macaroon.
The LND directory can be overriden with an environment variable:
`BOS_DEFAULT_LND_PATH=/path/to/lnd/data/dir`
### Saved Nodes
If you have another node and it is already using `balanceofsatoshis`, you can

View file

@ -21,6 +21,7 @@ const certPath = ['tls.cert'];
platform: <Platform Function> () => <Platform Name String>
userInfo: <User Info Function> () => {username: <User Name String>}
}
[path]: <Lnd Data Directory Path String>
}
@returns via cbk or Promise
@ -28,7 +29,7 @@ const certPath = ['tls.cert'];
[cert]: <Cert File Base64 Encoded String>
}
*/
module.exports = ({fs, node, os}, cbk) => {
module.exports = ({fs, node, os, path}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
@ -50,9 +51,9 @@ module.exports = ({fs, node, os}, cbk) => {
return cbk();
}
const {path} = lndDirectory({os});
const dir = path || lndDirectory({os}).path;
return fs.getFile(join(...[path].concat(certPath)), (err, cert) => {
return fs.getFile(join(...[dir].concat(certPath)), (err, cert) => {
if (!!err) {
return cbk([503, 'UnexpectedErrorGettingCertFileData', {err}]);
}

View file

@ -23,6 +23,7 @@ const macName = 'admin.macaroon';
platform: <Platform Function> () => <Platform Name String>
userInfo: <User Info Function> () => {username: <User Name String>}
}
[path]: <LND Data Directory Path String>
}
@returns via cbk or Promise
@ -30,7 +31,7 @@ const macName = 'admin.macaroon';
[macaroon]: <Base64 Encoded Macaroon String>
}
*/
module.exports = ({fs, node, os}, cbk) => {
module.exports = ({fs, node, os, path}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
@ -55,7 +56,7 @@ module.exports = ({fs, node, os}, cbk) => {
const [chains, nets] = defaults;
let defaultMacaroon;
const {path} = lndDirectory({os});
const dir = path || lndDirectory({os}).path;
const all = chains.map(chain => {
return nets.map(network => ({chain, network}));
@ -65,7 +66,7 @@ module.exports = ({fs, node, os}, cbk) => {
return asyncDetectSeries(flatten(all), ({chain, network}, cbk) => {
const macPath = [].concat(macDirs).concat([chain, network, macName]);
return fs.getFile(join(...[path].concat(macPath)), (_, macaroon) => {
return fs.getFile(join(...[dir].concat(macPath)), (_, macaroon) => {
defaultMacaroon = macaroon;
return cbk(null, !!defaultMacaroon);

71
lnd/get_path.js Normal file
View file

@ -0,0 +1,71 @@
const {join} = require('path');
const asyncAuto = require('async/auto');
const asyncDetect = require('async/detect');
const {returnResult} = require('asyncjs-util');
const certPath = ['tls.cert'];
const umbrelUser = 'umbrel';
const umbrelV0Path = '/home/umbrel/umbrel/lnd';
const umbrelV1Path = '/home/umbrel/umbrel/app-data/lightning/data/lnd';
/** Look for the LND directory path
{
fs: {
getFile: <Get File Function>
}
os: {
userInfo: <Get User Info Function>
}
}
@returns via cbk or Promise
{
[path]: <Found LND Directory Path String>
}
*/
module.exports = ({fs, os}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!fs) {
return cbk([400, 'ExpectedFileSystemMethodsToGetPath']);
}
if (!os) {
return cbk([400, 'ExpectedOperatingSystemMethodsToGetPath']);
}
return cbk();
},
// Paths to look for
paths: ['validate', ({}, cbk) => {
// Exit early when the user is not Umbrel
if (os.userInfo().username !== umbrelUser) {
return cbk(null, []);
}
return cbk(null, [umbrelV0Path, umbrelV1Path]);
}],
// Look through the paths to find a cert file
findCert: ['paths', ({paths}, cbk) => {
return asyncDetect(paths, (path, cbk) => {
return fs.getFile(join(...[path].concat(certPath)), (err, cert) => {
return cbk(null, !err && !!cert);
});
},
cbk);
}],
// Final path result
path: ['findCert', ({findCert}, cbk) => {
return cbk(null, {path: findCert || undefined});
}],
},
returnResult({reject, resolve, of: 'path'}, cbk));
});
};

View file

@ -25,6 +25,7 @@ const scheme = 'rpc://';
platform: <Platform Function> () => <Platform Name String>
userInfo: <User Info Function> () => {username: <User Name String>}
}
[path]: <Lnd Data Directory Path String>
}
@returns via cbk or Promise
@ -32,7 +33,7 @@ const scheme = 'rpc://';
[socket]: <RPC Socket String>
}
*/
module.exports = ({fs, node, os}, cbk) => {
module.exports = ({fs, node, os, path}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
@ -55,9 +56,9 @@ module.exports = ({fs, node, os}, cbk) => {
return cbk();
}
const {path} = lndDirectory({os});
const dir = path || lndDirectory({os}).path;
return fs.getFile(join(...[path].concat(confPath)), (err, conf) => {
return fs.getFile(join(...[dir].concat(confPath)), (err, conf) => {
// Don't report errors, the conf file is either there or not
return cbk(null, conf);
});

View file

@ -17,12 +17,14 @@ const {decryptCiphertext} = require('./../encryption');
const {derAsPem} = require('./../encryption');
const getCert = require('./get_cert');
const getMacaroon = require('./get_macaroon');
const getPath = require('./get_path');
const {getSavedCredentials} = require('./../nodes');
const getSocket = require('./get_socket');
const {noSpendPerms} = require('./constants');
const {permissionEntities} = require('./constants');
const config = 'config.json';
const defaultLndDirPath = process.env.BOS_DEFAULT_LND_PATH;
const defaultNodeName = process.env.BOS_DEFAULT_SAVED_NODE;
const fs = {getFile: readFile};
const home = '.bos';
@ -89,14 +91,29 @@ module.exports = (args, cbk) => {
});
},
// Look for a special path
getPath: ['forNode', ({forNode}, cbk) => {
// Exit early when a specific node is used
if (!!forNode) {
return cbk(null, {});
}
// Exit early when there is a default LND path
if (!!defaultLndDirPath) {
return cbk(null, {path: defaultLndDirPath});
}
return getPath({fs, os}, cbk);
}],
// Get the default cert
getCert: ['forNode', ({forNode}, cbk) => {
return getCert({fs, os, node: forNode}, cbk);
getCert: ['forNode', 'getPath', ({forNode, getPath}, cbk) => {
return getCert({fs, os, node: forNode, path: getPath.path}, cbk);
}],
// Get the default macaroon
getMacaroon: ['forNode', ({forNode}, cbk) => {
return getMacaroon({fs, os, node: forNode}, cbk);
getMacaroon: ['forNode', 'getPath', ({forNode, getPath}, cbk) => {
return getMacaroon({fs, os, node: forNode, path: getPath.path}, cbk);
}],
// Get the node credentials, if applicable
@ -109,8 +126,8 @@ module.exports = (args, cbk) => {
}],
// Get the socket out of the ini file
getSocket: ['forNode', ({forNode}, cbk) => {
return getSocket({fs, os, node: forNode}, cbk);
getSocket: ['forNode', 'getPath', ({forNode, getPath}, cbk) => {
return getSocket({fs, os, node: forNode, path: getPath.path}, cbk);
}],
// Node credentials

16
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "balanceofsatoshis",
"version": "12.11.2",
"version": "12.12.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "balanceofsatoshis",
"version": "12.11.2",
"version": "12.12.0",
"license": "MIT",
"dependencies": {
"@alexbosworth/caporal": "1.4.4",
@ -4460,9 +4460,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/electron-to-chromium": {
"version": "1.4.150",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz",
"integrity": "sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA==",
"version": "1.4.151",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.151.tgz",
"integrity": "sha512-XaG2LpZi9fdiWYOqJh0dJy4SlVywCvpgYXhzOlZTp4JqSKqxn5URqOjbm9OMYB3aInA2GuHQiem1QUOc1yT0Pw==",
"dev": true
},
"node_modules/elliptic": {
@ -13840,9 +13840,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"electron-to-chromium": {
"version": "1.4.150",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.150.tgz",
"integrity": "sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA==",
"version": "1.4.151",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.151.tgz",
"integrity": "sha512-XaG2LpZi9fdiWYOqJh0dJy4SlVywCvpgYXhzOlZTp4JqSKqxn5URqOjbm9OMYB3aInA2GuHQiem1QUOc1yT0Pw==",
"dev": true
},
"elliptic": {

View file

@ -84,5 +84,5 @@
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
},
"version": "12.11.2"
"version": "12.12.0"
}

55
test/lnd/test_get_path.js Normal file
View file

@ -0,0 +1,55 @@
const {test} = require('@alexbosworth/tap');
const getPath = require('./../../lnd/get_path');
const os = {userInfo: () => ({username: 'umbrel'})};
const tests = [
{
args: {},
description: 'File system methods are required',
error: [400, 'ExpectedFileSystemMethodsToGetPath'],
},
{
args: {fs: {getFile: () => {}}},
description: 'OS methods are required',
error: [400, 'ExpectedOperatingSystemMethodsToGetPath'],
},
{
args: {os, fs: {getFile: ({}, cbk) => cbk('err')}},
description: 'A filesystem error results in no path',
expected: {path: undefined},
},
{
args: {os, fs: {getFile: ({}, cbk) => cbk()}},
description: 'An absent file results in no path',
expected: {path: undefined},
},
{
args: {
fs: {getFile: ({}, cbk) => cbk()},
os: {userInfo: () => ({username: 'username'})},
},
description: 'A normal user returns no path',
expected: {path: undefined},
},
{
args: {os, fs: {getFile: ({}, cbk) => cbk(null, Buffer.alloc(1))}},
description: 'A path is returned',
expected: {path: '/home/umbrel/umbrel/lnd'},
},
];
tests.forEach(({args, description, error, expected}) => {
return test(description, async ({end, rejects, strictSame}) => {
if (!!error) {
await rejects(getPath(args), error, 'Got expected error');
} else {
const res = await getPath(args);
strictSame(res, expected, 'Got expected result');
}
return end();
});
});